Idempotency and safe retries
Send an Idempotency-Key on any write: a retry replays the first response instead of repeating the action.
Networks fail. A retry must never create a record twice or send a message twice. Hermeticor works the way Stripe does: put an Idempotency-Key header on the call, and a retry with the same key gets the first answer back.
1. Idempotency-Key on every write
Any POST, PATCH or DELETE takes the header:
curl -s -X POST "$BASE/v1/business-goals" \
-H "Authorization: Bearer $HERMETICA_API_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: goal-q4-paid-1" \
-d '{"name":"Q4 2026: first 25 paying workspaces","metric":"conversions","targetValue":25,"startsOn":"2026-10-01","endsOn":"2026-12-31"}'Same key, same request, within 24 hours: you get the first call's status and body back, with the header
Idempotent-Replayed: true. Nothing is created or sent again.Same key, different request (another body, URL or method):
422with codeidempotency_key_reused. Use a new key for a new request.Two calls with the same key at the same moment: the second waits for the first and then gets its answer.
A call that failed (any
4xxor5xx) is not stored, because nothing was changed. Retrying it with the same key runs it again.A response that carries a secret, such as a new site key or a webhook signing secret, is never stored, so a retry of that call runs again.
Keys are 1-255 characters and belong to the API key (or signed-in user) that sent them. After 24 hours a key can be used again.
Use a key that names the thing you are doing, such as receipt-order-1042, not a random value made at retry time. Then a retry after a crash still sends the same key.
2. Sends keep their own permanent key
Send routes (transactional sends, messages, in-app, push, WhatsApp, social) require a key, as the header or as idempotencyKey in the body:
curl -s -X POST "$BASE/v1/transactional/send" \
-H "Authorization: Bearer $HERMETICA_API_KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: receipt-order-1042" \
-d '{"to":[{"email":"ada@example.com"}],"subject":"Your receipt","html":"<p>Thanks!</p>","from":{"email":"hello@example.com"}}'No key:
400. Header and body key that differ:409.Within 24 hours a retry replays the first response, as above.
After 24 hours the send's own key still holds: a repeat answers
202withstatus: duplicateand the original job id. Nothing new is sent, ever.
One-off cold email (POST /v1/outbound/send) takes the key in the body as idempotencyKey.
3. dedupeKey on events
curl -s -X POST "$BASE/v1/events" \
-H "Authorization: Bearer $HERMETICA_API_KEY" -H "Content-Type: application/json" \
-d '{"name":"Signed up","profile":{"externalId":"usr_ada"},"dedupeKey":"signup-usr_ada"}'A repeat counts in duplicates and is not stored again, with no time limit.
4. Without a key
A retried create without a key can make a second record. If you cannot send a key, use upsert routes where they exist (POST /v1/profiles/upsert, imports with "onExisting":"update"), or list or search for the record before retrying.