This walkthrough does the full “pay a payroll” flow: calculate an employee’s
net salary, then create a payout for that amount and watch it settle.
Sandbox payouts are handled by an in-process mock provider — no real funds
are moved. Every response carries mock: true and the X-Clevis-Mock: true
header.
1. Calculate the payroll
Run a normal payroll calculation and capture summary.net_salary. We’ll use
a Colombia 2026 ordinario example.
curl -X POST https://api.clevis.dev/v1/payroll/calculate \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"country": "CO",
"scheme": "ordinario",
"year": 2026,
"period": {
"type": "monthly",
"start_date": "2026-05-01",
"end_date": "2026-05-31",
"days": 30
},
"employee": {
"id": "emp-001",
"monthly_salary": "5000000",
"overrides": { "aplica_auxilio_transporte": false }
},
"employer": { "id": "employer-001" }
}'
Treat net_salary as an opaque string. Don’t Number()/float() it before
passing it to /v1/payouts — that’s where floating-point precision sneaks in.
2. Create the payout
Pass net_salary straight through as the amount. Setting processing.step_seconds: 0
runs the mock state machine synchronously so the response already has
status: "PAID" — exactly what you want in scripts and tests.
curl -X POST https://api.clevis.dev/v1/payouts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Idempotency-Key: payroll-co-2026-05-emp-001" \
-H "Content-Type: application/json" \
-d '{
"amount": "4600000.00",
"currency": "COP",
"country": "CO",
"external_id": "payroll-co-2026-05-emp-001",
"description": "Nómina mayo 2026 — Juan Pérez",
"beneficiary": {
"name": "Juan Pérez",
"document_type": "CC",
"document_id": "1023456789",
"bank_account": {
"bank_code": "1007",
"account_type": "CHECKING",
"account_number": "12345678901"
}
},
"payroll_ref": {
"country": "CO",
"scheme": "ordinario",
"year": 2026,
"concept": "net_salary"
},
"processing": { "step_seconds": 0 }
}'
3. Read the response
{
"id" : "po_01JABCXYZ..." ,
"object" : "payout" ,
"mock" : true ,
"mode" : "sandbox" ,
"status" : "PAID" ,
"status_detail" : "Payout received and queued." ,
"status_code" : "200" ,
"amount" : "4600000.00" ,
"currency" : "COP" ,
"country" : "CO" ,
"external_id" : "payroll-co-2026-05-emp-001" ,
"payment_method_id" : "BANK_TRANSFER" ,
"beneficiary" : { ... },
"payroll_ref" : {
"country" : "CO" ,
"scheme" : "ordinario" ,
"year" : 2026 ,
"concept" : "net_salary"
},
"created_at" : "2026-05-12T15:04:05Z" ,
"updated_at" : "2026-05-12T15:04:05Z" ,
"status_history" : [
{ "status" : "PENDING" , "at" : "2026-05-12T15:04:05Z" , "detail" : "Payout received and queued." },
{ "status" : "PROCESSING" , "at" : "2026-05-12T15:04:05Z" },
{ "status" : "PAID" , "at" : "2026-05-12T15:04:05Z" }
]
}
Without processing.step_seconds: 0, the response would come back with
status: "PENDING" and advance to PROCESSING then PAID on a 2-second
timer (configurable). Use GET /v1/payouts/{id} to poll, or subscribe to
webhooks for push notifications.
4. Try a deterministic failure
Force the mock to reject the payout by prefixing external_id with
MOCK_REJECT_BANK_:
curl -X POST https://api.clevis.dev/v1/payouts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": "4600000.00",
"currency": "COP",
"country": "CO",
"external_id": "MOCK_REJECT_BANK_demo-001",
"beneficiary": { ... },
"processing": { "step_seconds": 0 }
}'
The response comes back with status: "REJECTED", status_code: "301", and
status_detail ending in "(mock trigger).". The full menu of triggers is on
the Magic triggers page.
Next steps
Status lifecycle Every legal state transition the mock can make.
Idempotency Safely retry create calls without double payments.
Webhooks Get pushed on every status change.
Country requirements Beneficiary fields and check-digits for every supported country.