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:
code | Block | Decision |
|---|---|---|
critair_not_eligible | critair | stop |
vin_check_digit_invalid | vin_decode | stop |
recall_sources_unavailable | recalls | insufficient_coverage |
block_unavailable | the missing block | insufficient_coverage |
registration_cost_inputs_missing | registration_tax | insufficient_coverage |
registration_cost_out_of_schedule_range | registration_tax | insufficient_coverage |
open_recall_campaign | recalls | review |
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_coverageoutranksreview, 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 isinsufficient_coverage, and thereviewis still listed beneath it.okrequires no reason at all: all five blocks produced, no campaign targeting the vehicle, a Crit’Air class obtained, a registration cost computed. Neverokon 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
vin | string | yes | A 17-character VIN over the VIN alphabet (no I, O, Q) |
fuel | string | no | Declared fuel for the Crit’Air block and the tax costing (electric, petrol, diesel…) |
euro_norm | string | no | Declared emission standard (pre_euro, euro1..euro6) |
first_registration | string | no | Declared date of first registration (YYYY-MM-DD) - fixes the Crit’Air class when no norm is given |
registration_date | string | no | The date that chooses the tax schedule (YYYY-MM-DD) - see below |
vehicle_class | string | no | m1 (default) or n1 |
region | string | no | French region (INSEE code or exact name) - needed for the registration cost |
weight_kg | int | no | Declared mass, when the CO2 block cannot resolve it |
fiscal_power | int | no | Fiscal power in CV - a French record, absent from the EU registry; needed for the registration cost |
GET /vehicle/prepurchase?vin=5YJ3E1EA0KF000316&fuel=electric®ion=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:
| Block | Source | Present when |
|---|---|---|
vin_decode | NHTSA vPIC | always - it is the anchor |
recalls | NHTSA + RappelConso | the decoded make is known to the recall sources |
co2 | EEA CO2 monitoring (model years 2018-2022) | vPIC resolves a model and the year is in the window |
registration_tax | French tax code + EEA figures | always - computed, or naming what it is missing |
critair | Crit’Air regulation + ZFE open data | fuel 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®ion=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®istration_date=2026-10-15®ion=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.
| Status | code | Case |
|---|---|---|
| 400 | MISSING_PARAMETER | vin absent or empty |
| 400 | INVALID_VIN | vin not 17 characters or outside the VIN alphabet; or a wrong check digit on a North American VIN |
| 404 | UNKNOWN_WMI | Well-formed VIN whose manufacturer identifier is absent from the vPIC tables |
| 500 | INTERNAL | Internal 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
/catalogand how settlement works.