Concepts
Errors and retries
The error format, common error codes, and how to retry safely.
Error format
Every error has one shape: an error object with a machine-readable code, the HTTP status, a human-readable message, and, for some codes, a details object whose fields are fixed per code.
{
"error": {
"code": "INSUFFICIENT_BALANCE",
"message": "The team balance does not cover the approved payouts.",
"status": 422,
"details": {
"total": "50.00",
"required": "150.00",
"currency": "EUR"
}
}
}Branch on code, never on message.
Common codes
| Status | Code | Meaning |
|---|---|---|
| 400 | MALFORMED_REQUEST | The body is not valid JSON |
| 400 | VALIDATION_FAILED | The request did not match the schema; details.issues[] lists each failing path and a message. path is an array of segments, and an array index is a number: ["payouts", 1, "amount"]. Unknown fields are rejected, so a misspelt field surfaces here. |
| 401 | UNAUTHORIZED | Missing or invalid token |
| 403 | MISSING_SCOPE | The token lacks the scope the endpoint needs |
| 403 | BRANDING_UNAVAILABLE | A session asks for team or platform branding without the white-label feature, or platform from an API key |
| 404 | PAYOUT_NOT_FOUND, COUNTERPARTY_NOT_FOUND, WEBHOOK_NOT_FOUND | No such resource for the authenticated team |
| 409 | CUSTOM_ID_TAKEN | Another resource holds this customId; details.id names it, or is null when a deleted payout holds it |
| 409 | PAYOUT_NOT_EDITABLE, PAYOUT_LOCKED, PAYOUT_NOT_DELETABLE, PAYOUT_NOT_APPROVABLE, PAYOUT_NOT_PAYABLE, PAYOUT_NOT_CLAIMABLE | The payout's status (in details) does not allow the action |
| 409 | PAYOUT_NOT_YET_AVAILABLE | The payout cannot be claimed, sent, or paid before availableOn; details.availableOn says the day |
| 409 | KYB_ALREADY_SUBMITTED | A kyb session after the team submitted its application; check kybStatus first |
| 409 | IDEMPOTENCY_KEY_REUSED, IDEMPOTENCY_KEY_IN_PROGRESS | See Idempotency |
| 422 | INSUFFICIENT_BALANCE | Approval would exceed the team balance; details carries total (the balance.total of the account), required, and currency |
| 422 | VERIFICATION_REQUIRED | The team is not ready to approve; details.missing lists every step left, with the same values as missing on GET /team |
| 422 | ALLOWANCE_REQUIRED | The team owner must set a daily spending allowance for automatic approvals |
| 422 | COUNTERPARTY_NOT_PAYABLE | The counterparty misses data a push payment needs; details.missing names the gaps |
| 422 | RECIPIENT_NOT_COUNTERPARTY | Only a payout to a counterparty can be paid directly; send a claim link instead |
| 500 | INTERNAL_ERROR | Unexpected server fault |
Each endpoint's reference page lists the codes it can return.
Retry guidance
- 4xx errors are yours to fix — retrying the identical request fails again (except 429).
- 5xx errors and network timeouts are safe to retry with exponential backoff. Send the same
Idempotency-Keyon the retry, so a request that did succeed is replayed instead of repeated — see Idempotency. - A replayed 500 (
Idempotent-Replayed: true) does not change on another retry: the first attempt failed on our side and can have done part of its work. Check the state before you send the request again with a new key. - Write clients that ignore unknown fields. New fields, enum values, and endpoints appear without notice; see Changes without a new version.