Chio/Docs
LOGIN · JOIN

Receipt Query API

Page through signed receipts, filter decisions, and read aggregate activity from the Trust Control Plane over HTTP, the CLI, or an SDK.

Reference chio@5f63e6e

On this page

Signed receipts in the Trust Control Plane's receipt store come back through 4 read routes: a filtered query, one agent's history, aggregate analytics, and the composed operator report.

A tenant read token scopes every read to its tenant; analytics and the operator report require the admin service token. The routes return the stored receipts byte for byte, so each kernel signature still verifies in the client.

Synopsis

Each read route takes its filters from the query string, requires a bearer token, and returns JSON. The smallest request reads the first page of the whole corpus.

Terminalbash
curl --fail-with-body -sS https://kernel.larkfield.example/v1/receipts/query \
  -H "Authorization: Bearer $CHIO_CONTROL_TOKEN"
MethodPathQuery typeResponse type
GET
/v1/receipts/query
ReceiptQueryHttpQuery
ReceiptQueryResponse
GET
/v1/agents/{subject_key}/receipts
AgentReceiptsHttpQuery
ReceiptQueryResponse
GET
/v1/receipts/analytics
ReceiptAnalyticsQuery
ReceiptAnalyticsResponse
GET
/v1/reports/operator
OperatorReportQuery
OperatorReport

Endpoint summary

State storesChoose a join key.

receipt_id joins rows inside one file

A receipt’s id, unique in each table that holds it. Each insert into either receipt table fires a trigger that adds the receipt to the claim log, pointing back with receipt_kind and source_seq. Receipts.

chio_tool_receiptsreceipt database · receipt_id

One row per evaluated tool call, allowed or denied. The store’s single group-commit writer inserts it (append_chio_receipt); the receipt query API reads it (query_receipts).

seq PK, receipt_id UK, capability_id, grant_index, decision_kind, policy_hash

claim_receipt_log_entriesreceipt database · receipt_id

The ordered log of tool and child receipts that checkpoints commit to. A trigger on each receipt table writes the entry, with receipt_kind and source_seq, in the same transaction as the receipt. Child receipts come from chio_child_receipts, keyed by seq with a unique receipt_id and tied to their parent_request_id rather than to a capability.

entry_seq PK, receipt_id UK, receipt_kind, source_seq, capability_id

chio_child_receiptsreceipt database · receipt_id

Receipts for the child operations of a session, keyed to their parent request rather than to a capability. They enter the same claim log as tool receipts.

seq PK, receipt_id UK, request_id, parent_request_id, terminal_state

In Rust · open.rs:110715 lines
CREATE TRIGGER chio_tool_receipts_project_claim_log_entry
AFTER INSERT ON chio_tool_receipts
BEGIN
    INSERT INTO claim_receipt_log_entries (
        receipt_id,
        receipt_kind,
        source_seq,
        // …
    ) VALUES (
        NEW.receipt_id,
        'tool_receipt',
        NEW.seq,
        // …
    );
END;
Ten of the tables that carry admission and evidence, each with its primary key; chio_child_receipts, chio_hitl_resolved, tool_outcomes, and budget_authorization_holds are described with the keys they hold. The store crate defines more than 200 tables, including budget replication, the finding market, fiscal schedules, liability, and credit. With durable admission off, budgets and revocations sit in their own --budget-db and --revocation-db files.What survives a crash

The read routes query the receipt database, and the operator report also reads the budget store. Two further routes read the same store behind a different envelope: /v1/receipts/tools takes ToolReceiptQuery and /v1/receipts/children takes ChildReceiptQuery. Both accept a receiptId that point-loads exactly one row, which is how a deployment resolves a parent or child receipt the kernel's bounded in-memory mirror has evicted.

Both return ReceiptListResponse: configured, backend, kind, count, filters, and receipts, with no cursor. The child listing is admin-only: a tenant read token gets 403, because child receipts carry no tenant attribution. POST appends a receipt, requires the service token, forwards to the cluster leader, and carries a body-size limit.

MethodPathPurpose
GET
/v1/receipts/query
Receipt query with filters and cursor pagination
GET
/v1/agents/{subject_key}/receipts
Agent-scoped listing; accepts limit and cursor only
GET
/v1/receipts/analytics
Aggregate counters and groupings over the same corpus
GET
/v1/reports/operator
Composed operator report over the receipt and budget stores
GET, POST
/v1/receipts/tools
List or append tool receipts
GET, POST
/v1/receipts/children
List or append child receipts

Every route requires a bearer token in the Authorization header. Filters combine with AND semantics, and each filter applies only when its parameter is present.

Authentication

The trust-control server compares the bearer token against two sets in constant time and resolves one of two principals: the admin service token, or a per-tenant read token. The principal becomes the read context the store query runs under, so a tenant token reads only that tenant's receipts.

The operator provisions tokens out of band, alongside chio trust serve. /v1/receipts/query and /v1/agents/{subject_key}/receipts accept either principal. The analytics and operator-report routes accept the admin service token only: a tenant read token gets 403 with the message <surface> requires admin receipt read authority.

A trust-control token grants read access to the receipt log it is scoped to. Store it in a secrets manager, rotate it on personnel change, keep it out of source control, and pass it through the CHIO_CONTROL_TOKEN environment variable, since the argv form leaks through ps.

Filter parameters

Every parameter is a query-string parameter. The names are camelCase on the wire, because ReceiptQueryHttpQuery carries serde(rename_all = "camelCase").

ParameterTypeDescription
capabilityId
string
Exact match on capability id
toolServer
string
Exact match on tool server name
toolName
string
Exact match on tool name
outcome
string
One of allow, deny, cancelled, incomplete; maps to the decision_kind column
since
u64
Only receipts with timestamp >= since (Unix seconds, inclusive)
until
u64
Only receipts with timestamp <= until (Unix seconds, inclusive)
minCost
u64
Minimum cost_charged in minor units. Receipts without financial metadata are excluded when it is set.
maxCost
u64
Maximum cost_charged in minor units. Receipts without financial metadata are excluded when it is set.
costCurrency
string
Currency for the cost bounds, a three-letter uppercase code. Required whenever minCost or maxCost is present.
agentSubject
string
Hex-encoded Ed25519 agent subject key, resolved through the capability-lineage join rather than by replaying issuance logs
cursor
u64
Pagination cursor. Returns only receipts with seq > cursor (exclusive).
limit
usize
Results per page. Clamped to the range 1 to 200; the default when absent is 50.

The server validates the cost filters before it touches the store. A minimum above the maximum, a cost bound with no costCurrency, or a currency that is not three uppercase ASCII letters each return 400 with the reason in the error string.

The server cap is authoritative. limit passes through list_limit, which clamps the requested value into the range fixed by DEFAULT_LIST_LIMIT and MAX_LIST_LIMIT, and a larger value is reduced without an error. The TypeScript and Python ReceiptQueryClient helpers forward limit unchanged and follow nextCursor, with no higher logical cap and no double fetch. Size bandwidth around 200-item pages.

Response shape

ReceiptQueryResponse has exactly three fields, and an empty result still returns all three.

Empty responsejson
{
  "totalCount": 0,
  "nextCursor": null,
  "receipts": []
}

totalCount counts every receipt matching the filters, independent of page limit and cursor, so a caller renders a total without paginating to the end.

nextCursor is the seq of the last receipt in the page, and the store sets it only when the page came back full. A short page means no more rows matched, and the field serializes as null. Pass a non-null value back as cursor to fetch the next page. A full final page can still return a cursor; the following request then returns an empty page with a null cursor.

receipts holds the stored ChioReceipt values, serialized one for one and ordered by seq ascending. The handler serializes the stored receipt without rewriting it, so the signature still covers what the array carries and a client can check it against the kernel key. Receipt Format documents the fields.

Cursor pagination

The cursor is the seq column value of the last receipt in a page. Pagination is forward-only and append-safe: new receipts land at higher seq values and appear on later pages without moving the boundaries of a page already read.

HTTP requeststext
# Page 1
GET /v1/receipts/query?toolServer=shell&limit=50

# Response carries nextCursor: 147

# Page 2
GET /v1/receipts/query?toolServer=shell&limit=50&cursor=147

# Continue until nextCursor is null; a full final page can require one empty fetch.

The cursor is a raw u64. Treat the value as opaque: round-trip what the server returned in nextCursor, and never parse or construct a cursor.

Example request

A filtered request for refusals since a point in time, two per page:

HTTP requesttext
GET /v1/receipts/query?outcome=deny&since=1700000000&limit=2
Authorization: Bearer <control token>

The array elements are the receipts the kernel wrote. The CLI reads the same store and prints the same objects, one per line, so the receipt below, selected with jq, is the shape an HTTP caller iterates over as well.

quickstart · receipt-jsontranscript
$ chio --receipt-db .chio/receipts.db receipt list --admin-all \
    | jq 'select(.decision.verdict == "deny")'
{
  "id": "28b6e2576ca2ccbd031e769d8d2bd504317161115a600f27771a7f932307a284",
  "timestamp": 1788529912,
  "capability_id": "cap-01a06cb0-aae3-7ad3-8ba7-01350a3e01a0",
  "tool_server": "hello",
  "tool_name": "drop_tables",
  "action": {
    "parameters": {},
    "parameter_hash": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"
  },
  "decision": {
    "verdict": "deny",
    "reason": "requested tool drop_tables on server hello is not in capability scope",
    "guard": "kernel"
  },
  "receipt_kind": "mediated_decision",
  "boundary_class": "prevent",
  "tool_origin": "caller_executed",
  "redaction_mode": "none",
  "content_hash": "74234e98afe7498fb5daf1f36ac2d78acc339464f950703b8c019892f982b90b",
  "policy_hash": "69e943b96e9ce64d0264bb56bf8930e77bd4e68adf68fcf1395790dae03e6b55",
  "metadata": {
    "attribution": {
      "delegation_depth": 0,
      "issuer_key": "3dbf7b1230475796d5c78cdcf441c84721d9e3c24362fbbfb4225e307d1b9fb4",
      "subject_key": "af2da8097f2c133affd89145302f029c7a5854e4e3a692a92b0cd20f84d07b37"
    },
    "chio_receipt_signing_nonce": "rcpt-01a06cb0-ab51-70d0-bcb3-72a8b78fe057",
    "receipt_context": {
      "request_id": "check-001"
    }
  },
  "trust_level": "mediated",
  "kernel_key": "3dbf7b1230475796d5c78cdcf441c84721d9e3c24362fbbfb4225e307d1b9fb4",
  "signature": "e89f7a9cbe6c6b207cf5ac0c3e197a3957d62d2994217a7dcaeeeb9869abef1233680328432ee7a502a76238eb6ee9e72b19715e2458a3ef85aebfbed0df9202"
}
exit 0denyin my-agent

A denial is a row in the same corpus as an allow, with the refused parameters still attached. Filtering outcome=deny is how an auditor reads the refusals without reading the whole log.

Agent-scoped route

A shorter URL covers per-agent lookups.

HTTP requesttext
GET /v1/agents/{subject_key}/receipts?limit=50&cursor=0
Authorization: Bearer <control token>

AgentReceiptsHttpQuery carries cursor and limit and nothing else, so the subject key in the path is the only filter. The response type is the same ReceiptQueryResponse. Use it when a routing layer makes per-agent path scoping easier than a query string.

TypeScript SDK

@chio-protocol/sdk ships a ReceiptQueryClient over this route. The constructor takes positional arguments: new ReceiptQueryClient(baseUrl, authToken, fetchImpl?). query(params) fetches one page. paginate(params) is an async generator yielding one array of receipts per page, and it throws QueryError when the returned cursor fails to advance.

receipts.tstypescript
import { ReceiptQueryClient } from "@chio-protocol/sdk";

const client = new ReceiptQueryClient(
  "https://kernel.larkfield.example",
  process.env.CHIO_CONTROL_TOKEN!,
);

// Single page.
const page = await client.query({
  outcome: "deny",
  since: 1700000000,
  limit: 50,
});

console.log(`${page.totalCount} total denies`);
for (const receipt of page.receipts) {
  console.log(receipt.id, receipt.decision);
}

// paginate() yields one array of receipts per page.
for await (const receipts of client.paginate({
  toolServer: "pricing",
  since: 1700000000,
})) {
  for (const receipt of receipts) {
    if (receipt.decision?.verdict === "deny") {
      console.warn(`denied: ${receipt.decision.reason}`);
    }
  }
}

Python SDK

The chio.ReceiptQueryClient constructor is ReceiptQueryClient(base_url, auth_token, *, client=None). The keyword is auth_token, not bearer_token. query() and paginate() are synchronous: the default transport is a blocking urllib request, and passing client= substitutes an object with a get() method. Filters are a dict of the same camelCase keys the route accepts, and paginate() yields one list of receipts per page.

receipts.pypython
import os
from chio import ReceiptQueryClient

client = ReceiptQueryClient(
    "https://kernel.larkfield.example",
    auth_token=os.environ["CHIO_CONTROL_TOKEN"],
)

# Single page. query() and paginate() are synchronous.
page = client.query({"outcome": "deny", "since": 1700000000, "limit": 50})
print(f"{page['totalCount']} total denies")
for receipt in page["receipts"]:
    print(receipt["id"], receipt.get("decision"))

# paginate() yields one list of receipts per page.
for receipts in client.paginate({"toolServer": "pricing", "since": 1700000000}):
    for receipt in receipts:
        decision = receipt.get("decision")
        if decision and decision["verdict"] == "deny":
            print(f"denied: {decision['reason']}")

Both clients thread the cursor and stop when the server returns no nextCursor. Token refresh, retries on transient errors, and receipt signature verification stay with the caller.

Pagination without an SDK

Any language with an HTTP client runs the same loop; in shell it is short.

Terminalbash
cursor=""
while : ; do
  resp=$(curl --fail-with-body -sS \
    "https://kernel.larkfield.example/v1/receipts/query?outcome=deny&limit=200${cursor:+&cursor=$cursor}" \
    -H "Authorization: Bearer $CHIO_CONTROL_TOKEN") || break

  echo "$resp" | jq -r '.receipts[].id'

  cursor=$(echo "$resp" | jq -r '.nextCursor // empty')
  [ -z "$cursor" ] && break
done

Analytics route

/v1/receipts/analytics aggregates the same corpus in the store. ReceiptAnalyticsQuery takes capabilityId, agentSubject, toolServer, toolName, since, and until, plus two aggregation parameters. It has no cost filters and no cursor.

  • groupLimit: rows per grouped dimension. The store clamps it to the range 1 to MAX_ANALYTICS_GROUP_LIMIT, which is 200; the query default is 50.
  • timeBucket: bucket width, hour (3600 seconds) or day (86400 seconds). Default day.

The selected response below shows the summary. Grouped rows in byAgent, byTool, and byTime carry the same metrics under their metrics member.

Selected response fieldsjson
{
  "summary": {
    "totalReceipts": 12,
    "allowCount": 9,
    "denyCount": 1,
    "cancelledCount": 1,
    "incompleteCount": 1,
    "totalCostCharged": 750,
    "totalAttemptedCost": 500,
    "reliabilityScore": 0.8181818182,
    "complianceRate": 0.9166666667,
    "budgetUtilizationRate": 0.6
  }
}

Every row carries the same ReceiptAnalyticsMetrics under metrics. The three ratios are Option<f64> and are omitted from the JSON when their denominator is zero: reliabilityScore divides allows by allows plus cancelled plus incomplete, complianceRate divides non-denies by total receipts, and budgetUtilizationRate divides charged cost by charged plus attempted cost. The other seven fields are u64 and always present.

Operator report route

/v1/reports/operator composes the analytics, cost-attribution, budget, compliance, settlement, metered-billing, authorization-context, and shared-evidence slices into one body, reading the receipt store and the budget store in one request.

OperatorReportQuery takes the analytics filters and aggregation parameters, plus six row limits, each defaulting to the value shown.

ParameterDefaultBounds the rows in
attributionLimit
100
costAttribution
budgetLimit
50
budgetUtilization
settlementLimit
50
settlementReconciliation
meteredLimit
50
meteredBillingReconciliation
authorizationLimit
50
authorizationContext
economicLimit
50
the economic receipt projection

OperatorReport declares no optional top-level key, so a client indexes every member without a presence check. The outline names those members, with empty objects in place of the typed report bodies the next table describes.

Response outlinejson
{
  "generatedAt": 1700000000,
  "filters": { "toolServer": "shell", "toolName": "run_command" },
  "activity": {},
  "costAttribution": {},
  "budgetUtilization": { "summary": {}, "rows": [] },
  "compliance": {},
  "settlementReconciliation": { "summary": {}, "receipts": [] },
  "meteredBillingReconciliation": { "summary": {}, "receipts": [] },
  "authorizationContext": { "schema": "", "profile": {}, "summary": {}, "receipts": [] },
  "sharedEvidence": { "summary": {}, "references": [] }
}
KeyTypeCarries
generatedAt
u64
Unix seconds the report was built
filters
OperatorReportQuery
The filters as the server resolved them; the read context is never serialized
activity
ReceiptAnalyticsResponse
The same body the analytics route returns
costAttribution
CostAttributionReport
The same body /v1/reports/cost-attribution returns
budgetUtilization
BudgetUtilizationReport
A summary and one row per capability grant, with invocation and cost usage
compliance
ComplianceReport
Checkpoint and lineage coverage, settlement backlog counts, and the evidence-export query
settlementReconciliation
SettlementReconciliationReport
A summary and the receipts awaiting settlement reconciliation
meteredBillingReconciliation
MeteredBillingReconciliationReport
A summary and the receipts awaiting metered-billing reconciliation
authorizationContext
AuthorizationContextReport
A schema id, the OAuth authorization profile, a summary, and one row per receipt
sharedEvidence
SharedEvidenceReferenceReport
A summary and one row per federated evidence share matched to local receipts

compliance carries childReceiptScope, whose value is one of full_query_window, time_window_context_only, or omitted_no_join_path. It says how far an evidence export over the same filters would reach for child receipts.

The other report routes

The operator report is one of the routes the trust-control router registers under /v1/reports/. The other 32 cover credit, liability, settlement, authorization, and underwriting reports, each with its own request and response type. The credit and underwriting reports belong to optional economy modules.

MethodPathHandler
GET
/v1/reports/authorization-context
handle_authorization_context_report
GET
/v1/reports/authorization-profile-metadata
handle_authorization_profile_metadata_report
GET
/v1/reports/authorization-review-pack
handle_authorization_review_pack_report
GET
/v1/reports/behavioral-feed
handle_behavioral_feed_report
GET
/v1/reports/bond-loss-policy
handle_credit_loss_lifecycle_report
GET
/v1/reports/bond-losses
handle_query_credit_loss_lifecycle
GET
/v1/reports/bond-policy
handle_credit_bond_report
POST
/v1/reports/bonded-execution-simulation
handle_credit_bonded_execution_simulation_report
GET
/v1/reports/bonds
handle_query_credit_bonds
GET
/v1/reports/capital-book
handle_capital_book_report
GET
/v1/reports/comptroller-surface
handle_comptroller_surface_report
GET
/v1/reports/cost-attribution
handle_cost_attribution_report
GET
/v1/reports/credit-backtest
handle_credit_backtest_report
GET
/v1/reports/credit-scorecard
handle_credit_scorecard_report
GET
/v1/reports/economic-completion-flow
handle_economic_completion_flow_report
GET
/v1/reports/economic-receipts
handle_economic_receipt_report
GET
/v1/reports/exposure-ledger
handle_exposure_ledger_report
GET
/v1/reports/facilities
handle_query_credit_facilities
GET
/v1/reports/facility-policy
handle_credit_facility_report
GET
/v1/reports/liability-claims
handle_query_liability_claim_workflows
GET
/v1/reports/liability-market
handle_query_liability_market_workflows
GET
/v1/reports/liability-providers
handle_query_liability_providers
GET
/v1/reports/metered-billing
handle_metered_billing_report
GET
/v1/reports/provider-risk-package
handle_credit_provider_risk_package_report
POST
/v1/reports/runtime-attestation-appraisal
handle_runtime_attestation_appraisal_report
POST
/v1/reports/runtime-attestation-appraisal-result
handle_runtime_attestation_appraisal_result_export
POST
/v1/reports/runtime-attestation-appraisal/import
handle_runtime_attestation_appraisal_import
GET
/v1/reports/settlements
handle_settlement_report
GET
/v1/reports/underwriting-decision
handle_underwriting_decision_report
GET
/v1/reports/underwriting-decisions
handle_query_underwriting_decisions
GET
/v1/reports/underwriting-input
handle_underwriting_policy_input
POST
/v1/reports/underwriting-simulation
handle_underwriting_simulation_report

The chio receipt list subcommand

chio receipt list reads the same corpus, from a local SQLite receipt store or over HTTP. It prints one JSON receipt per line.

Synopsistext
chio receipt list [OPTIONS]

Options:
  --capability     <ID>      Filter by capability ID
  --tool-server    <NAME>    Filter by tool server ID
  --tool-name      <NAME>    Filter by tool name
  --outcome        <OUT>     allow | deny | cancelled | incomplete
  --since          <SEC>     Receipts with timestamp >= this Unix seconds value
  --until          <SEC>     Receipts with timestamp <= this Unix seconds value
  --min-cost       <UNITS>   Minimum cost in minor units; requires --cost-currency
  --max-cost       <UNITS>   Maximum cost in minor units; requires --cost-currency
  --cost-currency  <CODE>    Three-letter uppercase currency code for the cost filters
  --limit          <N>       Maximum receipts per page [default: 50]
  --cursor         <SEQ>     Cursor for pagination (seq value to start after)
  --tenant         <ID>      Tenant read boundary; conflicts with --admin-all
  --admin-all                Read across all tenants as an administrative operation

Global options that apply here:
  --receipt-db     <PATH>    SQLite database path for durable receipt persistence
  --control-url    <URL>     Shared trust-control service base URL
  --control-token  <TOK>     Bearer token for the trust-control service

--min-cost and --max-cost each declare requires = "cost_currency", so clap rejects either one on its own. That mirrors the HTTP route, where a cost bound without costCurrency is a 400.

In local --receipt-db mode the read path fails closed unless exactly one of --tenant <ID> or --admin-all is supplied; --admin-all declares conflicts_with = "tenant". Against a running server the scope comes from the control token.

quickstart · receipt-listtranscript
$ chio --receipt-db .chio/receipts.db receipt list --admin-all \
    | jq -r '[.decision.verdict, .tool_server, .tool_name, .id] | @tsv'
allow	hello	hello_world	448440b65fa15559a364d24512cbe4f08631befe2c3d2ab471dad7204b8b69c8
deny	hello	drop_tables	28b6e2576ca2ccbd031e769d8d2bd504317161115a600f27771a7f932307a284
exit 0in my-agent

Omitting the boundary is an error, and so is supplying both.

Terminalbash
chio receipt list --receipt-db ./chio.db --outcome deny
chio receipt list --receipt-db ./chio.db --tenant team-a --admin-all

The first exits with the CLI error domain, because the read path refuses to infer a boundary. The second is rejected by clap before the command runs, from the conflicts_with declaration.

The CLI writes receipts as JSON Lines on stdout and pagination metadata as next_cursor=<seq> total_count=<n> on stderr. Pass that cursor as --cursor on the next call; the SDK paginate() helpers do that threading for a caller. The cookbook pagination recipe separates the two output streams.

Rate limits

The receipt query, analytics, and report routes apply no per-token request-rate limit. The router applies a body-size limit to the POST routes and one response-header layer that sets a Content-Security-Policy on every response; the crate's rate limiters govern federation admission and cluster peer authentication. Size a scan accordingly: use /v1/reports/operator for a dashboard, and reuse nextCursor to continue a scan from where it stopped.

Error responses

Every error path on these handlers returns a JSON object with one error string, built by plain_http_error. The status gives the class of failure and the string gives the detail.

401 bodyjson
{ "error": "missing or invalid control bearer token" }

A request with no bearer token and a request with a token the server does not hold land on the same response: 401, that body, and a www-authenticate: Bearer header. The statuses:

  • 400: cost filters failed validation: a minimum above the maximum, a bound with no costCurrency, or a currency that is not three uppercase letters.
  • 401: missing or invalid bearer token.
  • 403: a tenant read token reached an admin-only route, or asked for another tenant.
  • 500: the service config failed validation, the store would not open, or serialization failed; the string carries the detail.

Operational notes

  • Ordering. A page returns receipts by seq ascending. New receipts append at higher seq values, so a long-running forward pagination reaches new data without repeating old data.
  • Signature verification. The handler serializes the stored receipt unchanged, so the kernel signature over the canonical body survives the round trip. Verify it against the kernel key with the bindings helpers before treating a copied receipt as evidence.
  • Backfill. A large log takes a while to scan end to end. Start with a tight since bound and widen the window. The operator report gives a current picture in one request.
  • Read authority. The read context comes from the token, never from a query parameter. The query types that carry a read_context field mark it serde(skip), so a caller cannot set it on the wire.
Implementation sources

The crate is the contract for these routes. The lookup reflects the Chio trust-control implementation: service_types/paths.rs (the path constants and the list limits), service_types/requests.rs (the query and response types), service_runtime/router.rs (which handler each path is registered against), trust_control/receipt_handlers.rs and trust_control/report_validation.rs (authentication and status codes).

The response bodies come from crates/kernel/chio-kernel/src/receipt_analytics.rs and crates/kernel/chio-kernel/src/operator_report/, and the SQL behind them from crates/platform/chio-store-sqlite/src/receipt_store/reports/. The CLI flags come from crates/products/chio-cli/src/cli/types/receipt.rs, and the clients from sdks/typescript/chio-ts/src/receipt_query_client.ts and sdks/python/chio-py/src/chio/receipt_query.py.

Next: read the receipts these routes return