Idempotency
A request that times out has an unknowable outcome. The connection dropped; the
write may have committed anyway. Retrying is the only sensible thing to do, and
without protection it creates a second record — which for /v1/employees means
a second person on the payroll.
Send an Idempotency-Key header on every POST:
curl -X POST https://app.operentra.com/api/v1/employees \
-H "Authorization: Bearer $OPERENTRA_API_KEY" \
-H "Idempotency-Key: 8f14e45f-ceea-467a-9b8a-0d4a1f2c3b55" \
-H "Content-Type: application/json" \
-d '{"first_name":"Ayesha","last_name":"Khan","joining_date":"2026-10-01"}'
How it behaves
A repeat of the same key returns the original response, byte for byte, with
Idempotent-Replay: true on it. The work is not done twice.
The same key with a different body is a 409. That combination means a bug
— either a key was reused across two different requests, or a body changed
between attempts — and guessing which one you meant would be worse than
refusing.
Keys are remembered for 24 hours, which comfortably covers any retry schedule worth having.
The claim happens before the handler runs, so two concurrent retries cannot both get through. Checking first and writing afterwards leaves a window between them, and that window is exactly where a duplicate employee comes from.
Choosing a key
Use a UUID per logical operation, generated once and reused for every retry of that operation. Generating a new one per attempt defeats the whole mechanism.
If your source system has a stable identifier for the thing you are creating, deriving the key from it is even better — then a retry from a different process is still recognised:
const idempotencyKey = `hris-employee-${sourceRecord.id}`;
What it does not protect
Worth being clear about, because the honest boundary is narrower than the feature name suggests.
Creating an employee has side effects that are not part of the transaction — notifications, the onboarding pipeline, and other follow-on work. A replay returns the stored response and does not re-run them, which is correct. But a crash mid-flight on the original attempt can leave the employee created and some of that follow-on work undone. HTTP idempotency cannot repair that; it guarantees you will not create a second employee, not that the first one is complete.
The one that bites hardest: an employee created without a shift assignment
has no expected working hours, so attendance has nothing to grade against and
payroll cannot classify their days. After creating an employee, assign a shift —
see POST /v1/employee_shifts — and treat that as part
of the same logical operation in your own code.
Which endpoints support it
Every POST that creates a resource. GET is already safe to repeat, and
PATCH and DELETE are idempotent by nature — applying the same patch twice,
or deleting an already-deleted record, does not compound.
POST /v1/attendance_records is a special case: it upserts, so it is naturally
idempotent per (employee, date) and answers 200 rather than 201. Sending
it twice replaces the day rather than adding a second record.