−This diagram appears on PDF page 4, printed page 3, of Malcolm Featonby's Making retries safe with idempotent APIs. The caller has not received the first response, so it retries with the same identifier; the service checks processing state in StateStore. © Amazon Web Services.
A customer can deliberately order the same item and quantity twice. If the server merges orders solely because their request bodies have the same hash, it loses that intention. Here the caller supplies an identifier: it reuses the key for the same operation and chooses a new one for a separate order. The server defines the caller and key scope, how it handles different content under one key, and how long it retains the record. AWS article.
+This diagram is from Malcolm Featonby's Making retries safe with idempotent APIs. The caller has not received the first response, so it retries with the same identifier while the service checks processing state in StateStore.
A customer may intentionally order the same item and quantity twice, so hashing the request body alone would collapse two legitimate orders into one. Instead, the caller supplies an explicit request key—reusing it when retrying the same operation and generating a new key for a separate order. The server scopes each key to the authenticated caller, rejects payload mismatches under the same key, and defines the retention window for stored records. AWS article.
This Python 3 example makes sequential calls on one thread in one process. The server supplies an authenticated caller identifier as owner; key and item are already validated strings. One dictionary entry stores the order contents and number together, and records remain in the dictionary for the run.
requests = {}
def create_order(owner, key, item):
identity = (owner, key)
if identity in requests:
previous_item, order_id = requests[identity]
if previous_item != item:
raise ValueError("KEY_REUSED_WITH_DIFFERENT_REQUEST")
return order_id
order_id = len(requests) + 1
requests[identity] = (item, order_id)
return order_id
first = create_order("alice", "request-1", "book")
retry = create_order("alice", "request-1", "book")
another = create_order("alice", "request-2", "book")
print(first, retry, another) # 1 1 2
The same caller, key, and item return the existing order number; a new key creates a separate order for the same item. Calling create_order("alice", "request-1", "pen") with changed contents under the same key raises ValueError. The (owner, key) pair separates keys belonging to different callers.
Because this example uses an in-memory dictionary, both orders and retry records vanish when the process exits. Production servers must also handle concurrent requests and crash recovery, which requires atomic commitment.