# Connect a ChatGPT Custom GPT

> Import the Caramel OpenAPI spec into a ChatGPT Custom GPT so users can manage forms, contacts, and domains through natural language.

Wire the Caramel API into a ChatGPT Custom GPT via OpenAI Actions. Once set up, users can list forms, submit leads, check domain verification, and review usage in plain conversation — ChatGPT handles parameter extraction and Caramel handles the operations.

Estimated time: 15 minutes. You need a ChatGPT Plus account.

## Why REST, not MCP

ChatGPT Actions speak OpenAPI 3.0, not JSON-RPC 2.0. Your Custom GPT calls the Caramel REST gateway at `gateway.caramelme.com` rather than the MCP endpoint. The underlying data and auth are the same — only the call shape differs.

## Step 1 — Get an OAuth client

ChatGPT's redirect URI follows this pattern:

```
https://chat.openai.com/aip/g-<your_gpt_id>/oauth/callback
```

You won't know `<your_gpt_id>` until the GPT is created, so request a wildcard client upfront.

Email `aymen@reactmotion.com`:
- App name
- Redirect URI: `https://chat.openai.com/aip/*/oauth/callback`
- Requested scopes: `meta:read forms:read forms:write`

You receive a `client_id`. There is no client secret — PKCE is mandatory.

## Step 2 — Create the Custom GPT

In ChatGPT:

1. Click your profile → **My GPTs → Create a GPT**.
2. Open the **Configure** tab.
3. Set **Name**: `Caramel Assistant`
4. Set **Description**: `Manages Caramel marketing forms, contacts, and audiences.`
5. Paste the following into **Instructions**:

```
You are Caramel Assistant. You help users manage their Caramel marketing
communications, forms, and customer audiences.

Available actions (auto-discovered from the OpenAPI spec):
- caramelV1BusinessList — list the user's businesses
- caramelV1FormList — list forms for a business
- caramelV1FormSubmit — submit data to a form
- caramelV1DomainStatus — check sender-domain verification
- caramelV1TemplateList — list deployed templates
- caramelV1MetaUsage — get this month's usage

Conventions:
- Start every session by calling caramelV1BusinessList to learn the user's business IDs.
- Refer to businesses by name, not by raw ID.
- Never invent business IDs or form IDs — discover them via the list tools first.
- When a call returns 429, ask the user to wait a minute and try again.
- When a call returns 403 tier_required, explain which tier is needed and suggest caramelme.com/billing.

Privacy:
- Never reveal OAuth tokens or internal request IDs.
- Never store contact data beyond the duration of the action call.

Tone: concise, friendly, no emoji.
```

6. Add optional **Conversation starters**: "List my forms", "Check my domain status", "How much usage do I have left?"

## Step 3 — Import the OpenAPI spec

1. Scroll to **Actions → Create new action**.
2. In the **Schema** field, paste the contents of `docs/public-api/openapi-3.0.yaml` from this repository.

> **Important** ChatGPT requires OpenAPI **3.0**, not 3.1. The repository ships both: `openapi.yaml` (3.1, for tooling) and `openapi-3.0.yaml` (3.0, for ChatGPT). Use the 3.0 file. The 3.0 file is auto-generated from 3.1 — do not hand-edit it.

> **Coming soon** A hosted spec URL at `gateway.caramelme.com/v1/openapi.json` is on the roadmap. Until then, paste-import from the repository file is the path. Alternatively, host the 3.0 file at any public HTTPS URL you control (for example, a GitHub raw URL) and use ChatGPT's **Import from URL** field.

After parsing, you should see these operations in the actions list:

- `caramelV1BusinessList`
- `caramelV1FormList`
- `caramelV1FormSubmit`
- `caramelV1DomainStatus`
- `caramelV1TemplateList`
- `caramelV1MetaUsage`

**Spec import errors:**

| Error | Cause | Fix |
|---|---|---|
| "Could not parse the schema" | Wrong file version | Use `openapi-3.0.yaml`, not `openapi.yaml` |
| "Server URL is missing" | `servers:` block removed | Re-fetch the canonical file |
| "Unsupported security scheme" | Modified copy with `flows.implicit` | The official spec uses `bearerAuth` only |

## Step 4 — Configure OAuth

In the same Actions panel, click **Authentication**:

| Field | Value |
|---|---|
| Type | OAuth |
| Client ID | The `client_id` from step 1 |
| Client Secret | Leave empty (PKCE only) |
| Authorization URL | `https://app.caramelme.com/oauth/authorize` |
| Token URL | `https://app.caramelme.com/oauth/token` |
| Scope | `meta:read forms:read forms:write` |
| Token Exchange Method | Default (POST request) |

Click **Save**.

## Step 5 — Test

In the **Preview** pane:

> List my Caramel businesses.

ChatGPT prompts you to authorize. Click **Authorize**, complete the magic-link flow. ChatGPT calls `caramelV1BusinessList` and responds with your business name and tier.

Try:

> What forms are configured?

ChatGPT calls `caramelV1FormList` with the business ID it discovered in the previous step.

> Is mail.brewmaster.cafe verified?

ChatGPT calls `caramelV1DomainStatus`.

## Step 6 — Publish (optional)

Click **Save → Publish**:

- **Only me** — private testing.
- **Anyone with a link** — shareable.
- **Everyone** — public in the GPT Store. Email `aymen@reactmotion.com` to coordinate if you use the "Caramel" name in the GPT title.

When published publicly, each user goes through their own OAuth flow against their own Caramel business. Your `client_id` is shared across all users; each user gets a distinct token.

## What ChatGPT handles well

- **Listing and summarizing** — "Show me my forms and their submission counts."
- **Filling defaults** — "Submit this lead" (ChatGPT discovers the right form via list, prompts for missing fields).
- **Multi-step orchestration** — "Check my domain, then list my templates."

## Limitations

- **Bulk operations** — ChatGPT calls one action at a time. For 1,000 contacts, write a script. See [Sync contacts](sync-contacts).
- **Scheduled tasks** — Custom GPTs have no background task support. "Submit this every Monday" isn't possible through a GPT.
- **File uploads** — ChatGPT can't pass binary data to actions. For forms with file fields, call the REST gateway directly.

## Token lifetime

ChatGPT manages OAuth refresh internally. Tokens expire after 1 hour and refresh automatically. If a user doesn't use the GPT for 30+ days, the refresh token expires and they're prompted to reauthorize.

## Error reference

| Code | Meaning | GPT response |
|---|---|---|
| `401 unauthorized` | Token expired | ChatGPT re-authorizes automatically in most cases |
| `403 tier_required` | Business tier too low | Explain upgrade path: `caramelme.com/billing` |
| `403 scope_required` | OAuth scope missing | "Disconnect and reconnect with the additional scope" |
| `429 rate_limited` | Per-token cap hit | Ask user to wait a minute |
| `404 form_not_found` | Form inactive or ID wrong | "Make sure the form is active in the Caramel dashboard" |

The instructions block from step 2 already covers these — ChatGPT paraphrases them to the user.

## Next steps

- [Build with Lovable](build-with-lovable) — same Caramel backend, MCP instead of REST.
- [Sync contacts](sync-contacts) — bulk operations that Custom GPTs can't handle.
- [Authentication](../get-started/authentication) — full OAuth flow details.
- [Tool reference](../reference/tools) — every tool, parameter, and error code.
