Idempotency
Prevent duplicate charges with idempotency keys.
Idempotency prevents duplicate payments when network failures cause request retries. Safe to retry POST /v1/payment-intents with the same key and body.
OpenAPI:
Idempotency-Keyheader oncreatePaymentIntent— /docs/api-reference
How It Works
- Send
Idempotency-Keyheader withPOST /v1/payment-intents. - BSync stores the key + request body hash.
- Same key + same body → returns the original response (replay).
- Same key + different body → returns
409 IDEMPOTENCY_CONFLICT.
Response headers on replay:
Idempotency-Key: your-key-here
Idempotency-Replayed: true
SDK Behavior
The SDK auto-generates an Idempotency-Key on every create() call:
const payment = await bsync.paymentIntents.create({
amount: 150,
currency: "EGP",
redirectDelay: 3,
});Override with a deterministic key tied to your order:
const payment = await bsync.paymentIntents.create(
{
amount: 150,
currency: "EGP",
externalOrderId: "ORD-42",
redirectDelay: 3,
},
{ idempotencyKey: "order-ORD-42" }
);Generate a UUID key:
import { generateIdempotencyKey } from "@bsync/node-sdk";
const key = generateIdempotencyKey();Safe Retries
The SDK retries on network errors and 5xx automatically. The same Idempotency-Key is sent on every retry attempt for a single create call — preventing duplicate payments.
Attempt 1: POST /v1/payment-intents Idempotency-Key: order-ORD-42 → 503
Attempt 2: POST /v1/payment-intents Idempotency-Key: order-ORD-42 → 201 (or replay)
Only one payment is created.
Duplicate Prevention
Use your order ID as the idempotency key:
await bsync.paymentIntents.create(params, {
idempotencyKey: `order-${orderId}`,
});If the customer clicks "Pay" twice, or your server retries after a timeout, only one Payment Intent is created.
TTL
Idempotency keys are stored for a limited period. After TTL expires, the same key can be reused for a new request. Use fresh keys for genuinely new payments.
Common Mistakes
Reusing keys across different orders
// Wrong — same key for different orders
await bsync.paymentIntents.create({ amount: 100, ... }, { idempotencyKey: "my-key" });
await bsync.paymentIntents.create({ amount: 200, ... }, { idempotencyKey: "my-key" });
// → 409 IDEMPOTENCY_CONFLICTUse a unique key per order: order-${orderId}.
Changing the body with the same key
// Wrong — same key, different amount
await bsync.paymentIntents.create({ amount: 100, ... }, { idempotencyKey: "order-1" });
await bsync.paymentIntents.create({ amount: 200, ... }, { idempotencyKey: "order-1" });
// → 409 IDEMPOTENCY_CONFLICTIf you need to change the amount, use a new key or cancel the existing payment first.
Not using idempotency at all
Without an idempotency key, network retries can create duplicate payments. The SDK handles this automatically, but if you use curl or raw HTTP, always send Idempotency-Key.
Generating a new key on every retry
// Wrong — new key per attempt creates duplicates
for (let i = 0; i < 3; i++) {
await bsync.paymentIntents.create(params); // auto-generates new key each time
}Pass an explicit stable key when implementing your own retry logic.
curl Example
curl -X POST https://api.bsyncapp.com/v1/payment-intents \
-H "Authorization: Bearer bsync_test_your_key" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-ORD-42" \
-d '{"amount": 150, "currency": "EGP", "externalOrderId": "ORD-42", "redirectDelay": 3}'Related
- Payment Intents
- Error Handling —
IDEMPOTENCY_CONFLICT(409) - Best Practices
- FAQ
Was this page helpful?
idempotency