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

ScenarioResult
First call with a new keyResource created. Response cached for ~24h.
Same key + same bodyOriginal response replayed. The reply carries Idempotent-Replayed: true so you can detect retries.
Same key + different body409 Conflict. Use a new key for a new operation.
Missing headerNo 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/projects
  • POST /api/v1/tasks
  • POST /api/v1/tasks/{id}/subtasks
  • POST /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.

On this page