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

# Troubleshooting

> Common integration errors by status and code, with the cause and the fix, for identity verification and Risk Intelligence.

Find the response you received, then follow the fix. Identity verification (`/api/kyc`) returns a flat error body; Risk Intelligence (`/api/v1`) wraps it in an envelope. Always branch on the code, never on the message text.

**`/api/kyc and /api/identity`**

```json title="/api/kyc and /api/identity"
{ "error": "secret_key_required" }
```

**`/api/v1 (illustrative values)`**

```json title="/api/v1 (illustrative values)"
{
  "error": {
    "type": "PERMISSION_ERROR",
    "code": "secret_key_required",
    "message": "This endpoint requires a secret key.",
    "request_id": "9a88638d-0c1e-4c5e-9d2a-1f0f6a7b2c11"
  }
}
```

## Authentication

| You see                                         | Cause                                                            | Fix                                                                                        |
| ----------------------------------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `401` `Missing or invalid Authorization header` | No `Authorization: Bearer ...` header, or a malformed one.       | Send the full key as a Bearer token.                                                       |
| `401` `Invalid API key`                         | The key is unknown or has been revoked.                          | Check you copied the whole key. Create a new one if it was revoked.                        |
| `403` `secret_key_required`                     | A publishable key called an endpoint that returns identity data. | Call it from your backend with a secret key.                                               |
| `403` `environment_mismatch`                    | The key's environment is not served by that host.                | Use `sk_test_` with the Sandbox host and `sk_live_` with Production for Risk Intelligence. |
| `403` `origin_not_allowed` or `ip_not_allowed`  | The key is restricted and this caller is not on its list.        | Add the origin or address in the console, or call from an allowed one.                     |
| `403` `business_not_approved`                   | A live key was used before your business was approved.           | Keep building in Sandbox and complete **Onboarding**.                                      |

## Starting a verification

| You see                        | Cause                                                                                | Fix                                                                       |
| ------------------------------ | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------- |
| `422` `only_test_ids_allowed`  | A real ID number was sent with a test key.                                           | Use a [test ID](/get-started/sandbox-testing), such as `00000000001`.     |
| `422` `workflow_not_available` | The workflow is a draft, archived or suspended, or belongs to the other environment. | Publish it in the environment your key belongs to.                        |
| `404` `workflow_not_found`     | The workflow ID does not exist for this key's organisation and environment.          | Copy the ID from **Workflows** with the right environment selected.       |
| `403` `id_type_not_allowed`    | That country and ID type is not enabled for your organisation.                       | Call `GET /api/kyc/config` to see what is enabled.                        |
| `400` `invalid_input`          | The body failed validation.                                                          | Read `message`, which names the first problem.                            |
| `429` `rate_limited`           | Too many requests in the window.                                                     | Wait and retry with backoff. See [Rate limits](/get-started/rate-limits). |

## Reading results

| You see                                           | Cause                                                                        | Fix                                                                   |
| ------------------------------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| Status stays `processing`                         | Checks are still running, or the workflow is waiting on something.           | Read `waitingOn` on the status response. Prefer a webhook to polling. |
| Status is `in_progress`                           | The customer has not submitted yet.                                          | Nothing is wrong. `step` shows where they are.                        |
| Status is `error`                                 | A system or provider fault. It is not a decline, and the charge is returned. | Let the customer try again.                                           |
| `404` on a verification you just created          | You are reading it with a key from the other environment.                    | Use the same environment throughout a flow.                           |
| The SDK's `onSubmit` fired but there is no result | `onSubmit` means submitted, not decided.                                     | Wait for the webhook or read the status.                              |

## Webhooks

| You see                                             | Cause                                                                                    | Fix                                                                                              |
| --------------------------------------------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| The signature never matches                         | The body was parsed and re-serialised before verifying.                                  | Verify against the raw request bytes. See [Verify signatures](/integrate/webhooks/signatures).   |
| The signature matches in Sandbox but not Production | Each endpoint has its own secret.                                                        | Use the Production endpoint's secret.                                                            |
| The same event arrives more than once               | Deliveries are retried until your endpoint returns `2xx`.                                | Store the event `id` and ignore repeats.                                                         |
| No events arrive                                    | The endpoint is registered in the other environment, or is not subscribed to that event. | Check the endpoint's environment and its event list, then send a test delivery from the console. |
| Events arrive out of order                          | Delivery order is not guaranteed.                                                        | Read the current state from the API before acting.                                               |

## Still stuck?

Check the [status page](https://status.toden.co) first. Then email [hello@toden.co](mailto:hello@toden.co) with the request time, the endpoint, the HTTP status and, for `/api/v1`, the `request_id` from the error. Never send an API key.