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

# API errors

> The error envelope, every error code with its status, and how to handle each one.

Errors return a `4xx` or `5xx` status and the same envelope. Branch on `code`: it is stable, while `message` is for people and may change.

```json theme={"system"}
{
  "success": false,
  "message": "amount: Invalid input: expected number, received undefined",
  "code": "validation_error",
  "doc_url": "https://docs.tolt.com/errors#validation_error",
  "errors": [
    {
      "field": "amount",
      "message": "Invalid input: expected number, received undefined"
    }
  ]
}
```

`errors` lists each failing field when the problem is tied to specific fields, as with `validation_error`, `invalid_reference`, `already_exists` and `invalid_cursor`. Treat it as optional on any error. `doc_url` links to the code's section below.

## All codes

| Code | Status | Retry? |
| - | - | - |
| [`validation_error`](#validation_error) | 400 | Fix the request |
| [`invalid_reference`](#invalid_reference) | 400 | Fix the id |
| [`invalid_cursor`](#invalid_cursor) | 400 | Use a cursor from a response |
| [`bad_request`](#bad_request) | 400 | Fix the request |
| [`payload_too_large`](#payload_too_large) | 413 | Send a smaller body |
| [`unauthorized`](#unauthorized) | 401 | Fix the key |
| [`forbidden`](#forbidden) | 403 | No |
| [`not_found`](#not_found) | 404 | No |
| [`already_exists`](#already_exists) | 409 | No, fetch the existing one |
| [`commission_paid`](#commission_paid) | 400 | No |
| [`commission_frozen`](#commission_frozen) | 409 or 400 | No |
| [`transaction_refunded`](#transaction_refunded) | 409 | No |
| [`commission_exists`](#commission_exists) | 409 | No |
| [`conflict`](#conflict) | 409 | After the state changes |
| [`rate_limited`](#rate_limited) | 429 | Yes, after `Retry-After` |
| [`internal_error`](#internal_error) | 500 | Yes, with backoff |

## Codes

### validation\_error

A field is missing or invalid. `errors` names every failing field and why.

### invalid\_reference

An id in the body, such as `partner_id`, `link_id` or `customer_id`, doesn't exist in your organization or doesn't fit where you put it. `errors` names the field.

### invalid\_cursor

`starting_after` or `ending_before` isn't a valid position in this list: the cursor is malformed, it was issued for a different `sort`, or the item id isn't in the list. Pass a `next_cursor` or `prev_cursor` from a previous page. An item id is accepted as a position only with `sort=created_at`; with `sort=updated_at` it's a `validation_error`.

### bad\_request

The request can't be processed for another reason, such as a value out of range. `message` explains.

### payload\_too\_large

The request body is over 1 MB. Split bulk work into smaller requests.

### unauthorized

The API key is missing, malformed or revoked. See [authentication](/api-reference/authentication).

### forbidden

The key is valid but not allowed to do this.

### not\_found

No resource with this id exists in your organization, or the route doesn't exist.

### already\_exists

A resource with these details exists, for example a partner with the same email in the program, a taken link value, or a customer with the same email.

### commission\_paid

The commission is paid and locked: it can't be approved, declined, updated or deleted. Deleting a customer with paid commissions is blocked too.

### commission\_frozen

The commission can't be changed. `409` when it is part of a refund (a refund commission, or one on a refunded transaction). `400` when its invoice is confirmed or linked to autopayouts.

### transaction\_refunded

The transaction is refunded. It can't be updated, deleted, refunded again, or have its flows rerun, and you can't create or update a commission on it.

### commission\_exists

The transaction already has a commission. Rerunning its flows would create a duplicate, and it can't be deleted until the commission is removed.

### conflict

The action doesn't fit the resource's current state. `message` explains.

### rate\_limited

The key went over 25 requests per second. Wait `Retry-After` seconds, then retry. See [rate limits](/api-reference/rate-limits).

### internal\_error

Something failed on Tolt's side. Retry with backoff; for a write, check first whether it took effect. If it persists, email [support@tolt.io](mailto:support@tolt.io) with the `Request-Id` response header.

## Handle errors in code

The SDKs throw a typed error for `400`, `401`, `404`, `409` and `429`, and a generic `ToltApiError` for anything else. The envelope is on `body`:

```ts TypeScript theme={"system"}
import { ToltApi, ToltApiClient } from "@tolt/api";

const tolt = new ToltApiClient({ token: process.env.TOLT_API_KEY! });

try {
  await tolt.transactions.refund({ id: "txn_5MqRw9KxL2tZb8NvYc4HdFsJ" });
} catch (err) {
  if (
    err instanceof ToltApi.ConflictError &&
    err.body.code === "transaction_refunded"
  ) {
    // already refunded: nothing to do
  } else {
    throw err;
  }
}
```

```python Python theme={"system"}
import os
from tolt import ConflictError, ToltApi

client = ToltApi(token=os.environ["TOLT_API_KEY"])

try:
    client.transactions.refund(id="txn_5MqRw9KxL2tZb8NvYc4HdFsJ")
except ConflictError as err:
    if err.body.code != "transaction_refunded":
        raise
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.