/v1 path has this shape.
{
"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"
}
}
codeis stable. Branch on it.messageis for people and can change.detailsis context for that code, as strings, and is{}when there is none. Its keys aresnake_case, and so isrequest_id, unlike the rest of the API.request_ididentifies the call. Send your own in anX-Request-Idheader to choose it.INVALID_REQUESThas none.
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. |
| 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. |