REST API
Idempotency
Safely retry POST requests with the Idempotency-Key header.
Why it matters
POST requests create resources. If the network drops between the server completing the work and the response reaching you, a naive retry would create a duplicate. The Idempotency-Key header lets you safely retry without that risk — the server replays the stored response instead of repeating the work.
How to use it
Generate a unique key per logical operation (a UUID v4 is the simplest choice) and send it on every retry of that operation:
curl -X POST https://your-domain/api/v1/projects \
-H "Authorization: Bearer ba_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 1f3c0e22-7a36-4f6b-9a73-3a3a89aa1f0e" \
-d '{"name":"Sample project"}'Behavior
| Scenario | Result |
|---|---|
| First call with a new key | Resource created. Response cached for ~24h. |
| Same key + same body | Original response replayed. The reply carries Idempotent-Replayed: true so you can detect retries. |
| Same key + different body | 409 Conflict. Use a new key for a new operation. |
| Missing header | No deduplication — every call creates a new resource. |
Keys are scoped per organization, so two different orgs can use the same string without collision.
Where it applies
Every POST endpoint that creates a resource honors the header:
POST /api/v1/projectsPOST /api/v1/tasksPOST /api/v1/tasks/{id}/subtasksPOST /api/v1/time-entries
PATCH and DELETE are inherently idempotent at the HTTP layer and do not use this header.
Constraints
- The header is optional but recommended for any client that retries failed requests.
- Maximum key length: 255 characters.
- Records are retained for ~24 hours. After that, replays no longer fire — a request with an expired key behaves as a fresh request.
- Two retries that race the original request: the server still serializes them through the unique constraint, so only one underlying resource is ever created.
Suggested patterns
- Per-button-click UUID — generate the UUID when the user clicks, not when the request is built. The same click that retries gets the same UUID.
- Job-scoped keys — for batch jobs, derive the key from a deterministic job id (e.g.
Idempotency-Key: import-2026-05-20-row-42) so a re-run of the same job re-uses the same keys safely.