Retrying safely
When a write times out, you do not know whether it happened. Sending it again
must not make a second booking for Anna Müller or book the same till sale
twice. The API gives you three ways to make a repeat harmless: the
Idempotency-Key header on any write, and a reference of your own on bookings
and on till sales.
Your system to Grondia:
POST bookings/. WithIdempotency-Key: 5f0c...Grondia to Your system: 201 Created. Lost on the way
Your system: Timeout, so send the same request again
Your system to Grondia:
POST bookings/. WithIdempotency-Key: 5f0c...Grondia to Your system: 201 Created. The stored answer
Grondia: Still one booking
Idempotency-Key
Section titled “Idempotency-Key”Add an Idempotency-Key header to a POST or PUT, with a new random value
for each operation, such as a UUID. Keep the key and the exact body together
until the operation has an answer.
curl https://api.campone.ch/api/v1/public/bookings/ \ -H "Authorization: Bearer ck_a1b2cXXXXXXXXXXXXXXXXXXXXXXXX" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 5f0c2d8e-7b1a-4c3e-9d6f-2a8b4c6e1f90" \ --data-binary @booking.json-
The first answer in the
2xxrange is stored, status and body, for this campground and this key. -
The same key with the same request (method, path and body, byte for byte) gets that status and body back, and nothing runs a second time.
-
The same key with a different request answers
409:{ "detail": "Idempotency-Key was reused with a different request.", "code": "idempotency_key_reuse" } -
An error is not stored. A repeat after a
4xxor5xxruns the request again.
What to watch for:
- Byte for byte. Keep the bytes you sent and send those again. Building the JSON again from the same data can change the order of keys or the spacing, and that is a different request.
- One request at a time. The answer is stored when the first request
finishes. A repeat that arrives while the first one is still running is not
recognised, and runs as well. Repeat only after the first attempt has failed
or timed out, and for bookings also send an
external_reference, which holds even then. - Keys belong to the campground, not to your API key, and they do not expire today. Use a fresh random value for every operation, never a counter or a value another integration could also pick. Grondia reads the first 200 characters.
- A repeat counts against your rate limit like any request.
Bookings: external_reference
Section titled “Bookings: external_reference”Send your own id for the booking as external_reference in
POST bookings/
(up to 200 characters; spaces at either end are dropped). A campground has at
most one booking per external_reference:
- The first request makes the booking:
201. - A repeat with the same
external_reference, from the same key, returns that booking with200, guest data redacted as in any read (Personal data says why). Everything else in the repeat is ignored: it does not change the dates or the guests. - Two requests at the same moment cannot both make a booking. The second gets
the first one’s booking with
200, or a409saying the pitch is taken; either way there is one booking. - If the booking with that reference was made by another key, or reached
Grondia some other way, the answer is
409with{"detail": "external_reference_conflict"}, and nothing about that booking. - The match is on the key that made the booking. After the campground replaces
your key, a repeat of a booking made with the old key answers
409.
With an Idempotency-Key on the same request, the key is checked first: a
repeat gets the stored 201.
Channel managers shows a push that survives a retry end to end.
Cancelling
Section titled “Cancelling”POST bookings/{id}/cancel/
is not harmless to repeat by itself: the second call answers 400 with
{"detail": "not_cancellable", "status": "cancelled"}. Send an
Idempotency-Key to get the first answer, with its refund, back. Without one,
treat not_cancellable with "status": "cancelled" as done.
Till sales: external_ref
Section titled “Till sales: external_ref”POST pos/transactions/
requires your till’s id for the sale in external_ref (up to 128 characters,
not blank). The first request records the sale: 201 and
"idempotent_replay": false. A repeat with the same external_ref records
nothing and returns the recorded sale: 200 and "idempotent_replay": true.
The lines and amounts of the repeat are not compared with the first, so a new
sale needs a new external_ref. To find out whether a sale arrived, ask
GET pos/transactions/{external_ref}/:
404 with not_found means it did not. Shop and restaurant tills
posts a sale this way, with the lines and the totals worked out.
The other writes
Section titled “The other writes”PUT ari/restrictions/is safe to repeat as it is: each row sets the rules of one site type on one date, and a row with every rule off removes them. Only the counts in the answer (upserted,deleted) can differ.POST webhooks/is keyed by the URL: a repeat updates the endpoint and answers200without the secret. If the answer to the first request was lost, the secret went with it; post the URL again with"rotate_secret": trueto get a new one (see Webhooks).POST webhooks/deliveries/{delivery_id}/replay/queues one more delivery each time it is called. Send anIdempotency-Keyif you repeat it.
When to retry
Section titled “When to retry”| What happened | Retry? |
|---|---|
| No answer: a timeout or a dropped connection | Yes: the same request, with the same Idempotency-Key |
429 |
Yes, after the seconds in Retry-After (Rate limits) |
503 or another 5xx |
Yes, after a pause, with the same Idempotency-Key |
409 |
Not unchanged. Read the body (Errors): a taken pitch needs other dates or another pitch |
400, 401, 403, 404, 422 |
No. Fix the request first. A date in the query that is not a real date is a 400 (bad dates in a query) |
For the pause, our recommendation: start at one second, double it after each attempt, add a random part so that many clients do not retry in step, and stop after a few minutes and raise an alert.
In the sandbox
Section titled “In the sandbox”The sandbox key is read-only, so none of these writes can be tried with it. For a key that may write, ask Felix, felix@grondia.com, or Siro, siro@grondia.com, and say what you are building.