# Receipts

> The per-validator execution log every agent request produces, and how to view or fetch it.

Every agent invocation produces an **execution receipt**: a per-validator log of what happened during execution. Receipts provide transparency and auditability for agent operations.

> **Note.** Receipts are currently stored on centralized infrastructure (a public Google Cloud Storage bucket). We plan to migrate to decentralized storage in the future to make receipts tamper-proof and permanently available.

## Understanding receipts

### Consensus vs. receipts

It's important to understand the distinction:

- **Result:** the final output of the agent is what validators reach **consensus** on. All nodes must agree on this value for it to be accepted.
- **Receipt:** the execution steps are **subjective** and may vary slightly between nodes. Receipts are for transparency and debugging, not consensus.

This means:

- You can trust the **result** completely: it has been verified by multiple validators.
- Each receipt shows **what one node did** to compute that result.
- Different nodes may have different receipts (timing, network paths) but still agree on the final result.

### One request, one receipt per subcommittee member

A single agent request is processed by every member of its subcommittee (default size **3**, configurable via `createAdvancedRequest`). Each member produces its own receipt, so a default request yields **3 receipts**. The receipts service exposes them as a manifest keyed by `(contractAddress, requestId)`.

## Viewing receipts

### In the Somnia Explorer

Browse recent invocations or look up a request by ID on the Requests page of the Somnia Explorer:

```
https://explorer.somnia.network/agents/requests
https://explorer.testnet.somnia.network/agents/requests
```

Mainnet's explorer lists mainnet requests and testnet's lists testnet's; the chain selector picks the chain within the network. This is useful when you don't have the request URL handy or want to scan recent activity.

If you already know the request ID, go straight to its page:

```
https://explorer.somnia.network/agents/requests/<requestId>
https://explorer.testnet.somnia.network/agents/requests/<requestId>
```

Replace `<requestId>` with the numeric request ID returned by `createRequest`, on the explorer of the network the request was made on. The request page shows the on-chain request state plus each validator's signed receipt from the receipts service. See [Using the explorer](../explorer).

### From the Agent Explorer app

If you invoke agents from the standalone Agent Explorer app ([https://agents.somnia.network](https://agents.somnia.network/) for mainnet, [https://agents.testnet.somnia.network](https://agents.testnet.somnia.network/) for testnet):

1. Invoke an agent.
2. After execution completes, click **View Receipt**.
3. Explore each step of the execution.

The app displays receipts in a readable format, showing:

- Per-validator execution timelines
- External calls made (URLs, responses)
- Data transformations
- Timing information

## Fetching receipts programmatically

Fetching is a **two-step flow**:

1. Hit the receipts service with the platform `contractAddress` and `requestId` — you get back a manifest of public Google Cloud Storage URLs.
2. Fetch each URL directly to read the per-validator receipt JSON.

| Network | Receipts service base URL | Platform contract |
| --- | --- | --- |
| Mainnet | `https://receipts.mainnet.agents.somnia.host` | `0x5E5205CF39E766118C01636bED000A54D93163E6` |
| Testnet | `https://receipts.testnet.agents.somnia.host` | `0x037Bb9C718F3f7fe5eCBDB0b600D607b52706776` |

```javascript
const baseUrl   = 'https://receipts.testnet.agents.somnia.host';
const platform  = '0x037Bb9C718F3f7fe5eCBDB0b600D607b52706776';
const requestId = '<your-request-id>'; // returned by platform.createRequest

// Step 1 — manifest
const manifest = await (await fetch(
  `${baseUrl}/agent-receipts?contractAddress=${platform}&requestId=${requestId}`
)).json();
// → { contractAddress, requestId, receipts: [<gcs url>, ...], count }

// Step 2 — per-validator receipts
const receipts = await Promise.all(
  manifest.receipts.map(url => fetch(url).then(r => r.json()))
);
console.log(`${receipts.length} validator receipts for request ${requestId}`);
```

Both query parameters are required. Omitting `contractAddress` returns:

```json
HTTP 400 { "error": "Missing contractAddress query parameter" }
```

### Quick triage with `type=minimal`

For lightweight triage — "did this resolve, did the subcommittee agree, what was the error?" — the receipts service also exposes a `type=minimal` mode that returns all per-validator receipt JSON **inline in a single call**, with no second-step GCS fetch needed.

```
GET {baseUrl}/agent-receipts?contractAddress=<platform>&requestId=<id>&type=minimal
```

The minimal response:

- Inlines every validator's receipt JSON in `receipts[]` (objects, not GCS URLs).
- Caps long string fields (HTML page text, model output, raw responses) at 1024 characters with a `<truncated, fetch the receipt URL for complete contents>` marker.
- Adds derived `consensusType`, `requestDetails`, and `count` at the top level for convenience.

Use it for triage and dashboards. If a field you need was truncated, fall back to the default (URL manifest) mode and fetch the receipt's GCS URL directly for the full content.

## Receipt structure

### Top-level fields

A single per-validator receipt looks like this:

```json
{
  "requestId": "<requestId>",
  "agentId": "<agentId>",
  "contractAddress": "<platform address>",
  "agentRunnerAddress": "<validator address>",
  "consensusType": 0,
  "requestDetails": {
    "requester": "<caller address>",
    "callbackAddress": "<callback address>",
    "callbackSelector": "0x........",
    "threshold": 2,
    "subcommitteeSize": 3,
    "timeoutSeconds": 300
  },
  "status": "success",
  "startedAt": "2026-01-15T10:30:00.000Z",
  "completedAt": "2026-01-15T10:30:09.800Z",
  "elapsedMs": 9800,
  "agentImageUri": "https://storage.googleapis.com/somnia-agents-artifacts/agents/<agent>/<hash>.tar",
  "request": "0x...",
  "response": { "result": "0x..." },
  "agentReceipt": {
    "steps": [ /* see below */ ],
    "llmUsage": { "promptTokens": 0, "completionTokens": 0, "totalTokens": 0, "requests": 0, "streamingRequests": 0 },
    "bandwidthUsage": { "bytesIn": 0, "bytesOut": 0, "requests": 0 },
    "request": "0x...",
    "result": "0x..."
  }
}
```

| Field                                           | Purpose                                               |
| ----------------------------------------------- | ----------------------------------------------------- |
| `status`                                        | `success`, `failed`, or `timeout`                     |
| `elapsedMs`                                     | End-to-end duration on this validator                 |
| `agentRunnerAddress`                            | Which validator produced this receipt                 |
| `agentImageUri`                                 | Pinned artifact bundle the agent ran from (auditable) |
| `requestDetails.subcommitteeSize` / `threshold` | Consensus config in force for this request            |
| `consensusType`                                 | `0` = Majority, `1` = Threshold                       |
| `agentReceipt.steps`                            | Per-agent execution stages (see below)                |
| `agentReceipt.llmUsage`                         | Token counts for cost analysis                        |
| `agentReceipt.bandwidthUsage`                   | Outbound HTTP volume                                  |

Failed requests follow the same envelope but add a top-level `errorMessage` and omit `agentReceipt` and `response`. Always check `status` before reading `agentReceipt`.

### Step names by agent

Step names live under `agentReceipt.steps` and **vary by agent type**:

| Agent             | Step sequence                                                                                                |
| ----------------- | ------------------------------------------------------------------------------------------------------------ |
| JSON API Request  | `request_received → request_decoded → handler_started → handler_completed → response_encoded`                |
| LLM Inference     | `request_received → request_decoded → handler_started → llm_response → handler_completed → response_encoded` |
| LLM Parse Website | `request → fetch → convert → extract`                                                                        |

Each step entry includes `name`, `started_at`, `status`, and an `inputs` object (and `output` where applicable). LLM-backed agents may also include auto-injected `reasoning`, `answerable`, and `confidence_score` (0–100) fields in the structured-extraction step. For LLM Inference requests where chain-of-thought was enabled (via the `chainOfThought` argument), the `llm_response` step also surfaces the model's `thinking` output.

## Using receipts for debugging

### Tracing failures

When a request fails or times out, check `status` and the top-level `errorMessage`. For successes that returned an unexpected result, walk `agentReceipt.steps` — each step's `status` and any embedded output show exactly where execution diverged. For HTTP-based agents (JSON API Request, Parse Website), `bandwidthUsage.requests` is a quick sanity check that an outbound call happened at all.

### Checking LLM reasoning

For LLM agents, the `agentReceipt.steps` entry covering the model call carries the inputs and the structured output. When the request opted into chain-of-thought (`chainOfThought = true`), the `thinking` field on the `llm_response` step surfaces the model's intermediate reasoning. The auto-injected `confidence_score` is useful for understanding low-confidence answers.

### Verifying external data (JSON API Request)

The `handler_completed` step records the upstream response payload and the value extracted via the selector path — useful for confirming that the JSON shape on the API hasn't changed underneath you.

### Verifying scraped content (LLM Parse Website)

Parse Website's `fetch` step records the URL(s) loaded; the `convert` step records the markdown handed to the model; the `extract` step records the structured-output result. If extraction quality is low, the `convert` output usually shows why (collapsed structure, missing sections, dynamic content not rendered).

### Monitoring cost

Both `llmUsage` and `bandwidthUsage` are aggregated per validator on every receipt. Summing across requests gives a reliable per-agent cost profile (especially for LLM Inference and Parse Website, where token usage drives the bulk of work).

## Next steps

- [Gas fees](./gas-fees): understand costs.
- [Using the explorer](../explorer): view requests and receipts in the Somnia Explorer.

---

Every page is also Markdown: append `.md` to its URL, or ask for `text/markdown`. Found something wrong, missing or confusing in these docs? Tell us: `POST /api/feedback` on this site with JSON `{"message": "…", "context": "what you were doing and what happened", "page": "…"}`, or a plain-text body. `GET /api/feedback` gives the JSON Schema. The team reads every one.
