mcp

POST /v1/mcp/connections

Create an MCP connection (Connect AI tool).

All mcp endpoints

All developer docs →

Authentication

Send an API key as a bearer token. The key must carry the mcp.manage permission; a key without it is refused with 403, not 404.

Where your organisation id goes

This endpoint takes org_id as a field in the JSON body.

Your organisation id is on the API keys screen in your dashboard, beside the key itself. It is the same id in every call you make.

Try it

Replace anything in angle brackets with your own values, and the key placeholder with a key from your dashboard.

curl -X POST https://api.zinndigital.com/v1/mcp/connections \
  -H "Authorization: Bearer zdk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "name": <string>, "scopes": <string[]> }'

Signed in? The API console in your dashboard fills in your real organisation id and your own key, and runs the request against the live API so you can see the actual response. Open this endpoint in the API console

Details

Mints a per-tool MCP connection and returns the full zmcp_… token once (only its hash is stored) plus a paste-ready client config snippet. The requested scopes must be permissions the caller already holds in the target org — an unheld scope is a 403 (a connection can never out-scope its creator), an unknown one a 422. A scope that GET /v1/mcp/scopes does not offer to AI connections is also a 422: holding a permission is not enough to hand it to an assistant. An optional spend cap bounds AI-triggered paid actions. Requires mcp.manage.

Request body

NameTypeRequiredWhat it is
namestringYes—
clientMcpClientNoThe AI client a connection is for (informational).
scopesstring[]YesRBAC permission keys to grant the token. Each must be a permission the caller holds in the target org (a connection can never out-scope its creator); an unheld scope is a 403,…
sandboxbooleanNo—
org_idUuid | nullNoThe org the connection belongs to. Defaults to the caller's sole managed org.
spend_cap_minorinteger | nullNoOptional per-window paid-action budget (integer minor units). Null = no cap.
spend_currencyCurrencyCodeNoISO 4217 currency code (money is minor units + this code — CLAUDE.md §2.8).
spend_window_secondsintegerNo—

Response

NameTypeRequiredWhat it is
idUuidYesUUIDv7 identifier — sortable by creation time (docs/02 §8).
namestringYes—
clientMcpClientYesThe AI client a connection is for (informational).
prefixstringYesThe token's public lookup prefix (not a secret).
scopesstring[]YesThe RBAC permission keys this connection's token may exercise.
sandboxbooleanYes—
kindstring<token, oauth>Notoken = a pasted token; oauth = an AI app that signed in (docs/27 §3a).
oauth_client_namestring | nullNoFor kind: oauth, the app's own name as it registered.
spend_cap_minorinteger | nullNoOptional per-window budget (integer minor units, CLAUDE.md §2.8) for AI-triggered paid actions. Null = no cap.
spend_currencyCurrencyCodeNoISO 4217 currency code (money is minor units + this code — CLAUDE.md §2.8).
spend_window_secondsintegerYesThe rolling window the spend cap applies over.
last_used_atstring | nullNo—
revoked_atstring | nullNo—
created_atstringYes—
tokenstringYesThe full zmcp_… token, shown exactly once.
endpointstringYesThe MCP protocol endpoint URL to configure in the AI client.
configobjectYesA paste-ready config for the connection's client, embedding the endpoint and token. The shape differs per client: Claude Code gets an http-typed server, Cursor a url +…
config_fileobjectYesWhere that client reads config — e.g. .mcp.json. null when it has no file.
commandobjectYesA one-line alternative for a client with a command line (Claude Code), else null. It embeds the token, so treat it like the token.
noteobjectYesSet when the client cannot use a bearer token at all. chatgpt_needs_oauth: ChatGPT connects by OAuth sign-in only, so this token cannot reach it.

Errors this endpoint can return

401 · 403 · 422 · 429