GET /vehicle/report
Returns a composite report for a single VIN in one x402 settlement: the VIN decode, the recall campaigns that could concern the vehicle, its type-approval CO2, the French registration tax it would cost, and - when the agent declares the characteristics - its Crit’Air class and low-emission-zone restrictions. Instead of five separate calls, an agent vetting one vehicle pays once and gets a single grounded answer with per-block provenance.
The report is composed internally from cores this platform already serves - VIN decode, recalls, CO2, registration tax and Crit’Air - so the runtime performs no network call, holds no secret and makes no HTTP call to itself. The store-backed blocks read the pre-ingested local data; the Crit’Air and tax blocks are pure calculation.
The VIN is the anchor of the report and is required; there is no lookup by
licence plate. The secondary blocks are best-effort: each one is either present
or listed - with a reason - in coverage.missing, never faked and never turned
into a 5xx. See the live /catalog for the authoritative
endpoint listing and price.
x402 golden rule: the agent pays for the answer to its question. As soon as the VIN decodes, the report is a successful answer -> 200, even when it is partial (recalls and/or Crit’Air missing) - the decode plus honest provenance and an explicit coverage map is the data being sold. Only failure of the anchor leaves the 200 range - a missing or malformed VIN, or a well-formed VIN whose manufacturer is absent from the tables - and those are not billed.
Parameters
Query-string parameters. vin is always required (the anchor). Everything else
is declarative and optional: the Crit’Air characteristics follow the contract
of GET /vehicle/critair, and the two administrative
figures of GET /vehicle/tax unlock a tax costing.
| Parameter | Type | Required | Description |
|---|---|---|---|
vin | string | yes | A 17-character VIN over the VIN alphabet (no I, O, Q); the anchor of the report |
fuel | string | no | Declared fuel for the Crit’Air block: electric, hydrogen, petrol, diesel, petrol_hybrid or diesel_hybrid. Omit to skip Crit’Air |
euro_norm | string | no | Declared emission standard (pre_euro, euro2..euro6); one of euro_norm/first_registration feeds Crit’Air |
first_registration | string | no | Declared date of first registration (YYYY-MM-DD); derives the Euro norm when euro_norm is absent, and is the default registration date of the tax costing |
vehicle_class | string | no (default m1) | Vehicle category for Crit’Air, m1 or n1 (scope is M1/N1 only) |
region | string | no | French region (INSEE code or exact name) - declarative; needed for a tax costing |
fiscal_power | int | no | Fiscal power in CV - declarative, a French record absent from the EU registry; needed for a tax costing |
The Crit’Air block appears only when at least fuel is supplied and the core
can interpret the declared input; otherwise it is honestly listed in
coverage.missing (see below), never guessed. The tax block is always
present, computed when region and fiscal_power are supplied and the
technical figures resolve, and otherwise stating what it is missing.
GET /vehicle/report?vin=1HGCM82633A004352
GET /vehicle/report?vin=5YJ3E1EA7KF000316&fuel=electric®ion=11&fiscal_power=7
200 response - UnifiedResponse
{
"data": {
"vin_decode": { "...": "...", "provenance": { "...": "..." } },
"recalls": { "...": "...", "provenance": { "...": "..." } },
"co2": { "...": "...", "provenance": { "...": "..." } },
"critair": { "...": "...", "provenance": { "...": "..." } },
"registration_tax": { "coverage": "computed", "...": "..." },
"coverage": { "complete": true, "missing": [] }
},
"provenance": {
"source": "nhtsa-vpic",
"fetched_at": "2026-07-04T12:00:00Z",
"freshness": { "kind": "snapshot", "as_of": "2026-06-01T00:00:00Z" }
}
}
The report is a composite: each block carries its own provenance alongside
its data, exactly as the standalone endpoint would return it. The root
provenance mirrors the vin_decode block (the primary source that satisfies the
envelope) - it is never a lying global provenance; the per-block provenances
are what stand for each block.
Fields of data
| Field | Type | Description |
|---|---|---|
vin_decode | object | Anchor - always present. The full VIN decode plus its vPIC provenance |
recalls | object | Best-effort. The recalls lookup for the decoded make and year plus its provenance; omitted when unavailable (see coverage) |
co2 | object | Best-effort. The type-approval CO2 keyed by the decoded make, model and model year, plus its provenance; omitted when unavailable (see coverage) |
critair | object | Conditional. The Crit’Air result plus its provenance; present only when usable characteristics were declared |
registration_tax | object | Always present, with its own closed coverage: the French registration tax costing, or what it is missing |
coverage | object | Which secondary blocks are present, and the reason for each one that is absent |
Each block’s shape is exactly that of its standalone endpoint, so the same
field-level honesty applies (unresolved fields omitted, an empty recalls list is
still a present block, check_digit_valid is information not a gate, etc.).
vin_decode (anchor)
The decoded VIN fields (vin, wmi, manufacturer, make, country,
region, model_year, check_digit_valid, attributes, coverage) with a
vPIC provenance (source: "nhtsa-vpic"). See
/vehicle/vin/decode for the full block.
recalls (best-effort)
The recalls result for the vehicle - searched by the decoded make and model
year (the make from the WMI, falling back to the manufacturer; the decoded
model is not reliable enough to filter on) - with a recall-sources provenance.
An empty recalls list is a present block (“no known recall in the covered
sources”). Any core failure (make absent from every source, store unavailable)
degrades the block to a coverage.missing entry, never a 5xx. See
/vehicle/recalls for the full block; note the same
candidate-campaign caveat - matched on make/year, never a per-VIN verdict.
co2 (best-effort)
The type-approval CO2 block - WLTP grams per kilometre,
fuel, engine capacity, power and mass - keyed by the decoded make, model and
model year, with its own eea-co2-monitoring provenance.
Because a European VIN carries no type-approval variant, this block is
always match_level: "model_year", with its p10/p50/p90 dispersion. That
is a limit of the VIN, not of the dataset, and it is never worked around by
serving a single figure as if it were the vehicle’s own. A model absent from the
registry, a model year outside the ingested window or a dataset not deployed puts
the block in coverage.missing with its reason - never an error of the report.
The block’s own limits are returned verbatim, exactly as on the standalone page.
registration_tax (always present)
The French registration tax costing, with a closed two-branch coverage of its own:
coverage | Meaning |
|---|---|
computed | The full costing: inputs_used, the three items with their brackets and exemptions by article, schedule with its legal_basis, and total_eur |
insufficient_inputs | No costing, and missing_inputs[] names what is missing - never a partial or approximated total |
The technical figures come from the co2 block (marked source: "resolved"),
so a report whose CO2 block is missing usually lists co2_wltp and weight_kg
among missing_inputs. Add region and fiscal_power to the query for a
costing; registration_date defaults to first_registration when declared, else
today, and country is implicitly FR.
The same refusal as the standalone endpoint applies inside the computed
branch: as soon as one item is undetermined, total_eur disappears rather than
being served partial. This block is deliberately not listed in the report’s
coverage.missing - it is always there, and it carries its own honesty.
critair (conditional)
The Crit’Air result for the declared characteristics, with a Crit’Air/ZFE
provenance. Present only when at least fuel is declared and the core can
interpret the input. See /vehicle/critair for the
full block and its declarative contract.
The core is the same one, so the same rules hold - including the
bounds on first_registration
and the fact that a Euro norm read from a date is an
inference capped at the Euro 6 mandate.
One difference: here an impossible date is not a 400. It degrades the
critair block into a coverage.missing entry carrying the core’s own message -
dated and reasoned ("…got '2099-01-01', today is 2026-07-26 UTC…") - while the
vin_decode anchor is still served and the report is still a billed 200.
coverage
| Field | Type | Description |
|---|---|---|
complete | bool | true only when no secondary block is missing |
missing | array | One entry per absent secondary block: { block, reason } - what is missing and why |
missing[].block is "recalls", "co2" or "critair"; missing[].reason
states the cause in plain text - "declared characteristics not provided" for a
skipped Crit’Air block, or "the vPIC tables resolve no model for this VIN: the European CO2 dataset is keyed by make, model and year" for a VIN that decodes
without a model. A base-only report - VIN decoded, no recalls searched or
returned, no characteristics declared - is a partial report with
complete: false. The map is never a silent error and never invents a block.
registration_tax never appears here: it is always present and reports its own
coverage.
Example - full report (VIN + declared characteristics + tax inputs)
GET /vehicle/report?vin=5YJ3E1EA7KF000316&fuel=electric®ion=11&fiscal_power=7
- the VIN decodes, recalls are found for the decoded make and year, the CO2 block
resolves from the decoded make/model/year, the declared electric powertrain yields
a Crit’Air 0 without needing a norm or a date, and the two declarative
administrative figures unlock the tax costing. Every secondary block is present,
so
coverage.completeistrue.
{
"data": {
"vin_decode": {
"vin": "5YJ3E1EA7KF000316",
"wmi": "5YJ",
"manufacturer": "Tesla, Inc.",
"make": "TESLA",
"country": "United States",
"region": "North America",
"model_year": 2019,
"check_digit_valid": true,
"attributes": { "Model": "Model 3" },
"coverage": { "complete": true },
"provenance": {
"source": "nhtsa-vpic",
"fetched_at": "2026-07-30T18:42:33Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-01T00:00:00Z" }
}
},
"recalls": {
"make": "TESLA",
"year": 2019,
"recalls": [
{
"source": "NHTSA",
"campaign_id": "24V935000",
"component": "TIRES:PRESSURE MONITORING AND REGULATING SYSTEMS",
"summary": "Tesla, Inc. (Tesla) is recalling certain 2024 Cybertruck, 2017-2025 Model 3, and 2020-2025 Model Y vehicles. The tire pressure monitoring system (TPMS) warning light may not remain illuminated between drive cycles...",
"remedy": "Tesla released an over-the-air (OTA) software update, free of charge...",
"published_at": "2024-12-17",
"url": "https://www.nhtsa.gov/recalls?nhtsaId=24V935000",
"match": { "make": "TESLA", "model": ["MODEL 3"], "years": { "from": 2019, "to": 2019 } }
}
],
"coverage": {
"sources": [
{ "name": "NHTSA", "as_of": "2026-07-01", "fresh": true },
{ "name": "RappelConso", "as_of": "2026-07-01", "fresh": true },
{ "name": "Safety Gate", "missing": true, "reason": "Safety Gate source is not currently ingested" }
],
"complete": false,
"reason": "Safety Gate source is not currently ingested"
},
"provenance": {
"source": "nhtsa+rappelconso",
"fetched_at": "2026-07-30T18:42:33Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-01T00:00:00Z" }
}
},
"co2": {
"match_level": "model_year",
"vehicle": {
"make": "TESLA",
"commercial_name": "MODEL 3",
"year": 2019,
"fuel": "ELECTRIC",
"engine_power_kw": 150,
"mass_kg": 1931
},
"co2": {
"wltp": { "p50": 0, "p10": 0, "p90": 0 },
"nedc": 0,
"unit": "g/km",
"sample_size": 42680,
"low_sample": false
},
"coverage": {
"years_covered": [2018, 2019, 2020, 2021, 2022],
"versions_aggregated": 110,
"min_sample": 5
},
"provenance": {
"source": "eea-co2-monitoring",
"fetched_at": "2026-07-30T18:42:33Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-28T00:00:00Z" }
}
},
"critair": {
"vehicle_class": "m1",
"fuel": "electric",
"critair_class": "0",
"derivation": { "via": "fuel", "fuel": "electric" },
"zfe_restrictions": [],
"regulation": {
"reference": "Arrêté du 21 juin 2016 (nomenclature Crit'Air, art. R. 318-2 du code de la route)",
"as_of": "2026-07-04"
},
"provenance": {
"source": "critair-regulation+zfe-open-data",
"fetched_at": "2026-07-30T18:42:33Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-04T00:00:00Z" }
}
},
"registration_tax": {
"coverage": "computed",
"country": "FR",
"registration_date": "2026-07-30",
"inputs_used": {
"co2_wltp_g_km": { "value": 0, "source": "resolved" },
"weight_kg": { "value": 1931, "source": "resolved" },
"fiscal_power_cv": { "value": 7, "source": "declared" },
"fuel": { "value": "electric", "source": "declared" },
"region": {
"value": { "insee_code": "11", "name": "Île-de-France", "eur_per_fiscal_hp": 68.95 },
"source": "declared"
},
"resolution": {
"match_level": "model_year",
"source": "eea-co2-monitoring",
"make": "TESLA",
"commercial_name": "MODEL 3",
"year": 2019
}
},
"co2_malus": {
"status": "exempt",
"amount_eur": 0,
"co2_g_km": 0,
"threshold_g": 108,
"exemption": {
"id": "electric_or_hydrogen",
"article": "L. 421-67",
"label": "vehicle powered exclusively by electricity, hydrogen or both"
}
},
"weight_malus": {
"status": "exempt",
"amount_eur": 0,
"mass_kg": 1931,
"threshold_kg": 1500,
"exemption": {
"id": "low_carbon_footprint_electric_or_hydrogen",
"article": "L. 421-79",
"label": "low-carbon-footprint vehicle, and hydrogen or electric vehicle"
}
},
"regional_tax": {
"status": "exempt",
"amount_eur": 11.0,
"proportional_eur": 0.0,
"fixed_fee_eur": 11,
"exemption": {
"id": "regional_clean_vehicle_exemption",
"article": "L. 421-49",
"label": "region has voted the exemption of electric and hydrogen vehicles"
}
},
"total_eur": 11.0,
"provenance": {
"source": "fr-registration-tax+eea-co2-monitoring",
"fetched_at": "2026-07-30T18:42:33Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-28T00:00:00Z" }
}
},
"coverage": { "complete": true, "missing": [] }
},
"provenance": {
"source": "nhtsa-vpic",
"fetched_at": "2026-07-30T18:42:33Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-01T00:00:00Z" }
}
}
Two details worth reading twice. The tax limits (elided above) gain a line
stating that the CO2 came from a model-year aggregate spanning 110 versions
- the precision limit is propagated all the way into the euro amount. And the
regional tax is
exemptwith a non-zeroamount_eur: the flat national fee of article L. 421-38 stays due even when the region exempts the vehicle.
Example - missing blocks and no costing, still a 200
GET /vehicle/report?vin=VF1RFA00567123456&fuel=diesel&euro_norm=euro6 - the VIN
decodes and Crit’Air is computed from the declared characteristics, but the vPIC
tables resolve no model, so the CO2 block cannot be keyed. Without CO2 and
without the two administrative figures, the tax costing has nothing to compute
and says exactly what it lacks:
{
"coverage": {
"complete": false,
"missing": [
{
"block": "co2",
"reason": "the vPIC tables resolve no model for this VIN: the European CO2 dataset is keyed by make, model and year"
}
]
},
"registration_tax": {
"coverage": "insufficient_inputs",
"missing_inputs": ["co2_wltp", "weight_kg", "fiscal_power", "region"]
}
}
missing_inputs is the actionable part: it names the four figures that would
turn this into a costing - two the agent could declare, two that a resolvable
make/model/year would have supplied. No block is faked, no total is approximated,
and the report is still a billed 200 built on its anchor.
Composition and honesty
The report is not a new dataset: it orchestrates the VIN-decode, recalls, CO2, registration-tax and Crit’Air cores already documented on their own pages, so every scope limit stated there still holds - candidate recall campaigns (not a per-VIN verdict), model-year CO2 aggregates (a VIN carries no type-approval variant), a French-only tax costing of the published schedules (not tax advice), declared (not verified) Crit’Air characteristics, and no lookup by licence plate. Nor are vehicle valuations, market prices or purchase advice in scope. The value the single settlement buys is the composition itself: one grounded answer per VIN with a provenance for each block and an explicit coverage map.
Because the core is composition over local stores and pure calculation, there is
no store 5xx and no staleness 5xx for the secondary blocks: a store that is
unavailable or a make absent from the sources degrades recalls or co2 to a
coverage.missing entry, and missing figures degrade the tax block to
insufficient_inputs, rather than failing the report. Only the anchor can
fail the call, and when it does the request is not billed.
Errors
Only failure of the anchor VIN leaves the 200 range, and those requests are
not billed (without a decode there is no report). Secondary blocks never
produce an error - they degrade into coverage.missing.
| Status | code | Case |
|---|---|---|
| 400 | MISSING_PARAMETER | vin is absent or empty |
| 400 | INVALID_VIN | vin is not 17 characters, or contains a character outside the VIN alphabet |
| 404 | UNKNOWN_WMI | The VIN is well formed but its manufacturer identifier is absent from the tables |
| 404 | NOT_FOUND | Unknown route |
| 500 | INTERNAL | Internal error (detail logged, not exposed) |
{ "error": "query parameter `vin` is required (17-character VIN)", "code": "MISSING_PARAMETER" }
{ "error": "invalid VIN `1HGCM82633A00435`; expected 17 characters over the VIN alphabet (no I, O, Q)", "code": "INVALID_VIN" }
Attribution
Each block keeps the attribution of the source it composes:
- VIN decode - NHTSA vPIC tables (US public domain).
- Recalls - NHTSA (US public domain) and RappelConso (Licence Ouverte / Etalab). The EU Safety Gate (European Commission) is an expected source on the roadmap, not yet served.
- CO2 - the EU CO2 monitoring dataset published under Regulation (EU) 2019/631 (European Environment Agency / European Commission).
- Registration tax - the code des impositions sur les biens et services published by Légifrance (DILA) under the Licence Ouverte / Etalab, each applied article returned with its LEGI identifier.
- Crit’Air - French regulation (Arrêté du 21 juin 2016) and the ZFE reference under the Licence Ouverte / Etalab.
See also
GET /vehicle/vin/decode- the anchor block on its own: decode a VIN into manufacturer, model year and deterministic attributes.GET /vehicle/recalls- candidate recall campaigns for a vehicle, consolidated offline from public sources.GET /vehicle/co2- type-approval CO2 and technical characteristics, with the precision each figure was matched at.GET /vehicle/tax- the French registration tax on its own, with brackets, exemptions by article and the schedule applied.GET /vehicle/critair- Crit’Air class and ZFE restrictions from declared characteristics.- For agents - discovery surfaces, the live
/catalogand how settlement works.