> ## Documentation Index
> Fetch the complete documentation index at: https://docs.proof.community/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Handling

> Real error codes returned by the Proof API

All Proof API errors return a JSON body of the shape:

```json theme={null}
{
  "error": "<slug>",
  "code": "<code>",
  "message": "<human-readable description>"
}
```

The HTTP status code, the `error` slug, and the `code` together identify the failure mode. Use them together for programmatic handling; use `message` for logs only.

The `X-Request-Id` response header carries a request identifier; include it in any support ticket.

## Error matrix

| HTTP | `error`                | Example `code` values                                                       | Cause                                                                           | Action                                              |
| ---- | ---------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------- |
| 400  | `invalid_request`      | `validation_error`, `decode_error`, `missing_param`, `currency_not_allowed` | Bad request body, missing field, or `currency` not enabled for your integration | Fix the request                                     |
| 400  | `wallet_not_supported` | `WALLET_NOT_SUPPORTED`                                                      | No active wallet for the requested `(currency, network)`                        | Ask Proof to provision the wallet                   |
| 401  | `unauthorised`         | `missing_token`, `invalid_token`                                            | `Authorization` missing or `client_token` unknown/inactive                      | Check the header value; rotate token if compromised |
| 403  | `forbidden`            | `forbidden_origin`                                                          | Request IP or `Origin` not on your allowlist                                    | Verify your server IP is registered with Proof      |
| 404  | `not_found`            | `not_found`                                                                 | Transaction, user, or wallet does not exist or does not belong to your partner  | Verify the identifier                               |
| 500  | `internal_error`       | `db_error`, `token_error`                                                   | Proxy-side fault                                                                | Retry once; contact support if it persists          |
| 500  | `publish_failed`       | `kafka_unavailable`                                                         | Proof failed to publish a lifecycle event (webhook path only)                   | Retry your webhook delivery; Proof will redeliver   |
| 502  | `upstream_error`       | `provider_error`                                                            | An external service returned an error                                           | Retry with the same body                            |
| 502  | `upstream_unavailable` | `upstream_unavailable`, `upstream_invalid_response`                         | An external service is unreachable or returned an unparseable body              | Retry; back off if it persists                      |

## Common scenarios

### `currency_not_allowed` (400)

The `currency` field of your session request is not one of the currencies enabled for your integration.

```json theme={null}
{
  "error": "invalid_request",
  "code": "currency_not_allowed",
  "message": "currency=BTC"
}
```

Ask the Proof team to add the currency, or pick one already on your list.

### `wallet_not_supported` (400)

You requested a `(currency, network)` pair for which Proof has no active wallet for your partner.

```json theme={null}
{
  "error": "wallet_not_supported",
  "code": "WALLET_NOT_SUPPORTED",
  "message": "no active wallet for currency=USDT network=BEP20"
}
```

Either request a network for which Proof has a wallet (e.g. `TRC20`), or ask the Proof team to provision a wallet for the desired pair.

### `forbidden_origin` (403)

The request came from an IP address not on your partner record's allowlist.

* Confirm the IP address your backend server makes outbound requests from.
* Ask the Proof team what IP is on file for your partner, or to add a new IP.
* When adding a new server or moving to a new cloud region, register the IP before the traffic starts.

### `upstream_error` (502)

An external service returned an error. The proxy does not retry automatically for `POST /widget/session` — your client should.

```javascript theme={null}
async function createSessionWithRetry(body, attempt = 0) {
  const res = await fetch("https://DOMAIN/widget/session", {
    method: "POST",
    headers: { Authorization: "Bearer <client_token>", "Content-Type": "application/json" },
    body: JSON.stringify(body)
  });
  if (res.status === 502 && attempt < 2) {
    await new Promise(r => setTimeout(r, 500 * (attempt + 1)));
    return createSessionWithRetry(body, attempt + 1);
  }
  return res.json();
}
```

## Widget-level errors

The widget's `onStatusChange` callback also fires for payment-side failures (e.g. card declined, 3DS failure). These are informational — the authoritative status comes from the API. See [Transaction Status](/on-off-ramp/transaction-status).
