POST /v1/webhooks and the connection dropped before the response arrived. Did the webhook get created? Retrying blind risks creating it twice. The Idempotency-Key header solves this: retry safely, and HASP guarantees the mutation only happens once.
Using it
Send anIdempotency-Key header on any JSON POST, PATCH, or PUT request:
What happens on replay
The header is entirely optional. Requests without it behave exactly as they did before this primitive existed.
Scope and boundaries
Applies to: JSON request bodies on mutatingPOST/PATCH/PUT routes across /v1 and the Data API. The key is scoped per organization + route + key value, so two different orgs — or two different routes — can reuse the same key value without colliding.
Does not apply to:
- Multipart/form-encoded requests (e.g.
POST /v1/files). Deduping would require buffering the entire upload into memory to hash it; HASP does not pay that cost, so these requests always execute, key or no key. - The AI inference endpoints —
/v1/messages,/v1/messages/count_tokens,/v1/chat/completions,/v1/ai/chat. AnIdempotency-Keysent to these is silently ignored. Their response content may carry re-identified PHI, and persisting that into the idempotency store would put it outside the governed, retention-and-crypto-shredding-aware storage path the rest of HASP’s PHI handling relies on. GET/DELETErequests.GETis already safe to retry;DELETEidempotency is handled by the resource’s own soft-delete semantics (a secondDELETEon an already-deleted resource is a no-op, not an error).
plaintext, a webhook’s secret, a credential’s token), a replay returns that field redacted (null) rather than handing the secret out again — consistent with those endpoints’ “shown once, never persisted” guarantee. The mutation itself still dedupes correctly; only the secret re-disclosure is suppressed.
Choosing a key
A UUID v4 is the simplest choice and what most HTTP clients generate by default. Whatever you use, derive it from something that uniquely identifies the logical operation in your system — e.g. an internal job ID or order ID — rather than generating a fresh key on every attempt, or retries stop being idempotent by definition.IDEMPOTENCY_CONFLICT error shape.