POST /rentals requires an Idempotency-Key header. It is the only endpoint
that does, and the reason is narrow: creating a rental spends money, and a
network timeout tells you nothing about whether it happened.
The problem it solves
Your request times out. Three things could have happened:- It never reached us — no rental.
- It reached us and failed — no rental.
- It reached us and succeeded, and the response was lost — a rental you are now paying for and do not know about.
What a retry returns
Same key, same body → the original rental, with201 and the original id.
Not a new machine, not an error.
The three failure modes
Retried too fast — 409 idempotency_key_in_flight
Retried too fast — 409 idempotency_key_in_flight
The first attempt is still being processed. Rather than risk a second machine, we
refuse and tell you how long to wait:Wait and retry with the same key. A new key here would create the second
machine you were trying to avoid.
Same key, different body — 409 idempotency_key_reused
Same key, different body — 409 idempotency_key_reused
{"a":1,"b":2} and {"b":2,"a":1} are the same request.The alternatives were both worse. Returning the first rental would hand you a
machine that does not match what you just asked for. Creating a second would
make the key meaningless. An error is the only answer that cannot mislead.The usual cause is a key derived from something too coarse — see below.No key at all — 400 idempotency_key_required
No key at all — 400 idempotency_key_required
Choosing keys
One key per intent to create one rental. 8–255 characters; a UUID v4 is the easy right answer.Deriving keys from your own ids
A deterministic key is fine, and often better, as long as it is unique per intent:idempotency_key_reused and refuses to start the second
rental at all, which is a confusing outage rather than a billing incident.
Scope and lifetime
- Keys are scoped to your account. Two of your keys using the same string collide; another customer using it does not affect you.
- Records are retained long enough to cover any realistic retry, then swept. A key reused weeks later may no longer be recognised — which is fine, since by then it is a new intent anyway.
Everything else
No other endpoint takes the header. It would be noise:GETrequests change nothing.POST /rental-quotesreserves nothing and costs nothing.POST /rentals/{id}/stopis idempotent by nature — stopping an already-stopping or already-stopped rental returns the rental as it is, not an error. Retry it freely.