DEV Community

desgh white
desgh white

Posted on

Idempotency Keys: Making Withdrawals Safe to Retry

Any endpoint that moves money must survive being called twice. Networks time out, clients retry, users double-click — and without idempotency, a retried withdrawal pays out twice. The fix is a small amount of discipline at the boundary.

The client supplies the key

The caller generates a unique key per logical operation and sends it with the request:

POST /withdrawals
Idempotency-Key: 8f14e45f-ceea-467a-9d0e-...
{ "amount": 50.00, "currency": "AUD" }
Enter fullscreen mode Exit fullscreen mode

The server stores the key with the result of the first successful call. A retry with the same key returns the stored response instead of executing again.

Store the outcome, not just the key

def withdraw(key, amount, account):
    existing = db.get_idempotent(key)
    if existing:
        return existing.response          # replay, do not re-execute
    with db.transaction():
        result = execute_payout(account, amount)
        db.put_idempotent(key, result)    # same tx as the side effect
    return result
Enter fullscreen mode Exit fullscreen mode

The record and the side effect must commit in the same transaction. If they don't, a crash between them either double-pays (key not saved) or loses the payout (key saved, payout rolled back).

Scope and expiry

Keys are scoped per account and per endpoint, so one user's key can't collide with another's. Expire them after a day or two — long enough to cover any sane retry window, short enough that the table doesn't grow without bound.

Reference

Cashier flows are a good place to see this pattern under real load, since a duplicated withdrawal is an immediate, visible failure. Watching how a withdrawal at a site like wantedwin casino review confirms a request exactly once — even if you refresh mid-submit — is a concrete example of idempotency done right.

Takeaway

Take a client-supplied key, persist the result alongside the side effect in one transaction, and replay stored responses on retry. A few lines at the boundary turn a double-payout bug into a non-event.

Top comments (0)