Invariant 7: every tool call gets an idempotency key and a receipt. The receipt names the policy that decided the call, the safe upstream id the vendor returned, the vendor status, and the trust class of the evidence behind all of it. The gateway is the only receipt authority (ADR-0019). No connector, no worker, no catalog broker, and no control-plane consumer mints a receipt or assigns an evidence class. Control reads receipts off a Redis stream and projects them into Postgres; it copies fields and drops malformed envelopes rather than repairing them.

What a receipt answers

A receipt answers three separate questions, and keeps them separate on purpose:
  1. Was this allowed? The authorization summary names the policy pack, its revision, the decision, the matched policies, and the approval that unblocked it.
  2. What did dispatch observe? The dispatch summary carries the vendor HTTP status, the vendor operation status, the safe upstream id, a response digest, a typed error code, and whether the failure was retryable.
  3. How much is that worth? The verification summary carries a conclusion, the effect claim it supports, and the evidence trust class that supports it.
The first question is tier-independent. The third is not. A native route the gateway executed itself can produce gateway_observed evidence; a federated catalog route cannot, and its evidence is untrusted or broker_attested. “Receipts prove what a tool call actually did” is true natively and false on a federated route. The receipt always names the class — read it. See Evidence trust.

One invocation, many receipts

The gateway mints an InvocationId before the visibility and admission checks run, and keeps it across approval, transport retries, and safe redispatch. That id is the stable identity of one logical tool call. A ReceiptId is not. Each revision of a receipt gets a fresh rcp_ id and an incremented receipt_revision, so an invocation whose verification conclusion changes later — a readback landed, a signed webhook arrived — produces a new receipt in the same history. Listing endpoints show the latest revision per invocation; the revisions endpoint returns the immutable history. A revision is minted only when the public verification summary changes. Evidence attempts that leave the summary unchanged advance the evidence sequence and emit a VerificationEvent, not a receipt (ADR-0046).

Fields

Raw response bodies, credentials, request headers, and compared assertion values never enter a receipt. Assertion results carry a name and an outcome, never the value that was compared.

An example

A native Stripe refund, dispatched and observed by the gateway:
Receipt
Read the two evidence_trust fields separately. dispatch.evidence_trust is the provenance of the transport facts; verification.evidence_trust is the provenance of whatever evidence produced the conclusion, and it is absent when nothing did.

Which calls get a receipt

Every authenticated tools/call does, including calls that stop before dispatch. A denied call, a rate-limited call, and a call held for approval each finalize with a receipt whose state says so and whose dispatch.state is not_attempted. Hidden and unknown tools follow ADR-0015’s non-revealing client behavior — the caller learns nothing about a tool the key cannot see — while still producing an owner-visible receipt. That is the point: the audit log is for the workspace, not for the agent.

How a receipt reaches you

The gateway writes an InvocationAuditEnvelope — one receipt paired with its verification event — to a workspace-qualified Redis stream, and acknowledges it there before exposing receipt metadata. A control-plane consumer drains that stream per workspace, projects each envelope into a queryable row, and keeps the whole envelope beside it. Delivery is at-least-once and revisions can arrive out of order, so the projection is a conditional upsert that only moves forward: an incoming revision must be strictly greater than the stored one, and the stored workspace must match. Pending deliveries are reclaimed with bounded XAUTOCLAIM pages from a persisted scan position, and each drain commits before acknowledging (ADR-0042).

Reading receipts

Receipts are exposed through the control plane, scoped to a workspace and gated on the reader’s operator role.
List receipts
You can filter by invocation state, evidence_trust, verification_state, decision, tool, key_id, connection_id, and a UTC window. Pagination is a versioned opaque cursor over (occurred_at, invocation_id) descending; a malformed cursor is rejected rather than silently reset to now. Legacy RFC3339 before values are still accepted as timestamp bounds. GET /workspaces/{workspace_id}/receipts/{receipt_id} returns the detail projection plus the whole envelope the gateway wrote, and .../revisions returns the immutable revision history.

Next

Evidence trust

The five trust classes, which tier can reach which, and why verified is a conclusion rather than a class.

Idempotency

Obol-minted keys, capability scope, retry safety, and disabled broker retries.

Verification

Response bindings, verification plans, readback probes, and per-tier limits.

Webhooks

Signed vendor events as evidence, and how a subscription is provisioned.