Skip to content
Cirrus home
Sign in

Partner apps (OAuth)

Apps that act for other users: register, exchange tokens, disconnect.

List your partner apps #

GET /v1/partner-apps app session

The partner apps the signed-in user registered, oldest first, disabled ones included (disabled: true).

More about this

Client secrets are never shown again.

Errors

HTTP Code When
401 unauthorized

No credential, or the credential is invalid, expired or revoked.

403 forbidden

The credential may not use this route (missing scope, app-session-only route, IP not allowed).

429 rate_limited

Too many requests for this credential (or too many failed attempts); retry after the Retry-After header (seconds; error.retry_after_ms has the exact wait).

503 service_unavailable

A dependency is temporarily unavailable, or the feature is off on this server; retry later.

All fields (request and response schema)

Loading fields…

GET
curl "https://api.cirrus.trade/v1/partner-apps" \
  -H "Authorization: token $CIRRUS_API_KEY"
Response · 200
{
  "status": "success",
  "data": [
    {
      "client_id": "cp_FALC8i71s3F8cUwwozJ3",
      "owner": "user-01M65VA95TDN35YPSTZ49AQXKA",
      "name": "Charts Co",
      "verified": false,
      "logo_url": "https://partner.example/logo.png",
      "redirect_uris": [
        "https://partner.example/callback"
      ],
      "scopes": [
        "read",
        "orders"
      ],
      "created_at": "2026-10-30T07:27:47.011712+00:00",
      "disabled": false
    }
  ],
  "error": null
}

Register a partner app #

POST /v1/partner-apps app session

Registers an OAuth client owned by the signed-in user (at most 10 active apps per user).

More about this

The response carries client_secret once: store it now, it cannot be shown again. New apps are unverified.

Body fields

  • name string required

    Name shown to users on the consent screen (1-80 characters).

  • redirect_uris string[] required

    1-10 redirect URIs, matched exactly (no prefixes, no wildcards): https://, or http://localhost / http://127.0.0.1 for development; no fragment, at most 512 characters.

  • scopes string[] required

    The most a user can grant the app (at least one).

    One of: read, orders, triggers, strategies, strategies_live, strategies_signal, webhooks, notifications, watchlists

  • logo_url string | null

    HTTPS URL of the app's logo.

Errors

HTTP Code When
400 malformed_json

The body is not JSON, or does not fit the request shape (unknown or missing fields, wrong types).

401 unauthorized

No credential, or the credential is invalid, expired or revoked.

403 own_session_required

Credentials can only be created from the user's own signed-in session.

403 forbidden

The credential may not use this route (missing scope, app-session-only route, IP not allowed).

422 invalid_request

The request is well-formed but not valid; field names the input.

422 invalid_scope

OAuth: a requested scope is unknown or not allowed for the app.

429 rate_limited

Too many requests for this credential (or too many failed attempts); retry after the Retry-After header (seconds; error.retry_after_ms has the exact wait).

503 service_unavailable

A dependency is temporarily unavailable, or the feature is off on this server; retry later.

All fields (request and response schema)

Loading fields…

POST
curl -X POST "https://api.cirrus.trade/v1/partner-apps" \
  -H "Authorization: token $CIRRUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Charts Co",
    "redirect_uris": [
      "https://partner.example/callback"
    ],
    "scopes": [
      "read",
      "orders"
    ],
    "logo_url": "https://partner.example/logo.png"
  }'
Response · 201
{
  "status": "success",
  "data": {
    "client_id": "cp_FALC8i71s3F8cUwwozJ3",
    "owner": "user-01M65VA95TDN35YPSTZ49AQXKA",
    "name": "Charts Co",
    "verified": false,
    "logo_url": "https://partner.example/logo.png",
    "redirect_uris": [
      "https://partner.example/callback"
    ],
    "scopes": [
      "read",
      "orders"
    ],
    "created_at": "2026-10-30T07:27:47.011712+00:00",
    "disabled": false,
    "client_secret": "cps_CwtJDmZDiRmYlwiK6Vysz7KlDl8VGnJ6vDK6H5avLE328NAl",
    "note": "Store the client secret now: it is not shown again."
  },
  "error": null
}

Set a partner app's webhook #

PATCH /v1/partner-apps/{client_id} app session

Sets, changes, turns off or removes the webhook of one of the user's own active apps.

More about this

For every user connected to the app it gets order_update and trade for orders the app placed, account_alert for the user's accounts (also with kind account_removed when an account is no longer linked) and connection_revoked when a connection ends. Each body also carries username (top level): the bodies are PartnerWebhookOrderUpdate, PartnerWebhookTrade, PartnerWebhookAccountAlert (kinds PartnerWebhookAlertKind) and WebhookConnectionRevoked, together PartnerWebhookBody. Signing, retries and turning off after repeated failures are as for a user's webhooks (see webhooks). Deliveries to partner apps start only once the server turns on partner webhooks; until then this route answers 503. The first webhook_url creates the webhook and returns webhook_secret once; webhook_url: null removes it. Own session only.

Parameters

  • client_id path · string required

    The app's client id (cp_...).

Body fields

  • webhook_url string | null

    The receiver: an https:// URL on a public host (private, loopback and link-local addresses are refused). The first one creates the webhook and its secret; null removes the webhook.

  • webhook_events string[] | null

    Events to send, at least one of order_update, trade, account_alert, connection_revoked (all four when a webhook is created without this).

    One of: order_update, trade, account_alert, connection_revoked

  • webhook_enabled boolean | null

    false turns deliveries off; true turns them back on (also after the webhook was turned off for failing).

Errors

HTTP Code When
400 malformed_json

The body is not JSON, or does not fit the request shape (unknown or missing fields, wrong types).

401 unauthorized

No credential, or the credential is invalid, expired or revoked.

403 own_session_required

Credentials can only be created from the user's own signed-in session.

403 forbidden

The credential may not use this route (missing scope, app-session-only route, IP not allowed).

404 unknown_app

Unknown or disabled partner app.

409 conflict

A partner app's webhook was changed at the same time as this request; retry.

422 invalid_request

The request is well-formed but not valid; field names the input.

429 rate_limited

Too many requests for this credential (or too many failed attempts); retry after the Retry-After header (seconds; error.retry_after_ms has the exact wait).

503 service_unavailable

A dependency is temporarily unavailable, or the feature is off on this server; retry later.

All fields (request and response schema)

Loading fields…

PATCH
curl -X PATCH "https://api.cirrus.trade/v1/partner-apps/<client_id>" \
  -H "Authorization: token $CIRRUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_url": "https://partner.example/hook",
    "webhook_events": [
      "order_update",
      "trade",
      "account_alert",
      "connection_revoked"
    ]
  }'
Response · 200
{
  "status": "success",
  "data": {
    "client_id": "cp_nTYrRysBvVEizO0aMNCX",
    "owner": "user-01M4Z65SRSKA2K7SGT33094P5V",
    "name": "Charts Co",
    "verified": false,
    "logo_url": "https://partner.example/logo.png",
    "redirect_uris": [
      "https://partner.example/callback"
    ],
    "scopes": [
      "read",
      "orders"
    ],
    "created_at": "2026-10-15T07:07:11.535867+00:00",
    "disabled": false,
    "webhook": {
      "url": "https://partner.example/hook",
      "events": [
        "order_update",
        "trade",
        "account_alert",
        "connection_revoked"
      ],
      "enabled": true,
      "disabled_reason": null,
      "consecutive_failures": 0,
      "last_success_at": null,
      "last_failure_at": null,
      "previous_secret_expires_at": null,
      "secret_rotated_at": null
    },
    "webhook_secret": "whsec_…",
    "note": "Store the webhook secret now: it is not shown again."
  },
  "error": null
}

Disable a partner app #

DELETE /v1/partner-apps/{client_id} app session

Disables one of the user's own apps: every token issued to it stops working at once, for every user who connected it.

More about this

Cannot be undone.

Parameters

  • client_id path · string required

    The app's client id (cp_...).

Errors

HTTP Code When
401 unauthorized

No credential, or the credential is invalid, expired or revoked.

403 forbidden

The credential may not use this route (missing scope, app-session-only route, IP not allowed).

404 unknown_app

Unknown or disabled partner app.

429 rate_limited

Too many requests for this credential (or too many failed attempts); retry after the Retry-After header (seconds; error.retry_after_ms has the exact wait).

503 service_unavailable

A dependency is temporarily unavailable, or the feature is off on this server; retry later.

All fields (request and response schema)

Loading fields…

DELETE
curl -X DELETE "https://api.cirrus.trade/v1/partner-apps/<client_id>" \
  -H "Authorization: token $CIRRUS_API_KEY"
Response · 200
{
  "status": "success",
  "data": {
    "client_id": "cp_FALC8i71s3F8cUwwozJ3",
    "disabled": true
  },
  "error": null
}

Rotate a partner app's client secret #

POST /v1/partner-apps/{client_id}/rotate-secret app session

Issues a new client secret (shown once).

More about this

The previous secret keeps working until previous_secret_expires_at (24 hours), so the partner can roll the new one out without downtime; rotating again replaces it. Own session only.

Parameters

  • client_id path · string required

    The app's client id (cp_...).

Errors

HTTP Code When
401 unauthorized

No credential, or the credential is invalid, expired or revoked.

403 own_session_required

Credentials can only be created from the user's own signed-in session.

404 unknown_app

Unknown or disabled partner app.

429 rate_limited

Too many requests for this credential (or too many failed attempts); retry after the Retry-After header (seconds; error.retry_after_ms has the exact wait).

503 service_unavailable

A dependency is temporarily unavailable, or the feature is off on this server; retry later.

All fields (request and response schema)

Loading fields…

POST
curl -X POST "https://api.cirrus.trade/v1/partner-apps/<client_id>/rotate-secret" \
  -H "Authorization: token $CIRRUS_API_KEY"
Response · 200
{
  "status": "success",
  "data": {
    "client_id": "cp_thoAw2SKE8Q8mv2sno0m",
    "client_secret": "cps_eu0PNR6P8S3lBc6aiWLr7fHtwZjdBiNSF9jmPF6zd9G1zosO",
    "previous_secret_expires_at": "2026-09-30T12:02:21Z"
  },
  "error": null
}

Rotate a partner app's webhook secret #

POST /v1/partner-apps/{client_id}/rotate-webhook-secret app session

Issues a new webhook signing secret (shown once).

More about this

Deliveries are signed with both the new and the previous secret until previous_secret_expires_at (24 hours), so the receiver can switch without downtime. Own session only. Answers 503 until the server turns on partner webhooks.

Parameters

  • client_id path · string required

    The app's client id (cp_...).

Errors

HTTP Code When
401 unauthorized

No credential, or the credential is invalid, expired or revoked.

403 own_session_required

Credentials can only be created from the user's own signed-in session.

404 unknown_app

Unknown or disabled partner app.

409 conflict

A partner app's webhook was changed at the same time as this request; retry.

422 invalid_request

The request is well-formed but not valid; field names the input.

429 rate_limited

Too many requests for this credential (or too many failed attempts); retry after the Retry-After header (seconds; error.retry_after_ms has the exact wait).

503 service_unavailable

A dependency is temporarily unavailable, or the feature is off on this server; retry later.

All fields (request and response schema)

Loading fields…

POST
curl -X POST "https://api.cirrus.trade/v1/partner-apps/<client_id>/rotate-webhook-secret" \
  -H "Authorization: token $CIRRUS_API_KEY"
Response · 200
{
  "status": "success",
  "data": {
    "client_id": "cp_nTYrRysBvVEizO0aMNCX",
    "webhook_secret": "whsec_…",
    "previous_secret_expires_at": "2026-10-16T07:07:11.542Z"
  },
  "error": null
}

Exchange a code or refresh token for tokens #

POST /v1/oauth/token no auth

The partner's server-to-server token endpoint (RFC 6749).

More about this

No API credential: the client authenticates with client_id + client_secret in the body or with HTTP Basic; a client registered with POST /v1/oauth/register is public and sends only client_id (its code is bound by PKCE).

  • grant_type=authorization_code: exchanges the code from the redirect (single use, valid 60 seconds) with the same redirect_uri and the PKCE code_verifier.
  • grant_type=refresh_token: rotates the refresh token. Each refresh token works once; presenting a used one again revokes the whole connection (a theft signal), except within 30 seconds of its rotation, which returns the same new pair (so concurrent refreshes are safe).

Access tokens last one day (expires_in: 86400), refresh tokens 90 days. Responses (success and error) are not the usual envelope: success is the RFC 6749 token response, errors are {"error", "error_description"}. Every response carries Cache-Control: no-store. After too many failed client or grant checks from one source (counted with failed API-key attempts), requests get 429 slow_down with Retry-After until the window is over.

Parameters

  • Authorization header · string

    Optional HTTP Basic client authentication: Basic base64(client_id:client_secret).

Body fields

  • grant_type string required

    authorization_code or refresh_token; missing or anything else is unsupported_grant_type.

    One of: authorization_code, refresh_token

  • code string | null

    authorization_code: the code from the redirect (oc_...).

  • redirect_uri string | null

    authorization_code: the same redirect_uri the code was issued for.

  • code_verifier string | null

    authorization_code: the PKCE verifier (43-128 characters of A-Z a-z 0-9 - . _ ~) whose S256 challenge was sent to authorize.

  • refresh_token string | null

    refresh_token: the latest refresh token (pr_...).

  • client_id string | null

    The app's client id (unless sent with HTTP Basic).

  • client_secret string | null

    The app's client secret (unless sent with HTTP Basic). Left out by a dynamically registered client (token_endpoint_auth_method: none).

  • resource string | null

    RFC 8707 resource (optional): when sent, it must be the one the user authorized (the MCP server URL), else invalid_grant.

Errors

HTTP Code When
400 invalid_request

Unreadable body or a missing field (code, redirect_uri, code_verifier, refresh_token).

400 unsupported_grant_type

grant_type missing or not authorization_code / refresh_token.

400 invalid_grant

The code is invalid, expired or already used, was issued to another client or redirect_uri, or code_verifier does not match; the refresh token is invalid, already used, expired, or the user revoked the connection; resource is not the one the user authorized.

401 invalid_client

No client credentials, an unknown, disabled or deleted app, a wrong secret, or a secret sent by a public client.

429 slow_down

Too many failed attempts from this source; retry after Retry-After seconds.

503 temporarily_unavailable

Partner access is off on this server or its store is temporarily unavailable; retry.

All fields (request and response schema)

Loading fields…

POST
curl -X POST "https://api.cirrus.trade/v1/oauth/token" \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "authorization_code",
    "code": "oc_3zb5bRr1InWe3ipR7EXinezj5kpxNWDpk9piRJNB",
    "redirect_uri": "https://partner.example/callback",
    "code_verifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
    "client_id": "cp_3Oq3YXcuepv3Qx5Z2iC2",
    "client_secret": "cps_dG69y0yLbVBxwXqsWbWbrQk1aP0sEuT7mYw2HcN4vZ8LxJfR"
  }'
Response · 200
{
  "access_token": "pa_skuF0MRH41ytp5qLfnrdbtWjm4mXpHG8VsfirtfNG9T25WFF",
  "token_type": "Bearer",
  "expires_in": 86400,
  "refresh_token": "pr_X0I0VMDKFSkae48wpL4Yd6PSF7mLh12LbmwW3vHeZpcmgMf83zEOgHlj",
  "scope": "read orders"
}

List connected partner apps #

GET /v1/connections app session

The partner apps the user has granted access to, plus those revoked in the last 30 days (they carry revoked_at; live ones do not).

Errors

HTTP Code When
401 unauthorized

No credential, or the credential is invalid, expired or revoked.

403 forbidden

The credential may not use this route (missing scope, app-session-only route, IP not allowed).

429 rate_limited

Too many requests for this credential (or too many failed attempts); retry after the Retry-After header (seconds; error.retry_after_ms has the exact wait).

503 service_unavailable

A dependency is temporarily unavailable, or the feature is off on this server; retry later.

All fields (request and response schema)

Loading fields…

GET
curl "https://api.cirrus.trade/v1/connections" \
  -H "Authorization: token $CIRRUS_API_KEY"
Response · 200
{
  "status": "success",
  "data": [
    {
      "client_id": "cp_ZY0ZTp7e7G4JH2oGkHgr",
      "app_name": "Charts Co",
      "verified": false,
      "dynamic": false,
      "scopes": [
        "read",
        "orders"
      ],
      "connected_at": "2026-10-30T14:25:07.285908+00:00",
      "updated_at": "2026-10-30T14:25:07.285908+00:00",
      "last_used_at": null,
      "calls_today": 0
    }
  ],
  "error": null
}

Disconnect a partner app #

DELETE /v1/connections/{client_id} app session

Revokes the user's connection to the app: every access token dies at once and every refresh token is deleted.

More about this

The partner must ask the user to connect again.

Parameters

  • client_id path · string required

    The app's client id (cp_...).

Errors

HTTP Code When
401 unauthorized

No credential, or the credential is invalid, expired or revoked.

403 forbidden

The credential may not use this route (missing scope, app-session-only route, IP not allowed).

404 connection_not_found

No connected partner app with this client id.

429 rate_limited

Too many requests for this credential (or too many failed attempts); retry after the Retry-After header (seconds; error.retry_after_ms has the exact wait).

503 service_unavailable

A dependency is temporarily unavailable, or the feature is off on this server; retry later.

All fields (request and response schema)

Loading fields…

DELETE
curl -X DELETE "https://api.cirrus.trade/v1/connections/<client_id>" \
  -H "Authorization: token $CIRRUS_API_KEY"
Response · 200
{
  "status": "success",
  "data": {
    "client_id": "cp_Ay2T4dfVLByf9ct7XzAH",
    "revoked": true
  },
  "error": null
}

What a connected app left running #

GET /v1/connections/{client_id}/left app session

What the app client_id created for the signed-in user that is still running: its open orders, active GTTs, open v2 triggers and strategy deployments that are not stopped.

More about this

Works after the connection was revoked: disconnecting an app never cancels what it started. The app session of the owning user only. A client_id the user never connected gives empty lists.

Parameters

  • client_id path · string required

    The partner app's client id.

Errors

HTTP Code When
401 unauthorized

No credential, or the credential is invalid, expired or revoked.

403 own_session_required

Credentials can only be created from the user's own signed-in session.

429 rate_limited

Too many requests for this credential (or too many failed attempts); retry after the Retry-After header (seconds; error.retry_after_ms has the exact wait).

503 service_unavailable

A dependency is temporarily unavailable, or the feature is off on this server; retry later.

All fields (request and response schema)

Loading fields…

GET
curl "https://api.cirrus.trade/v1/connections/<client_id>/left" \
  -H "Authorization: token $CIRRUS_API_KEY"
Response · 200
{
  "status": "success",
  "data": {
    "orders": [
      {
        "broker_tag": null,
        "order_id": "250101000123",
        "parent_tag": null,
        "username": "user-01M66MX7P51T9S9V6RA68F15JR",
        "account": "P1",
        "broker": "paper",
        "tradingsymbol": "RELIANCE-EQ",
        "side": "BUY",
        "order_type": "LIMIT",
        "product": "MIS",
        "quantity": 5,
        "price": 2400,
        "trigger_price": null,
        "state": "OPEN",
        "filled_qty": 0,
        "average_price": null,
        "status_message": null,
        "broker_updated_at": null,
        "created_at": "2026-10-30T14:55:03.706582Z",
        "status_history": [],
        "partner_client_id": "cp_9oN8crnsKGBC0epxXm9q",
        "lot_size": 1,
        "exchange": "NSE",
        "instrument_type": "EQUITY",
        "symbol": "RELIANCE",
        "expiry": "",
        "strike": 0,
        "option_type": "",
        "ltp": 2500,
        "prev_close": 2490,
        "instrument_token": "CT:TEST:RELIANCE",
        "order_tag": "01M66MX90TMJPA92R4EKMZ55NR"
      }
    ],
    "gtt": [],
    "triggers": [],
    "deployments": []
  },
  "error": null
}

A connection's limits #

GET /v1/connections/{client_id}/limits app session

The limits of the user's live connection to a partner app: what the app may trade and deploy on top of its scopes.

More about this

They bind mcp tokens (AI agents) only; classic partner tokens are not limited by them. When the user never set limits, the defaults are returned with is_default: true.

Parameters

  • client_id path · string required

    The app's client id.

Errors

HTTP Code When
401 unauthorized

No credential, or the credential is invalid, expired or revoked.

403 own_session_required

Credentials can only be created from the user's own signed-in session.

403 forbidden

The credential may not use this route (missing scope, app-session-only route, IP not allowed).

404 connection_not_found

No connected partner app with this client id.

429 rate_limited

Too many requests for this credential (or too many failed attempts); retry after the Retry-After header (seconds; error.retry_after_ms has the exact wait).

503 service_unavailable

A dependency is temporarily unavailable, or the feature is off on this server; retry later.

All fields (request and response schema)

Loading fields…

GET
curl "https://api.cirrus.trade/v1/connections/<client_id>/limits" \
  -H "Authorization: token $CIRRUS_API_KEY"
Response · 200
{
  "status": "success",
  "data": {
    "client_id": "cp_uLZ1purc60GwZKfU8e0K",
    "limits": {
      "trading": true,
      "accounts": [],
      "paper_only": true,
      "max_order_value": null,
      "max_orders_per_day": 20,
      "live_strategies": false,
      "max_deployments": 1
    },
    "is_default": true,
    "applies_to": [
      "mcp"
    ]
  },
  "error": null
}

Change a connection's limits #

PUT /v1/connections/{client_id}/limits app session

Replaces the limits of the user's live connection to a partner app.

More about this

Takes effect at once, on every server. Every listed account must be the user's; live_strategies needs trading, paper_only off and the strategies_live scope on the connection. max_order_value is in whole rupees. Recorded in activity with the before and after.

Parameters

  • client_id path · string required

    The app's client id.

Body fields

  • trading boolean required

    false: a read-only connection.

  • accounts string[] required

    Accounts it may trade or deploy on (none until the user picks).

  • paper_only boolean

    Act only on paper-broker accounts (orders, GTT, triggers and deployments alike).

  • max_order_value integer (int64) | null

    Largest order value, in whole rupees.

  • max_orders_per_day integer (int32) | null

    Orders it may place per IST trading day.

  • live_strategies boolean

    May deploy live / go live (needs paper_only off).

  • max_deployments integer (int32) | null

    Most of the user's active deployments it may have started.

Errors

HTTP Code When
400 malformed_json

The body is not JSON, or does not fit the request shape (unknown or missing fields, wrong types).

401 unauthorized

No credential, or the credential is invalid, expired or revoked.

403 own_session_required

Credentials can only be created from the user's own signed-in session.

403 forbidden

The credential may not use this route (missing scope, app-session-only route, IP not allowed).

404 connection_not_found

No connected partner app with this client id.

422 invalid_request

The request is well-formed but not valid; field names the input.

429 rate_limited

Too many requests for this credential (or too many failed attempts); retry after the Retry-After header (seconds; error.retry_after_ms has the exact wait).

503 service_unavailable

A dependency is temporarily unavailable, or the feature is off on this server; retry later.

All fields (request and response schema)

Loading fields…

PUT
curl -X PUT "https://api.cirrus.trade/v1/connections/<client_id>/limits" \
  -H "Authorization: token $CIRRUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "trading": true,
    "accounts": [
      "P1"
    ],
    "paper_only": true,
    "max_order_value": 10000,
    "max_orders_per_day": 5,
    "max_deployments": 1
  }'
Response · 200
{
  "status": "success",
  "data": {
    "client_id": "cp_uLZ1purc60GwZKfU8e0K",
    "limits": {
      "trading": true,
      "accounts": [
        "P1"
      ],
      "paper_only": true,
      "max_order_value": 10000,
      "max_orders_per_day": 5,
      "live_strategies": false,
      "max_deployments": 1
    },
    "is_default": false,
    "applies_to": [
      "mcp"
    ]
  },
  "error": null
}

Register an OAuth client (AI agents) #

POST /v1/oauth/register no auth

Dynamic client registration (RFC 7591) for AI agents connecting through the MCP server.

More about this

No credential needed. The client is public: no secret is issued; it authenticates at POST /v1/oauth/token with client_id and the PKCE code_verifier (token_endpoint_auth_method: none). It is never verified, never listed among anyone's partner apps, and may be granted at most read, orders, triggers, strategies and strategies_live. The answer says what was registered, whatever grant_types, response_types or token_endpoint_auth_method asked for; other client metadata is ignored. Redirect URIs are stored and returned as parsed (scheme and host lower-cased). client_name is printable ASCII; one naming a known AI vendor is refused unless every redirect URI other than loopback http or a known desktop app scheme is that vendor's own. Its access tokens work only through the MCP server.

A client that no user ever connected through is deleted after 24 hours, one no user is still connected through after 30 days; the token endpoint then answers invalid_client and the agent registers again. At most 10 registrations an hour per client IP (an IPv6 caller: per /64), and a cap on all registrations together per hour. Only served where agent access is enabled; elsewhere 404 with no body. Responses are not the usual envelope.

Body fields

  • client_name string | null

    Name shown to the user on the consent screen (printable ASCII, at most 64 characters); unverified. A known AI vendor's name is refused unless every redirect URI other than loopback http or a known desktop app scheme is that vendor's.

  • redirect_uris string[]

    1-5 redirect URIs: https:// (any host), http://localhost, http://127.0.0.1 or http://[::1] on any port (the port is ignored when matching), or a known desktop app scheme. No fragment, at most 512 characters each.

  • grant_types string[] | null

    Ignored: the client is registered for authorization_code and refresh_token.

  • response_types string[] | null

    Ignored: the client is registered for code.

  • token_endpoint_auth_method string | null

    Ignored: the client is public (none).

Errors

HTTP Code When
400 invalid_request

Unreadable body, client_name over 64 characters or naming an AI vendor whose redirect URIs these are not, no or more than 5 redirect_uris, or a redirect URI that is not https, loopback http or a known desktop app scheme.

404 —

Agent access is not enabled on this server (no body).

429 slow_down

Too many registrations from this client IP (IPv6: its /64), or from all callers together this hour; retry after Retry-After seconds.

503 temporarily_unavailable

The store is temporarily unavailable; retry.

All fields (request and response schema)

Loading fields…

POST
curl -X POST "https://api.cirrus.trade/v1/oauth/register" \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "Claude",
    "redirect_uris": [
      "https://claude.ai/api/mcp/auth_callback"
    ],
    "grant_types": [
      "authorization_code",
      "refresh_token"
    ],
    "response_types": [
      "code"
    ],
    "token_endpoint_auth_method": "none"
  }'
Response · 201
{
  "client_id": "cp_RV0iQ1LU929D23gJ8DkW",
  "client_name": "Claude",
  "redirect_uris": [
    "https://claude.ai/api/mcp/auth_callback"
  ],
  "token_endpoint_auth_method": "none",
  "grant_types": [
    "authorization_code",
    "refresh_token"
  ],
  "response_types": [
    "code"
  ],
  "client_id_issued_at": 1793364132
}

OAuth authorization server metadata #

GET /.well-known/oauth-authorization-server no auth

RFC 8414 metadata for OAuth clients that discover the server themselves (AI agents connecting through the MCP server): where users consent, the token and registration endpoints, PKCE S256 only, and the scopes an agent may be granted.

More about this

No credential needed. Only served where agent access is enabled; elsewhere 404 with no body.

Errors

HTTP Code When
404 —

Agent access is not enabled on this server (no body).

All fields (request and response schema)

Loading fields…

GET
curl "https://api.cirrus.trade/.well-known/oauth-authorization-server"
Response · 200
{
  "issuer": "https://api.test.example",
  "authorization_endpoint": "https://app.test.example/oauth/consent",
  "token_endpoint": "https://api.test.example/v1/oauth/token",
  "registration_endpoint": "https://api.test.example/v1/oauth/register",
  "response_types_supported": [
    "code"
  ],
  "grant_types_supported": [
    "authorization_code",
    "refresh_token"
  ],
  "code_challenge_methods_supported": [
    "S256"
  ],
  "token_endpoint_auth_methods_supported": [
    "none",
    "client_secret_basic",
    "client_secret_post"
  ],
  "scopes_supported": [
    "read",
    "orders",
    "triggers",
    "strategies",
    "strategies_live",
    "offline_access"
  ]
}