Status: Stable.
Normative home:
idempotency.
Why this exists
A retried request MUST NOT create a second run, and a retried node MUST NOT issue a second external effect. A host MUST implement Layer 1 for every mutating endpoint; a host advertising idempotency MUST implement Layer 2 for every node executor that performs an external side effect (security-defaults.md).
Layer 1: Idempotency-Key
The header keeps its standard name and applies to every mutating operation in api/v2/openapi.yaml; GET operations MUST NOT honor it.
Key and record
- Grammar. The value MUST match
^[A-Za-z0-9._~-]{22,128}$and MUST carry at least 128 bits of entropy (a UUIDv4 in canonical or base64url form satisfies it). A host MUST reject a value outside the grammar with400 idempotency_key_invalid. - Record key. A record MUST be keyed by
(authenticatedTenantId, canonicalEndpointId, callerIdempotencyKey); the tenant MUST come from the credential, never the body. - Retention. A record MUST be retained for at least 24 hours.
- Keyspace. Host-minted identifiers MUST NOT share the caller idempotency store. Logs and spans MUST NOT expose keys.
Outcomes
- Final. A host MUST cache
2xxand non-retryable4xxresponses (status, headers, body) and MUST return the cached response to a same-key duplicate. - Retryable.
429and5xxMUST NOT be replayed from cache; a same-key retry MUST re-execute, and a later final outcome replaces the record. - Not cached.
400 idempotency_key_invalid,400 validation_error,401and403MUST NOT be cached. - Digest mismatch. A different request digest under the same record key MUST fail with
409 idempotency_key_mismatchand MUST NOT return the cached body. This is the only mismatch code. - Replay marker. A response served from cache MUST carry
OpenWOP-Idempotent-Replay: true.
Concurrency
Of two concurrent same-key requests a host MUST process exactly one to completion and MUST NOT process both.
- The other MAY wait, bounded by the host's request timeout, and receive the winner's response only if it is a final outcome (one this record caches), marked
OpenWOP-Idempotent-Replay: true. - Otherwise the host MUST answer
409 idempotency_in_flightwith no retry timing indetails, and SHOULD setRetry-After.
The code's registry retriable: false means not retryable without waiting.
Layer 2: effect identity
Layer 2's unit is the effect, identified once and stable across every transport or provider retry.
- Keying. An effect MUST be keyed on its business identity (
keying: business-identity): derived from the business operation, stable across every entry point, containing norunId,nodeIdor ordinal. The activity recipe (keying: activity-recipe: tenant, run, node, ordinal,providerKey) is the fallback for a provider with no business key. - Attempts. The retry counter MUST NOT participate in the identity. Two distinct logical invocations MUST receive different identities.
- Claim. The persist that guards the effect MUST be an atomic claim (compare-and-set or insert-if-absent) that at most one executor can win, so at most one concurrent duplicate performs the effect.
- Provider key. When the provider accepts an idempotency key, the host MUST inject the effect identity (or a documented deterministic derivative), stable across retries. A host that cannot use the provider's convention MUST still persist the outcome.
- Streaming. A streamed body MUST NOT be cached in the ledger; the host SHOULD record the request and its final outcome.
- Retention. An effect record MUST be retained for at least 14 days.
Witness: GET /runs/{runId}/effects
A host that advertises idempotency MUST serve schemas/v2/effect-ledger-projection.schema.json at GET /runs/{runId}/effects (getRunEffects): { runId, effects[] }. Each record carries:
effectId(tenant-bound,schemas/v2/ids.schema.json),nodeId,attempt,keying,at;state:claimed|completed|released|escaped;- optional
invocationIdand a redaction-safeproviderKey.
The projection MUST be content-free of provider payloads and credential material.
Composition
Layer 1 deduplicates the caller's request; Layer 2 deduplicates the run's effects. A retried provider call inside a run MUST resolve to the same effect record. Effects under replay and fork are in replay.md; identifier grammars are in identity.md.
Multi-region
The two region facets of idempotency, multiRegion and crossRegion, claim different things.
crossRegionnames the host's deployment posture for this axis and MUST be held constant for the life of an advertisement.multiRegionis the behavioural claim. When a host advertises it, both layers MUST hold across regions: anIdempotency-Keyreplayed into a second region MUST resolve to the first region's response rather than starting new work, and effect identity MUST collapse a duplicate effect wherever it is observed.- A host that does not advertise
multiRegionmakes no cross-region promise, and a client MUST NOT infer one fromcrossRegionalone.
Sources: RFCs 0170, 0171, 0173, 0213.