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

# Errors

> The error envelope and every code an API key can meet.

Every refusal on a `/v1` path has this shape.

```json theme={null}
{
  "error": {
    "code": "INSUFFICIENT_FUNDS",
    "message": "available balance is 500000, 900000 requested",
    "details": {
      "account_id": "acc_01M6F1BK8KE6JT32WRN50HYC4R",
      "available": "500000",
      "requested": "900000"
    },
    "request_id": "01a19e15-cf37-7330-b5a2-f7792f1e709c"
  }
}
```

* **`code`** is stable. Branch on it.
* **`message`** is for people and can change.
* **`details`** is context for that code, as strings, and is `{}` when there is none. Its keys are `snake_case`, and so is `request_id`, unlike the rest of the API.
* **`request_id`** identifies the call. Send your own in an `X-Request-Id` header to choose it. `INVALID_REQUEST` has none.

Branch on `code` rather than the HTTP status. Most business refusals share `400`.

## Requests the server cannot read

| Status | Code | Meaning |
| - | - | - |
| 400 | `INVALID_REQUEST` | The request never reached the ledger: an unknown query parameter, a bad parameter value, or malformed JSON. A JSON number where a string belongs, such as an amount, is one of these. Fix the request; nothing happened. |

## Credentials and access

| Status | Code | Meaning |
| - | - | - |
| 401 | `UNAUTHENTICATED` | The key is missing or not valid. |
| 403 | `MISSING_SCOPE` | The key lacks a scope. `details.scope` names it. |

## Writes

| Status | Code | Meaning |
| - | - | - |
| 400 | `IDEMPOTENCY_KEY_REQUIRED` | The write has no `Idempotency-Key`. |
| 400 | `IDEMPOTENCY_KEY_REUSED` | The key was used with a different request, or its stored answer has expired. |
| 400 | `EXTERNAL_REF_REQUIRED` | `uniqueExternalRef` needs an `externalRef`. |
| 400 | `INVALID_AMOUNT` | An amount is not a positive whole number of minor units, or is more than is owed. |
| 400 | `INVALID_CODE` | A value is badly formed: an account code, a topic, a loan reference, an account number. `message` says which. |
| 400 | `REASON_REQUIRED` | This needs a reason of at least 10 characters. |
| 404 | `NOT_FOUND` | There is no such thing on this tenant. Another tenant's id looks the same as one that does not exist. |

## Ledger

| Status | Code | Meaning |
| - | - | - |
| 400 | `UNBALANCED_ENTRY` | Debits and credits differ, or the entry has fewer than two postings, no `kind`, or one account twice on a side. |
| 400 | `CURRENCY_MISMATCH` | A posting's currency differs from the account's, or a code's currency differs from `currency`. |
| 400 | `INSUFFICIENT_FUNDS` | A wallet's available balance is too low. `details` has both figures. |
| 400 | `ACCOUNT_FROZEN` | The account is frozen. |
| 400 | `ACCOUNT_CLOSED` | The account is closed. |
| 400 | `PERIOD_CLOSED` | The entry's business date is in a closed period. |
| 400 | `LEDGER_MISSING` | The tenant has no ledger in that currency. |
| 400 | `ENTRY_TOO_LARGE` | The entry is above the cap PanoQore set for this tenant. |
| 409 | `ACCOUNT_EXISTS` | That account code is taken. |
| 409 | `EXTERNAL_REF_EXISTS` | A live entry already holds this `externalRef`, and the entry set `uniqueExternalRef`. |
| 409 | `VERSION_CONFLICT` | An account changed since you read it. See `ifMatchVersion` in [Entries](https://docs.panoqore.com/concepts/entries.md). |
| 409 | `ENTRY_NOT_PENDING` | Only a pending entry can be posted or voided. |
| 409 | `ENTRY_NOT_POSTED` | Only a posted entry can be reversed. |
| 409 | `ALREADY_REVERSED` | The entry has been reversed. `details.reversed_by` is the reversal. |
| 409 | `HOLD_NOT_ACTIVE` | Only an active hold can be captured or released. |
| 409 | `HOLD_IN_USE` | The hold protects a pending entry. Post or void the entry. |

## Lending

| Status | Code | Meaning |
| - | - | - |
| 409 | `MODULE_OFF` | The Lending module is not on for this tenant. |

## FX

| Status | Code | Meaning |
| - | - | - |
| 400 | `RATE_UNAVAILABLE` | There is no signed-off rate for the pair. |
| 400 | `RATE_STALE` | The rate is older than the tenant's maximum age, so conversions are blocked. |
| 400 | `QUOTE_EXPIRED` | The quote is over 60 seconds old. |
| 400 | `FX_POSITION_MISSING` | An `AST:FXP:<CCY>` account is missing. |
| 409 | `QUOTE_USED` | The quote has been used. `details.entry_id` is the entry. |

## Named accounts and partners

| Status | Code | Meaning |
| - | - | - |
| 400 | `NOT_A_WALLET` | Only a customer wallet can have a named account. |
| 400 | `PARTNER_NOT_CONNECTED` | The tenant has no integration with that partner. |
| 400 | `INTEGRATION_MODE_FULL` | The partner is in full integration, so this cannot be done by hand. |
| 400 | `PARTNER_ACCOUNT_UNKNOWN` | The partner has no open account with that number under your credentials. |
| 400 | `SETTLEMENT_ACCOUNT_MISSING` | The settlement or suspense account for that partner and currency is missing. |
| 404 | `PARTNER_UNKNOWN` | There is no such partner. |
| 409 | `ACCOUNT_NUMBER_TAKEN` | The number is already bound. |
| 409 | `BINDING_EXISTS` | The account already has a named account at that partner. |
| 503 | `PARTNER_UNAVAILABLE` | The partner could not be reached. Nothing changed. Retry shortly. |

## Reconciliation and webhooks

| Status | Code | Meaning |
| - | - | - |
| 409 | `ALREADY_REQUESTED` | A statement for that partner, currency and day is already in. |
| 400 | `WEBHOOK_URL_INVALID` | The endpoint URL is not a full `https` URL. |
| 400 | `WEBHOOK_TEST_FAILED` | The endpoint did not answer the test event with a 2xx. Nothing was saved. |
| 409 | `WEBHOOK_EXISTS` | That URL is already an endpoint, or the tenant has its 20. |

## Tenant and service state

| Status | Code | Meaning |
| - | - | - |
| 503 | `TENANT_PAUSED` | PanoQore has paused the tenant. |
| 503 | `MAINTENANCE` | Planned maintenance. Retry when it ends. |
| 409 | `TENANT_CLOSING` | The tenant is closing and can only read. |
| 403 | `TENANT_CLOSED` | The tenant is closed. |
| 500 | `INTERNAL_ERROR` | A fault on PanoQore's side. Nothing was changed, and nothing was stored against your `Idempotency-Key`, so retry with the same key. |


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