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.

ParameterTypeRequiredDescription
vinstringyesA 17-character VIN over the VIN alphabet (no I, O, Q); the anchor of the report
fuelstringnoDeclared fuel for the Crit’Air block: electric, hydrogen, petrol, diesel, petrol_hybrid or diesel_hybrid. Omit to skip Crit’Air
euro_normstringnoDeclared emission standard (pre_euro, euro2..euro6); one of euro_norm/first_registration feeds Crit’Air
first_registrationstringnoDeclared 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_classstringno (default m1)Vehicle category for Crit’Air, m1 or n1 (scope is M1/N1 only)
regionstringnoFrench region (INSEE code or exact name) - declarative; needed for a tax costing
fiscal_powerintnoFiscal 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&region=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

FieldTypeDescription
vin_decodeobjectAnchor - always present. The full VIN decode plus its vPIC provenance
recallsobjectBest-effort. The recalls lookup for the decoded make and year plus its provenance; omitted when unavailable (see coverage)
co2objectBest-effort. The type-approval CO2 keyed by the decoded make, model and model year, plus its provenance; omitted when unavailable (see coverage)
critairobjectConditional. The Crit’Air result plus its provenance; present only when usable characteristics were declared
registration_taxobjectAlways present, with its own closed coverage: the French registration tax costing, or what it is missing
coverageobjectWhich 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:

coverageMeaning
computedThe full costing: inputs_used, the three items with their brackets and exemptions by article, schedule with its legal_basis, and total_eur
insufficient_inputsNo 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

FieldTypeDescription
completebooltrue only when no secondary block is missing
missingarrayOne 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&region=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.complete is true.
{
  "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 exempt with a non-zero amount_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.

StatuscodeCase
400MISSING_PARAMETERvin is absent or empty
400INVALID_VINvin is not 17 characters, or contains a character outside the VIN alphabet
404UNKNOWN_WMIThe VIN is well formed but its manufacturer identifier is absent from the tables
404NOT_FOUNDUnknown route
500INTERNALInternal 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 /catalog and how settlement works.