> ## Documentation Index
> Fetch the complete documentation index at: https://docs.clinkbill.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Connect an Agent to Clink MCP

> Connect a server-side agent to Clink MCP with OAuth, PKCE, and per-user tokens.

Clink MCP lets a server-side agent call Clink tools on behalf of an authorized user.

<Note>
  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.
</Note>

## Choose a client registration method

Choose the method that matches where your agent runs and whether it can host public HTTPS metadata.

| Scenario | Method | Recommendation |
| :- | :- | :- |
| A server-side agent that can host public HTTPS metadata | **CIMD** | Recommended |
| A server-side agent for a fixed enterprise or partner | **Pre-registration** | Recommended |
| A client-side agent that can host public HTTPS metadata | **CIMD** | Recommended |
| A client-side agent that cannot host public HTTPS metadata, or a host that supports only dynamic registration | **DCR** | Recommended when required |

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:

```text theme={null}
UAT_BASE_URL=https://uat-api.clinkbill.com
PRODUCTION_BASE_URL=https://api.clinkbill.com
```

Use the resource URL for the environment where the request will run. The OAuth `resource` and token `audience` must exactly match that environment's MCP URL.

| Environment | MCP resource | Protected Resource Metadata | Authorization Server Metadata |
| :- | :- | :- | :- |
| Sandbox (UAT) | `${UAT_BASE_URL}/mcp` | `${UAT_BASE_URL}/.well-known/oauth-protected-resource/mcp` | `${UAT_BASE_URL}/.well-known/oauth-authorization-server` |
| Production | `${PRODUCTION_BASE_URL}/mcp` | `${PRODUCTION_BASE_URL}/.well-known/oauth-protected-resource/mcp` | `${PRODUCTION_BASE_URL}/.well-known/oauth-authorization-server` |

<Warning>
  MCP configuration contains only the MCP address. Never put an access token, refresh token, or API key in the MCP configuration.
</Warning>

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.

| Purpose | Path | HTTP method and condition |
| :- | :- | :- |
| Authorize a user | `/agent/cwallet/oauth/authorize` | `GET` |
| Exchange or refresh a token | `/agent/cwallet/oauth/token` | `POST` |
| Register a client (DCR) | `/agent/cwallet/oauth/register` | `POST` when metadata returns `registration_endpoint` |
| Revoke a refresh token | `/agent/cwallet/oauth/revoke` | `POST` |
| JWKS public keys | `/agent/cwallet/oauth/jwks` | `GET` |

<Warning>
  Call the DCR registration endpoint only when Authorization Server Metadata includes `registration_endpoint`. Protected Resource Metadata does not provide a DCR address. If it is absent, use CIMD or Pre-registration.
</Warning>

## Registration and authorization frequency

| Object | Frequency | Details |
| :- | :- | :- |
| **OAuth client** | Once per host, deployment, tenant, or installation | CIMD does not call a registration endpoint. Clink configures Pre-registration. DCR registers once, then the server persists and reuses the returned `client_id`. Never register one for each user. |
| **User OAuth authorization** | Once when each user first connects | Isolate and refresh tokens per user. Ask the user to authorize again after token revocation, expiry that cannot be refreshed, or a scope change. |

## 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 with `invalid_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:

```text theme={null}
wallet:read payment:execute instruction:read instruction:write refund:read refund:write events:read events:consume offline_access
```

Each authorization request can still ask for a subset of these scopes.

#### CIMD

CIMD is suitable for a server-side agent that can maintain public HTTPS metadata. Host a public JSON file and use its URL as `client_id`:

```json theme={null}
{
  "client_id": "https://agent.example.com/.well-known/oauth-client.json",
  "client_name": "Example Agent",
  "redirect_uris": ["https://agent.example.com/oauth/clink/callback"],
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "scope": "wallet:read payment:execute instruction:read instruction:write refund:read refund:write events:read events:consume offline_access"
}
```

Clink fetches this file to validate authorization requests. The file must meet these requirements:

* The URL uses HTTPS, includes a path, and has no query string or fragment.
* The `client_id` in 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.

The CIMD URL is a public client identifier. Do not put a client secret in the file.

#### Pre-registration

Pre-registration is suitable for a fixed enterprise or partner. Before integration, provide Clink with your `client_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:

```text theme={null}
CLINK_CLIENT_ID=clink-provided-client-id
CLINK_REDIRECT_URI=https://agent.example.com/oauth/clink/callback
CLINK_RESOURCE=${UAT_BASE_URL}/mcp
CLINK_SCOPES=wallet:read events:read events:consume offline_access
```

When a user selects **Connect Clink**, use this fixed client ID in the authorization request. Store each user's tokens separately.

#### 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 returned `registration_endpoint` only if it is present.

Register once during deployment, tenant activation, or first installation, then persist the returned `client_id` for all users:

```http theme={null}
POST {registration_endpoint}
Content-Type: application/json

{
  "client_name": "Example Agent",
  "redirect_uris": ["https://agent.example.com/oauth/clink/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none",
  "scope": "wallet:read payment:execute instruction:read instruction:write refund:read refund:write events:read events:consume offline_access"
}
```

The current Clink flow uses a public client (`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**:

1. 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 from `authorization_servers`.
2. Fetch `{issuer}/.well-known/oauth-authorization-server` and read `authorization_endpoint` and `token_endpoint`. Do not hardcode these values.
3. Generate a cryptographically random `state` and PKCE `code_verifier` for the user. Temporarily bind them to your internal user ID, and derive an S256 `code_challenge`.
4. Redirect the user to the authorization endpoint. URL-encode every parameter.

Set `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.

The `redirect_uri`, `resource`, `state`, and PKCE parameters are handled the same way for all three methods.

```text theme={null}
response_type=code
client_id=https://agent.example.com/.well-known/oauth-client.json
redirect_uri=https://agent.example.com/oauth/clink/callback
scope=wallet:read events:read events:consume offline_access
resource=${UAT_BASE_URL}/mcp
state={random_state}
code_challenge={s256_challenge}
code_challenge_method=S256
```

Request the scopes your integration needs now, within the client's registered scopes. The MCP server's first `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 receives `code`, `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:

```text theme={null}
grant_type=authorization_code
client_id=https://agent.example.com/.well-known/oauth-client.json
code={code}
redirect_uri=https://agent.example.com/oauth/clink/callback
code_verifier={code_verifier}
resource=${UAT_BASE_URL}/mcp
```

This is a public client, so do not send `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.

1. Send `initialize` with `protocolVersion` set to `2025-06-18`.
2. Send `notifications/initialized`.
3. Call `tools/list` to discover available tools.
4. Call `tools/call` to execute a business operation.

```http theme={null}
POST ${UAT_BASE_URL}/mcp
Authorization: Bearer {user_access_token}
Content-Type: application/json
Accept: application/json, text/event-stream

{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"example-agent","version":"1.0.0"}}}
```

Include the `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:

```http theme={null}
POST ${UAT_BASE_URL}/mcp
Authorization: Bearer {user_access_token}
Content-Type: application/json
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2025-06-18

{"jsonrpc":"2.0","id":2,"method":"tools/list"}
```

### Handle user redirects

Some tool results include a `next_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 same `token_endpoint`:

* `grant_type=refresh_token`
* The original `client_id`
* The current refresh token
* The same `resource` used for authorization

Each refresh returns a new refresh token and retires the old one. Store the new refresh token before you use the new access token. If a retired refresh token is sent again, Clink treats it as token reuse and revokes all refresh tokens from that authorization, so the user must authorize again. Serialize refreshes for each user, environment, and `client_id`, including across server instances.

* For `401`, refresh the token. If refresh fails, send the user through authorization again.
* For `403` with `insufficient_scope`, read the required scopes from the `scope` parameter of the `WWW-Authenticate` header. 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_operation` to 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_USER` links to the user instead of opening them in an agent-controlled browser.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.