Appearance
Idempotency
Summary: every request that creates or changes something carries an Idempotency-Key header. Send the same key with the same body again and you get the first response back, so a retry after a timeout can never create a second order. Use a new key for each new action.
Which endpoints need a key
Needs Idempotency-Key | Takes no key |
|---|---|
POST /clinics, PATCH /clinics, PUT /clinics/banking, POST /clinics/banking/authorize, PATCH /clinics/identity | Every GET |
POST /orders, PATCH /orders/{id}, POST /orders/{id}/cancel | POST /orders/check (it changes nothing) |
POST /tickets, POST /tickets/{id}/messages, POST /tickets/{id}/resolve | POST /uploads, POST /support-sessions |
A request to an endpoint that needs a key and has none is refused with 400 idempotency_key_required.
How it works
- The key is any string from 1 to 128 characters after trimming spaces. A UUID per action is the simplest choice.
- The body is fingerprinted. We hash the exact bytes of the request body. A repeat with the same key and identical bytes replays the stored response, status code included. The same key with different bytes is refused with
409 idempotency_key_reused, because that is a bug in the caller, not a retry. Re-serializing JSON with a different key order or spacing counts as different bytes, so keep the original body for retries. - Only successes are stored. A
201or200is remembered and replayed. A refusal (400,409,422) is not stored, so after you fix the problem you can resend under the same key and it runs again. - Stored responses last 7 days. After that, a repeat no longer replays the stored response. On order create the key is also recorded on the order itself, so a key that created an order can never be used for a different order: that is refused with
409 idempotency_key_reusedhowever old the order is. - Keys are scoped to your account, the endpoint and the environment, so a sandbox key and a production key never collide. Edit, cancel, reply and resolve are also scoped to the order or ticket in the path, so one key used on two different orders cannot replay the wrong order's answer.
Order create: two places, one key
POST /orders carries the key twice: the Idempotency-Key header and the body's idempotency_key. They must be the same string (surrounding spaces are ignored in the comparison). If they differ, the request is refused with 400 idempotency_key_mismatch and nothing is created, so you can correct it and resend under the same header.
Separately, your partner_order_id must be unique among your live orders. If you lose a create response and retry with a new key, the retry is refused with 409 partner_order_id_duplicate, which carries the existing axiomOrderId. Retrying with the original key returns the original 201 instead, which is why you should keep the key with the order on your side.
Retrying safely
| You got | Do this |
|---|---|
| A timeout or network error | Retry with the same key and the same body. |
5xx, 429 or 503 | Retry with the same key after a pause. Honour Retry-After when it is present. |
409 order_in_flight or 409 idempotency_key_in_flight | Another request with this key is still running. Retry shortly with the same key. If order_in_flight keeps coming back, the key belongs to an earlier order with this partner_order_id that is failed or cancelled: read that order first (GET /orders/{partnerOrderId}) and follow the failed-order guidance; never resubmit one whose failure.code is pharmacy_outcome_unknown. Only then resubmit, under a new key. |
409 idempotency_key_reused | Your code reused a key for a different body. Use a new key for the new action. |
400 or 422 | Fix the request. Resend with the same key (refusals are not stored) or a new one. |
200 or 201 | Done. Store the response. |
A good pattern: create the key when the user starts the action, save it with your pending record, and use that saved key for every attempt until you get a final answer.
Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | idempotency_key_required | The endpoint needs an Idempotency-Key header. |
| 400 | idempotency_key_invalid | The key is empty after trimming, or longer than 128 characters. |
| 400 | idempotency_key_mismatch | Order create only: header and body keys differ. |
| 409 | idempotency_key_reused | The key was used before with a different body. |
| 409 | idempotency_key_in_flight | A concurrent request with this key has not finished. Retry shortly. (The order and clinic endpoints add retryable: true; the ticket endpoints do not, so branch on the code.) |

