The short path
Point an MCP SDK client athttps://api.apostra.com/mcp/v3. A client that
supports MCP authorization does the rest:
- It calls the endpoint without a token and gets
401. TheWWW-Authenticateheader carries aresource_metadataURL. - It reads that metadata to find Apostra’s authorization server, then reads
/.well-known/oauth-authorization-serverfor the endpoints, includingregistration_endpointandclient_id_metadata_document_supported: true. - It identifies itself, with a Client ID Metadata Document or by registering.
- It opens a browser. The person signs in to Apostra, then approves or denies your agent on the consent screen.
- The browser returns to your agent’s redirect URI with an authorization code. The client exchanges it, with a PKCE verifier, for an access token and a refresh token.
Authorization: Bearer <access token>.
Call get_status first to confirm which account the person signed in as.
An MCP SDK client does all of this when you give it a redirect URI and a place to
store tokens. Read the OAuth section of the SDK you use. The rest of this page
is for when you write the flow yourself, or when something fails.
What the person sees
Apostra asks every time an agent authorizes, including when the person is already signed in. The consent screen shows:
That screen is the trust anchor. The person, not an admin, decides whether this
agent may act as them. Approve issues the code. Deny sends the browser to
your redirect URI with
error=access_denied and issues nothing. The decision is
tied to a one-time session that lasts 10 minutes.
Register your agent
Your agent identifies itself to Apostra in one of two ways. Both create a public client: no client secret, with PKCE doing the proof. Use one or the other.Client ID Metadata Documents
A Client ID Metadata Document is a JSON file on your own domain. Yourclient_id
is the URL of that file. Nothing is registered ahead of time, and Apostra fetches
the file when a person starts authorizing. The MCP specification (2026-07-28)
prefers this method, and Apostra advertises support with
client_id_metadata_document_supported: true, so clients that prefer it use it
without being told.
client_id when you start OAuth. Requirements:
- The URL uses
https, has a path beyond/, and has no fragment, username, password, or.or..path segments. It must already be in canonical form: lowercase host, no default port. client_idin the file equals the URL exactly.redirect_urisis required and each one follows the redirect URI rules. Theredirect_uriyou send to/authorizemust match one of them exactly, character for character. A metadata document gets no loopback subpath allowance.- The file is public. Leave
token_endpoint_auth_methodout or set it tonone. Aclient_secretmakes the document invalid. - Serve it with status
200, a JSON content type, and a body of at most 5 KB. Apostra does not follow redirects, connects only to public internet addresses, and gives up after a few seconds. - Apostra caches a valid file for as long as your
Cache-Control: max-agesays, between 1 minute and 24 hours, and 5 minutes when you set none.no-storeandno-cachecount as the 1-minute minimum. It remembers a failed lookup for about a minute. A change toredirect_uristakes effect when the cached copy expires.
Dynamic client registration
Dynamic client registration is aPOST to /auth/register. Use it when you
cannot host a public file, for example a desktop tool or a local development
agent.
201 response carries a generated client_id that starts with
mcp_client_, plus the redirect URIs you sent. Do not treat it as a secret.
Notes:
- Always send
redirect_uris. A request without them returns a shared, preconfigured client that is not yours. - Send up to 25 redirect URIs of at most 2048 characters each.
client_nameis at most 120 characters.client_uriandlogo_uri, if you send them, must behttpsURLs. - A registration you never use to start an authorization is deleted after 24
hours. A registration unused for 90 days is deleted too. Register again when
you see
redirect_uri is not allowedfor a client that used to work.
Redirect URI rules
Both registration methods use the same rules for each redirect URI:- Web agents:
httpson any host. - Local tools:
http://localhost,http://127.0.0.1orhttp://[::1], with or without a port. No otherhttpaddress is accepted. - Desktop and mobile apps: a private-use scheme, such as
cursor://anysphere.cursor-mcp/oauth/callbackorcom.example.agent:/oauth/callback. The scheme is a letter followed by letters, digits,+,-or., at least two characters long, and something must follow it (a host or a path beyond/). These schemes are refused:http(other than loopback),javascript,data,file,blob,vbscript,about,filesystem,ws,wssandftp. - Never: a fragment (
#...) or embedded credentials (user:pass@). A URI is at most 2048 characters.
redirect_uri you send to /authorize and to /auth/token must match a
registered URI exactly. The one exception is a client registered with dynamic
registration and a localhost or 127.0.0.1 redirect: the request may use the
same host and port with a deeper path (registered http://localhost:8123/cb
accepts http://localhost:8123/cb/done), as long as neither has a query string.
The port must still match. http://[::1] and custom schemes match exactly.
Tokens and PKCE
PKCE is required, with theS256 method only. Send code_challenge (43 to 128
base64url characters) and code_challenge_method=S256 to /authorize, then the
matching code_verifier to /auth/token. plain is refused.
resource. It names the MCP endpoint the token is for, and it must
be the endpoint’s exact URL, with no query or fragment. If you leave it out, the
grant is for the bare /mcp endpoint. Apostra accepts a /mcp grant at
/mcp/v3 too, but at no other endpoint, so send the URL you will call.
The browser comes back to your redirect URI with code, state and iss (the
issuer, https://api.apostra.com). Check state, and check iss if your OAuth
library supports it: Apostra advertises
authorization_response_iss_parameter_supported. The code works once and expires
after 10 minutes. Exchange it. /auth/token accepts form-encoded or JSON bodies.
token_type: Bearer, scope
mcp:access, and:
Both tokens are bound to the person, your client, and the exact
resource.
Refresh before the access token expires:
What ends a grant
A grant lasts until one of these happens:- Your agent calls the revocation endpoint.
POST /auth/revokewithtokenset to the access or refresh token (form-encoded or JSON) ends the whole grant. Sendclient_idtoo. Aclient_idthat does not match the token’s client is silently ignored, and the grant stays live. The endpoint returns200for tokens it does not recognize, and400only whentokenis missing. - The person disconnects your agent from Connected apps in their account menu. Both tokens stop working immediately.
- The refresh token goes 30 days without use.
- Apostra refuses a refresh because the account no longer has active access,
or because of a platform-wide security sign-out. The refresh returns
invalid_grantand your agent must send the person through sign-in again.
Agents that run in a browser
A browser-based agent can run on any origin. These paths answer a request from any origin with CORS headers and no approval step:/authorize, /auth/register, /auth/token, /auth/revoke,
/auth/mcp/authorize, /.well-known/*, /mcp, /mcp/* (including
/mcp/v3), and the per-Seller /seller/{id}/mcp endpoints.
Some rules follow from that:
- Bearer token only. Apostra returns
Access-Control-Allow-Originbut notAccess-Control-Allow-Credentials. SendAuthorization: Bearer <access token>and do not setcredentials: "include". Do not rely on Apostra cookies. A page with no token gets nothing, which is why no origin needs approval. - Everything else stays closed. These paths do not open the rest of the REST
API, such as
/api/v3/*, to your origin. An MCP access token is rejected on the HTTP API anyway. nullorigins are refused. A sandboxedsrcdocframe or a page opened from afile:URL sendsOrigin: null, and Apostra does not answer it with CORS headers. Serve the agent from a realhttpsorigin.- Do not put an API key in browser code. Use the OAuth flow above.
- Redirect URIs still matter. An agent on another origin can call these paths, but it only receives an authorization code at a redirect URI its client declared, and only after the person approves it.
Agents with no person present
A background agent, a scheduled job, or a deployed backend has nobody to approve a consent screen. Do not use this flow. Choose one of these:- An agent that has no credential yet starts with
https://api.apostra.com/auth.mdand the machine-readable contract at/.well-known/buyer-activation.json. See Agent registration. - A user API key for simple automation. A person creates it in Settings → API Access. See Authentication.
- An M2M application when your backend needs the OAuth
client_credentialsflow. See Authentication. - An identity-only buyer-agent credential: see buyer-agent credentials.
Troubleshooting
These are the messages your agent can receive. Errors from/authorize,
/auth/token, /auth/revoke and /auth/register are OAuth JSON bodies with
error and error_description.
If sign-in still fails after you check this table, include the exact
error and error_description, the client_id, and the time of the failure
when you contact support.
Related
- Authentication covers credentials, resource binding, and how a person approves an MCP client.
- MCP client setup has commands for Codex, Claude, and other hosts.
- Connect to Microsoft Copilot Studio shows a manual client registration for a host whose wizard cannot register itself.