Skip to content

Quickstart

A one-page cheat sheet for integrations that are already familiar with OAuth 2.0 + PKCE. For a step-by-step walkthrough, see Your first call.

  • A Caramel account at caramelme.com.
  • An OAuth client ID. Clients using a localhost redirect URI self-register — see step 1b. Other redirect URIs require manual allow-listing (email aymen@reactmotion.com).
Terminal window
curl https://app.caramelme.com/.well-known/oauth-authorization-server
  • authorization_endpoint → redirect the user here
  • token_endpoint → exchange code and refresh here
  • registration_endpoint → self-register a client here (loopback + claude.ai URIs only)
Terminal window
curl -X POST https://api.caramelme.com/functions/v1/mcp-oauth/register \
-H "Content-Type: application/json" \
-d '{"client_name":"my-app","redirect_uris":["http://localhost:3000/callback"],"grant_types":["authorization_code","refresh_token"],"response_types":["code"],"token_endpoint_auth_method":"none"}'

Save the returned client_id.

GET https://api.caramelme.com/functions/v1/mcp-oauth
?response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=YOUR_REDIRECT_URI
&code_challenge=BASE64URL_SHA256_OF_VERIFIER
&code_challenge_method=S256
&state=RANDOM_CSRF_TOKEN
&scope=meta:read forms:write audience:write

User receives a magic-link email, clicks it, browser redirects to redirect_uri?code=AUTH_CODE&state=….

Verify state matches before proceeding. Discard the code if it doesn’t.

Terminal window
curl -X POST https://api.caramelme.com/functions/v1/mcp-oauth \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code&code=AUTH_CODE&redirect_uri=YOUR_REDIRECT_URI&client_id=YOUR_CLIENT_ID&code_verifier=YOUR_VERIFIER"

Response:

{
"access_token": "eyJ…",
"refresh_token": "rt_…",
"expires_in": 3600,
"token_type": "Bearer"
}
  • Access token: valid for 1 hour.
  • Refresh token: valid for 30 days from last use, rotates on every use — persist the new one immediately.

All tools use the MCP endpoint. The JSON-RPC method is always tools/call.

Terminal window
curl -X POST https://app.caramelme.com/api/functions/caramel-mcp \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"id": 1,
"params": { "name": "caramel.v1.business.list", "arguments": {} }
}'

Note The REST gateway (https://gateway.caramelme.com/v1/*) is in limited preview. Use the MCP endpoint for new integrations.

The result.content[0].text field is a JSON string — parse it to get the payload.

Refresh ~60 seconds before the token expires. Do not wait for a 401.

Terminal window
curl -X POST https://api.caramelme.com/functions/v1/mcp-oauth \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token&refresh_token=$REFRESH_TOKEN&client_id=$CLIENT_ID"

Always replace the stored refresh token with the new one from the response. Using a rotated-out token returns 400 invalid_grant and forces re-authorization.

When you receive a 429, read the Retry-After header and wait that many seconds before retrying.

async function callWithRetry(body, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const res = await fetch('https://app.caramelme.com/api/functions/caramel-mcp', {
method: 'POST',
headers: { 'Authorization': `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
if (res.status === 429) {
const wait = parseInt(res.headers.get('Retry-After') ?? '5', 10);
await new Promise(r => setTimeout(r, wait * 1000));
continue;
}
return res.json();
}
throw new Error('Rate limit retries exhausted');
}
WhatValue
MCP endpointhttps://app.caramelme.com/api/functions/caramel-mcp
Token endpointhttps://api.caramelme.com/functions/v1/mcp-oauth
Registration endpointhttps://api.caramelme.com/functions/v1/mcp-oauth/register
Discovery URLhttps://app.caramelme.com/.well-known/oauth-authorization-server
Access token lifetime1 hour
Refresh token lifetime30 days from last use
Refresh token rotationYes — always persist the new token
Scopes always grantedmeta:read
MCP JSON-RPC methodtools/call
List all toolscaramel.v1.meta.capabilities
List businessescaramel.v1.business.list (or legacy list_businesses)
Error typoSome errors use messsage (3 s’s) — normalize to message
CodeMeaningFix
400 invalid_grantCode used/expired or refresh token rotatedRestart authorize flow
401 unauthorizedToken expired or missingRefresh, then retry
403 scope_requiredToken missing required scopeRe-authorize with the scope
403 tier_requiredBusiness tier too low for this toolUpgrade the business tier
-32001 (JSON-RPC)Token expired mid-sessionRefresh and retry the call