Idempotency keys for safe retries
Networks fail between “the server did the work” and “you received the answer”. An idempotency key lets you repeat a POST request without doing the work twice, so a timeout never turns into a duplicate lead or invoice.
How the Idempotency-Key header works
Section titled “How the Idempotency-Key header works”Add an Idempotency-Key header of up to 255 characters to a POST request. The first request with a key runs normally and its response is stored for 24 hours for that API key. A repeat with the same key and the same request returns the stored status and body without running again, and the response has the header Idempotent-Replayed: true. Without the header, requests are never deduplicated. Only POST requests are affected.
What counts as the same request
Section titled “What counts as the same request”Dakoty fingerprints the method, path, query string and body. The same key sent with a different body, or to a different endpoint, returns 422 IDEMPOTENCY_KEY_REUSED. Send the identical body when you retry, including the same field order and values. Keys are scoped to one API key, so two keys can use the same string without clashing.
Which responses are stored
Section titled “Which responses are stored”Successful responses (2xx) and deterministic validation failures (400 and 422) are stored and replayed. Everything else releases the key: 5xx errors, 401, 403, 404, 409 and 429 are not stored, so you can fix the cause and retry with the same key. Responses larger than 1 MiB are not stored either. If the key store is unavailable the request simply runs without deduplication, so keep your own duplicate protection for critical writes.
Requests that are still running
Section titled “Requests that are still running”If you repeat a request while the first one is still being processed, you get 409 IDEMPOTENCY_IN_PROGRESS. Wait a moment and send the same request again; once the first one finishes you receive its stored response.
Choosing a good key
Section titled “Choosing a good key”Use a value that is stable for one logical operation and unique across operations. A UUID generated once per business action works, as does an identifier from your own system, such as order-4711-create-invoice. Do not generate a new key inside the retry loop, because then every retry looks like a new operation.
Safe retry in Node.js
Section titled “Safe retry in Node.js”The key is created once, before the first attempt, and reused on every retry. This builds on the retry helper from Errors and rate limits and needs Node.js 18 or newer.
import { randomUUID } from "node:crypto";
export async function createLead(lead, key = randomUUID()) { for (let attempt = 0; attempt < 4; attempt++) { try { const res = await fetch("https://api.dakoty.com/api/v1/public/leads", { method: "POST", headers: { Authorization: `Bearer ${process.env.DAKOTY_API_KEY}`, "Content-Type": "application/json", "Idempotency-Key": key, }, body: JSON.stringify(lead), }); if (res.status === 409 || res.status >= 500) throw new Error(`retry ${res.status}`); return { replayed: res.headers.get("idempotent-replayed") === "true", body: await res.json() }; } catch (err) { if (attempt === 3) throw err; await new Promise((resolve) => setTimeout(resolve, 2 ** attempt * 500)); } }}Safe retry in Python
Section titled “Safe retry in Python”The same idea with requests. The key is created before the loop, and the same dictionary produces the same JSON body on every attempt.
import osimport timeimport uuidimport requests
def create_lead(lead, key=None): key = key or str(uuid.uuid4()) headers = { "Authorization": f"Bearer {os.environ['DAKOTY_API_KEY']}", "Idempotency-Key": key, } for attempt in range(4): try: r = requests.post("https://api.dakoty.com/api/v1/public/leads", json=lead, headers=headers, timeout=30) if r.status_code != 409 and r.status_code < 500: return r.headers.get("Idempotent-Replayed") == "true", r.json() except requests.RequestException: if attempt == 3: raise time.sleep(2**attempt * 0.5) raise RuntimeError("Dakoty did not accept the request after 4 attempts")