Skip to content

Missing receipts ​

Use this runbook when someone expects a signed receipt for an action and cannot find one: a customer disputing a refund, an auditor sampling evidence, or an agent whose receipt_url is absent.

When Axiom issues a receipt ​

A receipt is issued when an action reaches an outcome, and only then:

Action stateReceipt?
succeeded, failedYes. Destination confirmed only if the provider signed an outcome attestation.
outcome_unknownYes, recording that the outcome is unknown. Reissued (same receipt ID and salts) once reconciled.
pending_approval, approved, executingNot yet. Nothing has a recorded outcome.
denied, expired, revoked, cancelledNo. These close a request before dispatch; the action timeline and audit log are the evidence.

So "missing" usually means one of four things: the action has not reached an outcome, it closed before dispatch, the caller cannot see it, or the receipt passed its plan retention date and was deleted (receipts on legal hold are never deleted).

1. Find the action and read its state ​

Console: Evidence (/control/orgs/:oid/evidence) for a cross-project search, or Project → Actions → Action detail (/app/actions/:aid).

sh
axiom_get "/v1/orgs/$ORG/actions?q=act_01…"
axiom_get /v1/actions/act_01…/timeline

(The shell helpers are defined in Runbooks.)

The timeline returns status (with receipt_url when a receipt exists), the agent, the policy decision, every event, approvals with their decisions, related child actions and a diagnosis: which step is waiting and who is responsible next. Missing evidence is reported as missing, not as a denial. See “Finance already approved this” for reading the diagnosis.

A 404 means you cannot see the action: it is in another tenant or project, or your organisation's enrollment terms exclude action evidence (evidence_transfer: none, or history before enrollment with since_enrollment). Check scope before escalating.

2. If the action is approved but not dispatched ​

The approval never executes anything. An approved action waits for the original agent to resume it before resume_deadline; after that it expires and no receipt will be issued. Tell the agent's operator; nothing is wrong with evidence.

3. If the action is executing or outcome_unknown ​

executing means the dispatch is in flight; outcome_unknown means the target timed out, returned a 5xx, or returned an attestation Axiom could not verify. Axiom reconciles by asking the provider for the authoritative result of the operation key:

http
GET {upstream_url}/.well-known/axiom/operations/{operation_key}
Axiom-Authorization: <dispatch JWS for this action>

The provider answers { state, attestation, target_ref } (or 404 if the key never committed). Only a valid provider attestation bound to this action, operation key and request digest restores a final state; Axiom then reissues the receipt. Providers using the SDK implement this with AxiomProvider.handleOperationLookup (see Embedded SDK provider guide).

Checks for the responder:

  1. Confirm the provider exposes the lookup endpoint and that the service has its outcome key registered (outcome_keys on the service). Without a registered key no attestation can verify, and the action stays outcome_unknown.
  2. Look for reconciliation_started events on the action timeline: they show Axiom is retrying.
  3. Ask the provider's team to check their own record for the operation key. Do not ask the agent to retry: a retry under a new key could execute twice.

Reconciliation backs off: attempt n waits 30 × 2^n seconds (at most one hour) and Axiom stops after 12 attempts. The five-minute maintenance sweep re-drives only actions that have been stalled for an hour (for example after a lost queue message) and are still under that limit. An action whose attempts are exhausted stays outcome_unknown for an operator: resolve it with the provider's team, who can report the signed outcome (POST /v1/actions/:id/outcome in embedded-SDK mode) or fix the lookup endpoint so a re-drive succeeds.

4. If a receipt exists but the requester cannot open it ​

  • The agent itself can download it (GET /v1/actions/:id/receipt, axiom receipt).
  • People need evidence.read on the action's project or tenant. Exports in Control need evidence.export: POST /v1/orgs/:oid/exports returns { id, status, receipt_count, download_url } (up to 1000 receipts per export).
  • A redacted export (?reveal=operation,params.amount) still verifies: hidden fields stay committed. See Receipt verifier.

5. Preserve it ​

If the receipt is part of a dispute, place it on legal hold:

sh
axiom_post /v1/receipts/rcp_01…/legal-hold '{"hold":true,"reason":"Dispute 2026-117 with Acme Corp"}'

Requires evidence.export on the receipt's project or tenant; the response is { ok, legal_hold } and the audit stream records receipt.legal_hold_set.

Each receipt carries a retention date from the plan's history window (retain_until). The maintenance sweep deletes receipts past it unless they are on legal hold, and only for actions in a final state (succeeded or failed); receipts for actions still outcome_unknown are kept. Put a disputed receipt on hold before its retention date.

Developer preview. Contract version 2026-11-14.