Idempotency
Retry a creating call safely — the same Idempotency-Key replays the first answer instead of creating twice.
A network can drop an answer after the work was done. Retry a POST without
protection and you may send the customer two invitations, or invite a team member
twice. Every POST of the API therefore carries an Idempotency-Key: send the same key
again and you get the first answer back instead of a second action.
Required on every POST
- Without the header the answer is a 400
errors.api.idempotencyKeyRequired. - A key longer than 255 characters is a 400
errors.api.idempotencyKeyTooLong. - Resending a team member's invitation needs a key too: a replayed resend would mail twice.
One key per user action
- Make a UUID when the user acts — the click in your CRM — and store it with that action in your system. Reuse it only to retry that same request.
- A key belongs to your API key and the endpoint, and is bound to the request. The
same key on another path (another invitation's resend) or with another body is a 422
errors.api.idempotencyKeyReused. - Retry with the same API key. A retry sent with another key runs again; see the rotation steps under Authentication.
The examples in this chapter set the key on a line of its own:
IDEMPOTENCY_KEY=$(uuidgen)
curl -X POST https://api.schedulinq.com/api/v1/users \
-H "Authorization: Bearer $SCHEDULINQ_API_KEY" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "piet+ov@loodgieter-bakker.example", "firstName": "Piet", "lastName": "Bakker", "roleId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e04"}'
To retry, run the curl line again, not the assignment: the same key replays instead
of creating twice.
The answers
- The same key with the same request, once the first one has finished: the stored
status and body, with the header
Idempotent-Replayed: trueand a newrequestId. - The same key with a different request: a 422
errors.api.idempotencyKeyReused. - The same key while the first call is still running: a 409
errors.api.duplicateRequestInProgress. Retry after a moment, with the same key. - A first call that has not finished after 60 seconds may be taken over by a retry.
What is stored, and what that means for a retry
Stored and replayed for 24 hours: every 2xx, and every final refusal — 400, 402, 403, 404, 409 (except the two below) and 422. After you fix the cause of a stored refusal — an admin added a seat, a team member was activated, you corrected the request — send a new key. The same key would replay the old refusal.
Not stored: a 409 errors.api.duplicateRequestInProgress, a 409
errors.api.conflict (changed by another request at the same time), 408, 425, 429 and
every 5xx. Retry those with the same key.
Stored answers are encrypted, because they can contain links.
Frequently asked questions
Can I use the same key for two different customers?
No. One key is one action. The second request is refused with a 422
errors.api.idempotencyKeyReused.
Do GET requests need a key?
No. Reading changes nothing, so a GET can be repeated as often as you like.