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

# Errors

> Every error.code the API returns, when it happens, and what details carries.

Errors are `{"error": {"code", "message", "details"}}`. Branch on `code`; `message` is for humans and may change.

## Authentication and transport

| Status | `error.code` | Usual cause |
| - | - | - |
| 401 | `authentication_required` | none of the three `X-Ladder-*` headers was sent |
| 401 | `authentication_failed` | a header missing; timestamp not unix seconds or more than 5 minutes off; wrong or revoked key; wrong signature; live key before approval |
| 403 | `forbidden` | calling from an IP outside your organisation's allowlist |
| 403 | `sandbox_only` | a sandbox endpoint called with a live key |
| 400 | `idempotency_key_required` | POST without `Idempotency-Key` |
| 409 | `idempotency_key_reused` | same key, different body |
| 409 | `idempotency_request_in_progress` | same key while the first call is still running |
| 429 | `rate_limited` | over 120 requests per minute per key; honour `Retry-After` |

## Business

| Status | `error.code` | When | `details` |
| - | - | - | - |
| 400 | `validation_error` | a field failed validation | per-field messages |
| 400 | `kyb_incomplete` | submit before everything required is in | `missing` |
| 409 | `kyb_not_editable` | editing KYB outside `draft` or `more_info_required` | — |
| 409 | `settlement_account_verified` | deleting an account Ladder has verified | — |
| 404 | `investment_not_found` | unknown or unavailable investment | — |
| 400 | `investment_closed` | outside start and close dates, or not open | — |
| 400 | `currency_mismatch` | `currency` differs from the investment's | `investment_currency` |
| 400 | `amount_below_minimum` | below the investment minimum | `minimum` |
| 400 | `allocation_exceeded` | over the remaining partner allocation | `allocation_remaining` |
| 402 | `insufficient_funds` | the wallet cannot cover a `wallet` order | `currency`, `balance`, `required` |
| 409 | `duplicate_client_reference` | `client_reference` already used in this environment | — |
| 409 | `order_not_cancellable` | cancel after fulfilment | — |
| 404 | `holding_not_found` | a holding that is not yours | — |
| 409 | `holding_not_active` | sell against a closed or matured holding | — |
| 400 | `units_unavailable` | sell more than the unreserved units | `available`, `requested` |
| 400 | `holding_units_unknown` | the holding has no unit count yet | — |
| 404 | `unsupported_currency` | funding a currency other than NGN, GHS, USD | — |
| 400 | `funding_method_unavailable` | `method` not offered for that currency | `currency`, `available` |
| 503 | `funding_account_not_configured` | the wire rail for that currency is not open yet | — |
| 502 | `funding_provider_unavailable` | the payment provider could not create instructions; retry | — |
| 409 | `mandate_required` | direct debit with no active mandate | — |
| 409 | `mandate_not_yet_active` | approved, but usable only from `activated_at` | `activated_at` |
| 409 | `mandate_exists` | a pending or active mandate already covers that account | — |
| 400 | `mandate_limit_exceeded` | debit above the mandate's limit | `limit_amount` |
| 400 | `invalid_webhook_url` | not https, credentials in the URL, or a non-public host | — |
| 409 | `webhook_endpoint_limit` | more than five endpoints per environment | — |
| 409 | `delivery_already_succeeded` | retrying a delivered webhook | — |
| 404 | `instrument_not_found` | unknown market symbol | — |
| 400 | `invalid_type`, `invalid_date`, `invalid_pagination` | a bad market-data query parameter | — |


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