BBankConnectorDeveloper Docs

Idempotency

Audience: ๐Ÿ”Œ Integration Client

Payments move money. If a request times out and you retry, you must not send twice. BankConnector makes this safe โ€” but only if you use idempotency deliberately. Read this before you write any retry logic.

The one rule

Send a stable Idempotency-Key on every POST /journal/payments.

Idempotency-Key: 2f9a1c7e-order-10432

Key format

8โ€“255 characters, printable ASCII, no spaces (^[\x21-\x7e]+$). A UUID, or a stable business identifier like order-10432-attempt, both work. Make it deterministic for a given logical payment so a retry reuses the same key.

Scope and lifetime

The four outcomes

When you POST with a key, one of these happens:

OutcomeWhenWhat you get
newfirst time this key is seenthe payment runs; 201
replaysame key and identical bodythe stored response, verbatim, plus header Idempotency-Replayed: true
conflictsame key, different body409 โ€” "already used with a different request payload"
in-progresssame key, still processing409 โ€” "already being processed. Retry shortly."

The two 409s mean opposite things:

Distinguish them by the message text (and see Errors for how to branch cleanly).

Why "same body" matters

A replay is only a replay if the body is byte-for-byte equivalent. The server fingerprints sha256 of {bankKey, payment, validate, maker}. If you change any of those under the same key, you get the different-body 409. So: freeze the payload and the key together. Generate both when you first decide to send, store them, and reuse both on every retry of that logical payment.

Crash safety (you get this for free)

If BankConnector crashes between starting and finishing a payment, the pending idempotency row self-heals: after 5 minutes, a retry either replays the committed document (as 201) or, if nothing committed, returns a terminal error telling you to retry with a new key โ€” so a mid-flight crash can never double-create.

Recommended client pattern

// 1. When you decide to pay, mint the key + freeze the payload TOGETHER.
const idempotencyKey = `order-${orderId}-v1`;   // deterministic, stable
const payload = buildPayment(order);            // freeze this too
persist(orderId, { idempotencyKey, payload });  // store before sending

// 2. Send. On any transient failure (timeout, 429, 503, in-progress 409),
//    retry with the SAME key and SAME payload.
// 3. On a 201 with Idempotency-Replayed: true, treat it as success โ€”
//    it already ran; you're just seeing the stored result.
// 4. On a different-body 409, alert โ€” do not retry. It means the payload
//    changed under a reused key.

Beyond payments

Idempotency-Key is not honoured on other endpoints. For non-payment writes, rely on natural keys where they exist. Note also: without a key and without a messageId on a non-host-to-host submission, there is no fingerprint dedup โ€” resubmits create new documents. Always send a key.