GET /vehicle/prepurchase

Builds a pre-purchase dossier for one VIN in a single x402 settlement: the five blocks of /vehicle/report - VIN decode, recall campaigns, CO2, French registration cost and Crit’Air - each with its own provenance, plus one verdict with every reason attached to the block it came from. The dossier adds exactly two things to the report: the verdict, and a fixed sentence saying what it does not attest. Read that sentence first.

What this dossier does not attest

Every response carries this sentence in data.limits, verbatim:

This dossier is built from public reference data keyed by the VIN alone. It attests neither the service history, the mileage, an outstanding lien or finance, accident or flood damage, the identity of the seller, nor a valid roadworthiness test (contrôle technique). Recall campaigns are candidates matched by make, model and model year: open sources never state whether the remedy was carried out on this VIN. Nothing here is a market value, an inspection or a purchase recommendation.

So, before anything else:

  • Not attested: service history, mileage, an outstanding lien or finance, accident or flood damage, the seller’s identity, a valid roadworthiness test. The service holds none of these sources, and no combination of inputs will make them appear.
  • Not provided: a market value, an inspection, or a recommendation to buy or not to buy. The verdict says what the dossier found and what it could not find - the decision is the buyer’s.
  • A recall campaign is a candidate, not a defect found on this VIN. Open sources publish campaigns; they never say whether the remedy was carried out on a given vehicle. “31 campaigns” means “31 campaigns to check with the manufacturer network”, never “31 unrepaired defects”.

The verdict

data.verdict.decision is one of four values, and data.verdict.reasons lists every finding with its code, the block it comes from, the decision it carries and a message naming the quantity or campaign at stake:

codeBlockDecision
critair_not_eligiblecritairstop
vin_check_digit_invalidvin_decodestop
recall_sources_unavailablerecallsinsufficient_coverage
block_unavailablethe missing blockinsufficient_coverage
registration_cost_inputs_missingregistration_taxinsufficient_coverage
registration_cost_out_of_schedule_rangeregistration_taxinsufficient_coverage
open_recall_campaignrecallsreview

Three rules make the verdict worth reading:

  • The verdict is the most severe reason, and nothing is merged. Severity runs ok < review < insufficient_coverage < stop; the reasons are sorted most severe first, all of them kept. An agent that wants to weigh them differently reads the list - it never has to reverse-engineer a score.
  • insufficient_coverage outranks review, deliberately. Not knowing is more binding than a point to check: a dossier with a hole must never read as a complete dossier with a remark. So a vehicle with open campaigns and a missing CO2 block is insufficient_coverage, and the review is still listed beneath it.
  • ok requires no reason at all: all five blocks produced, no campaign targeting the vehicle, a Crit’Air class obtained, a registration cost computed. Never ok on a missing block.

stop means an established fact disqualifies the vehicle for the use the dossier covers - a vehicle with no Crit’Air class is barred from every low-emission zone that restricts it, listed in the critair block. It is not a judgement on the car.

ok is rare in practice, and we say so. It needs zero recall campaigns for the decoded make and model year, and the recall sources are generous (see below). Across 197 dossiers built while writing this page - some thirty makes, model years 2018 to 2022 - not one came back with zero campaigns, so not one was ok. That is why no ok example appears below: we would rather say it than fabricate one.

Parameters

The VIN is the only key - there is no lookup by licence plate. Every other parameter is declarative: stated by the caller, never verified, and its absence produces a named coverage gap, never a guessed value.

ParameterTypeRequiredDescription
vinstringyesA 17-character VIN over the VIN alphabet (no I, O, Q)
fuelstringnoDeclared fuel for the Crit’Air block and the tax costing (electric, petrol, diesel…)
euro_normstringnoDeclared emission standard (pre_euro, euro1..euro6)
first_registrationstringnoDeclared date of first registration (YYYY-MM-DD) - fixes the Crit’Air class when no norm is given
registration_datestringnoThe date that chooses the tax schedule (YYYY-MM-DD) - see below
vehicle_classstringnom1 (default) or n1
regionstringnoFrench region (INSEE code or exact name) - needed for the registration cost
weight_kgintnoDeclared mass, when the CO2 block cannot resolve it
fiscal_powerintnoFiscal power in CV - a French record, absent from the EU registry; needed for the registration cost
GET /vehicle/prepurchase?vin=5YJ3E1EA0KF000316&fuel=electric&region=11&fiscal_power=7

Two dates, on purpose. A used car was registered once - which fixes its Crit’Air class (first_registration) - and will be registered again at resale, under the tax schedule of that date (registration_date). When registration_date is absent, the first registration date is used, then the day of the call. The schedule is always the one in force at the date given, never today’s by default.

The check digit: refused here, and only where it is law

This route is stricter than the report on one point. A VIN whose check digit (position 9) is wrong where it is regulatory - North America - is refused with 400 INVALID_VIN before any work, and not billed. The dossier has a single key: a mistyped VIN would bill five blocks describing another vehicle. 5YJ3E1EA7KF000316 - one character away from the valid 5YJ3E1EA0KF000316 used below - is refused here:

{ "code": "INVALID_VIN", "error": "VIN '5YJ3E1EA7KF000316' fails its check digit (position 9), which is regulatory in North America: the VIN is mistyped — re-read it from the vehicle before ordering a pre-purchase dossier" }

The symmetric rule matters as much: outside North America, position 9 is constrained by no text. A European VIN such as WVWZZZCDZLW000001 decodes with check_digit_valid: false - which is the norm there - and the dossier is served; the flag is never held against the VIN. /vehicle/vin/decode itself is unchanged: it returns the information and lets the caller decide.

The five blocks

The dossier’s data carries the same five blocks as /vehicle/report - flattened at the root of data, with the same presence conditions - plus coverage, verdict and limits. Each block carries its own provenance, never smoothed under one date:

BlockSourcePresent when
vin_decodeNHTSA vPICalways - it is the anchor
recallsNHTSA + RappelConsothe decoded make is known to the recall sources
co2EEA CO2 monitoring (model years 2018-2022)vPIC resolves a model and the year is in the window
registration_taxFrench tax code + EEA figuresalways - computed, or naming what it is missing
critairCrit’Air regulation + ZFE open datafuel is declared and interpretable

The block-by-block contract - fields, presence conditions, provenance - is the report’s, documented on its page and not repeated here. The root provenance is the anchor’s (nhtsa-vpic).

Recalls: candidates, matched wide

Said a second time because it is the misreading that would cost a buyer the most: a recall campaign in this dossier is a candidate, not a defect found on this VIN. Open sources never state whether the remedy was carried out on a given vehicle; only the manufacturer network can.

The match is also wider than the model. Campaigns are found for the decoded make and model year, and a campaign whose source publishes no model-year range matches at brand level. The block says how many, in recalls.coverage.filters.year: on the 1996 Honda example below, 34 of the 35 campaigns match at brand level - including a recall for a model that did not exist in 1996. That is the honest reading of the count: an upper bound of campaigns to check, never a list of faults.

The recall sources’ own freshness and the EU Safety Gate source (expected, not yet ingested) are reported in recalls.coverage and do not move the verdict - otherwise every production dossier would read insufficient_coverage. If no recall source is served at all, the claim “no known campaign” no longer exists, and the reason recall_sources_unavailable says so.

Example - review

GET /vehicle/prepurchase?vin=5YJ3E1EA0KF000316&fuel=electric&region=11&fiscal_power=7

  • all five blocks produced, the registration cost computed, open campaigns for the make and year. Blocks abbreviated; their full shape is on the report page:
{
  "data": {
    "vin_decode": {
      "vin": "5YJ3E1EA0KF000316",
      "wmi": "5YJ",
      "make": "Tesla",
      "region": "North America",
      "model_year": 2019,
      "check_digit_valid": true,
      "check_digit_applicable": true,
      "attributes": { "Model": "Model 3", "Fuel Type - Primary": "Electric", "…": "…" },
      "provenance": { "source": "nhtsa-vpic", "freshness": { "kind": "snapshot", "as_of": "2026-09-01T00:00:00Z" }, "…": "…" }
    },
    "recalls": {
      "make": "TESLA",
      "year": 2019,
      "recalls": [ "… 31 campaigns …" ],
      "total": 31,
      "truncated": false,
      "coverage": {
        "sources": [
          { "name": "NHTSA", "as_of": "2026-10-11", "fresh": true },
          { "name": "RappelConso", "as_of": "2026-10-11", "fresh": true },
          { "name": "Safety Gate", "missing": true, "reason": "Safety Gate source is not currently ingested" }
        ],
        "complete": false,
        "reason": "expected source(s) not ingested: Safety Gate",
        "filters": {
          "year": {
            "applied": "partial",
            "unfilterable": 6,
            "reason": "6 of 31 matched campaigns carry no model year range in the source: they match at brand level and the 'year' filter did not discriminate them"
          }
        }
      },
      "provenance": { "source": "nhtsa+rappelconso", "freshness": { "kind": "snapshot", "as_of": "2026-10-11T00:00:00Z" }, "…": "…" }
    },
    "critair": {
      "vehicle_class": "m1",
      "fuel": "electric",
      "critair_class": "0",
      "derivation": { "via": "fuel", "fuel": "electric" },
      "zfe_restrictions": [ "…" ],
      "provenance": { "source": "critair-regulation+zfe-open-data", "freshness": { "kind": "snapshot", "as_of": "2026-07-04T00:00:00Z" }, "…": "…" }
    },
    "co2": {
      "match_level": "model_year",
      "vehicle": { "make": "TESLA", "commercial_name": "MODEL 3", "year": 2019 },
      "co2": { "wltp": { "p50": 0, "p10": 0, "p90": 0 }, "unit": "g/km", "sample_size": 42680, "…": "…" },
      "provenance": { "source": "eea-co2-monitoring", "freshness": { "kind": "snapshot", "as_of": "2026-07-28T00:00:00Z" }, "…": "…" }
    },
    "registration_tax": {
      "coverage": "computed",
      "country": "FR",
      "registration_date": "2026-10-15",
      "co2_malus": { "status": "exempt", "amount_eur": 0, "…": "…" },
      "weight_malus": { "status": "exempt", "amount_eur": 0, "…": "…" },
      "regional_tax": { "status": "exempt", "amount_eur": 11.0, "…": "…" },
      "total_eur": 11.0,
      "provenance": { "source": "fr-registration-tax+eea-co2-monitoring", "…": "…" },
      "…": "…"
    },
    "coverage": { "complete": true, "missing": [] },
    "verdict": {
      "decision": "review",
      "reasons": [
        {
          "code": "open_recall_campaign",
          "block": "recalls",
          "decision": "review",
          "message": "31 recall campaign(s) target this make, model and model year; open sources never state whether the remedy was carried out on this VIN, so each one stays to be checked with the manufacturer network"
        }
      ]
    },
    "limits": "This dossier is built from public reference data keyed by the VIN alone. It attests neither the service history, the mileage, an outstanding lien or finance, accident or flood damage, the identity of the seller, nor a valid roadworthiness test (contrôle technique). Recall campaigns are candidates matched by make, model and model year: open sources never state whether the remedy was carried out on this VIN. Nothing here is a market value, an inspection or a purchase recommendation."
  },
  "provenance": {
    "source": "nhtsa-vpic",
    "fetched_at": "2026-10-15T15:55:05.205619514Z",
    "freshness": { "kind": "snapshot", "as_of": "2026-09-01T00:00:00Z" }
  }
}

Five blocks, five dates. The single reason is the campaign count - 31 campaigns to check with the manufacturer network, 6 of them matched at brand level - and the registration cost is computed (total_eur: 11.0, the regional fixed fee: the vehicle is exempt from both maluses and from the proportional regional tax).

Example - insufficient_coverage

GET /vehicle/prepurchase?vin=1HGCM82633A004352 - a 2003 Honda Accord, with nothing declared:

{
  "data": {
    "vin_decode": { "vin": "1HGCM82633A004352", "make": "Honda", "model_year": 2003, "attributes": { "Model": "Accord", "…": "…" }, "…": "…" },
    "recalls": { "make": "HONDA", "year": 2003, "total": 56, "…": "…" },
    "registration_tax": {
      "coverage": "insufficient_inputs",
      "reason": "provide the vehicle characteristics ('co2_wltp', 'weight_kg', 'fiscal_power', 'fuel', 'region') or a resolution key ('type_approval', or 'make' + 'model' + 'year')"
    },
    "coverage": {
      "complete": false,
      "missing": [
        { "block": "critair", "reason": "declared characteristics not provided" },
        { "block": "co2", "reason": "year 2003 is outside the ingested window (covered years: 2018-2022)" }
      ]
    },
    "verdict": {
      "decision": "insufficient_coverage",
      "reasons": [
        { "code": "registration_cost_inputs_missing", "block": "registration_tax", "decision": "insufficient_coverage", "message": "the registration cost cannot be totalled: provide the vehicle characteristics ('co2_wltp', 'weight_kg', 'fiscal_power', 'fuel', 'region') or a resolution key ('type_approval', or 'make' + 'model' + 'year')" },
        { "code": "block_unavailable", "block": "critair", "decision": "insufficient_coverage", "message": "the 'critair' block could not be produced: declared characteristics not provided" },
        { "code": "block_unavailable", "block": "co2", "decision": "insufficient_coverage", "message": "the 'co2' block could not be produced: year 2003 is outside the ingested window (covered years: 2018-2022)" },
        { "code": "open_recall_campaign", "block": "recalls", "decision": "review", "message": "56 recall campaign(s) target this make, model and model year; open sources never state whether the remedy was carried out on this VIN, so each one stays to be checked with the manufacturer network" }
      ]
    },
    "limits": "This dossier is built from public reference data keyed by the VIN alone. …"
  },
  "provenance": { "source": "nhtsa-vpic", "…": "…" }
}

The review is there, in fourth place: the coverage gaps outrank it. Each gap names what would close it - declare fuel and a date for Crit’Air, the missing figures for the cost - except the CO2 block, which no input can bring back for a 2003 model year. A 200, billed: knowing what to supply is the answer.

Example - stop

GET /vehicle/prepurchase?vin=1HGCD5635TA004352&fuel=petrol&first_registration=1996-05-02&registration_date=2026-10-15&region=11&fiscal_power=8

  • a 1996 Honda Accord, declared petrol, first registered in May 1996:
{
  "data": {
    "vin_decode": { "vin": "1HGCD5635TA004352", "make": "Honda", "model_year": 1996, "check_digit_valid": true, "attributes": { "Model": "Accord", "…": "…" }, "…": "…" },
    "recalls": {
      "make": "HONDA",
      "year": 1996,
      "total": 35,
      "coverage": {
        "filters": {
          "year": { "applied": "partial", "unfilterable": 34, "reason": "34 of 35 matched campaigns carry no model year range in the source: they match at brand level and the 'year' filter did not discriminate them" }
        },
        "…": "…"
      },
      "…": "…"
    },
    "critair": {
      "vehicle_class": "m1",
      "fuel": "petrol",
      "first_registration": "1996-05-02",
      "critair_class": "non_classe",
      "derivation": { "via": "first_registration", "euro_norm": "pre_euro", "fuel": "petrol" },
      "zfe_restrictions": [ "… 3 zones …" ],
      "…": "…"
    },
    "registration_tax": { "coverage": "insufficient_inputs", "missing_inputs": ["co2_wltp", "weight_kg"] },
    "coverage": { "complete": false, "missing": [ { "block": "co2", "reason": "year 1996 is outside the ingested window (covered years: 2018-2022)" } ] },
    "verdict": {
      "decision": "stop",
      "reasons": [
        { "code": "critair_not_eligible", "block": "critair", "decision": "stop", "message": "the vehicle qualifies for no Crit'Air sticker (non_classe): it is barred from every low-emission zone that restricts this class, listed in the critair block" },
        { "code": "registration_cost_inputs_missing", "block": "registration_tax", "decision": "insufficient_coverage", "message": "the registration cost cannot be totalled: co2_wltp, weight_kg" },
        { "code": "block_unavailable", "block": "co2", "decision": "insufficient_coverage", "message": "the 'co2' block could not be produced: year 1996 is outside the ingested window (covered years: 2018-2022)" },
        { "code": "open_recall_campaign", "block": "recalls", "decision": "review", "message": "35 recall campaign(s) target this make, model and model year; open sources never state whether the remedy was carried out on this VIN, so each one stays to be checked with the manufacturer network" }
      ]
    },
    "limits": "This dossier is built from public reference data keyed by the VIN alone. …"
  },
  "provenance": { "source": "nhtsa-vpic", "…": "…" }
}

A petrol car first registered before 1997 has no Crit’Air class (non_classe), which bars it from the low-emission zones listed in the block. That is a fact about the use the buyer intends, stated with the regulation behind it - not an opinion on the car. The other reasons stay listed, most severe first; the 34 brand-level campaigns are why the recall count reads 35 on a 1996 model.

Errors

Only failure of the anchor VIN leaves the 200 range - none of these are billed. Every verdict, stop included, is a 200 and billed: a vehicle with an open campaign or no Crit’Air class is a useful answer.

StatuscodeCase
400MISSING_PARAMETERvin absent or empty
400INVALID_VINvin not 17 characters or outside the VIN alphabet; or a wrong check digit on a North American VIN
404UNKNOWN_WMIWell-formed VIN whose manufacturer identifier is absent from the vPIC tables
500INTERNALInternal error (detail logged, not exposed)
{ "code": "INVALID_VIN", "error": "VIN must be exactly 17 characters (got 3)" }
{ "code": "UNKNOWN_WMI", "error": "WMI 'AAA' is not present in the vPIC tables" }

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 identifier.
  • Crit’Air - French regulation (Arrêté du 21 juin 2016) and the ZFE reference under the Licence Ouverte / Etalab.

See also

  • GET /vehicle/report - the same five blocks without the verdict: the block-by-block contract.
  • GET /vehicle/lookup - when the agent holds a designation (“Renault Clio V”) rather than a VIN.
  • GET /vehicle/recalls - the candidate campaigns on their own, with the filters’ scope.
  • GET /vehicle/critair - the Crit’Air class and the zone restrictions on their own.
  • For agents - discovery surfaces, the live /catalog and how settlement works.