Idempotency
Every POST to the Hoursmith API requires an Idempotency-Key so retries never create duplicates. Learn how it works.
Network calls fail and get retried. To make sure a retried POST never creates a duplicate record,
the Hoursmith API uses idempotency keys.
The rule
Every POST request must include an Idempotency-Key header. It's required — a missing key
returns 422.
POST /api/v1/clients HTTP/1.1
Authorization: Bearer hsk_live_...
Idempotency-Key: 6b9d6f2e-2a1c-4f6b-9d2e-7c1a0b3e5f44
Content-Type: application/json
{ "name": "Globex" }Use a unique value per logical operation — a UUID or ULID works well (up to 255 characters).
How replays work
Hoursmith remembers each key for 24 hours:
- Same key, same request body → you get the original response replayed verbatim, with a
Idempotent-Replayed: trueheader. No second record is created. - Same key, different body →
409 conflict(the key was already used for a different request). - New key → processed normally.
This means you can safely retry a POST after a timeout: if the first attempt actually succeeded,
the retry returns that same result instead of creating a duplicate.
Generating keys
const key = crypto.randomUUID();
await fetch('https://hoursmith.app/api/v1/time-entries', {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Idempotency-Key': key,
'Content-Type': 'application/json',
},
body: JSON.stringify({ projectId, entryDate: '2026-06-11', durationSeconds: 5400 }),
});import uuid, requests
key = str(uuid.uuid4())
requests.post(
"https://hoursmith.app/api/v1/time-entries",
headers={"Authorization": f"Bearer {token}", "Idempotency-Key": key},
json={"projectId": project_id, "entryDate": "2026-06-11", "durationSeconds": 5400},
)Generate the key once per operation and reuse it across retries of that same operation. If you generate a fresh key on every retry, you lose the protection.
GET, PATCH, and DELETE don't require an idempotency key — GET is naturally safe to repeat,
and PATCH/DELETE target a specific record by id.