The key
An Obol idempotency key is anidt_-prefixed id. You may supply one on the MCP call:
Supplying a key
Idempotency-Key header and in the result metadata, so a client that did not supply one can still retry against the same logical call.
A malformed Idempotency-Key header is an error, not a silently ignored value.
Approval binds the key
When a call is held for approval, the approval ticket carries the idempotency key it was minted under. On resume, a supplied key that disagrees with the approved key is denied rather than reconciled. Approval resumption addresses the same logical slot; it does not open a second one.Scope
The claim is coordinated atomically in Redis, keyed by workspace, resolved connection, tool, and idempotency key:Idempotency key layout
cap.* invocation uses the literal unresolved connection segment, so retrying the same capability and idempotency key after a pin change or a republication addresses the same logical slot instead of dispatching another effect through a newly selected provider (ADR-0044). The concrete connection, tool, and non-secret dispatch facts become immutable fingerprint data once the claim succeeds; an existing record with another destination conflicts rather than redispatching, and a matching replay preserves the original route record.
Native tool calls keep their connection-scoped identity — the connection is resolved before the claim, so there is nothing to defer.
Propagating the key to the vendor
Whether Obol’s key reaches the vendor is declared per tool in theEffectSpec:
Propagation is
none on federated routes, because Obol does not construct the vendor request there. Gateway-side idempotency is unconditional regardless — the declaration is about the vendor hop, not about Obol’s — and an honest unsupported is policy-deniable for production writes.
Retries
The gateway owns every retry, and every attempt is durably reserved before it happens, soattempt_count on the receipt cannot disagree with the number of outbound calls (ADR-0031).
A worker executor performs exactly one outbound attempt. Gateway orchestration may reserve one immediate second attempt only when:
- the failure is a proven before-send retryable failure, or
- the result is a responded, internally consistent retryable upstream error and the tool declares a vendor idempotency binding.
unknown with one attempt. It is never converted into an immediate retry merely because a connector reports that a retry would be safe. A later request under the same key may redispatch only through the ordinary idempotency lifecycle, with the same safety checks.
Dispatch status classification is declarative, not inferred: each tool’s EffectSpec names the statuses that mean accepted and the ones that are definitely_rejected. Anything else is unknown, and unknown never becomes optimistic success.
Why broker retries are disabled
A catalog broker is a vendor, never an authority. Retries executed by a broker bypass the gateway’s compare-and-set claim, its attempt reservation, its receipts, and its policy ownership — and they can duplicate a write with no record on Obol’s side that a second effect occurred. Both brokers make the case themselves. Nango’s documentation tells the caller to be idempotent while shipping retry knobs that generate duplicate writes. Composio disabled retries after duplicate sends. Neither exposes an idempotency mechanism Obol could propagate to. So federated connectors declare idempotencyunsupported, broker-side retries are turned off on every call, and the only retry that can happen is one the gateway reserved.
Related
Receipts
Where the effective key, request hash, and attempt count are recorded.
Federated connectors
What a broker does and does not get to decide.