Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.clevis.dev/llms.txt

Use this file to discover all available pages before exploring further.

The Payouts API uses the same error envelope as the rest of the Clevis API:
{
  "error": {
    "code": "PAYOUT_NOT_FOUND",
    "message": "No payout 'po_01J...' for this API key.",
    "details": {},
    "request_id": "01HX9B2KM3..."
  }
}
Use the code field for programmatic branching — never the HTTP status, which can be shared by several codes (e.g. 409 covers two distinct error codes below).

Error code reference

CodeHTTPWhen it firesWhat to do
PAYOUTS_DISABLED403The server has PAYOUTS_ENABLED=false. All /v1/payouts* routes return this.Check your environment configuration; payouts are gated and may be off in some regions.
PAYOUT_NOT_FOUND404No payout with that id is visible to your API key.The id may be wrong, or it belongs to another API key. Don’t retry with the same id.
PAYOUT_COUNTRY_NOT_SUPPORTED404country is not one of MX, CO, AR, BR, CL, PE.Adding a country is a schema-only change on the server — open a support request.
PAYOUT_CURRENCY_MISMATCH400currency does not match the country’s payout currency (e.g. USD for country: "CO").Send the country’s native currency. See Country requirements.
EXTERNAL_ID_CONFLICT409The external_id is already used by this API key, and no idempotency replay match was found.Use a unique external_id, or include the original Idempotency-Key header to replay.
IDEMPOTENCY_KEY_REUSED409Same Idempotency-Key, but the request body is different from the original.Either fix the caller to send the original body, or pick a new key. Surface this as a bug — silent retries are not safe here.
PAYOUT_NOT_CANCELLABLE409Cancel was called on a payout that’s already terminal (PAID, REJECTED, or CANCELLED).Read the resource and act on its current status.
PAYOUT_STORE_FULL503The mock store hit PAYOUTS_MAX_OPEN (default 10,000). Mock-only failure.Restart the API to clear the in-memory store, or raise PAYOUTS_MAX_OPEN. Not an issue with the real provider.
VALIDATION_ERROR422Request body failed schema validation, including per-country beneficiary checks and bad CLABE/CPF/CBU/RUT/CCI check digits.Read details for the specific field. A bad check digit is a bad request, not a REJECTED payout — fix the data, don’t retry.

Don’t confuse these

A few distinctions worth calling out:
  • REJECTED status vs. 422 VALIDATION_ERROR. A REJECTED payout was accepted by the API (the request is well-formed) and then turned away by the provider/bank. Bad input (e.g. malformed CLABE, missing beneficiary field) is rejected with 422 before a payout is created.
  • EXTERNAL_ID_CONFLICT vs. IDEMPOTENCY_KEY_REUSED. Both are 409s. The first means you reused an external_id for a different payout. The second means you reused an Idempotency-Key for the same logical create, but the body changed — usually a caller bug.
  • PAYOUT_NOT_FOUND vs. PAYOUT_COUNTRY_NOT_SUPPORTED. Both are 404s. The first means “this id doesn’t exist for you”; the second means “this country isn’t a payout destination yet”.

Handling errors in code

import httpx

resp = httpx.post(
    "https://api.clevis.dev/v1/payouts",
    headers={
        "Authorization": "Bearer YOUR_API_KEY",
        "Idempotency-Key": idempotency_key,
    },
    json=payload,
)

if resp.status_code in (200, 201):
    payout = resp.json()
else:
    err = resp.json()["error"]
    match err["code"]:
        case "VALIDATION_ERROR":
            print(f"Bad request: {err['details']}")
        case "PAYOUT_CURRENCY_MISMATCH":
            print("Wrong currency for country — check requirements/{country}")
        case "PAYOUT_COUNTRY_NOT_SUPPORTED":
            print("Country not in the supported set")
        case "EXTERNAL_ID_CONFLICT":
            print("external_id already used — fetch the existing payout")
        case "IDEMPOTENCY_KEY_REUSED":
            raise RuntimeError("Idempotency-Key reused with a different body")
        case "PAYOUT_STORE_FULL":
            # mock-only — retry after the operator restarts the API
            ...
        case _:
            print(f"{err['code']}: {err['message']} (request_id={err['request_id']})")

See also