1/128 scale realistic detail programming

What is the idempotency-key header?

24 Mar 2021

In Computer Science (and in its cooler older sibling, Mathematics) idempotence is a property of an action (e.g. an API call) such that doing the action again with the same inputs produces the same result. In the context of HTTP, a GET request is usually idempotent; making a request to the same URL should return the same response, and not change any relevant data in the backing data store. A DELETE request is likewise typically idempotent; no matter how many times you DELETE a given URL, the resource stays deleted after the first call, no other resources are deleted, etc.

Idempotence is a great property for APIs to have. Idempotence coupled with retries can mitigate many problems related to the unreliability of networks, computers, and software. If any request along the chain between the client and your data store fails, the downstream initiator can safely retry. Even impatient users frantically clicking buttons and refreshing pages are no match for idempotence!

Looking at the HTTP methods used in REST APIs, some are inherently idempotent, and others are not:

POST and PATCH are not idempotent. PATCH is especially resistant to being made idempotent, as a PATCH operation is meant to apply context-aware changes to a resource in its current state. Applying the same change multiple times, as the resource's state is updated, will at best result in an error, and at worst result in a new state that the caller didn't intend. Strategies like optimistic concurrency control are often best for PATCH.

POST, on the other hand, can be made idempotent - particularly for uses meant to create a resource. If we're building an API to sell Cat Bonnets, POST requests to create new store listings could include the bonnet size and color as the most important and meaningful fields. A content-aware idempotence scheme would treat any POSTs with the same size and color as intention to ensure such a listing exists. As long as one already does, the POST won't create a new one; N > 1 requests to create a large blue bonnet always result in only 1 large blue bonnet existing.

The idempotency-key header is another way to bring idempotence to non-idempotent HTTP methods (particularly POST). The caller can supply a unique value for the header. If the header is supplied, then the server returns the same response for any requests with the same value for idempotency-key (usually within a 24 hour window; we can't expect infinite storage). But if we can make a POST idempotent based on the request contents, what's the use of this header?

A hint lies in companies that implement it: Stripe, Square, and Twilio (to name a few).

These are companies that provide APIs interacting with the outside world, where each action is expensive (sometimes in real money), and the expense compounds with duplicates (e.g. charging a user twice for the same good also damages reputation).

An SMS API wants to let its clients send a message to user X with message body "you have a new notification" more than once, maybe within minutes or seconds of each other, or even at the same time. Further, they also want to allow the clients to have a means to prevent duplicate sends if from the client's point of view, there is only one message. Doing this with content-based idempotence would require additional significant fields in the request body, ultimately boiling down to a unique key - essentially identical to the idempotency-key header. Putting the field in an HTTP header allows for easier reuse between API endpoints, and keeps values out of the schema that aren't directly meaningful for the problem space.

Ignoring any imposed expiration on idempotency-key values, adding the header to POST requests makes them look a lot like PUTs: The unique value a client uses for idempotency-key maps to the URL (typically including a unique ID in the path) that they would call PUT on. There are a few reasons why a PUT may not make sense for an API:

As a final note, this post is not idempotent. Repeated readings may further enhance your understanding of idempotence for APIs.