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.
POST /v1/payouts is idempotent. Retry the same logical request as many
times as you need — the API will return the original payout instead of
creating a new one.
How keys are picked
Two sources, in order:Idempotency-Keyheader, if you send one. Any opaque string is fine — a ULID, a UUID, or a deterministic key derived from your payroll run (e.g.payroll-co-2026-05-emp-001).external_idfrom the body, if no header is provided. Every payout already requires a uniqueexternal_id, so this gives you idempotency “for free” without managing a separate key.
external_id without colliding.
Replay semantics
| Scenario | Response |
|---|---|
| First call with key K, body B | 201 Created — payout created |
| Replay with key K, body B (identical) | 200 OK — same payout returned |
| Same key K, different body | 409 IDEMPOTENCY_KEY_REUSED |
Same external_id (no idempotency header match) | 409 EXTERNAL_ID_CONFLICT |
Note the status code: idempotent replays return
200, not 201. This
lets your client tell “I just created this” from “I called this same request
again”.Recommended pattern
Derive an idempotency key from the payroll run that produced thenet_salary you’re paying out. A good key is deterministic (so retries
match) and unique per logical payout (so unrelated requests don’t
collide).
What counts as “identical body”?
The whole JSON request body. If you change any field — amount, beneficiary details, metadata — and reuse the sameIdempotency-Key, you get
409 IDEMPOTENCY_KEY_REUSED. That is intentional: it surfaces caller bugs
(e.g. you re-derived the amount but kept the key) instead of silently paying
out something different from the original intent.
Idempotency-Key vs. external_id
Idempotency-Key | external_id | |
|---|---|---|
| Where it lives | Request header | Request body |
| Required? | Optional | Required |
| Visible on the payout resource | No | Yes |
| Used to dedupe | Yes (preferred) | Yes (fallback) |
Searchable via GET /v1/payouts?external_id=... | No | Yes |
external_id (and skip the header), you still get full
idempotency. The header exists for cases where you want to retry the same
logical create with a fresh external_id — uncommon, but supported.