Skip to content

Connect a ChatGPT Custom GPT

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.

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.

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.

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.
  1. Add optional Conversation starters: “List my forms”, “Check my domain status”, “How much usage do I have left?”
  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:

ErrorCauseFix
”Could not parse the schema”Wrong file versionUse openapi-3.0.yaml, not openapi.yaml
”Server URL is missing”servers: block removedRe-fetch the canonical file
”Unsupported security scheme”Modified copy with flows.implicitThe official spec uses bearerAuth only

In the same Actions panel, click Authentication:

FieldValue
TypeOAuth
Client IDThe client_id from step 1
Client SecretLeave empty (PKCE only)
Authorization URLhttps://app.caramelme.com/oauth/authorize
Token URLhttps://app.caramelme.com/oauth/token
Scopemeta:read forms:read forms:write
Token Exchange MethodDefault (POST request)

Click Save.

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.

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.

  • 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.”
  • Bulk operations — ChatGPT calls one action at a time. For 1,000 contacts, write a script. See 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.

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.

CodeMeaningGPT response
401 unauthorizedToken expiredChatGPT re-authorizes automatically in most cases
403 tier_requiredBusiness tier too lowExplain upgrade path: caramelme.com/billing
403 scope_requiredOAuth scope missing”Disconnect and reconnect with the additional scope”
429 rate_limitedPer-token cap hitAsk user to wait a minute
404 form_not_foundForm 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.