One tag, the right door.
A drop-in form for Dutch and Belgian addresses. Postcode and number fill in the street and city from the official registers, the shopper picks the toevoeging or bus from the ones that exist, and your form gets a carrier-ready address with RightDoor's verdict. No integration code, no proxy on your server.
1. Quickstart
- In the dashboard, open API keys and create a publishable key. List the sites it may be used on, like
https://shop.example. A test key works anywhere, localhost included. - Add the script and the element to your form.
- Read the fields when the form is submitted, or listen for the change event.
HTML
<script src="https://js.rightdoor.eu/v1/address.js" async></script>
<form action="/checkout" method="post">
<rightdoor-address key="rd_pk_live_…" countries="NL,BE"></rightdoor-address>
<button>Continue</button>
</form>2. Your own fields, or ours
Our fields. An empty <rightdoor-address> creates labelled fields in your page, in the order shoppers know: NL postcode, number and toevoeging, then street and city; BE postcode, street, number, bus and municipality. They submit with your form:
| Name | Value |
|---|---|
postcode | The postcode as the register writes it (2513 AA, 1000) |
house_number | The number |
addition | NL toevoeging or BE bus, if any |
street | The street, from the register |
city | The place, from the register |
address1 | Street, number and addition on one line |
country | NL or BE |
rightdoor_status | valid, corrected, ambiguous, invalid, unverified or incomplete |
rightdoor_receipt | The signed receipt (see section 5) |
rightdoor_session | The session id of this address |
Your fields. Put your own inputs inside the element and mark them with data-rightdoor. The form adds the lookup, the picker and the messages, and writes the register's values back into your inputs (with input and change events, so React and Vue see them).
Attach to your inputs
<rightdoor-address key="rd_pk_live_…" country="NL" theme="none">
<input name="zip" data-rightdoor="postcode" autocomplete="shipping postal-code">
<input name="number" data-rightdoor="house-number">
<input name="addition" data-rightdoor="addition">
<input name="street" data-rightdoor="street">
<input name="city" data-rightdoor="city" autocomplete="shipping address-level2">
<input type="hidden" name="receipt" data-rightdoor="receipt">
<div data-rightdoor="slot"></div> <!-- where the picker and messages go -->
</rightdoor-address>Both modes keep the inputs in your page itself, not in an iframe or a shadow root, so the browser's saved addresses and password managers fill them as usual.
3. Attributes
| Attribute | What it does |
|---|---|
key | Your publishable key (rd_pk_live_… or rd_pk_test_…). Required. |
country | NL or BE. Default NL. |
countries | NL,BE adds a country picker. |
lang | nl, fr, de or en. Default: the page's language, else Dutch. |
theme | rightdoor (default), minimal, or none (no stylesheet at all). |
name-prefix | Prefix for our field names, like shipping_. |
autocomplete-section | shipping (default) or billing, for the browser's autofill. |
carrier | postnl, bpost, dhl or sendcloud: carrier-specific field limits. |
4. The result
JavaScript
const form = document.querySelector("rightdoor-address");
form.addEventListener("rightdoor:change", (event) => {
const { status, complete, address, carrier, receipt } = event.detail;
// status: valid | corrected | ambiguous | invalid | unverified | incomplete
});
form.getValue(); // the same, any time| Status | Meaning | What to do |
|---|---|---|
| valid | The address exists as entered. New builds count too. | Ship it. |
| corrected | A small mistake was fixed in the fields. | Ship the corrected address. |
| ambiguous | The number has several additions and none is picked yet. | Wait: the form asks the shopper. |
| invalid | Not in the register. The shopper may tick that it's correct (customerConfirmed). | Your call: flag the order, ask again, or accept it. |
| unverified | RightDoor couldn't check (see section 6). The shopper typed it by hand. | Accept it and check it again later. |
| incomplete | Not all parts are filled in yet. | Nothing yet. |
The result also has carrier (street, number and addition apart, as PostNL, bpost, DHL and Sendcloud want them), issues with messages in the shopper's language, and blocking: advice, true only for hard format errors. The form itself never stops a submit; you decide what blocks.
5. Trusting it on your server
Anything a browser sends can be changed on the way. Two ways to be sure on your server, without trusting the fields alone:
- Verify the receipt. Every checked address comes with a receipt signed by RightDoor, valid for 24 hours. It costs nothing and needs no API call.
- Or validate again with your secret key and the same
sessionId: it then counts as the same verification.
Node.js, Bun or Deno
import { createRemoteJWKSet, jwtVerify } from "jose";
const keys = createRemoteJWKSet(new URL("https://api.rightdoor.eu/.well-known/jwks.json"));
// receipt: the rightdoor_receipt field your form received
const { payload } = await jwtVerify(receipt, keys, {
issuer: "https://api.rightdoor.eu",
audience: "rd_pk_live_AbCdEf12", // your publishable key without its last part
algorithms: ["EdDSA"],
});
// Then check that the address you received is the one RightDoor checked:
// payload.address = { countryCode, address1, address2, zip, city }
// payload.status, payload.confidence, payload.sub (the session id)6. When RightDoor can't answer
If RightDoor doesn't answer within 1.5 seconds, answers with an error, refuses the key or the key reached its daily cap, the fields quietly become an ordinary form and the result is unverified. The shopper is never stuck, and nothing blocks the submit. Check those orders later with the API.
7. Content Security Policy
| Directive | Add |
|---|---|
script-src | https://js.rightdoor.eu |
style-src | https://js.rightdoor.eu (not with theme="none") |
connect-src | https://api.rightdoor.eu |
No unsafe-inline, no unsafe-eval, no frames. The form builds its fields without HTML strings, so it also runs under Trusted Types.
8. Versions
/v1/address.js is always the newest 1.x: fixes reach your page within minutes, and nothing changes for you. Breaking changes only ever come in a new major version, at a new address (/v2/), so you choose when to move.
9. Styling
The default theme is a stylesheet of a few kB, set with CSS variables on the element: --rd-af-accent, --rd-af-text, --rd-af-muted, --rd-af-border, --rd-af-bg, --rd-af-error, --rd-af-radius, --rd-af-font and --rd-af-gap. With theme="none", your own CSS styles everything (the fields have rd-af- classes).
CSS
rightdoor-address {
--rd-af-accent: #0a7c55;
--rd-af-radius: 6px;
--rd-af-font: "Inter", sans-serif;
}10. Keys, limits and billing
- Publishable keys are public by design. A live key only works from the origins you list (https, exact, or
https://*.example.comfor subdomains), and only for lookups, suggestions and single validations. - One address is one verification: every lookup, suggestion and the validation of one address entry share a session. At most 50 calls per session.
- A daily cap per key (default 1,000 verifications), with an email at 80% and 100%. At the cap the form falls back to manual entry until midnight UTC.
- Test keys are free on every plan, work from any site including localhost, 100 calls a day.
- The form is included in every plan, the free plan too.
11. Privacy
The form sets no cookies, uses no local storage, and sends no analytics or fingerprints. It talks to api.rightdoor.eu only, and only once the shopper types an address. RightDoor compares the address with the public registers and forgets it: nothing is stored or logged. For your customers' addresses, you are the controller and RightDoor the processor (data processing agreement).