Live Webinar on Dec 3rd: How They Built It: Albert, GrowIt, and NFTYDoor share the payment decisions they’d make again. Register now →

Glossary
•August 23, 2022(Updated October 21, 2026)

What is Idempotency?

An API call is idempotent if it has the same result, regardless of how many times it is applied. Inadvertent duplicate API calls can cause unintended consequences for a business, idempotency helps provide protection against that.

An API call is idempotent if it has the same result, regardless of how many times it is applied. Inadvertent duplicate API calls can cause unintended consequences for a business, idempotency helps provide protection against that.

Key takeaways

  • An operation is idempotent when running it once or many times leaves the same result, so a retried payment request never creates a second payment.
  • In a payments integration, idempotency works in three layers: API requests, webhooks and your own system's safeguards.
  • Idempotency keys and external IDs solve different problems, so payment orders and ledger transactions should carry both.

An API call is idempotent if it has the same result no matter how many times it's applied. In payments, that means a retried request, a double click or a redelivered webhook moves money once. Businesses get there by sending an idempotency key with each request that creates or changes a payment, de-duplicating webhooks and building safeguards into their own systems.

What Is Idempotency and How Does It Work?

The word “idempotent” stems from the Latin words “idem” and “potent,” which when put together, means “the same power.” The term was originally introduced in mathematics to refer to mathematical operations that can occur multiple times while only altering the end result once. For example, the operation “Multiply by 1” is idempotent. Whether you do 2x1 or 2x1x1x1, the result will always be 2.

This behavior is desirable in internet applications, especially those that involve payments. When you are shopping online and press the submit button on a checkout page twice, you only want your order to be placed once.

In the context of APIs, idempotency is significant in safeguarding the accuracy of a business’ data. A request that isn't idempotent creates a new record each time it's repeated. That's harmless for a search and expensive for a payment.

Why Idempotency Matters in Payments

When building a system to move money, it is paramount that operations that move money are idempotent. Failure to do this could result in errors such as double charging a customer or paying a vendor multiple times.

Duplicates rarely come from one dramatic failure. They come from ordinary behavior in distributed systems:

  • Network timeouts. Your system sends a payment request, the connection drops before the response arrives, and your code can't tell whether the payment exists.
  • Double submits. A customer or an operator presses submit twice.
  • Parallel jobs. Two background workers pick up the same payment. Modern Treasury has noted that this risk grows because developers build on scalable systems that process multiple items in parallel.
  • Webhook redelivery. A webhook your endpoint doesn't acknowledge in time gets sent again.

Picture a growing platform that loses its connection while creating a contractor payout. Without idempotency, its retry logic has two bad options: retry and risk paying the contractor twice, or skip and risk not paying at all. The same choice shows up for payroll providers, marketplaces and lenders.

The Three Layers of Idempotency in a Payments Integration

Modern Treasury’s approach is a two-pronged approach to idempotency: internal safeguards, and idempotency built into the API. For a business integrating a payments API, that becomes three layers, because the webhooks coming back to you need the same protection as the requests you send.

  • Layer 1, API requests: stops a retry from repeating a request that may already have succeeded. Send an Idempotency-Key header on every POST and PATCH.
  • Layer 2, webhooks: stops your system from processing the same event notification twice. Save each X-Webhook-ID and skip IDs you've already processed.
  • Layer 3, your own system: stops two jobs or code paths from acting on the same payment. Use state machines, database transactions, locks and external IDs.

The third layer is the one teams skip. Modern Treasury uses three internal safeguards, and each one carries over to your system:

  • State machines. A state machine, a fixed set of statuses and the moves allowed between them, enforces the sequence of steps a payment can take so a payment can't be processed twice. Modern Treasury publishes its payment order states in its API documentation.
  • Database transactions. Every critical operation is wrapped in a database transaction. When a payment needs approval, the payment and the pending approval are created together or not at all.
  • Mutexes. A mutex, a lock that lets one process work on a record at a time, ensures at most one job operates on a payment at once.

“We recommend that our customers build similar safeguards into their own systems, even when they are using Modern Treasury.” — Matt Marcus, Co-Founder and CEO, Modern Treasury

Why it matters: an idempotency key covers one request for 24 hours. The other two layers cover everything else, including retries that arrive after the window closes.

Which HTTP Methods Are Idempotent?

GET, PUT and DELETE are idempotent by definition. POST and PATCH aren't, which is why they need an idempotency key. In Modern Treasury's API, all POST and PATCH requests accept idempotency keys, and a key sent with GET, PUT or DELETE is ignored and not saved.

  • Idempotent by default: GET reads a resource, PUT replaces one and DELETE removes one, so repeating any of them leaves the same result.
  • Not idempotent by default: POST creates a resource, such as a payment order, and PATCH updates part of one. Each repeat can create or change data again.

Send keys on PATCH, not only POST. A repeated partial update can change data twice, and Modern Treasury accepts idempotency keys on every PATCH request.

How Do Idempotency Keys Work?

An idempotency key is a unique string your system attaches to a request so the API can recognize a repeat. In Modern Treasury's API it works like this:

  1. Your system generates a unique key for the operation, preferably an internal database UUID. It can't exceed 180 characters.
  2. It sends the key as a header: Idempotency-Key: <your-key>.
  3. When the request succeeds, Modern Treasury saves the result for 24 hours.
  4. If your system sends a new request with the same credentials and key within 24 hours, Modern Treasury echoes back the original response (status code and body) instead of creating a second payment.
Shell
curl --request POST \  -u ORGANIZATION_ID:API_KEY \  -H "Idempotency-Key: <your-key>" \  --url https://app.moderntreasury.com/api/<endpoint>

Example idempotent request, from Modern Treasury's Idempotent Requests docs.

The 24-hour window is a choice, and it says how keys should fit into your design:

“We chose 24 hours because we believe the role of idempotency keys are to solve for short-term bursts of the same API calls. They aren't meant to be an architectural crutch for our customer's systems.” — Matt Marcus, Co-Founder and CEO, Modern Treasury

Only successful requests are saved. If a connection interruption or another error stopped the original request, the next request with the same key runs and its result is cached. If a duplicate arrives while the first request with that key is still being stored, it gets a 409. Treat a 409 as “in progress” and retry later with the same key.

Common Idempotency Key Mistakes

Most duplicate payments that get past idempotency keys come from how the key is generated or reused on the client side:

  • Generating a new key on each retry. A new key makes the retry a new request. Create the key once per operation, store it with your record and reuse it on every retry.
  • Reusing one key for different operations. Keys are independent of the route. If you use a key to create a payment order and then use it to create a counterparty within 24 hours, you get back the cached payment order response.
  • Sending the same request with different API keys. Each API key executes its requests independently, so the same idempotency key under two API keys can create two updates.
  • Relying on the key after 24 hours. After the window, a repeat is a new request. Before retrying an old request, check whether the resource already exists, for example by its external ID.

Idempotency keys are scoped to the API key that sent the request. If several services share retry duties, make sure the service that sends a request with a given API key is the one that retries it.

Idempotency Keys vs. External IDs

An idempotency key protects a request. An external ID identifies a resource with your own ID, so your database and Modern Treasury's stay in sync. Modern Treasury's docs are explicit that external IDs don't replace idempotency keys: the two serve different purposes and work together.

  • Where it goes: an idempotency key is a request header. An external ID is a field on the object, such as a payment order or ledger transaction. Both take up to 180 characters, and external IDs allow only letters, numbers, underscores and dashes.
  • What it stops: a key stops a repeated request from having side effects. An external ID stops a second resource from being created with the same ID.
  • How long it lasts: a key lasts 24 hours. An external ID is permanent and can't be changed once assigned.
  • On a duplicate: a repeated key returns the original response. A reused external ID returns a 409 error (“external_id has been taken”).
  • Lookup: an external ID can stand in for a Modern Treasury ID in GET and PATCH requests. A key can't be used to look anything up.

Our recommendation: send both on every payment order and ledger transaction. The idempotency key handles fast retries; the external ID blocks a duplicate after the 24-hour window and lets you look up the record by your own ID. A key alone isn't enough if: your retries can arrive more than a day later, such as queued jobs replayed after an outage.

When a request carries both, the idempotency key decides. A repeat with a used key returns the original response, whatever external ID it carries.

How to Handle Duplicate Webhooks

Webhooks need their own de-duplication because Modern Treasury retries them. Its webhook system waits up to five seconds for a 2XX status code. If your endpoint takes longer or returns an error, the webhook is re-enqueued and retried with exponential backoff, so the same event can reach you more than once.

Each webhook carries headers that tell a retry apart from a new event:

  • X-Webhook-ID is unique to the webhook event and stays the same across retries. De-duplicate on this.
  • X-Event-ID is the ID of the Event object, also tied to every retry of that event.
  • X-Delivery-ID is unique to each delivery attempt and changes on every retry.

Return a 2XX quickly, save each X-Webhook-ID as you process it, and skip any ID you've already seen. Watch endpoint health too: Modern Treasury pauses a webhook endpoint once an event has used all 15 delivery attempts, roughly a day and a half after the first. Its docs recommend subscribing to webhook endpoint failure notifications.

De-duplicate on the webhook ID, not the delivery ID. A delivery ID changes on every attempt, so a handler keyed on it processes every retry as new.

How Modern Treasury Helps

Modern Treasury is a payment service provider that lets businesses send, receive, track, and reconcile payments through one API and dashboard, with idempotency built into both the requests you send and the webhooks you receive.

  • Idempotent requests: every POST and PATCH accepts an idempotency key, with the original response replayed for 24 hours.
  • Webhook IDs: every webhook carries an ID that stays the same across retries.
  • External IDs: payment orders and ledger transactions accept your own immutable ID and reject duplicates.
  • Concurrency control in Ledgers: an optional lock version on ledger entries rejects a write when the account changed underneath it.
Subscribe to our newsletter

Get the latest articles, guides, and insights delivered to your inbox.