The mock provider exposes magic trigger values that pick a deterministic
outcome at intake. Use them to drive demos and tests through any path —
happy, rejection, stuck, or slow — without timing flakiness.
These are dev/test affordances, not stable API behaviour. They are
available on the mock provider while sandbox is in place.
Order of evaluation
Triggers are checked in order — first match wins:
X-Mock-Outcome header
If present and recognised, the header overrides everything else (including
body triggers).
external_id prefix
MOCK_REJECT_INTAKE_, MOCK_REJECT_BANK_, MOCK_SLOW_, MOCK_STUCK_, MOCK_OK_.
amount suffix
.13 and .99 (after rounding to 2dp).
document_id
The literal value "00000000000" triggers a compliance-screening reject.
Default
Happy path: PENDING → PROCESSING → PAID.
external_id prefixes
| Prefix | Outcome | status_code | status_detail ends with |
|---|
MOCK_OK_ | Happy path (explicit; same as default) | "200" | Payout received and queued. |
MOCK_REJECT_INTAKE_ | Reject at intake — no PROCESSING step | "300" | Rejected at intake (mock trigger). |
MOCK_REJECT_BANK_ | PENDING → PROCESSING → REJECTED | "301" | Beneficiary bank rejected the transfer (mock trigger). |
MOCK_SLOW_ | Happy path forced to 30s per step | "200" | Payout received and queued. |
MOCK_STUCK_ | Advances to PROCESSING and never settles | "200" | Stuck in processing (mock trigger). |
MOCK_SLOW_ ignores any processing.step_seconds override and uses 30 seconds
per step — useful for demoing a “watch it process” UI.
Examples
# Force a bank rejection
"external_id": "MOCK_REJECT_BANK_demo-001"
# Stuck in PROCESSING — for testing your "stale payout" UX
"external_id": "MOCK_STUCK_polling-demo"
# Watch it advance every 30s — for live demos
"external_id": "MOCK_SLOW_audience-demo"
Amount suffixes
The mock looks at the 2-decimal string form of amount (so 1000.13 →
.13, but 1000.1300001 also rounds to .13 and triggers).
| Suffix | Outcome | status_code | status_detail ends with |
|---|
.13 | PENDING → PROCESSING → REJECTED | "302" | Insufficient funds in payout account (mock trigger). |
.99 | Reject at intake (no PROCESSING) | "303" | Amount exceeds per-payout limit (mock trigger). |
# Demo "insufficient funds in payout account"
"amount": "1000.13"
# Demo "amount exceeds per-payout limit" (intake reject)
"amount": "1000000.99"
Document ID trigger
| Field | Value | Outcome | status_code | status_detail ends with |
|---|
beneficiary.document_id | "00000000000" | Reject at intake | "304" | Beneficiary failed compliance screening (mock trigger). |
"beneficiary": {
"name": "Test User",
"document_type": "CC",
"document_id": "00000000000", # compliance screen trigger
...
}
The X-Mock-Outcome header takes precedence over everything. Combine with
X-Mock-Outcome-At: intake for REJECTED to skip the PROCESSING step.
| Header values | Outcome |
|---|
X-Mock-Outcome: PAID | PENDING → PROCESSING → PAID |
X-Mock-Outcome: REJECTED | PENDING → PROCESSING → REJECTED (bank reject) |
X-Mock-Outcome: REJECTED + X-Mock-Outcome-At: intake | Reject at intake (no PROCESSING) |
X-Mock-Outcome: CANCELLED | PENDING → PROCESSING → CANCELLED |
Unrecognised values are ignored (with a server-side warning log), and the
mock falls back to body triggers.
Example
curl -X POST https://api.clevis.dev/v1/payouts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "X-Mock-Outcome: REJECTED" \
-H "X-Mock-Outcome-At: intake" \
-H "Content-Type: application/json" \
-d '{
"amount": "100.00",
"currency": "COP",
"country": "CO",
"external_id": "force-reject-001",
"beneficiary": { ... }
}'
Reading the trigger off the response
Every mock-triggered terminal outcome carries a status_detail ending in
"(mock trigger)." — so your tests can assert on the trigger without
hardcoding the message.
assert payout["status"] == "REJECTED"
assert payout["status_code"] == "301"
assert payout["status_detail"].endswith("(mock trigger).")
Tip: synchronous + magic trigger = great for unit tests
Combine processing.step_seconds: 0 with any trigger. The terminal state is
already applied in the create response — no polling, no timer, no flakiness.
{
"external_id": "MOCK_REJECT_BANK_test-001",
"amount": "100.00",
"currency": "COP",
"country": "CO",
"processing": { "step_seconds": 0 },
"beneficiary": { ... }
}
The response comes back with status: "REJECTED" and status_code: "301"
on the same line that creates the payout.