Credentials and integrations
Prevent replay and duplicate work
Use expirations, nonces, idempotency keys, and stored operation results where repeated requests could charge, send, or mutate twice.
Networks retry. Attackers replay. Any sensitive operation needs a clear answer to one question: what happens if this exact request arrives again?
It will arrive again. Maybe from a flaky mobile connection. Maybe from someone who captured the request and resent it on purpose. The server can’t tell the difference, so it has to handle both the same way.
Idempotency keys for client retries
An idempotency key is a unique string the client attaches to a request. If the same key shows up again, the server returns the stored result instead of doing the work twice.
Tie the key to the caller and the operation, store the result for a bounded period, and reject conflicting reuse:
curl -X POST https://api.example.com/payments \
-H "Authorization: Bearer $TOKEN" \
-H "Idempotency-Key: pay-order-8912" \
-d '{"invoiceId":"inv_9f3c2a","amount":4900}'
Picture the client timing out right after the payment succeeds. It retries with the same key. The server returns the stored result and no second charge happens.
Now the other case. Same key, different body. That must fail:
# same key, different body
curl -i -X POST https://api.example.com/payments \
-H "Authorization: Bearer $TOKEN" \
-H "Idempotency-Key: pay-order-8912" \
-d '{"invoiceId":"inv_9f3c2a","amount":9900}'
# HTTP/1.1 422 Unprocessable Entity
# {"error":"idempotency key reused with a different request body"}
A key that silently accepted a different amount would be a bug, not a safety feature.
Make the store atomic
Here’s where most implementations go wrong. They check whether the key exists with a SELECT, and if not, they do the work and then insert the key. Two workers can both run the SELECT, both see nothing, and both charge the card.
The SELECT-then-INSERT pattern is the race condition, not the fix. Let the database do the locking:
-- the unique constraint is the actual lock
INSERT INTO idempotency_keys (key, account_id, request_hash)
VALUES ('pay-order-8912', 'acc_42', 'sha256:9d1e44…')
ON CONFLICT (key, account_id) DO NOTHING;
-- zero rows inserted -> another worker owns this key,
-- return its stored result instead of charging again
The unique constraint means only one worker can insert. The other sees zero rows and steps aside. Store the key and the result in the same transaction as the operation, so a rejected duplicate leaves nothing half-done.
Give stored keys a lifetime, say 24 hours. That covers realistic client retries, keeps the table small, and stops an old key from resurrecting a stale result months later.
Replay windows for signed requests
Signed requests need a different tool. Include a timestamp or a nonce (a value used once) in the signed payload, and enforce a replay window. A captured request older than a few minutes gets rejected even with a valid signature. Inside the window, a stored nonce blocks the same request from running twice.
Try this on your payment endpoint: send two concurrent requests with the same idempotency key and body. Prove exactly one charge exists, both callers got the same result, and reusing the key with a different body is rejected.
Lesson completed