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_reference on 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_id on 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

CallIdempotency-Key
POST /batches Create a batchrequired
POST /batches/{batch_id}/approve Approve a batchrequired
POST /batches/{batch_id}/retry-failed Retry a batch's failed itemsrequired
POST /checkout-sessions Create a checkout sessionrequired
POST /credit/topups Top up Jusa Creditrequired
POST /programs/{program_id}/defund Take budget backrequired
POST /programs/{program_id}/fund Add budget (and reserved credit)required
POST /referrals/{referral_id}/qualify Qualify a referralrequired
POST /reviews/{review_id}/approve Approve a held rewardrequired
POST /rewards Reward someonerequired
POST /rewards/{reward_id}/release Release an escrowed rewardrequired
POST /schedules/{schedule_id}/run-now Run a schedule nowrequired
POST /sends Create a sendrequired
POST /sends/{send_id}/retry Retry a failed sendrequired
POST /test/approvals/{approval_id}/approve Approve (test mode)required
POST /activities Record an activityoptional
POST /batches/{batch_id}/cancel Cancel a batchoptional
POST /checkins Check in to claim a geofenced linkoptional
POST /checkout-sessions/{session_id}/meter Fix the meter while it waitsoptional
POST /claims/{token} Claim a reward linkoptional
POST /cost-centers Create a cost centreoptional
POST /disputes Open a disputeoptional
POST /exports Export to CSVoptional
POST /groups Create a groupoptional
POST /members Invite a memberoptional
POST /programs Create a programoptional
POST /quotes Quote a sendoptional
POST /recipients Create or update a recipientoptional
POST /recipients/bulk Import recipientsoptional
POST /referrals Record a referraloptional
POST /reviews/{review_id}/reject Reject a held rewardoptional
POST /reward-links Create reward linksoptional
POST /rewards/{reward_id}/void Void a rewardoptional
POST /schedules Create a scheduleoptional
POST /sends/{send_id}/cancel Cancel a send not yet sentoptional
POST /sends/{send_id}/meter Change a ZESA send's meteroptional
POST /test/approvals/{approval_id}/reject Reject (test mode)optional
POST /test/credit Add test Jusa Creditoptional
POST /webhook-deliveries/{delivery_id}/redeliver Redeliveroptional
POST /webhook-endpoints Add a webhook endpointoptional
POST /webhook-endpoints/{endpoint_id}/replay Replay eventsoptional
POST /webhook-endpoints/{endpoint_id}/roll-secret Roll the signing secretoptional
POST /webhook-endpoints/{endpoint_id}/test Send a test eventoptional
POST /workforce/allocate Allocate airtime or ZESAoptional
POST /workforce/batches/{reference}/resend Resend the failed linesoptional
POST /workforce/schedules/{schedule_id}/run-now Run a plan nowoptional