At a glance#
A client that loses the answer to a POST cannot tell whether the resource was created. Sending the same Idempotency-Key on the retry makes the second request return the first answer instead of creating a second resource.
| Item | Rule |
|---|---|
| Header | Idempotency-Key, sent once, 1 to 255 printable ASCII characters. A random UUID is a good key. |
| Applies to | A POST that creates a resource |
| Key not sent | The request behaves as before: no replay, no protection |
| Same key, same request, inside 24 hours | The stored status, headers and body, plus Idempotent-Replayed: true. The provider's service is not called again. |
| Same key, a different request | 422 |
| Same key while the first request is still running | 409 |
| Key empty, longer than 255 characters, not printable ASCII, or sent twice | 400 |
"The same request" means the same method, route, caller, path with query string and body.
What is stored#
- Only what the provider's service answered, with a status from
200to499except408and429. - A
5xxanswer is not stored: the client retries with the same key and the request runs again. - A response body larger than 64 KB is not stored, and a request body larger than 64 KB is not handled at all: no replay and no
409or422. Split a large payload instead. - A refusal by the gateway itself, for example an exhausted quota, is never stored.
- A retry in the few milliseconds between the response and its storage can still see
409; the client waits and retries.
Which POSTs#
A POST creates a resource when it answers 201, when its operationId starts with create, add, register, submit, subscribe, invite, issue or generate, or when it carries x-api360-creates-resource: true. x-api360-creates-resource: false marks an action that only looks like a creation. A GET, PUT or DELETE is already safe to repeat and needs no key.
In the specification#
Document the header on every such POST, optional and bounded, and declare both conflict answers:
parameters:
- name: Idempotency-Key
in: header
required: false
description: Send the same key to retry this request safely. Valid for 24 hours.
schema: { type: string, minLength: 1, maxLength: 255 }
responses:
"201": { description: Created. }
"409": { $ref: "#/components/responses/Error" }
"422": { $ref: "#/components/responses/Error" }The error codes of the gateway for these cases are idempotency_key_conflict (409), idempotency_key_mismatch (422) and idempotency_key_invalid (400).
The gateway honours Idempotency-Key on every POST to a product route, with the behaviour in the table above.
The domain APIs of the platform itself (accounts, billing, catalogue and the others) are not behind that gateway feature, so the ruleset does not ask their specifications to document the header.
Checked by#
| Rule | Severity | Checks |
|---|---|---|
api360-idempotency-key-header | error | A POST that creates a resource documents an optional Idempotency-Key header with a maxLength of at most 255 |
api360-idempotency-responses | error | The same POST declares the 409 and 422 responses |
Both rules are on in the provider profile and off in the platform profile.
See also#
- Errors: the envelope of the
409and422. - Errors, status codes and retries: which failures a client retries.