> ## 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.

# Agent Platform MCP Login

> Exchange a platform client token for a user MCP session, with an authorization fallback for first-time users.

Agent platforms that have already verified a user's email can establish a Clink MCP session from their backend. The platform keeps the platform `client_id` and `client_secret` on its server and never sends the platform token to the browser or the Agent.

Clink uses one pre-registered platform client for both the MCP login described here and the [Customize Agent Portal](https://docs.clinkbill.com/api-reference/agent-portal/overview.md) read-only API. The two token types are different, but the client identity is the same.

## How it works

```mermaid theme={null}
sequenceDiagram
  participant P as Platform server
  participant C as Clink cwallet
  participant U as User browser
  P->>C: client_credentials (platform client)
  C-->>P: Platform client token
  P->>C: POST /agent/cwallet/platform/sessions {email}
  alt New user, or this platform already has a LoginSession
    C-->>P: authorized + MCP access token (+ refresh token on first login)
    P->>C: Connect to /mcp with the user access token
  else Existing Clink user has never authorized this platform
    C-->>P: authorization_required + authorization_url
    P-->>U: Open authorization_url
    U->>C: Sign in and consent (Authorization Code + PKCE)
    U-->>P: OAuth callback with code + state
    P->>C: Exchange code + code_verifier
    P->>C: Retry /platform/sessions
    C-->>P: authorized
  end
```

## Before you start

Ask Clink to pre-register the platform client in the target environment. You need:

* A platform `client_id` and `client_secret`.
* The exact OAuth `redirect_uri` you will use for first-time authorization.
* PKCE S256 support in the platform backend.
* The environment MCP resource, for example `https://uat-api.clinkbill.com/mcp`.
* A verified user email from your own user system. Do not send an unverified or user-editable address.

The platform client is never created through Dynamic Client Registration. It is registered by Clink.

## 1. Get a platform client token

```http theme={null}
POST {API_BASE_URL}/agent/cwallet/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id={PLATFORM_CLIENT_ID}
&client_secret={PLATFORM_CLIENT_SECRET}
&scope=agent-platform:account
```

The returned token is a client token, not a user token. It has `token_use=client`, audience `clink-agent-platform-api`, and scope `agent-platform:account`. Cache it until shortly before `expires_in`; do not send it to the Agent or the browser.

## 2. Create or resume the MCP session

```http theme={null}
POST {API_BASE_URL}/agent/cwallet/platform/sessions
Authorization: Bearer {PLATFORM_CLIENT_TOKEN}
Content-Type: application/json
```

| Field | Required | Meaning |
| - | - | - |
| `email` | Yes | Email the platform has verified for this user. |
| `external_user_id` | No | Stable platform-side user ID. Used for audit and callback binding. |
| `device_id` | No | Stable device identity; defaults to a hash derived from the platform client and customer. |
| `redirect_uri` | First-time authorization | Exact URI registered for the platform client. |
| `state` | First-time authorization | Opaque CSRF value; Clink returns it on the callback. |
| `code_challenge` | First-time authorization | PKCE S256 challenge. |
| `code_challenge_method` | First-time authorization | Must be `S256` when `code_challenge` is sent. |
| `issue_refresh` | No | Request a new refresh token when an existing session already has one. First login always returns one. |

```json theme={null}
{
  "email": "user@example.com",
  "external_user_id": "platform-user-123",
  "device_id": "device-abc",
  "redirect_uri": "https://agent.example.com/oauth/clink/callback",
  "state": "{RANDOM_STATE}",
  "code_challenge": "{S256_CODE_CHALLENGE}",
  "code_challenge_method": "S256"
}
```

The examples below show the full `{ code, msg, data }` response envelope. Read `data.status`:

| `status` | Meaning | What to do |
| - | - | - |
| `authorized` | Clink created the customer or found an existing LoginSession for this platform client. | Keep `customer_id`, use `access_token` for MCP, and store `refresh_token` when returned. |
| `authorization_required` | The customer exists, but this platform client has no active LoginSession for them. No token is returned. | Open `authorization_url` in the user's browser, complete the callback, then retry this endpoint. |

### `authorized`

```json theme={null}
{
  "code": 200,
  "msg": "Success",
  "data": {
    "status": "authorized",
    "customer_id": "ccus_...",
    "customer_created": true,
    "device_id": "device-abc",
    "token_type": "Bearer",
    "access_token": "<MCP user access token>",
    "expires_in": 3600,
    "refresh_token": "<first-login refresh token>",
    "refresh_expires_in": 2592000
  }
}
```

`customer_created` is `true` when Clink created a customer from the verified email. A refresh token is returned on first login. When an existing session is reused, only an access token is returned unless `issue_refresh: true` was requested.

### `authorization_required`

```json theme={null}
{
  "code": 200,
  "msg": "Success",
  "data": {
    "status": "authorization_required",
    "customer_id": "ccus_...",
    "device_id": "device-abc",
    "authorization_url": "https://api.clinkbill.com/agent/cwallet/oauth/authorize?..."
  }
}
```

The URL uses the standard Clink Authorization Endpoint. It carries an `email` prefill for the Portal login page when available, but the user still completes email OTP verification. Keep `state` and the PKCE `code_verifier` on the platform server until the callback completes.

## 3. Connect the Agent to MCP

Use the returned `access_token` as the Bearer token for the MCP endpoint:

```http theme={null}
POST {MCP_RESOURCE}
Authorization: Bearer {USER_ACCESS_TOKEN}
```

The user access token has:

| Claim | Value |
| - | - |
| `aud` | The environment MCP resource. |
| `sub` | `customer_id`. |
| `token_use` | `access`. |
| `client_id` | The platform client ID. |
| `scope` | The MCP scopes registered for the platform client. |
| `device_id` | A non-empty device identity. |

The Agent can now use the normal MCP connection for wallet and payment tools. Do not expose the platform client token to the Agent.

## 4. Complete first-time authorization

For `authorization_required`, redirect the user to `authorization_url`. After the user signs in and consents, the browser reaches your registered callback with `code`, `state`, and `iss`.

* Verify `state` belongs to the user who started the flow.
* Verify `iss` matches the environment issuer from Authorization Server Metadata.
* Exchange the code with PKCE exactly as described in [Browser Pay platform setup](https://docs.clinkbill.com/guides/agent/platform_integration/browser_pay.md).
* Retry `POST /agent/cwallet/platform/sessions` with the same email. The new LoginSession lets Clink return `authorized` without another browser round trip.

## Refresh tokens

On first login, the platform receives a refresh token. Store it on the server and refresh before it expires:

```http theme={null}
POST {API_BASE_URL}/agent/cwallet/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&client_id={PLATFORM_CLIENT_ID}
&refresh_token={USER_REFRESH_TOKEN}
```

If the refresh token expires or is revoked, call `/agent/cwallet/platform/sessions` again. A missing or revoked session falls back to `authorization_required`; do not bypass that flow by omitting the user's authorization.

## Environment endpoints

| Environment | API Base URL | MCP resource |
| - | - | - |
| Sandbox UAT | `https://uat-api.clinkbill.com` | `https://uat-api.clinkbill.com/mcp` |
| Production | `https://api.clinkbill.com` | `https://api.clinkbill.com/mcp` |

## Security

* Keep the platform `client_secret` and platform client token on the server.
* Treat `email` as a platform-asserted identity only after your own verification.
* Never accept a `customer_id` or `email` supplied by the Agent to choose a different user.
* Use a stable `device_id`; it is part of the user's login state.
* Never mix sandbox and production client credentials, callbacks, or MCP resources.

> [card] [Customize Agent Portal](https://docs.clinkbill.com/api-reference/agent-portal/overview.md)
>
> Use the same platform client to obtain a read-only portal token and render the user's Clink data.

> [card] [MCP connection and OAuth](https://docs.clinkbill.com/guides/agent/mcp_integration.md)
>
> Review the complete MCP authorization and connection lifecycle.


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