# A full vehicle report for one VIN: decode, recalls, Crit'Air > One paid call returns the VIN decode, the candidate recall campaigns and a Crit'Air class — each block with its own provenance, and an explicit coverage map. Published 2026-07-11 · Updated 2026-08-15 · HTML version: https://invoket.com/blog/a-full-vehicle-report-for-one-vin-in-one-settlement --- An agent vetting a vehicle end-to-end asks three questions: *what is this VIN, really?* — *which recall campaigns may concern it?* — *where is it allowed to drive?* Each has its own Invoket endpoint. When the agent wants all three for the same vehicle, [`GET /vehicle/report`](/docs/api/vehicle-report) composes them into **one response for one x402 settlement**: the VIN decode as the anchor, candidate recalls and a Crit'Air class as secondary blocks, each carrying **its own provenance**, plus an explicit coverage map of anything that could not be produced. Three answers, one payment loop. ## Why a composite endpoint exists For a machine buyer, every paid call is a full loop — receive the `402` challenge, sign, replay, verify. That per-call overhead is fine when the agent has one question; it is pure friction when the questions always travel together, as they do when vetting a single used-car listing or auditing one fleet unit. The report removes that friction without inventing anything new. It is **not a new dataset**: internally it orchestrates the same three cores documented on their own pages — [VIN decode](/docs/api/vehicle-vin-decode), [recalls](/docs/api/vehicle-recalls) and [Crit'Air](/docs/api/vehicle-critair) — over local stores and pure calculation, with no network call and no secret at runtime. What the single settlement buys is the **composition**: one grounded answer per VIN, with a per-block provenance and a coverage map instead of three separate envelopes. ## The call: a VIN anchor, optional declared characteristics The `vin` is required — it is the anchor of the report, and there is **no lookup by licence plate**. The Crit'Air characteristics are optional and follow the same declarative contract as the standalone endpoint: supply at least `fuel` (plus `euro_norm` or `first_registration`) to obtain the `critair` block. ```http GET /vehicle/report?vin=1HGCM82633A004352&fuel=petrol&euro_norm=euro6 ``` The three blocks have three different contracts: | Block | Contract | What it means | |--------------|-----------------|----------------------------------------------------------------------| | `vin_decode` | **Anchor** | Always present; if the VIN cannot decode, there is no report at all | | `recalls` | **Best-effort** | Searched by the decoded make and model year; degrades to a `coverage.missing` entry, never an error | | `critair` | **Conditional** | Present only when usable characteristics were declared | Each block has exactly the shape of its standalone endpoint, provenance included — `nhtsa-vpic` for the decode, the recall sources for `recalls`, the regulatory grid and ZFE reference for `critair`. The root provenance mirrors the anchor; the per-block provenances are what stand for each block. An agent that already parses the standalone responses parses the report for free. ## A partial report is a successful, billed answer The report's honesty lives in `coverage`. As soon as the anchor decodes, the call is a **200** and is billed — even when a secondary block is absent. Every absent block is listed in `coverage.missing` as `{ block, reason }`: `"declared characteristics not provided"` for a skipped Crit'Air, a make absent from every recall source, a store that was unavailable. The map is never a silent error, and a block is **never invented** to look complete. Three consequences worth designing around: - **Only the anchor can fail the call.** A missing or malformed VIN, or a well-formed VIN whose manufacturer is absent from the vPIC tables, leaves the 200 range and is **not billed** — without a decode there is no report. - **Secondary blocks never produce a 5xx.** Store trouble degrades the block into `coverage.missing`; the report still ships with what it has. - **An empty recall list is a present block.** "No known recall in the covered sources" is data, not a miss — the `missing` array is reserved for blocks that could not be produced at all. Every scope limit of the underlying cores still holds: recalls are **candidate campaigns** matched on make and year, never a per-VIN verdict; the Crit'Air class is computed from **declared, not verified** characteristics; sources and freshness are dated per block. ## Where it sits in the x402 loop One report is one standard loop — no signup precedes it: 1. **Discover** the endpoint via the live catalog and call it; receive the `402` challenge. 2. **Pay** — sign the chosen rail and replay the request. 3. **Read** — the decoded identity, the recall candidates, the Crit'Air verdict, and `coverage` for what is missing and why. 4. **Branch** — clear the vehicle, flag campaigns to confirm with the manufacturer, or re-call with declared characteristics to fill the `critair` block. The [Quickstart](/docs/quickstart) walks the full discover → `402` → pay → replay cycle with runnable snippets. Price and accepted rails are not pinned here — they are served live by the [catalog](https://api.invoket.com/catalog); see the [endpoint reference](/docs/api/vehicle-report) for the current figure. ## Report or standalone calls? The rule of thumb is the number of questions: - **One question, one core.** Just grounding a listing's claims? Call [`/vehicle/vin/decode`](/docs/api/vehicle-vin-decode). Only recall exposure? [`/vehicle/recalls`](/docs/api/vehicle-recalls). Only zone access, with characteristics in hand? [`/vehicle/critair`](/docs/api/vehicle-critair). - **The whole picture for one vehicle** — a purchase decision, a fleet intake — call [`/vehicle/report`](/docs/api/vehicle-report) once and read the coverage map. Used for what it is — a composition of three honestly-scoped cores under a single settlement — `/vehicle/report` turns vetting a vehicle from three payment loops into one. For the full block shapes, `coverage` semantics and error codes, see the [`GET /vehicle/report` documentation](/docs/api/vehicle-report); for how agents discover and call Invoket endpoints, see [For agents](/docs/for-agents).