An operation is idempotent if performing it once or multiple times has the same effect. For APIs, that means: same request (identified by an idempotency key) applied once or retried many times produces the same outcome and no duplicate side effects (e.g. one charge, one order).
Why it matters for payments
Networks and clients retry. Without idempotency, a double-click or a retry can create two charges or two orders. With an idempotency key (client-generated unique ID per logical operation), the server can recognize a repeat and return the same result without re-executing.
sequenceDiagram
participant C as Client
participant S as Server
participant DB as Store
C->>S: POST /charge Idempotency-Key: key-123, body
S->>DB: Has key-123 been seen?
alt First time
DB-->>S: No
S->>S: Process charge
S->>DB: Store result for key-123
S-->>C: 201 Created (charge id)
else Retry / duplicate
DB-->>S: Yes, result stored
S-->>C: 200 OK (same charge id, no new charge)
end
Implementing idempotency
Client sends a unique Idempotency-Key (UUID or similar) per logical operation.
Server stores (key → response/outcome) in DB or cache with a TTL (e.g. 24h).
First request with that key: process and store result. Subsequent requests with same key: return stored result, do not re-run the operation.
What should be idempotent
flowchart TB
subgraph Idempotent["Should be idempotent"]
I1[Payments / charges]
I2[Order creation]
I3[Subscription changes]
I4[PUT / PATCH / DELETE]
end
subgraph NonIdempotent["Inherently not"]
N1[POST create without key]
N2[Send email / notification]
end
Use idempotency keys for any non-idempotent mutation that must not be applied twice: payments, order creation, sign-ups that send email, etc. GET, PUT, DELETE are naturally idempotent; POST and custom actions need a key.