Fibric. Docs fibric.io →
v0.9 · preview
Reference

Receipts API

A receipt is the immutable record of one action: the event that triggered it, the plan it belonged to, the trust verdict, the idempotency key, and the outcome. Receipts are append-only. There is no update and no delete, and refusals are receipted the same as successes. This page documents listing, retrieving, and exporting receipts. The concept is covered in Receipts & audit.

Shared conventions, including authentication, pagination, and the error envelope, are defined in the API overview. Error codes are catalogued in Errors.

The receipt object

idstring

Unique identifier, prefixed rc_.

objectstring

Always receipt.

reseller_id / tenant_idstring or null / string

Tenancy on the row, the same as every envelope. reseller_id is null for Fibric-direct tenants.

operator_idstring

The operator whose plan produced the action.

operatorstring

The operator's name at the time of the action, denormalized so the receipt stays readable even if the operator is later removed.

event_id / plan_id / action_idstring

The full chain: the triggering event, the plan, and the action this receipt records. event_id is null for scheduled runs.

connector / toolstring

What was invoked, as proposed in the plan's PlannedAction.

entity_keystring

The entity the action touched, for example order:SO-10884.

idempotency_keystring

The dedup key that guarantees this side effect can never apply twice.

verdictobject

The trust evaluation as it was enforced: decision (ALLOW, ALERT, BLOCK, or DEDUP), tier, and rule, or null when the block came from the default-closed fallthrough.

outcomestring

applied, refused, deduplicated, or failed. A refused receipt records a policy block; a failed receipt records a connector error after an allow.

approverstring or null

Who approved the plan, when it ran through the human escalation path. null for auto-disposed plans.

request_idstring

The API request that caused the disposition, correlating the receipt with our logs and yours.

atstring

RFC 3339 timestamp of the disposition.

json · the receipt object
{
  "id": "rc_5b21",
  "object": "receipt",
  "reseller_id": null,
  "tenant_id": "t_8f2ac901",
  "operator_id": "op_8f2a1c",
  "operator": "order-risk",
  "event_id": "ev_3a91c7",
  "plan_id": "pl_7c1a",
  "action_id": "act_91d0",
  "connector": "cn_7d2f4a",
  "tool": "order.hold",
  "entity_key": "order:SO-10884",
  "idempotency_key": "order-risk:SO-10884:hold",
  "verdict": { "decision": "ALLOW", "tier": 1, "rule": "tool:order.hold max_value:500" },
  "outcome": "applied",
  "approver": "ops@acme.com",
  "request_id": "req_9b3e21f0",
  "at": "2026-07-02T15:04:41Z"
}
GET

List receipts

GET/v1/receiptsscope receipts:read

Returns receipts for the tenant, newest first, cursor-paginated. Filters combine with AND, so one query answers questions like "everything the order-risk operator was refused on this order last week".

operatorstring · query

Only receipts produced by this operator; accepts an id (op_8f2a1c) or a name (order-risk).

entitystring · query

Only receipts touching this entity_key.

verdictstring · query

Filter by decision: ALLOW, ALERT, BLOCK, or DEDUP.

outcomestring · query

Filter by outcome: applied, refused, deduplicated, or failed. outcome=applied selects exactly the billable actions counted against the action allowance.

request_idstring · query

Only receipts written by this API request. Use the request_id from an error body or X-Request-Id header to trace one call end to end.

sincestring · query

Only receipts at or after this RFC 3339 timestamp.

untilstring · query

Only receipts strictly before this RFC 3339 timestamp. Combine with since for a closed window.

limitinteger · query

Page size, 1–100. Defaults to 20.

cursorstring · query

Pagination cursor from a previous response's next_cursor.

curl
curl "https://api.fibric.io/v1/receipts?operator=order-risk&verdict=BLOCK&since=2026-06-25T00:00:00Z&until=2026-07-02T00:00:00Z" \
  -H "Authorization: Bearer $FIBRIC_KEY"
200 OK Response
json
{
  "object": "list",
  "data": [
    {
      "id": "rc_5a90",
      "object": "receipt",
      "operator": "order-risk",
      "tool": "order.hold",
      "entity_key": "order:SO-10871",
      "verdict": { "decision": "BLOCK", "tier": 1, "rule": null },
      "outcome": "refused",
      "at": "2026-06-28T11:19:03Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Error cases:

StatusCodeWhen
400invalid_parameterverdict is not one of the four decisions, or a timestamp is not RFC 3339.
400invalid_cursorThe cursor is malformed or was issued for a different query.
GET

Retrieve a receipt

GET/v1/receipts/{receipt_id}scope receipts:read

Returns the full receipt, the complete answer to "why did this happen": the triggering event, the plan, the verdict with the rule that produced it, the idempotency key, and the outcome.

receipt_idrequiredstring · path

The receipt id, for example rc_5b21.

curl
curl https://api.fibric.io/v1/receipts/rc_5b21 \
  -H "Authorization: Bearer $FIBRIC_KEY"
200 OK Response

Returns the receipt object. A cross-tenant id reads as 404 not_found.

POST

Export receipts

POST/v1/receipts/exportscope receipts:read

Starts an asynchronous export job over a filtered slice of the ledger, for an auditor, a data warehouse, or your own retention copy. The job runs in the background; poll the export until it completes, then download from the signed URL. This mirrors fibric receipt export in the CLI.

formatrequiredstring · body

csv or jsonl. JSONL rows are complete receipt objects; CSV flattens verdict into verdict_decision, verdict_tier, and verdict_rule columns.

filtersobject · body

The same filters as list receipts: operator, entity, verdict, since, until. Omit for the full ledger.

curl
curl -X POST https://api.fibric.io/v1/receipts/export \
  -H "Authorization: Bearer $FIBRIC_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: export-june-audit" \
  -d '{
    "format": "jsonl",
    "filters": {
      "since": "2026-06-01T00:00:00Z",
      "until": "2026-07-01T00:00:00Z"
    }
  }'
202 Accepted Response
json · export job
{
  "id": "exp_2f81",
  "object": "export",
  "status": "pending",
  "format": "jsonl",
  "filters": { "since": "2026-06-01T00:00:00Z", "until": "2026-07-01T00:00:00Z" },
  "created_at": "2026-07-02T16:00:00Z"
}

Error cases:

StatusCodeWhen
400missing_parameterformat is absent.
400invalid_parameterformat is not csv or jsonl, or a filter failed validation.
409idempotency_conflictThe Idempotency-Key was reused with a different body.
429quota_exceededToo many concurrent export jobs. See Rate limits & quotas.
GET

Retrieve an export

GET/v1/exports/{export_id}scope receipts:read

Returns the export job. Poll until status is complete, then download from url. The URL is signed and expires; re-fetch the export to mint a fresh one. Jobs progress pendingrunningcomplete, or failed with an error.

export_idrequiredstring · path

The export job id, for example exp_2f81.

curl
curl https://api.fibric.io/v1/exports/exp_2f81 \
  -H "Authorization: Bearer $FIBRIC_KEY"
200 OK Response
json · complete
{
  "id": "exp_2f81",
  "object": "export",
  "status": "complete",
  "format": "jsonl",
  "receipt_count": 4182,
  "url": "https://exports.fibric.io/t_8f2ac901/exp_2f81.jsonl?sig=…",
  "url_expires_at": "2026-07-02T17:03:12Z",
  "created_at": "2026-07-02T16:00:00Z",
  "completed_at": "2026-07-02T16:03:12Z"
}

Error cases:

StatusCodeWhen
404not_foundNo export with this id exists for the tenant. Completed exports are kept for 30 days, then removed.
i
The export is a copy, not a move

Exporting never removes anything from the ledger. Receipts remain queryable in place regardless of how many exports reference them. Because a receipt exists for every action, including every refusal and every dedup, the export is the complete answer to "what did the AI do last month", not a sample of it.