> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.crisscross.money/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.crisscross.money/_mcp/server.

# Authentication

> Learn how to authenticate requests to the CrissCross APIs (Collect, Exchange, and Payouts) using access tokens.

### Authentication

All requests to the CrissCross APIs (Collect, Exchange, and Payouts) require an access token. CrissCross uses OAuth 2.0 style access tokens for authenticating API requests. Authentication is machine-to-machine: request a token with your `client_id` and `client_secret`, then include it as a Bearer token in the `Authorization` header on every subsequent request.

#### Request an access token

`POST https://api.crisscross.money/v1/auth/oauth2/token`

```bash
curl --request POST 'https://api.crisscross.money/v1/auth/oauth2/token' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET"
  }'
```

The body must be JSON — form-encoded bodies are rejected with `400`. No `grant_type`, `scope`, or `audience` is needed; client credentials is implied.

#### Response

```json
{
  "access_token": "eyJhbGci...",
  "token_type": "Bearer",
  "expires_in": 86400
}
```

Store the token until it expires. `expires_in` is the token's lifetime in seconds (currently 86400, or 24 hours). There is no refresh token; request a new token when the current one expires.

#### Use the token

```bash
curl --request GET 'https://api.crisscross.money/v1/<your-endpoint>' \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN'
```

#### Handling authentication failures

Errors from the token endpoint are a single-field envelope, `{"error": "<description>"}`:

| Status                    | Body                                                    | Cause                                                                                                      |
| ------------------------- | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `400 Bad Request`         | `{"error": "Invalid request body"}`                     | The body is malformed or not JSON.                                                                         |
| `400 Bad Request`         | `{"error": "client_id and client_secret are required"}` | A required field is missing.                                                                               |
| `401 Unauthorized`        | `{"error": "Invalid client credentials"}`               | Wrong credentials — an unknown `client_id` and a wrong `client_secret` are deliberately indistinguishable. |
| `503 Service Unavailable` | `{"error": "Authentication service unavailable"}`       | Temporary outage — retry with backoff.                                                                     |

On every other API call, a missing, invalid, or expired access token returns `401` with a different envelope: `{"message": "Unauthorized"}`. If you see that shape, request a new token and retry.

#### Best practices

* **Secure storage:** keep your `client_secret` and access tokens in a secrets manager or environment variables, never in source code.
* **Rotate on compromise:** if your `client_secret` is exposed, rotate it immediately.