> ## 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.

# Conventions

> Envelopes, idempotency, amounts, identifiers, pagination, and what differs between sandbox and live.

## Envelopes

Success is `{"data": …}`. Lists add `"pagination": {"limit", "offset", "count"}`; `limit` defaults to 50 and caps at 200.

Errors are `{"error": {"code", "message", "details"}}`. Branch on `code`, never on `message`. `details` carries the specifics named in the [error reference](/reference/errors).

## Idempotency

Every POST needs an `Idempotency-Key` (document uploads are the one exception).

* Same key and same body: the original response again, with `Idempotent-Replayed: true`. Nothing executes twice.
* Same key and a different body: `409 idempotency_key_reused`.
* Same key while the first request is still running: `409 idempotency_request_in_progress`.

Generate one key per logical operation and **reuse it when you retry** after a timeout or network error.

## Amounts and timestamps

Amounts are decimal strings: four decimals on money (`"5000.0000"`), eight on units (`"40.00000000"`). Never send floats. Timestamps are UTC; market-data timestamps carry a `Z`.

## Identifiers

Every resource has a UUID `id`. Money objects also carry a human-readable reference you quote when you contact Ladder:

| prefix | object |
| - | - |
| `LPO_` | order |
| `LPF_` | funding request |
| `LPM_` | direct-debit mandate |
| `evt_` | webhook event |

## Environments

Two hosts, and on each host two kinds of key:

| host | purpose | keys |
| - | - | - |
| `https://api-dev.myladder.africa` | **UAT**: build and acceptance-test your integration; data is disposable | issued for UAT; both `pk_test_…` and `pk_live_…` exist here, and "live" on UAT moves no real money |
| `https://api.myladder.africa` | **Production** | issued after KYB approval; `pk_test_…` is the production sandbox, `pk_live_…` moves real money |

Keys are not shared between hosts: a UAT key is refused on production and the other way round. Every endpoint page on this site has a server selector for the two hosts, and the code samples take the host as a constant you switch.

## Sandbox versus live

| | Sandbox (`pk_test_`) | Live (`pk_live_`) |
| - | - | - |
| KYB | same API, either key | — |
| Funding | instructions are `type: sandbox`; settle with `POST /sandbox/funding-requests/{id}/simulate` | real rails |
| Direct-debit mandate | active immediately | pending until the bank approves |
| Buy order | completes at once with a holding | `pending_fulfilment` until Ladder places the position |
| Sell order | completes at once at the indicative value | `pending_fulfilment` until Ladder redeems |
| Webhooks | delivered | delivered; the URL must be `https` |

## Rate limit

120 requests per minute per key. Over it you get `429 rate_limited` with a `Retry-After` header in seconds. Spread polling out, and prefer webhooks to polling.


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