Skip to content
Web Development10 min read

10 Idempotency Patterns for APIs That Process Payments, Orders and Repeated Requests

Ten idempotency patterns that stop retries creating duplicate payments and orders: idempotency keys, natural keys, upserts, event IDs, state checks and more.

01

How do you stop repeated requests creating duplicates?

Make every operation that changes money or records safe to repeat. The main tools are client-supplied idempotency keys on create requests, unique natural keys and database constraints, upserts and PUT semantics for updates, event IDs for webhooks and messages, state checks before transitions, and outbox records so side effects happen once. Store the result of the first request and return it to retries, reject reuse of a key with different data, and handle the case where the original request is still in progress.

Duplicates happen because networks fail in the middle: the client times out, does not know whether the payment went through, and tries again. Users double-tap buttons, queues redeliver messages and webhooks arrive twice. This list is for developers and technical leads building APIs, checkouts and integrations. For payment-specific failure handling, see ecommerce payment failure handling.

02

Patterns for create requests

1. Client-supplied idempotency keys. The client generates a unique key (a random UUID, not personal data) for each logical operation and sends it with the request, usually in an Idempotency-Key header. The server stores the key with the result and returns that same result for any retry. Stripe's documentation describes saving the status code and body of the first request made with a key, including 500 errors, for its v1 API.

2. Reject key reuse with different data. If a retry arrives with the same key but different parameters, it is a bug, not a retry. Stripe compares parameters and returns an error; the IETF draft suggests 422 Unprocessable Content for this case.

3. Handle requests still in progress. A retry can arrive while the first request is still running. Lock on the key and return a conflict response (the IETF draft suggests 409) or wait, rather than starting a second operation.

4. Natural keys and unique constraints. Where the business has a natural unique reference, such as an order number from the storefront or an invoice number from the ERP, enforce uniqueness in the database. Even if application checks fail under concurrency, the constraint stops the duplicate.

03

Patterns for updates and state

5. PUT and upserts for updates. Setting a value ("status = shipped") is naturally idempotent; incrementing it ("stock = stock - 1") is not. Prefer set-style operations, or make increments conditional on a version or reference.

6. State-transition checks. Before acting, check the current state: only capture a payment that is authorised, only ship an order that is paid. A repeated "capture" on an already-captured payment then becomes a no-op that returns the current state.

7. Optimistic concurrency with versions. Include a version number or ETag with updates and reject updates based on stale data. This prevents a retried or delayed update from overwriting newer changes.

04

Patterns for events and side effects

8. Event and message IDs for consumers. Webhooks and queue messages can be delivered more than once. Record processed event IDs and skip duplicates; Stripe, Shopify and Razorpay all document duplicate deliveries. See webhook use cases.

9. The outbox pattern for side effects. When an operation must update the database and send an email or call another system, write the side effect to an outbox table in the same transaction, and have a worker deliver it once and mark it done. This avoids sending twice or not at all when something fails between the two steps.

10. Idempotent calls to third parties. When your system calls a payment provider or other API, pass your own idempotency key or unique reference so your retries are safe on their side too. Most payment APIs support this.

Building checkout, payments or order APIs?

ZSpace Labs builds APIs and integrations with idempotency, retries and reconciliation designed in. See our web application development service.

Start a Project
05

Pattern summary

PatternProtects againstImplementation note
1. Idempotency keysClient retries after timeoutsStore key, request hash and response
2. Reject mismatched reuseClient bugs reusing keysCompare parameters; return an error
3. In-progress handlingConcurrent retriesLock per key; conflict response
4. Natural keys and constraintsDuplicates under concurrencyUnique constraint in the database
5. PUT and upsertsRepeated updatesSet values; avoid blind increments
6. State checksRepeated transitionsReturn current state for no-ops
7. Versions and ETagsStale or delayed updatesReject updates on old versions
8. Event IDsDuplicate webhooks and messagesProcessed-events table
9. OutboxDouble or missing side effectsSame-transaction outbox plus worker
10. Keys to third partiesDuplicates at providersPass your reference downstream
06

Common mistakes

  • Generating the idempotency key on the server, which makes it useless for client retries.
  • Using personal data such as an email address as the key; Stripe's documentation advises against it.
  • Storing keys for too short a time to cover mobile apps that retry after reconnecting.
  • Saving the key only after the operation succeeds, so a crash in between allows a duplicate.
  • Treating idempotency as a client concern only; the server and database must enforce it.
  • Forgetting background jobs and scheduled tasks, which retry too.
07

Conclusion

Retries are normal; duplicates should not be. Add idempotency keys to create requests, back them with unique constraints, prefer set-style updates and state checks, deduplicate events, and use an outbox for side effects. Test by sending the same request twice, concurrently and after a simulated timeout. For the wider set of integration failures, see integration failure scenarios.

08

Sources

IETF RFC 9110, HTTP Semantics (idempotent methods). Stripe API reference, idempotent requests. IETF HTTPAPI working group, The Idempotency-Key HTTP Header Field (draft-ietf-httpapi-idempotency-key-header-08, October 2026). Accessed 28 October 2026.

FAQ

Common questions.

An operation is idempotent if doing it several times has the same effect as doing it once. In HTTP, RFC 9110 defines PUT, DELETE and the safe methods such as GET as idempotent; POST is not, which is why APIs add idempotency keys to POST requests that create payments or orders.

Get in touch

Have a project in mind?

Whether you're building a new digital product, improving an existing website, or looking to automate part of your business — let's talk.