Idempotency
Money operations must be safe to retry. SafeBank supports an
Idempotency-Key header on every POST: send the same key twice and the
API executes the operation once, returning the original result on the
repeat.
Use it for anything that creates or moves value — creating a wallet, funding, and especially creating a payment.
With the SDK (automatic)
The SDK generates an Idempotency-Key for every POST once, outside the
retry loop, and reuses it across its own automatic retries. So a 429/5xx
retry can never double-execute — the API's two-phase claim dedupes it.
- TypeScript
- curl
// Nothing to do — this POST already carries a stable Idempotency-Key,
// reused if the transport retries on 429/5xx.
const payment = await sb.payments.create({
sourceWalletId: 'w_1a2b3c',
destination: { type: 'handle', handle: 'volt-components' },
amount: { value: '25.00' },
})
# Generate one stable key per logical operation and reuse it on every retry.
KEY=$(uuidgen)
curl -X POST https://api.sandbox.safebank.ai/v1/payments \
-H "X-SafeBank-Api-Key: $SAFEBANK_API_KEY" \
-H "Idempotency-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{
"sourceWalletId": "w_1a2b3c",
"destination": { "type": "handle", "handle": "volt-components" },
"amount": { "value": "25.00" }
}'
Making a key stable across process restarts
The SDK's auto-generated key lives for the life of a single call (including its own retries) — it is not persisted across process restarts. If your code retries at a higher level — a job that reruns, a queue redelivery — derive a stable key from the logical operation so the second run dedupes against the first.
The typed resource methods (sb.payments.create(...)) manage the key for you.
When you need a deterministic key tied to a business event, send the
request with your own Idempotency-Key header — for example via curl or a
raw fetch:
# Deterministic key tied to the business event (e.g. an invoice id).
# Re-running the job with the same invoice id executes the payment once.
curl -X POST https://api.sandbox.safebank.ai/v1/payments \
-H "X-SafeBank-Api-Key: $SAFEBANK_API_KEY" \
-H "Idempotency-Key: pay:INV-2026-001" \
-H "Content-Type: application/json" \
-d '{
"sourceWalletId": "w_1a2b3c",
"destination": { "type": "handle", "handle": "volt-components" },
"amount": { "value": "25.00" },
"memo": "invoice INV-2026-001"
}'
Rules
- Scope:
POSTonly.GETis already safe;DELETE/PUTare handled by their own semantics. - Reuse the same key for genuine retries of the same operation. Use a new key for a genuinely new operation.
- Same key, different body is a conflict — don't reuse a key for a different request.
- Pair idempotency with the SDK's retry behavior for at-most-once execution under transient failures.