# Receipts

Each job has one receipt. It records what you asked for, which offer won, what the job used, what it cost
and how fast it ran. The exchange writes it just after it answers. A receipt is final once its
`settlement_status` is `settled`, `credited` or `released`.

Use the receipt to check a charge. `supplier_charge_usd` plus `fee_usd`, less `credits_usd`, is
`buyer_charge_usd`. [Fees](https://inference.ai.exchange/docs/fees.md) explains the fee.

## Read a receipt

- `GET /v1/jobs/{job_id}`: one receipt. The job ID is in the `x-liquid-job-id` header and in
  `exchange.job_id`.
- `GET /v1/exchange/receipts`: your receipts, newest first. It takes `limit`, `cursor`, `model` and
  `outcome` (comma-separated lists for the last two) and returns `receipts` and `next_cursor`.
- The console shows the same receipts with filters and charts. The `list_receipts` MCP tool lists them
  for an agent.

A key with `read` scope or higher can call these routes. A job of another workspace returns `404`. A call
at once after the answer can also return `404`. Try again after a moment.

```bash
curl "https://router.inference.ai.exchange/v1/exchange/receipts?limit=20&outcome=completed" \
  -H "Authorization: Bearer $LIQUID_API_KEY"
```

A stored receipt is one flat JSON object. A receipt for a job
with a winner also has the `exchange` block of the live response, and can have `requested_purchase`. An
absent field does not apply to the job. It is not zero. Money fields are USD numbers. A rate is USD per
million tokens.

## The exchange block

The `exchange` block of a live response has a `charge` object and a `usage` object. They name the same money as the fields below.

- Each amount has two fields. `supplier_usd` is a JSON number in USD. `supplier_nanos` is the same amount as an integer of nano-USD, so it is exact. `cap`, `estimated`, `supplier`, `fee`, `credits` and `total` each have both fields.
- `supplier_*` is what the provider earns, before the exchange fee. `total_*` is what you paid: `supplier` plus `fee`, less `credits`.
- `usage` counts tokens as `prompt_tokens`, `cached_tokens`, `cache_write_tokens` and `completion_tokens`. `reasoning_tokens` appears when the provider reports it.
- `usage.pricing` lists the use by billable. See Usage below.

```json
"charge": { "cap_nanos": 50000000, "supplier_nanos": 1200000, "fee_nanos": 60000, "credits_nanos": 0, "total_nanos": 1260000 },
"usage": { "prompt_tokens": 120, "completion_tokens": 40, "pricing": [{ "billable": "output_text", "unit": "mtok", "quantity": 40, "usd": 0.000019 }] }
```

## Identity

- `job_id`: one logical request has one job.
- `award_id`: the award of the job to an offer. Empty when no offer took the job. It is also the
  idempotency key that the provider receives.
- `tenant`, `key_id`: your workspace and the API key. `key_id` is absent for a job from the console.
- `client`: the program that sent the job: `name` and `version`.
- `cost_tag`: the label from your `x-liquid-tag` header. A provider never sees it.
- `applied_preset`, `named_preset`: the preset that the key applied, and the preset that you named as
  `model`, each with its revision.
- `workload`: the workload profile of the job. Absent when the profile names no stored workload.
- `requested_model`: the name that you sent as `model`. A job with no award has no `addressing`, so this
  is its only model name.
- `created_at`, `settled_at`: when the exchange received the job, and when it settled.

## Outcome

- `outcome`: how the job ended. See below.
- `settlement_status`: the state of the money. See below.
- `service`: `met`, `missed`, `failed`, `pending`, `unknown`, `not_started` or
  `single_supplier_refused`.
- `finish_reason`: `stop`, `length` (the answer reached the output limit) or `tool_calls`. Absent when no
  output came.
- `shortfall`: `needed_usd` and `balance_usd`. Present only when your balance excluded every offer that
  could take the job.
- `output_limit`: `asked_tokens` and `applied_tokens`. Present when the exchange lowered your output
  limit so that an offer could take the job.
- `reasoning_effort`: `asked` and `sent`. They differ when the offer accepts other levels and the exchange
  sent the nearest one `sent` is absent when the offer takes no level.

`outcome` and what each value charges:

| Value                         | Meaning                                                                   | Charge                                          |
| ----------------------------- | ------------------------------------------------------------------------- | ----------------------------------------------- |
| `completed`                   | The provider finished the job.                                            | The tokens used.                                |
| `no_eligible_offer`           | No offer passed the gate. See `eligibility`.                              | None. The hold is released.                     |
| `failed_before_output`        | The job failed before any output.                                         | None. The hold is released.                     |
| `cancelled_after_output`      | The job stopped after output began.                                       | The tokens used.                                |
| `provider_error_after_output` | The provider failed after output began. You keep the output.              | A full credit. The provider earns nothing.      |
| `partial_output`              | A stream stopped after output began, not by the provider's fault.         | The input and relayed tokens, as a lower bound. |
| `deadline_missed`             | Old receipts only. A late job is now `completed` with `service` `missed`. | A full credit.                                  |

`settlement_status`:

- `reserved`: the exchange holds the price cap. The job is not settled yet.
- `settled`: the job was charged.
- `credited`: the job was charged and then credited back.
- `pending`: usage is missing or in conflict. You get a credit after the deadline. The exchange does not
  bill you without a warning.
- `released`: the hold went back to your balance. You paid nothing.

A job that missed its deadline is charged like any other completed job. `credits_usd` is zero.

## Charge

- `cap_usd`: the most the job could cost, locked before the first token. Also the `x-liquid-cap-usd`
  header.
- `estimated_charge_usd`: the cold estimate that ranked the offer, before any cache credit.
- `cache_credit`: `hit_rate`, `source` (`buyer` or `measured`) and `credit_usd`. Ranking uses it. The cap,
  the hold and the charge do not.
- `supplier_charge_usd`: what the provider earns, before the exchange fee.
- `fee_usd`: the exchange fee, after any referral discount. See [Fees](https://inference.ai.exchange/docs/fees.md). `fee_discount_usd` is the
  part that a referral discount took off.
- `credits_usd`: credit that the exchange gave back for the job.
- `buyer_charge_usd`: what you paid. It never exceeds `cap_usd`.
- `charge_split`: what the input, cache reads, cache writes and output each cost, fee included, as `tokens`
  and `usd`. Absent for a job not priced in tokens. When the cap limited the job, the parts add up to
  `buyer_charge_usd` plus `cap_held_usd`.
- `cap_held_usd`: what the cap took off the metered charge, fee included. Present only when the job used
  more than its cap. The provider carries it.
- `uncached_charge_usd`, `cache_charge_usd`: the charge if all input had the uncached rate, and the part
  that cache reads and writes cost. Present only when some input used a cache. For information only.
- `rates`: the rates that priced the job: `uncached_input`, `cache_read`, `cache_write_short`,
  `cache_write_long` and `generation`. They stay as at award time if the provider changes them later.
- `web_search`: the search that you asked for with `:web`. Its `charge_usd` is part of `fee_usd` and
  `buyer_charge_usd`.

## Usage

- `meters`: what the provider reported, in its own tokenizer. See below.
- `reasoning_tokens`: hidden reasoning tokens, billed as output. Present only when the provider reports
  them.
- `tokenizer_id`, `rate_schema_version`: the tokenizer that counted the tokens, and the rate table that priced the job.
- `usage_provenance`: the upstream usage report, unchanged. On a failed job it holds an `error`. Null for some media jobs.
- `answer_checks`: what the exchange checked in the answer. See below. Absent means not checked, and you
  must not count it as a pass.

`meters` fields. A media meter is absent when it is zero.

- `uncached_input`: input tokens that no cache served or kept.
- `cache_read`: input tokens that a cache served.
- `cache_write_short`, `cache_write_long`: input tokens written to the cache, by lifetime.
- `generation`: output tokens, reasoning included. `audio_generation` is the audio part.
- `images`, `characters`, `video_seconds`, `audio_seconds`: the quantity of an image, speech, video or
  transcription job. Not tokens.

The `exchange.usage` block gives the same numbers in a simpler form. `prompt_tokens` is the sum of all input
meters.

`exchange.usage.pricing` lists the same use by billable. Each line has `billable`, `unit`, `quantity` and
`usd`. The `usd` is the provider charge for that quantity before the exchange fee, at the rate that priced the
job. A receipt with no stored rates has no table. The flat counters stay.

`answer_checks` changes no charge, except that a chat answer with a bad tool call is rejected before you
receive it.

- `structured_output.result`: the check against your `response_format`: `passed`, `not_json`,
  `schema_mismatch`, `cut`, `refused` or `unchecked`. `keyword` names the broken JSON Schema keyword for
  `schema_mismatch`. See [Structured output](https://inference.ai.exchange/docs/errors.md#structured-output).
- `tool_calls`, `tool_call_errors`: the tool calls in the answer, and how many were bad. See
  [Tool calls](https://inference.ai.exchange/docs/errors.md#tool-calls).
- `tool_call_faults`: the bad calls by kind: `invalid_json`, `unknown_name`, `schema_mismatch`.
- `tool_markup`: 1 when the text has tool-call markup but the answer has no tool call.

## Timing

- `ttft_ms`: time to first token. It starts when the request arrives, so it includes the auction.
- `completion_ms`: time from the first byte to the last.
- `promised_ttft_ms`: the first-token time that the offer promised. The receipt keeps it.
- `ttft_limit_ms`: your limit. Absent for an image job and for old receipts.
- `ttft_deadline_ms`: `ttft_limit_ms` plus 1 second for each 10,000 input tokens. The exchange judges
  the job against this, not against the promise. A job with a `ttft_ms` above `promised_ttft_ms` can
  still be `met`.

## Provider and offer

- `provider_id`: the provider, stable when it changes its slug. `provider_slug`: its slug at the time.
- `offer_id`: the offer that served the job. Use it to trace a charge.
- `provider_attribution`: what the provider said about its data handling at award time. Each claim has a
  `basis` (`declared` or `checked`), `checked_at` and `valid_until`. The receipt freezes it.

## Addressing

`addressing` says how your request became a served model. It is absent on a job with no award.

- `requested`, `target`: the name that you sent, and what it is: a `model`, a `router` or a `preset`.
- `match_type`: `exact`, `approved_set`, `router` or `auto`.
- `router`, `router_version`: the router model that chose the models, and the Auto mode version.
- `served_model`, `served_precision`: the model that answered, and its precision, such as `bf16` or
  `fp4`. `provider-managed` means the provider does not state it.
- `auto`: why Auto chose the model. See [Auto Router](https://inference.ai.exchange/docs/auto.md). `auto_fallback`: why a call to a router
  model ranked by price and not by Auto.
- `synthetic` (a local substitute answered), `unverified` (no operator reviewed the deployment) and
  `probe` (the exchange sent the job to measure a deployment): do not use such a job as evidence about a
  model.

## Competition

- `clearing_rule`: how the price was set. `first_price` charges the locked rates of the winner, up to its
  cap.
- `offers_considered`, `offers_eligible`: the offers that the gate looked at, and those that passed.
- `suppliers_eligible`, `single_supplier`: the independent providers behind the eligible offers, and
  whether only one was eligible.
- `eligibility.excluded`: each offer that the gate refused, with `provider`, `offer_id`, `reason` and,
  when it helps, `detail`. `exchange.routing.eligibility` gives the same as counts for each provider and
  reason.

`reason` values:

- `job_kind_not_served`, `model_not_served`: the offer does not make this kind of output, or this model.
- `feature_unsupported`, `media_unsupported`: the deployment cannot do a feature or accept an attachment.
  `detail` names it.
- `context_too_large`, `output_bound_unsupported`: the prompt, your output limit, or a media size is
  outside what the offer allows.
- `recently_failed`: the model failed a job recently, or health samples show it down.
- `service_too_slow`, `throughput_too_low`: the offer's promised deadline is too slow for your limit.
- `cap_exceeds_buyer_cap`, `cap_exceeds_budget`: the worst-case charge is above your `cap_usd`, or your
  balance.
- `rate_above_ceiling`, `above_cost_tier`: the published rate is above your maximum rate or cost tier.
- `region_policy`, `retention_policy`, `provider_requirements`: the provider does not meet your region,
  data-retention or provider-claim rules.
- `model_not_allowed`, `provider_not_allowed`, `vendor_not_allowed`, `provider_blocked`: your model list,
  provider list, creator list or block list excludes the offer.
- `offer_expired`, `pinned_offer_not_resting`: the offer is no longer on the book.
- `workload_profile_not_targeted`: the offer is outside the workload profile that you named.
- `superseded_by_own_offer`: the same provider had a lower offer that also qualified.
- `score_unmapped`, `score_missing`: Auto has no approved score for the deployment, or for the task.

## Exchange block and requested purchase

`exchange` is the block of the live response. See [Purchasing rules](https://inference.ai.exchange/docs/rules.md#response-fields).
`requested_purchase` is what the request asked for before the preset of its key limited it: `target`,
`models`, `strategy`, `rules` and `sources`. Absent on old jobs.

## Check a receipt

1. Add `supplier_charge_usd` and `fee_usd`, subtract `credits_usd`, and compare with `buyer_charge_usd`. It
   must not exceed `cap_usd`.
2. For a job priced in tokens, multiply each `meters` value by the matching rate in `rates`. The sum is
   `supplier_charge_usd`, unless the cap limited the job.
3. Compare `fee_usd` with [Fees](https://inference.ai.exchange/docs/fees.md).
4. Compare `ttft_ms` with `ttft_deadline_ms`, not with `promised_ttft_ms`.
