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.

Every payout is a small state machine. Knowing exactly which states are legal — and how the mock advances between them — makes it easy to write a polling loop, react to a webhook, or assert against the response in a test.

States

StatusTerminal?Meaning
PENDINGnoAccepted, queued, not yet sent to “bank rails”
PROCESSINGno”In flight” to the beneficiary bank
PAIDyesFunds “delivered”
REJECTEDyesNot paid — see status_detail / status_code
CANCELLEDyesCancelled by caller before completion
PAID, REJECTED, and CANCELLED are terminal — once a payout enters one of these, its status never changes. A cancel call on a terminal payout returns 409 PAYOUT_NOT_CANCELLABLE.

The full diagram

                ┌──────────┐
                │ PENDING  │ ──────► REJECTED   (intake reject)
                └────┬─────┘


                ┌────────────┐
                │ PROCESSING │ ────► PAID       (happy path)
                └────┬───────┘ ────► REJECTED   (bank reject)


                 CANCELLED          (caller cancels — from PENDING or PROCESSING)

Transitions in detail

1

Create → PENDING

POST /v1/payouts returns 201 with status: "PENDING". The mock schedules the rest of the state machine on a timer (default 2 seconds per step, overridable via processing.step_seconds).Some magic triggers skip this step and return 201 with a terminal status already applied — typically MOCK_REJECT_INTAKE_, amount-ending-in-.99, and document_id == "00000000000".
2

PENDING → PROCESSING

The payout has “left the queue” and is “in flight”. Default route always goes through this state, even when the final outcome is REJECTED or CANCELLED — observers see the full lifecycle.
3

PROCESSING → PAID

Happy path. status_code: "200", status_detail: "Payout received and queued."
4

PROCESSING → REJECTED

Bank-side reject. status_code is one of:
  • "301" — beneficiary bank rejected the transfer
  • "302" — insufficient funds in payout account
status_detail ends with "(mock trigger)." when the reject was forced by a magic trigger.
5

* → CANCELLED

POST /v1/payouts/{id}/cancel works from PENDING or PROCESSING. The payout transitions immediately. A cancel against a terminal payout returns 409 PAYOUT_NOT_CANCELLABLE.

When transitions actually fire

The mock advances payouts two ways, both routed through the same state machine code:
  1. Per-payout timer scheduled at create time. Default cadence: every 2 seconds (server-configurable via PAYOUTS_MOCK_STEP_SECONDS). Override per request with processing.step_seconds (clamped to [0, 60]).
  2. Lazy advance on read. GET /v1/payouts/{id} evaluates “where would this be by wall clock now?” and applies pending transitions before responding. This is why tests don’t have to depend on a real clock.
processing.step_seconds: 0 collapses the timer to “apply all transitions synchronously before returning the create response.” The 201 already reflects the terminal state. This is the recommended setting for tests.

Reading the result

The full lifecycle is preserved on the resource:
{
  "id": "po_01J...",
  "status": "PAID",
  "status_history": [
    { "status": "PENDING",    "at": "2026-05-12T15:04:05Z", "detail": "Payout received and queued." },
    { "status": "PROCESSING", "at": "2026-05-12T15:04:07Z" },
    { "status": "PAID",       "at": "2026-05-12T15:04:09Z" }
  ],
  "estimated_settlement_at": "2026-05-12T15:04:09Z"
}
estimated_settlement_at is set at create time to created_at + N * step_seconds, where N is the length of the planned route. It’s a hint, not a guarantee — read status and status_history for the truth.

Cancelling

curl -X POST https://api.clevis.dev/v1/payouts/po_01J.../cancel \
  -H "Authorization: Bearer YOUR_API_KEY"
Returns:
  • 200 OK with the cancelled Payout if the payout was PENDING or PROCESSING
  • 409 PAYOUT_NOT_CANCELLABLE if the payout was already terminal
  • 404 PAYOUT_NOT_FOUND if the id doesn’t exist for your API key
The mock allows cancelling from PROCESSING, which a real provider may not. This is intentional — it makes the cancellation path easy to exercise in demos.

Idle “stuck” state

The MOCK_STUCK_ external_id prefix advances a payout to PROCESSING and then never settles. Use it to exercise your polling-loop timeout / “stale payout” UX without actually waiting for a real provider to misbehave. See Magic triggers.