Developer Docs

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-Key header on createPaymentIntent/docs/api-reference

How It Works

  1. Send Idempotency-Key header with POST /v1/payment-intents.
  2. BSync stores the key + request body hash.
  3. Same key + same body → returns the original response (replay).
  4. 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_CONFLICT

Use 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_CONFLICT

If 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}'

Was this page helpful?

idempotency

Command Palette

Search for a command to run...