Idempotency
How Strimz makes sure retries are safe at the API, the scheduler, and on-chain.
Idempotency means an operation can be run more than once but only takes effect once. Without it, a network blip during a refund could turn into a double refund.
Strimz enforces idempotency at three separate layers. Any retry logic you write on top of the SDK is safe to run as many times as needed.
At the API
Every POST endpoint that creates a resource accepts an Idempotency-Key header. If you send the same key twice within 24 hours, you get the original response. The row is not created twice.
Generate the key once per logical operation, persist it in your DB alongside the request you're about to make, and re-send the same key on retry. The SDK does this for you on every retryable verb.
At the queue layer
When the API enqueues a job onto BullMQ (webhook delivery, subscription charge, agent action), the job ID is derived from the resource ID. It is not random. Two API replicas processing the same incoming request can't enqueue duplicate jobs.
The scheduler then processes each job exactly once. If the job fails partway through, BullMQ marks it failed and the worker can retry. The partial work it did is itself idempotent (see below).
On-chain
This is the layer where stablecoins genuinely pull their weight. The Strimz contracts use deterministic identifiers for every state-changing action:
PaymentSession.idis passed asbytes32 refon the on-chainpay()call. The contract emits it inPaymentExecuted. The indexer keys the resultingTransactionrow on(onchainTxHash, ref). Replays no-op.chargeAttemptIdiskeccak256(subscriptionId, periodEndAt). The contract has amapping(bytes32 => bool) usedAttempts. If you try to charge a sub for a period that's already been charged, the call reverts cheaply.- Refund tx hashes are recorded in
Refund.refundTxHashafter the merchant signs the transfer. The indexer matches incoming ERC-20 Transfer events by hash. Replays of the same hash flip nothing.
This differs from ACH or card processors where idempotency is a database guarantee. Here the chain itself enforces it, so even a maliciously-replayed signed transaction can't cause a duplicate charge.
When you don't need to think about it
If you're using @strimz/sdk, idempotency keys are added
automatically:
- A fresh
crypto.randomUUID()per call by default. 429and5xxresponses are retried with exponential backoff, re-using the same key.
The only reason to set the key yourself is when your own business
logic might fire the same operation more than once and you want them
to collapse to one. For example: a customer clicks "Pay" twice, both
clicks reach your server, both go on to create a session. Pass the
same cartId as the idempotency key on both, and the second call
returns the first call's session.
