Idempotency and concurrent updates
Prevent duplicate records on a retry and never accidentally overwrite a newer version.
Idempotency-Key
A network error or timeout does not mean your request failed. So with a POST, send an Idempotency-Key header with a unique value, for example a UUID:
curl -X POST "https://app.klantly.com/api/v1/customers" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f" \
-d '{"email": "jan@example.com"}'- If you send the same request again with the same key, you get the same response back, with the header
Idempotent-Replayed: true. Nothing is created twice. Only the secret of a webhook endpoint (on creating and rotating) is left out then: it is shown exactly once. - The same key with a different body returns
422 idempotency_key_reused. - If the first request is still running, you get
409 idempotency_in_progress. - Responses that depend on the current state (401, 403, 404, 409, 412, 422, 423, 429) and every 5xx are not stored: the same key with a corrected body simply executes the request.
An Idempotency-Key is valid for 24 hours and counts for your whole company, not per key. If two integrations (or two keys) use the same value for a different request, the second gets 422 idempotency_key_reused; give each integration its own prefix, for example crm- or webshop-. Because the guarantee belongs to the company, a retry stays safe when your integration switches to the new token during key rotation.
Warning
The upload routes (attachments on a quote and photos on a work order) do not work with an Idempotency-Key: with a multipart/form-data body Klantly cannot tell whether you are sending the same file again. Repeat an upload and you get a second file; delete that one yourself.
ETag and If-Match
Every single object comes with an ETag header. Send it in If-Match when you make a PATCH:
PATCH /api/v1/customers/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70 HTTP/1.1
Authorization: Bearer kly_4fZ2mQ8v...
If-Match: "3f2c9a41d6e8b07c5a2f"
Content-Type: application/jsonIf someone else changed the record in the meantime, you get 412 precondition_failed and nothing changes. Fetch the record again and retry. Without If-Match, the change is simply applied.
Last updated on September 21, 2026