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

# Errors

> The error body, what each status means, and the codes worth branching on

## The shape

Every Developer API error is JSON with the same two fields:

```json theme={null}
{
  "message": "Payment not found.",
  "code": "NOT_FOUND"
}
```

`message` is written for a human reading a log. It is not a stable contract and its wording can change. `code` is the stable identifier: branch on `code`, never on `message`.

Validation failures are the exception. They report which field failed and carry no `code`:

```json theme={null}
{
  "success": false,
  "error": {
    "issues": [
      { "path": ["limit"], "message": "Number must be less than or equal to 100" }
    ]
  }
}
```

<Note>
  The older unversioned endpoints answer differently. `/payment-method` returns the same `{ message, code }` shape, while the remaining unversioned endpoints answer with plain text. Only the Developer API is uniform.
</Note>

## Status codes

| Status | Meaning | What to do |
| - | - | - |
| `400` | The request was malformed or failed validation | Fix the request. Retrying unchanged will fail again |
| `401` | The `x-api-key` header is missing, malformed or revoked | Check the key. Do not retry |
| `403` | The key is valid but cannot be used here | See [When you get a 403](#when-you-get-a-403). Do not retry |
| `404` | No such resource **in your organization** | Check the id. Do not retry |
| `429` | Rate limited | Wait for `Retry-After`, then retry. See [Rate limits](/core-concepts/rate-limits) |
| `500` | Something broke on our side | Retry with backoff. If it persists, contact support |

### 404 also means "not yours"

An id belonging to another organization returns `404`, exactly as an id that does not exist does. This is deliberate: answering `403` for a real id and `404` for an unused one would let anyone with an API key confirm whether a given id exists on the platform.

The practical consequence is that a `404` never distinguishes "wrong id" from "someone else's id". Both mean the same thing to your integration.

## Codes

### Access

| Code | Meaning |
| - | - |
| `UNAUTHORIZED` | The `x-api-key` header is missing or the key is invalid |
| `KEY_NOT_SCOPED` | The key is not bound to an organization |
| `MERCHANT_ONLY` | The organization is not a merchant. This API is merchant only |
| `INSUFFICIENT_SCOPE` | The role that owns the key grants no access |
| `RATE_LIMIT_EXCEEDED` | Too many requests in the current window |

### General

| Code | Meaning |
| - | - |
| `NOT_FOUND` | No such resource in your organization |
| `INVALID_CURSOR` | The `cursor` was not issued by this API |

## When you get a 403

The Developer API only reads, so a `403` is never about an operation being disallowed. There are three causes, and the `code` tells you which:

| Code | Cause | Fix |
| - | - | - |
| `KEY_NOT_SCOPED` | The key is not bound to an organization. Keys created before organization binding can look like this | Create a new key in the dashboard |
| `MERCHANT_ONLY` | The organization is not a merchant | This API does not serve manufacturer organizations |
| `INSUFFICIENT_SCOPE` | The role of the member who created the key is not mapped to any access | Create the key under an owner, admin or member account |

<Note>
  Write operations are not "forbidden" on this API, they do not exist. A `POST`, `PATCH` or `DELETE` to any Developer API path returns `404`, because no such route is registered. Creating and editing data is done through the [payment-acceptance endpoints](/one-time-payment/create) and the dashboard.
</Note>

## Retrying safely

`400`, `401`, `403` and `404` are permanent for an unchanged request. Retrying them wastes your rate-limit budget and will not succeed.

`429` and `500` are worth retrying with exponential backoff.

Because every endpoint is a read, retrying is always safe: no request on this API changes anything.
