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

# Errors

> Error response format and the full catalog of API error codes

Every error the API returns uses the same response shape, so you can handle failures with a single code path. Match on the `code` field to decide what to do next, and keep the `requestId` for [support](mailto:support@lumx.io) requests. The catalog below covers the most common codes and is not exhaustive, so handle unknown codes gracefully.

## Error response

```typescript ErrorResponse theme={null}
interface ErrorResponse {
  requestId: string
  timestamp: string
  path: string
  status: number
  code: string
  message: string
  validationErrors?: { param: string; message: string }[]
}
```

```json Example theme={null}
{
  "requestId": "9f6f9741-4c65-4e0b-a48f-16d5b34d9e2f",
  "timestamp": "2026-07-31T10:30:45.123Z",
  "path": "/transactions/on-ramp",
  "status": 403,
  "code": "KYC_NOT_APPROVED",
  "message": "KYC/B is not approved for this customer"
}
```

| Field              | Description                                              |
| :----------------- | :------------------------------------------------------- |
| `requestId`        | Unique request ID. Include it when contacting support.   |
| `timestamp`        | ISO 8601 time the error was generated.                   |
| `path`             | Path of the request that failed.                         |
| `status`           | HTTP status code.                                        |
| `code`             | Machine-readable error code. Match on this field.        |
| `message`          | Human-readable description.                              |
| `validationErrors` | Field-level details. Only present on `VALIDATION_ERROR`. |

<Warning>
  Handle errors by `code` and not by `message`. The `message` is subject to changes. On any `5xx` response the `message` is always `"An unexpected error occurred."`, while the `code` stays specific.
</Warning>

## Validation errors

When the request body fails validation, the response is a `400` with `code` `VALIDATION_ERROR` and one entry per invalid field in `validationErrors`.

```json Response theme={null}
{
  "requestId": "9f6f9741-4c65-4e0b-a48f-16d5b34d9e2f",
  "timestamp": "2026-07-31T10:30:45.123Z",
  "path": "/customers",
  "status": 400,
  "code": "VALIDATION_ERROR",
  "message": "The request body contains invalid parameters.",
  "validationErrors": [
    { "param": "taxId", "message": "taxId must be a string" }
  ]
}
```

## Error codes

### General

| Code                        | Status | When it happens                                                |
| :-------------------------- | :----- | :------------------------------------------------------------- |
| `VALIDATION_ERROR`          | 400    | Request body failed validation. Details in `validationErrors`. |
| `BAD_REQUEST`               | 400    | Request is invalid without a more specific code.               |
| `UNAUTHORIZED`              | 401    | API key is missing, invalid, or inactive.                      |
| `FORBIDDEN`                 | 403    | API key lacks the scope required by the route.                 |
| `RESOURCE_PROJECT_MISMATCH` | 403    | Resource exists but belongs to another project.                |
| `NOT_FOUND`                 | 404    | Resource does not exist.                                       |
| `CONFLICT`                  | 409    | Request conflicts with the current resource state.             |
| `RESOURCE_ALREADY_EXISTS`   | 409    | A resource with the same unique value already exists.          |
| `UNPROCESSABLE_ENTITY`      | 422    | Request violates a business rule.                              |
| `TOO_MANY_REQUESTS`         | 429    | Rate limit exceeded.                                           |
| `INTERNAL_SERVER_ERROR`     | 500    | Unexpected server error.                                       |
| `NOT_IMPLEMENTED`           | 501    | Operation not supported by the active provider.                |

### Customers

| Code                       | Status | When it happens                                           |
| :------------------------- | :----- | :-------------------------------------------------------- |
| `CUSTOMER_NOT_FOUND`       | 404    | Customer does not exist.                                  |
| `CUSTOMER_LIMIT_REACHED`   | 403    | Project reached its maximum number of customers.          |
| `CUSTOMER_UPDATE_FAILED`   | 500    | Customer update failed.                                   |
| `WALLET_CREATION_FAILED`   | 500    | Wallet creation failed.                                   |
| `WALLET_NOT_FOUND`         | 404    | Customer has no wallet on the requested blockchain.       |
| `KYC_NOT_APPROVED`         | 403    | Customer identity verification (KYC/KYB) is not approved. |
| `INVALID_OR_EXPIRED_TOKEN` | 401    | Terms of service token is invalid or expired.             |

### Accounts

| Code                             | Status | When it happens                                        |
| :------------------------------- | :----- | :----------------------------------------------------- |
| `ACCOUNT_NOT_FOUND`              | 404    | Account does not exist.                                |
| `ACCOUNT_NOT_ACTIVE`             | 409    | Account is not in a usable state.                      |
| `ACCOUNT_ALREADY_EXISTS`         | 409    | Customer already has an account in that currency.      |
| `ACCOUNT_CREATION_FAILED`        | 500    | Account creation failed.                               |
| `ACCOUNT_CUSTOMER_MISMATCH`      | 400    | Account does not belong to the given customer.         |
| `NO_SOURCE_ACCOUNT_TO_PROVISION` | 404    | No existing account to use as the provisioning source. |

### Destinations

| Code                     | Status | When it happens                                                        |
| :----------------------- | :----- | :--------------------------------------------------------------------- |
| `DESTINATION_NOT_FOUND`  | 404    | Destination does not exist.                                            |
| `RAIL_NOT_ENABLED`       | 403    | Rail is not enabled for this project.                                  |
| `HOLDER_TYPE_MISMATCH`   | 403    | `holderType` differs from the customer type on `SELF` destinations.    |
| `HOLDER_TAX_ID_MISMATCH` | 403    | `holderTaxId` differs from the customer tax ID on `SELF` destinations. |

### Transactions

| Code                                     | Status | When it happens                                         |
| :--------------------------------------- | :----- | :------------------------------------------------------ |
| `TRANSACTION_NOT_FOUND`                  | 404    | Transaction does not exist.                             |
| `INSUFFICIENT_BALANCE`                   | 422    | Balance is too low for the operation.                   |
| `TRANSACTION_LIMIT_EXCEEDED`             | 422    | Exceeds the customer's single, daily, or monthly limit. |
| `PERSONAL_ACCOUNT_RELATIONSHIP_REQUIRED` | 400    | Personal transactions require a `SELF` bank account.    |
| `TRANSACTION_CREATION_FAILED`            | 500    | Transaction creation failed.                            |
| `INSOLVENT_TRADE`                        | 422    | Trade cannot be settled at the moment.                  |
| `INVALID_PAYMENT_DESTINATION`            | 400    | Payment destination is invalid.                         |
| `INVALID_TAX_ID`                         | 400    | Tax ID was rejected by bank validation.                 |
| `INVALID_AMOUNT`                         | 400    | Amount was rejected by bank validation.                 |

### Exchange rates

| Code                              | Status | When it happens                                         |
| :-------------------------------- | :----- | :------------------------------------------------------ |
| `EXCHANGE_RATE_NOT_FOUND`         | 404    | Exchange rate does not exist.                           |
| `EXCHANGE_RATE_EXPIRED`           | 422    | Locked rate has expired.                                |
| `EXCHANGE_RATE_ALREADY_USED`      | 422    | Rate was already consumed by another transaction.       |
| `EXCHANGE_RATE_CURRENCY_MISMATCH` | 422    | Rate currency does not match the transaction type.      |
| `MINIMUM_AMOUNT_NOT_MET`          | 422    | Amount is below the supported minimum for the currency. |
| `EXCHANGE_RATE_FETCH_FAILED`      | 500    | Fetching or locking the rate failed.                    |
| `NO_LIQUIDITY_PROVIDER`           | 422    | No liquidity available for this trade.                  |

### Autoconversion

| Code                            | Status | When it happens                      |
| :------------------------------ | :----- | :----------------------------------- |
| `AUTOCONVERSION_RULE_NOT_FOUND` | 404    | Autoconversion rule does not exist.  |
| `INVALID_AUTOCONVERSION_RULE`   | 400    | Autoconversion rule is invalid.      |
| `NO_DEPOSIT_HANDLER`            | 400    | No deposit handler matches the rule. |

### Partner fees

| Code                     | Status | When it happens                                       |
| :----------------------- | :----- | :---------------------------------------------------- |
| `PARTNER_FEE_NOT_FOUND`  | 404    | Partner fee does not exist.                           |
| `INVALID_WALLET_ADDRESS` | 400    | Partner wallet address is invalid for the blockchain. |

### Idempotency

| Code                        | Status | When it happens                                      |
| :-------------------------- | :----- | :--------------------------------------------------- |
| `IDEMPOTENCY_KEY_CONFLICT`  | 409    | Same `Idempotency-Key` reused with a different body. |
| `IDEMPOTENCY_KEY_IN_FLIGHT` | 409    | A request with that key is still processing.         |

### Service availability

| Code                     | Status     | When it happens                                       |
| :----------------------- | :--------- | :---------------------------------------------------- |
| `UPSTREAM_SERVICE_ERROR` | 500 or 502 | An internal dependency failed.                        |
| `TIMEOUT_ERROR`          | 503        | Provider call timed out. Safe to retry.               |
| `NETWORK_ERROR`          | 503        | Network failure reaching the provider. Safe to retry. |
| `SERVICE_UNAVAILABLE`    | 503        | Provider is unavailable. Safe to retry.               |
| `INVALID_REQUEST`        | 400        | Provider rejected the request data. Not retryable.    |

Didn't find what you need? [Let us know](mailto:support@lumx.io).
