Vehicle lookup module
Module id: vehicle_lookup. Dependencies: none.
Turns a license plate into vehicle facts. A visitor types 12-AB-CD,
the field fills in the brand, model and year, and those values arrive
with the form submission alongside the rest of the answers.
Built for quote requests, tyre finders and trade-in forms, so it has no dependency on the shop modules. Forms are core, and this works on a plain content site.
Country coverage
Coverage is provider-driven, and today there is exactly one provider:
| Country | Provider | Cost |
|---|---|---|
| NL | RDW open data | Free, no contract |
| others | none yet | Reported as unsupported |
A country with no provider is not an error: the endpoint reports it as unsupported and the field stays a plain text input, so a theme that offers a country picker degrades rather than breaking.
What operators set
Module settings → Vehicle lookup:
| Setting | Default | What it does |
|---|---|---|
| Countries | NL |
Which countries the site offers. A plate for a country not in this list resolves to "not found". |
| Cache TTL | 86400 (24h) | How long a resolved plate is reused before the registry is asked again. |
| Expose raw | off | Include the provider's raw record in the lookup response. Diagnostics only, see below. |
| Provider options | empty | Per-country provider credentials. RDW needs none. |
Leave "expose raw" off in production. It puts the registry's full record into a response any visitor can request, which is far more about the vehicle (and by extension its owner) than a form needs. It exists to diagnose a provider mapping, not to feed a theme.
The cache is per website. Lowering the TTL costs more lookups without making anything more accurate, since registry data changes on the order of months.
The lookup is advisory, always
Nothing about this module can block a submission. Four things can go wrong and all four end the same way, with the visitor submitting a form that has a plate and no vehicle facts:
- the plate is unknown or implausible;
- the country is not offered on this site;
- the registry is unreachable;
- JavaScript is off or the request is rate limited.
Design the form on that basis. If your process genuinely needs a brand and model, ask for them as fields the visitor can fill in themselves; do not mark the lookup's output as required.
The same point from the other direction: the hidden brand/model/year values are filled in by the browser, so a visitor can edit them. They are good enough to route a quote request. They are not evidence. A process that must be sure has to look the plate up again server-side.
Guard rails
The lookup endpoint is public and unauthenticated, so:
- It 404s when the module is off. The field's JS feature-detects on that and leaves the input as plain text.
- It is rate limited per IP (sliding window), which matters most on a paid provider where each call costs money.
- Answers are cached per website for the configured TTL.
- A miss returns
200 {"found": false}, not a 404, because 404 is reserved for "this endpoint does not exist here".
What a theme has to do
One helper, inside a normal form:
{{ form_label('license_plate', 'Kenteken') }}
{{ form_licenseplate('license_plate') }}
{{ form_licenseplate('license_plate', { country: 'NL', placeholder: '12-AB-CD' }) }}
It renders the visible plate input plus four hidden companions,
license_plate_brand, _model, _year and _country, which the field
JS fills from the lookup. They are registered in the signed form schema,
so they post with the form and land in the submission log next to
everything else. There is no second endpoint and no separate storage to
go and read.
The field ships its own JS (vehicle.js) only on pages that render one.
With the module off, the same call renders a bare text input: no data hooks, no hidden fields, no JS. One shared theme therefore serves licensed and unlicensed sites without branching.
Public routes
| Route | Name | What it does |
|---|---|---|
GET /vehicle/lookup |
cms_vehicle_lookup |
?plate=12-AB-CD&country=NL → vehicle facts |
GET /vehicle/countries |
cms_vehicle_countries |
Which countries this site offers |
Both 404 when the module is off.
Overridable templates
None. The module renders a form field, not a page, and everything about its appearance is the theme's own markup and CSS.