Idempotency keys
Make every write safe to retry by deriving its key from your own record.
Networks fail after a request has been sent, and a retry that creates a second payment is the worst outcome a payments API can have. Send an Idempotency-Key header with every write, and a retry with the same key returns the result of the first request instead of doing the work again.
With the SDK, pass it in the call context:
await unirail.paymentIntents.create(
{ quote: quote.id, digest: quote.digest, returnUri },
{ context: { idempotencyKey: `payment:${payment.id}` } },
);Choosing keys
Derive the key from the record on your side that the write belongs to, so every retry of the same intent produces the same key, wherever it runs:
| Write | Key |
|---|---|
| Create a customer | customer:${user.id} |
| Start a link session | link:${linkAttempt.id} |
| Register a payee | payee:${contact.id} |
| Create a payment intent | payment:${payment.id} |
Avoid random keys generated at call time: a crash between generating the key and storing it turns your retry into a new request.
Conflicts
Reusing a key for a different request fails with idempotency_conflict. That usually means two different records derived the same key, so make the key more specific rather than retrying.