Invariant 7 gives every tool call an idempotency key. Invariant 9 says whose it is: idempotency is always Obol’s. Neither Nango nor Composio offers one, and their retry knobs generate duplicate writes, so broker-side retries are disabled on every call.

The key

An Obol idempotency key is an idt_-prefixed id. You may supply one on the MCP call:
Supplying a key
If you do not, the gateway mints one. Either way the effective key comes back on the response, both as an 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
Two properties follow from that shape. A request-hash conflict never dispatches. The claim record pins the canonical request hash, the tool’s declared idempotency binding, and a dispatch fingerprint of the non-secret target facts. A second call with the same key but different arguments is refused; it does not become a second effect. A capability call is pinned independently of the provider it resolves to. A routed 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.
The capability scope corrected a previously deployed key-scope mismatch and is not a mixed-version rolling cutover. Drain older gateways and retire their capability idempotency and approval records before admitting traffic under the new scope. Do not delete live records to speed the transition: that can redispatch an already executed effect.

Propagating the key to the vendor

Whether Obol’s key reaches the vendor is declared per tool in the EffectSpec: 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, so attempt_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.
A post-send transport failure finalizes as 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 idempotency unsupported, broker-side retries are turned off on every call, and the only retry that can happen is one the gateway reserved.

Receipts

Where the effective key, request hash, and attempt count are recorded.

Federated connectors

What a broker does and does not get to decide.