> Toden documentation. For the full index fetch /llms.txt. Every page is available as Markdown by appending .md to its URL. Identity verification uses /api/kyc, Risk Intelligence uses /api/v1; they are separate contracts with different error shapes. Never invent an endpoint, field or enum value: read the API reference page for the operation first.

# Requests, retries and idempotency

> Idempotency rules for each API family, safe retry behaviour, pagination, request IDs and versioning.

Networks fail. The rules below let you retry any request without charging twice, creating a second verification or recording a second decision.

## Idempotency

The two contracts identify a repeated request differently.

#### Identity verification (/api/kyc)

Send your own unique value as `metadata.requestId` when you create a verification.

```json
{
  "country": "NG",
  "idType": "bvn",
  "idNumber": "00000000001",
  "metadata": { "requestId": "order_1001", "userId": "user_42" }
}
```

* A repeat with the **same** `requestId` returns the original verification with `200`. No new verification is created and nothing is charged again.
* Use a value tied to your own business action, such as an order or application ID, so that a retry after a crash still carries it.
* One-off checks accept a top-level `requestId` with the same behaviour.
* Sessions are keyed by `externalUserId`: a returning customer resumes their existing attempt.

#### Risk Intelligence (/api/v1)

Operations that create or change something require an `Idempotency-Key` header: 8 to 200 characters from `A-Z a-z 0-9 . _ : ~ -`.

```bash
curl -X POST "https://sandbox-api.toden.co/api/v1/risk-assessments" \
  -H "Authorization: Bearer $TODEN_SECRET_KEY" \
  -H "Idempotency-Key: onboarding-user_42-2026-08-20" \
  -H "Content-Type: application/json" \
  -d @body.json
```

* Same key, same request: you get the original result. No second charge, signal, alert or case.
* Same key, different request: `409` with type `IDEMPOTENCY_CONFLICT`.
* Transactions and activities are different: their required `externalActivityId` **is** the idempotency identity. The same ID and payload replays the stored decision; the same ID with a changed payload conflicts.

Each endpoint page shows whether the header is required.

## What is safe to retry

| Situation                                              | Retry?      | How                                                                            |
| ------------------------------------------------------ | ----------- | ------------------------------------------------------------------------------ |
| `GET` request failed or timed out                      | Yes         | Retry as is.                                                                   |
| Write with an idempotency key or `requestId` timed out | Yes         | Resend the **same** body and key.                                              |
| Write with no idempotency identity timed out           | Not blindly | Read the current state first, then decide.                                     |
| `429`                                                  | Yes         | Wait, then retry with increasing delays.                                       |
| `502`, `503`                                           | Yes         | Back off, starting at a second and doubling, for a bounded number of attempts. |
| `400`, `401`, `403`, `404`, `409`, `422`               | No          | The request will fail the same way. Fix it first.                              |

> **Warning**
>
> A risk decision that could not be made is never an approval. If a transaction or activity call fails or times out, hold the action or apply your own fallback rule. Do not treat an error as `ALLOW`.

## Pagination

| Family           | Style       | Parameters                                                                                                  |
| ---------------- | ----------- | ----------------------------------------------------------------------------------------------------------- |
| `/api/v1`        | Cursor      | `limit` (1 to 100, default 50) and `cursor`. Pass the cursor from the previous page until none is returned. |
| `/api/kyc` lists | Page number | `page` and `pageSize` (up to 100). The response carries the total.                                          |

A few `/api/v1` list endpoints are page-numbered instead; their reference pages say so.

## Request IDs

On `/api/v1` you may send `X-Request-Id` (up to 128 characters) to correlate a call with your own logs. Every `/api/v1` error returns a `request_id`. Quote it when you contact support.

## Versioning

* `/api/v1` is the current major version. You may pin the contract with the optional `Myaza-Version` request header; leave it out to use the default. The header keeps its earlier name.
* Additive changes are not breaking: new optional response fields, new endpoints, new webhook event types. Write clients that ignore what they do not recognise.
* A new enum value can still break a client that matches exhaustively. Always include a default branch.
* Removing or renaming a field, making an optional field required, or changing what a field means requires a new version.