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

# How to integrate RESTful API

> Learn how to integrate the Customer Portal API and Browser Pay through the Clink RESTful API to build Agent payment capabilities.

This page is for developers who build their own backend and user interface. It explains how to build Agent payment capabilities with the Clink RESTful API. You can use the Customer Portal API (the **Agent Portal API** in the Clink API docs) to manage a user's wallet, payment methods, purchase authorizations, and orders, and then use Browser Pay to complete payment on the merchant page.

This path suits platforms that want to control their own product interface, user experience, and business flows. It does not require your Agent to connect directly to the user MCP, and it does not require the Vault SDK. Clink owns user payment data and authorization boundaries; your platform owns user identity, business orchestration, the merchant browser, and order state.

> [!NOTE]
>
> This tutorial uses Sandbox UAT examples. First request a platform `client_id` and `client_secret` from Clink as described in [How to get platform credentials](https://docs.clinkbill.com/guides/agent/platform_credentials.md). The emails, user IDs, and tokens in the examples are placeholders.

## 1. Prepare the environment and platform credentials

Clink configures credentials separately for each environment. Do not mix Sandbox UAT and Production tokens, user connections, payment methods, or card associations.

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

The RESTful API uses the platform `client_id` and `client_secret` issued through Pre-registration, and shares the same platform client with MCP. For how to apply, what Clink registers, and the RSA public key, see [How to get platform credentials](https://docs.clinkbill.com/guides/agent/platform_credentials.md). During onboarding, register the `redirect_uri` used for first-time user authorization and the Portal scopes `portal:read portal:write`.

In addition to the platform credentials, your backend also stores `CLINK_API_BASE_URL`. Keep user tokens, decrypted card fields, and payment credentials only on a trusted server. Do not put them in browser code, URLs, logs, Agent input, or ordinary business messages.

## 2. Get a platform token

Call the OAuth token endpoint with the platform credentials. The platform token is used only on the server to manage user connections and exchange for Portal Tokens. It cannot call user Portal APIs directly, and it cannot replace user authorization.

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

grant_type=client_credentials
&client_id={CLINK_CLIENT_ID}
&client_secret={CLINK_CLIENT_SECRET}
&scope=agent-platform:account
```

Cache the platform token according to `expires_in` in the response, and get a new one after it expires.

```json theme={null}
{
  "token_type": "Bearer",
  "access_token": "<PLATFORM_CLIENT_TOKEN>",
  "expires_in": 3600,
  "scope": "agent-platform:account"
}
```

## 3. Get a Portal Token for a user

The Customer Portal API uses a Portal Token that represents a single Clink user. First verify the user's email in your system, and associate the Clink user with a stable platform user ID.

New users, and users who already have a valid authorization for the current platform, only need `email`. When an existing Clink user authorizes the current platform for the first time, the redirect and PKCE fields are also required. We recommend generating a random `state` and a PKCE `code_verifier` before every request, computing the S256 `code_challenge`, and temporarily binding them to the platform user:

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

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

### Authorized user

If the user is new, or has already completed authorization for the current platform, the endpoint returns a Portal Token:

```json theme={null}
{
  "code": 200,
  "msg": "Success",
  "data": {
    "status": "authorized",
    "customer_id": "ccus_...",
    "token_type": "Bearer",
    "access_token": "<PORTAL_USER_TOKEN>",
    "expires_in": 3600,
    "scope": "portal:read portal:write"
  }
}
```

Store the Portal Token encrypted on the server and bind it only to the returned `customer_id`. Use it when calling the Customer Portal API:

```http theme={null}
Authorization: Bearer {PORTAL_USER_TOKEN}
```

### User authorization required

If Clink finds the user but the current platform does not yet have a valid authorized connection, the response is:

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

Pass `authorization_url` unchanged to the corresponding user. After the user completes the OAuth callback, confirm that `state` belongs to the platform user who initiated the request and validate `iss`, then call the Portal Token endpoint again to get the token. Do not skip authorization, do not modify the authorization URL, and do not hand the authorization link to the model to rewrite. If the user cancels or denies authorization, initiate a new authorization request.

A Portal Token can access only the current user's data. It cannot call `/mcp`, and an MCP user token cannot call `/agent/platform/**`.

### Token expiry and renewal

A Portal Token has only an access token and no refresh token. After it expires, use the platform token to call the Portal Token endpoint again for a new one. If the current platform's authorization for that user is no longer valid, the endpoint returns `authorization_required` again. The platform token is always the source of authority for getting user tokens and must be stored only on the server.

## 4. Persist user tokens on the server

A Portal Token is a server-side credential that represents a single user. After getting or refreshing it, the platform must persist the token, its expiry time, `customer_id`, and the granted scopes, encrypted and keyed by platform user and environment. Do not keep them only in the current request or in Agent process memory.

* Use your platform's own stable user ID as the primary key. Do not use the email as the primary key, and do not reuse tokens across users or environments.
* Before calling a Portal API, read the token from secure server-side storage and check `expires_in`. After it expires, exchange for a new one with the platform token. Portal Tokens have no refresh token.
* Update the storage atomically when the token is refreshed or re-exchanged, so concurrent requests do not overwrite the new token. When authorization becomes invalid, delete the invalid local token and guide the user through authorization again.
* Use the token only on a trusted server. Do not write it into browser code, URLs, logs, Agent input, ordinary business messages, or client-side storage.

Example data structure:

```typescript theme={null}
type UserPortalToken = {
  platformUserId: string;
  environment: "sandbox" | "production";
  customerId: string;
  accessToken: string; // stored encrypted
  expiresAt: Date;
  scope: string;
};
```

After a service restart, you should be able to read this record by platform user ID and environment and continue calling the Portal API. After the user re-authorizes, atomically replace the old record with the new token.

## 5. Read the account and payment methods

Use the Portal Token to verify the current user's status:

```http theme={null}
GET {API_BASE_URL}/agent/platform/me
Authorization: Bearer {PORTAL_USER_TOKEN}
```

Read the user's saved payment methods:

```http theme={null}
GET {API_BASE_URL}/agent/platform/payment-methods
Authorization: Bearer {PORTAL_USER_TOKEN}
```

The response does not include PAN or CVV. Your interface displays only the brand, last four digits, and Agent Pay-eligible identifier returned by Clink. Do not infer from the existence of a card that the user has authorized a purchase.

If the user only needs to add a card, create a hosted bind session. The request has no body, and the session is always created for the user represented by the Portal Token:

```http theme={null}
POST {API_BASE_URL}/agent/platform/bind-sessions
Authorization: Bearer {PORTAL_USER_TOKEN}
```

```json theme={null}
{
  "session_id": "vs_...",
  "url": "https://agent.clinkbill.dev/vault/vs_...",
  "expires_at": "2026-11-09T10:30:00Z"
}
```

Show the short-lived `url` from the response to the user unchanged, and treat it as a credential: do not forward it, screenshot it, or write it to logs. After the user completes the Clink-hosted page, read the payment methods again. A bind session does not create a purchase authorization. To add a card and authorize in one step, see "Add a card and authorize" in the next step. Do not collect real card numbers or CVV on platform pages.

## 6. Create a purchase authorization

Browser Pay first requires an active purchase authorization (instruction) that applies to this purchase. `payment_instrument_id` determines the signing mode:

| Mode | `payment_instrument_id` | Status after creation | What the user completes in the activation link |
| - | - | - | - |
| Authorize an existing card | Pass one of the user's existing payment methods | `CREATED` | Confirm the card and complete Passkey verification |
| Add a card and authorize | Omit | `PENDING` | Add a card and authorize in a single Passkey flow |

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

{
  "request_id": "platform-ref-001",
  "title": "Office supplies budget",
  "description": "Monthly office supplies purchase",
  "payment_instrument_id": "pi_...",
  "is_recurring": false,
  "mandates": [
    {
      "description": "Purchase office supplies",
      "amount_limit": "100.00",
      "currency_code": "USD",
      "merchant_category_code": "5732"
    }
  ]
}
```

The response returns `instruction_id`, `status`, `activation_url`, and `activation_expires_at`. Activation steps:

1. Send `activation_url` only to the target user, and open it in the user's own browser. It is an activation link, not a Portal Token, but anyone who has the link can obtain the user's Clink browser session while it is valid. Do not forward it, screenshot it, or write it to logs.
2. Poll `GET /agent/platform/authorizations/{instructionId}` until `status` is `ACTIVE`.
3. Read the `mandate_id` that covers this purchase from the instruction. Use it in the next step when you get the credential.

If the user does not complete activation, the instruction stays `CREATED` or `PENDING` and does not authorize any spending. `request_id` is the idempotency key: retrying with exactly the same parameters within 24 hours returns the same instruction, and reusing the same `request_id` with different parameters returns `409`. For all fields, see the [Portal API reference](https://docs.clinkbill.com/api-reference/agent-portal/portal_apis.md#create-instruction).

To view the current user's authorizations:

```http theme={null}
GET {API_BASE_URL}/agent/platform/authorizations
Authorization: Bearer {PORTAL_USER_TOKEN}
```

Before every payment, recheck the amount, currency, merchant, validity period, remaining limit, and authorization status. Do not reuse an authorization across users or environments.

## 7. Get a payment credential with Browser Pay

The merchant browser first reads the amount, currency, merchant information, and order identifier from the final checkout page. The platform backend then requests a one-time payment credential with the active instruction's `instruction_id`, the `mandate_id` that covers this purchase, and this real checkout information.

The Browser Pay credential endpoint is part of the Portal API:

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

{
  "instruction_id": "inst_...",
  "mandate_id": "mand_...",
  "client_reference_id": "platform-ref-001",
  "amount": "12.50",
  "currency_code": "USD",
  "merchant_url": "https://shop.example.com/checkout",
  "merchant_name": "Example Store",
  "merchant_country_code": "US",
  "merchant_order_id": "shop-order-1"
}
```

`amount` is a decimal string in major currency units, and `merchant_url` must be the HTTPS checkout page URL. `client_reference_id` is the idempotency key for this purchase: save it and use the same value when you report the result. Retrying with the same parameters returns the same credential and does not issue a new one. For optional fields such as product details `products` and shipping address `shipping_address`, see the [Portal API reference](https://docs.clinkbill.com/api-reference/agent-portal/portal_apis.md#get-credential).

The response mode depends on whether the platform has registered a public key with Clink:

| Mode | Response | Platform handling |
| - | - | - |
| Public key registered (`matched: true`) | Sealed `key` / `payload` and `expires_at` | Decrypt only on a trusted server with the RSA private key. For decryption, see [Encrypted credential decryption](https://docs.clinkbill.com/guides/agent/browser_pay/decryption.md). |
| No public key registered (`matched: false`) | Plaintext DPAN and dynamic CVV | Returned only when plaintext mode is enabled on the server. The switch is off by default, in which case the endpoint returns `71211012` (`credential_public_key_not_configured`). Registering a public key is recommended for Production. |

Use decrypted or plaintext card fields only briefly inside a controlled checkout executor. Do not write them to logs, databases, model context, or browser local storage, and do not keep them after checkout. Use the expiry in the decrypted payload to determine how long the credential is usable; `expires_at` is only an estimate. If the credential response times out, retry with the same `client_reference_id` to retrieve the original credential. Do not create a new authorization.

## 8. Complete payment on the merchant page

Your browser executor is responsible for:

1. Opening the merchant checkout page and reading the final amount, currency, and merchant information.
2. Confirming that this information exactly matches the purchase authorization and the payment credential request.
3. Using the credential in a trusted payment-fill component, without exposing real card fields to the model or ordinary logs.
4. Submitting the merchant order and reading the final result from the merchant page or order API.
5. Calling the payment result endpoint once the result is clear.

A successful Clink API response, a 2xx response from the PSP, or a successfully issued credential does not on its own prove that the merchant order is paid. Only the final result from the merchant page or order API determines the next step.

## 9. Confirm the payment result

After you confirm the merchant result, report it with the same `client_reference_id`. This is the only entry point for confirming the transaction result:

```http theme={null}
POST {API_BASE_URL}/agent/platform/credential-results
Authorization: Bearer {PORTAL_USER_TOKEN}
Content-Type: application/json

{
  "client_reference_id": "platform-ref-001",
  "outcome": "SUCCEEDED",
  "paid_amount": "12.50",
  "currency_code": "USD",
  "failure_reason": null
}
```

| `outcome` | When to use |
| - | - |
| `SUCCEEDED` | The merchant explicitly confirms that payment succeeded. |
| `FAILED` | The card was submitted, and the merchant explicitly shows a failure. |
| `NOT_SUBMITTED` | The card was never submitted. |
| `UNKNOWN` | The card was submitted, but the result cannot be determined. Clink returns `PENDING` and `report_deadline`; report the final result before the deadline. |

Reporting rules:

* Report the final result within 24 hours of credential issuance (before `report_deadline`). Late reports return `410`.
* For `FAILED` and `NOT_SUBMITTED`, you can include `failure_reason`, such as `CARD_DECLINED`, `AMOUNT_CHANGED`, or `BUYER_CANCELLED`.
* `paid_amount` and `currency_code` must be sent together. If they do not match the authorized amount, the response returns `paid_amount_mismatch: true`.
* Reporting a failure after a success has been recorded (or the reverse) returns `409` (`result_already_reported`).

When the result is unknown, first query the original merchant order, then report the final result. Do not switch cards, create a second authorization, or resubmit the same payment. For the full rules, see the [Portal API reference](https://docs.clinkbill.com/api-reference/agent-portal/portal_apis.md#report-credential-result).

After the order is complete, you can read the order and its refund history:

```http theme={null}
GET {API_BASE_URL}/agent/platform/orders/{orderId}
Authorization: Bearer {PORTAL_USER_TOKEN}
```

The Customer Portal API can read refund records but cannot initiate refunds. Refunds go through other business flows provided by Clink.

## Related documentation

* [How to get platform credentials](https://docs.clinkbill.com/guides/agent/platform_credentials.md): request a `client_id` and `client_secret`, and compare the three registration methods.
* [Agent Portal overview](https://docs.clinkbill.com/api-reference/agent-portal/overview.md): capability boundaries, token types, and the API list.
* [Portal token](https://docs.clinkbill.com/api-reference/agent-portal/portal_token.md): request, response, and authorization status for exchanging a user Portal Token.
* [Portal APIs](https://docs.clinkbill.com/api-reference/agent-portal/portal_apis.md): account, payment method, purchase authorization, credential, result, order, and settings APIs.
* [Browser Pay guide](https://docs.clinkbill.com/guides/agent/browser_pay): merchant browser checkout, credential protection, and result recovery.
* [Encrypted credential decryption](https://docs.clinkbill.com/guides/agent/browser_pay/decryption.md): examples of decrypting `key` / `payload`.
* [How to integrate MCP](https://docs.clinkbill.com/guides/agent/platform_integration/mcp_integration.md): integrate Browser Pay through MCP + OAuth.
* [How to integrate Vault SDK](https://docs.clinkbill.com/guides/agent/platform_integration/vault_sdk.md): an SDK integration in which the Agent handles only placeholder cards.


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