# Payments AI — agent authentication

How an agent obtains credentials for Payments AI MCP and REST. This file is public. It does not grant access.

## MCP (recommended)

Streamable HTTP endpoint:

`https://mor.payments.ai/api/mcp`

### OAuth 2.1 + PKCE (S256)

Use this unless the host cannot complete a browser redirect.

1. Register a client at `POST https://mor.payments.ai/api/v1/oauth/register` (dynamic client registration) or let the MCP host do it from protected-resource metadata.
2. Send the user through `https://mor.payments.ai/api/v1/oauth/authorize` with `response_type=code` and `code_challenge_method=S256` (optional `scope` for client hints; the consent screen always lists every supported scope for the human to approve).
3. On the dashboard consent screen, the human chooses **read** and **write** tool scopes (all selected by default); **Approve** sends `granted_scopes` for the token.
4. Exchange the code at `https://mor.payments.ai/api/v1/oauth/token`.
5. Call MCP with `Authorization: Bearer <access_token>`.

Discovery:

| Document                        | URL                                                                   |
| ------------------------------- | --------------------------------------------------------------------- |
| Authorization server (RFC 8414) | https://mor.payments.ai/.well-known/oauth-authorization-server       |
| Protected resource (RFC 9728)   | https://mor.payments.ai/.well-known/oauth-protected-resource/api/mcp |
| MCP Server Card (SEP-1649)      | https://mor.payments.ai/.well-known/mcp/server-card.json             |
| Legacy MCP card                 | https://mor.payments.ai/.well-known/mcp.json                         |

Host config (OAuth, no token in the file):

```json
{
  "mcpServers": {
    "payments-ai": {
      "url": "https://mor.payments.ai/api/mcp"
    }
  }
}
```

The first tool call opens a browser. The human signs in on the Payments AI dashboard and chooses which MCP tool scopes to grant (grouped as read vs write access).

### User MCP Key (bearer fallback)

Use only when the host cannot do OAuth.

1. Human signs in at https://mor.payments.ai
2. Developer Tools → MCP Tokens → pick the **Sandbox** or **Live** tab → Generate, choose tool scopes (`pai_mcp_*`, shown once, max TTL 7 days)
3. Use the MCP URL of the tab the token was generated on. A Sandbox token works only with `https://sandbox.mor.payments.ai/api/mcp`; a Live token works only with `https://mor.payments.ai/api/mcp`.
4. Host config (Live token shown):

```json
{
  "mcpServers": {
    "payments-ai": {
      "url": "https://mor.payments.ai/api/mcp",
      "headers": {
        "Authorization": "Bearer pai_mcp_YOUR_TOKEN"
      }
    }
  }
}
```

Never put this token in source, prompts you will commit, or a shared skill file.

Rejected requests return **401** with a JSON body `{ statusCode, error, code, message, docUrl }`. The `WWW-Authenticate` header keeps the OAuth discovery challenge. Branch on `code`:

| `code`                         | Meaning and recovery                                                                                                                                                                    |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `missing_token`                | No `Authorization: Bearer` header. Add it, or use OAuth.                                                                                                                                |
| `invalid_token`                | Unknown, revoked or malformed token (a revoked key, or a placeholder such as `pai_mcp_YOUR_TOKEN`, lands here). Ask the human for their real token, or a new one from the matching tab. |
| `token_expired`                | Message is exactly `MCP Session Expired. Please generate a new token in the Developer Dashboard.` Ask the human for a new token.                                                        |
| `token_validation_unavailable` | The token could not be checked right now. Retry later; do not ask for a new token.                                                                                                      |

## Scopes

| Scope              | Tools                                                                |
| ------------------ | -------------------------------------------------------------------- |
| `docs:read`        | `search_payments_ai_docs`                                            |
| `merchant:create`  | `create_merchant_account`                                            |
| `merchant:read`    | `get_my_merchant`, `get_merchant_activation_status`                  |
| `merchant:go_live` | `go_live`                                                            |
| `product:create`   | `create_product`, image upload/confirm                               |
| `product:read`     | `list_products`                                                      |
| `checkout:read`    | `get_checkout_customization`                                         |
| `checkout:write`   | `update_checkout_customization`, `set_checkout_logo`, `create_order` |

Checkout branding tools need `checkout:read` / `checkout:write`. If you see "Insufficient scope", the human must mint a new token or re-consent with those scopes.

## Merchant Public API (REST)

Not MCP. Sandbox base: `https://sandbox.mor.payments.ai/api`

```
Authorization: Bearer pai_test_<64 hex>
```

Live keys use `pai_live_*` against the live API host. Do not send live keys to the docs Try It panel.

Partners create sub-merchants with `POST /v1/public-api/merchants` using their own Merchant API Key (`pai_test_*` / `pai_live_*`); the parent is derived from the key. Assign parent-team users to a sub-merchant with `POST /v1/public-api/merchants/{merchantId}/members`. Key lifecycle for self or children: `/v1/public-api/merchants/{merchantId}/api-keys`. Single-merchant flows can also create via dashboard or MCP (`create_merchant_account`). Catalog routes are `/v1/public-api/merchants/{merchantId}/…`.

## What this server will not do

No payout, refund, key revoke, or webhook-secret tools over MCP. Do not invent them.

## Stuck

- MCP setup: https://doc.mor.payments.ai/mcp
- Skill: https://doc.mor.payments.ai/SKILL.md
- Support: merchants@payments.ai
