Concepts
Idempotency
Retry anything that moves money without paying twice.
curl https://jusa.localhost.co.zw/dev/v1/sends -H "Authorization: Bearer jusa_sk_test_…" \ -H "Idempotency-Key: order-8812" -H "Content-Type: application/json" \ -d '{"product": "airtime-usd", "target": "0772123456", "amount": "1.00"}'
A network can drop the answer to a request that worked. Send the same request again with the same
Idempotency-Key and you get the first answer back; nothing runs twice.
The rules
- Required on every POST that moves money (below), up to 255 characters. Without it:
400 idempotency_key_required. - A replay returns the same object, as it is now, with the first status code. It never runs again, however long after.
- The same key with a different body is
422 idempotency_mismatch. - The same key while the first request is still running is
409 idempotency_in_progress: wait and retry. - A request refused before it did anything (most 4xx) is not remembered: fix it and send it again with the same key.
- Keys are kept 30 days. They are written in the same database transaction as the money they protect.
Two keys that last for good
client_referenceon sends, batch lines and checkout sessions: your own id, unique in your account for ever. A second send with it returns the first (200).completion_idon rewards: unique in the program for ever. The same survey submission is one reward however many times it is reported.
Use them as well as the header: they protect you across restarts, deploys and months.
In the SDKs
The SDKs add a random key to every operation that takes one and reuse it across that call's own retries. That only protects retries inside one process: pass your own (an order id, a submission id) to be safe across restarts.
Operations that take a key
| Call | Idempotency-Key | |
|---|---|---|
| POST /batches | Create a batch | required |
| POST /batches/{batch_id}/approve | Approve a batch | required |
| POST /batches/{batch_id}/retry-failed | Retry a batch's failed items | required |
| POST /checkout-sessions | Create a checkout session | required |
| POST /credit/topups | Top up Jusa Credit | required |
| POST /programs/{program_id}/defund | Take budget back | required |
| POST /programs/{program_id}/fund | Add budget (and reserved credit) | required |
| POST /referrals/{referral_id}/qualify | Qualify a referral | required |
| POST /reviews/{review_id}/approve | Approve a held reward | required |
| POST /rewards | Reward someone | required |
| POST /rewards/{reward_id}/release | Release an escrowed reward | required |
| POST /schedules/{schedule_id}/run-now | Run a schedule now | required |
| POST /sends | Create a send | required |
| POST /sends/{send_id}/retry | Retry a failed send | required |
| POST /test/approvals/{approval_id}/approve | Approve (test mode) | required |
| POST /activities | Record an activity | optional |
| POST /batches/{batch_id}/cancel | Cancel a batch | optional |
| POST /checkins | Check in to claim a geofenced link | optional |
| POST /checkout-sessions/{session_id}/meter | Fix the meter while it waits | optional |
| POST /claims/{token} | Claim a reward link | optional |
| POST /cost-centers | Create a cost centre | optional |
| POST /disputes | Open a dispute | optional |
| POST /exports | Export to CSV | optional |
| POST /groups | Create a group | optional |
| POST /members | Invite a member | optional |
| POST /programs | Create a program | optional |
| POST /quotes | Quote a send | optional |
| POST /recipients | Create or update a recipient | optional |
| POST /recipients/bulk | Import recipients | optional |
| POST /referrals | Record a referral | optional |
| POST /reviews/{review_id}/reject | Reject a held reward | optional |
| POST /reward-links | Create reward links | optional |
| POST /rewards/{reward_id}/void | Void a reward | optional |
| POST /schedules | Create a schedule | optional |
| POST /sends/{send_id}/cancel | Cancel a send not yet sent | optional |
| POST /sends/{send_id}/meter | Change a ZESA send's meter | optional |
| POST /test/approvals/{approval_id}/reject | Reject (test mode) | optional |
| POST /test/credit | Add test Jusa Credit | optional |
| POST /webhook-deliveries/{delivery_id}/redeliver | Redeliver | optional |
| POST /webhook-endpoints | Add a webhook endpoint | optional |
| POST /webhook-endpoints/{endpoint_id}/replay | Replay events | optional |
| POST /webhook-endpoints/{endpoint_id}/roll-secret | Roll the signing secret | optional |
| POST /webhook-endpoints/{endpoint_id}/test | Send a test event | optional |
| POST /workforce/allocate | Allocate airtime or ZESA | optional |
| POST /workforce/batches/{reference}/resend | Resend the failed lines | optional |
| POST /workforce/schedules/{schedule_id}/run-now | Run a plan now | optional |