Skip to content
Hoursmith Docs
API

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: true header. No second record is created.
  • Same key, different body409 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.

Was this page helpful?

On this page