Mental model: the idempotency key is a stable name for a business operation. The first request with that key records a decision; every later request with the same key returns the decision that already exists.
Evaluate vs Record — Idempotency applies to
record, the endpoint that persists a decision. evaluate records no decision, so it has no decision to deduplicate. It is not a no-op write, though: resolving the active policies may append one row to the tenant’s append-only policy_snapshots table, returned as policy_snapshot_id. That append is content-addressed — a repeat call resolving to the same policies reuses the existing snapshot and appends nothing.How it works
When you callPOST /v1/decisions/record with an idempotency_key, MeshQu checks whether a decision already exists with that key for your tenant:
The second case returns the stored decision without running policies again, so retries are both safe and cheap.
What counts as “the same content”
The comparison is over the decision context:decision_type, fields, evidence, source_artifact, and metadata.
Values the server writes into the context for you are excluded, so an ordinary network retry can never conflict:
metadata.correlation_id— regenerated per request when you send noX-Correlation-Idheader.- The
agent_licence_*metadata keys — resolved server-side from your API key, so a re-issued licence does not conflict.
evidence_manifest, evidence_manifest_digest, action and options.chain. Changing only one of those under an existing key returns the stored decision (200) rather than a 409. The 409 body publishes this list as fingerprint_ignores.
Recording with an idempotency key
The TypeScript SDK requires
idempotency_key on record() and throws if it is missing or blank. The REST API treats it as optional but strongly recommended — a record call without a key still succeeds, it just won’t be deduplicated. For any workflow that can retry (which is most of them), always supply one.Choosing good keys
A key should uniquely and deterministically identify the business operation, so that a retry of the same operation produces the same key. Good keys — derived from a stable business identifier:Key format
A key, once used, is bound to that decision permanently within the tenant. There is no expiry, so a retry months later still returns the original decision. If a decision genuinely needs to be re-made (for example after corrected input data), use a new key with a version suffix — this is enforced, not merely advised: reusing the old key with the corrected data is refused with
409.
Safe-retry pattern
Treat both200 and 201 as success. Retry only on server errors (5xx) and network failures — never on client errors (4xx), which indicate a request that won’t succeed on retry. 409 IDEMPOTENCY_KEY_REUSED is one of those: it means the key is already bound to different content, so retrying unchanged will fail identically. Choose a new key, or restore the original request body.
Common patterns
Queue processing — use the message ID so reprocessing a redelivered message is a no-op:Verifying a key was used
Recorded decisions carry theiridempotency_key. List recent decisions and filter:
Related
- Integration Patterns — where recording fits in your request flow.
- Decision Chains — grouping related decisions into an ordered sequence.