Payouts

Idempotency

Networks fail. Idempotency keys make it safe to retry any payout request without ever paying a recipient twice.

How it works#

Every request that creates a payout or a batch must carry an idempotency key — a unique string you choose, such as your order or commission ID. Send it as the Idempotency-Key header or the idempotency_key body field.

bash
curl https://api.muny.io/v1/payouts \
  -H "Authorization: Bearer $MUNY_API_KEY" \
  -H "Idempotency-Key: order_8392" \
  -H "Content-Type: application/json" \
  -d '{ "recipient": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU", "amount": "500" }'

Outcomes#

SituationResult
First request with a keyThe payout is created normally.
Same key, same bodyThe original payout is returned. Nothing new is created or sent.
Same key, different body409 idempotency_error with code idempotency_key_reused.
Same key while the first request is still running409 idempotency_error with code idempotency_key_in_progress. Retry after a short delay.

Retention#

  • The stored response for a key is kept for 24 hours, so retries within that window get the exact original response.
  • The guarantee itself is permanent: an organization can only ever have one payout per idempotency key, enforced by a unique constraint in the database. A retry days later still can't create a second payout.
  • Keys are scoped to your organization and are up to 255 printable ASCII characters.

Choosing keys#

Derive the key from the thing you are paying for — commission_7731, invoice_2026_0142 — rather than a random value generated per attempt. Then even a crashed worker that restarts from scratch cannot double-pay.

ts
// Safe to run as many times as you like
await muny.payouts.create(
  { recipient: affiliate.wallet, amount: commission.amount, reference: commission.id },
  { idempotencyKey: `commission_${commission.id}` },
);

The SDK has your back

If you don't pass a key, @muny/sdk generates one per call and reuses it across its own automatic retries. For full protection across process restarts, pass your own.

Beyond the request#

Idempotency continues after the API accepts a payout. Payout processing uses compare-and-set state transitions, so two workers can never send the same payout, and a payout is only re-sent after its previous transaction has provably expired without landing.