Reuse one OAuth
client_id for the same server application, deployment, tenant, or installation. Each user completes OAuth separately, and the server stores that user’s tokens. Do not register a new client for every request or user.Choose a client registration method
Choose the method that matches where your agent runs and whether it can host public HTTPS metadata.
The three methods differ in how you obtain
client_id and where the client’s redirect URIs and scopes are registered. After you have a client ID, use the same user authorization, callback, MCP request, and token refresh flow described below.
Environments and endpoints
Declare the environment base URLs once, then append the endpoint path shown in each table:resource and token audience must exactly match that environment’s MCP URL.
The OAuth endpoint paths are the same in both public environments. Use each path with the
base_url for the selected environment. The authorization and token paths should come from Authorization Server Metadata instead of being hardcoded when available.
Registration and authorization frequency
Server-side integration
Prepare the OAuth Client
Whichever method you use, the scopes registered for the client are the upper limit for every authorization request. A request for any scope outside that set fails withinvalid_scope, including a later request for more access. Register every scope your agent may need. Clink MCP uses the following scopes, and offline_access lets Clink issue refresh tokens:
CIMD
CIMD is suitable for a server-side agent that can maintain public HTTPS metadata. Host a public JSON file and use its URL asclient_id:
- The URL uses HTTPS, includes a path, and has no query string or fragment.
- The
client_idin the file exactly matches the file URL. - The URL returns HTTP 200 directly with
Content-Type: application/json. Redirects are not followed. - The file is 5 KB or smaller.
Pre-registration
Pre-registration is suitable for a fixed enterprise or partner. Before integration, provide Clink with yourclient_name, fixed HTTPS redirect_uri, the environment’s resource, and every scope your agent may need. Clink configures the client and returns a fixed client_id; your server does not call a registration endpoint and does not need to host a CIMD file.
Clink’s pre-registered MCP client is a public client. It does not need a client secret, but it must use Authorization Code with PKCE (S256). Store the returned client ID in server configuration:
DCR
DCR is suitable when you need a dynamic client ID per tenant or installation, or when the authorization server supports only dynamic registration. First read Authorization Server Metadata. Call the returnedregistration_endpoint only if it is present.
Register once during deployment, tenant activation, or first installation, then persist the returned client_id for all users:
token_endpoint_auth_method=none), so it does not need a client secret. A successful DCR response only means that the client was registered; every user still signs in and authorizes separately.
Clink does not support updating a registered client. To change its redirect URIs or scopes, register a new client; users then authorize again under the new client_id.
Authorize a user
When a user selects Connect Clink:- Fetch the current environment’s Protected Resource Metadata. For Sandbox (UAT), use
${UAT_BASE_URL}/.well-known/oauth-protected-resource/mcp, then read an issuer fromauthorization_servers. - Fetch
{issuer}/.well-known/oauth-authorization-serverand readauthorization_endpointandtoken_endpoint. Do not hardcode these values. - Generate a cryptographically random
stateand PKCEcode_verifierfor the user. Temporarily bind them to your internal user ID, and derive an S256code_challenge. - Redirect the user to the authorization endpoint. URL-encode every parameter.
client_id according to the registration method:
- CIMD: the public JSON file URL.
- Pre-registration: the fixed client ID returned by Clink.
- DCR: the persisted client ID returned by registration.
redirect_uri, resource, state, and PKCE parameters are handled the same way for all three methods.
401 challenge asks for wallet:read events:read events:consume; requesting both events scopes up front avoids a second consent while an operation is in progress. Include offline_access when you need a refresh token. Keep the resource matched to the selected environment.
Exchange the authorization code
After authorization, the callback receivescode, state, and iss, or error parameters if authorization failed. Reject the callback if state does not match the stored value, or if iss is missing or differs from the issuer in Authorization Server Metadata.
Then send a POST request to token_endpoint with Content-Type: application/x-www-form-urlencoded. Use the same redirect URI and the stored PKCE verifier, and URL-encode each value:
client_secret or device_id. Encrypt and store access_token, refresh_token, expiration, and scopes by internal user ID, environment, and client_id. An authorization code can be redeemed only once.
Call MCP
Use the current user’s access token to call the/mcp resource for the same environment. Clink MCP supports protocol versions 2026-07-28 and 2025-06-18. Use an official MCP client SDK when possible: it negotiates the version, sends the required headers, and parses streamed responses. Point the SDK at the MCP URL and supply the current user’s access token at runtime as an Authorization: Bearer header.
If you send HTTP requests yourself, use the 2025-06-18 flow. Version 2026-07-28 has no initialize handshake and adds protocol metadata to every request, so leave it to an SDK.
- Send
initializewithprotocolVersionset to2025-06-18. - Send
notifications/initialized. - Call
tools/listto discover available tools. - Call
tools/callto execute a business operation.
Accept header on every request; without it, the server returns 406. The server may stream a response as text/event-stream, where each data: line holds one JSON-RPC message. After initialize, send the negotiated version in an MCP-Protocol-Version header on every later request:
Handle user redirects
Some tool results include anext_action whose type is REDIRECT_USER, for example to add a card, complete 3DS, or register a card for Agent Pay. Send params.redirect_url to the user through your own channel. When params.actor is USER_DEVICE_ONLY, the user must open the link on their own device; do not open it in an agent-controlled browser. Returning from the page does not prove that the step succeeded. Call get_operation to read the result.
Refresh tokens and recover
When an access token expires, submit the following to the sametoken_endpoint:
grant_type=refresh_token- The original
client_id - The current refresh token
- The same
resourceused for authorization
client_id, including across server instances.
- For
401, refresh the token. If refresh fails, send the user through authorization again. - For
403withinsufficient_scope, read the required scopes from thescopeparameter of theWWW-Authenticateheader. Combine them with the scopes already granted, then authorize the user again. The combined scopes must stay within the client’s registered scopes. - If a payment, refund, or instruction times out, call
get_operationto inspect its status. Do not repeat the operation blindly.
Launch checklist
- Register the OAuth client only during initialization, deployment, tenant activation, or first installation.
- Register every scope your agent may need, and request only the scopes you need at authorization time.
- Isolate each user’s tokens, sessions, and operations.
- Match the callback URL character for character with the client configuration.
- Keep tokens and any client secret out of configuration files, CIMD files, and logs.
- Keep Sandbox (UAT) and Production resources separate.
- Serialize refresh token use per user, environment, and
client_id. - Send
REDIRECT_USERlinks to the user instead of opening them in an agent-controlled browser.