๐ฌ Questions or feedback? Join the discussion on the Home Assistant community.
A custom Home Assistant integration that tracks your PostNL shipments and announced MyMail letters.
- Features
- Requirements
- Installation
- Configuration
- Options
- Removal
- Sensors
- Parcel status reference
- Events
- Examples
- Debugging
- Troubleshooting
- Related integrations
- Disclaimer
- Contributing
- License
- Incoming and outgoing active-parcel count sensors
- Per-parcel sensor per active incoming shipment, with full status details as attributes
- Optional per-parcel status history timeline (opt-in; off by default)
- Configurable delivered-parcels sensor (last N days, or N most recent)
- Next delivery datetime sensor (device class
timestamp) - PostNL Punt sensor โ parcels destined for a PostNL Point pickup location
- MyMail letters sensor plus a per-letter image entity holding the scanned photo
- Automatic lifecycle management โ per-parcel sensors are created and removed as parcels move through delivery
- Re-authentication support โ silently refreshes the PostNL token, prompts only when the refresh fails
- Home Assistant 2024.7 or newer
- A PostNL account (the credentials you use on jouw.postnl.nl / the PostNL mobile app)
- Both Netherlands and Belgium PostNL accounts are supported โ they share the same backend, so your parcels come through either way. (MyMail letter scanning is a Netherlands-only feature and stays empty for Belgian mail.)
- Open HACS โ Integrations โ โฎ โ Custom repositories
- Add this repository URL and select category Integration
- Search for PostNL and install it
- Restart Home Assistant
- Copy the
postnlfolder into yourconfig/custom_components/directory - Restart Home Assistant
- Go to Settings โ Devices & Services โ Add Integration
- Search for PostNL
- Enter your PostNL email address and password
- Choose how you want the delivered parcels sensor to filter (last N days, or N most recent)
- Click Submit
| Field | Description |
|---|---|
| The email address of your PostNL account. | |
| Password | The password for that account. Stored in the HA config entry and refreshed automatically when the integration triggers a re-authentication. |
Click Configure on the integration card. The form is split into three sections:
| Option | Description |
|---|---|
| Filter by | Days keeps delivered parcels visible for the last N days. Number of parcels keeps only the N most recent regardless of age. |
| Amount | The N used by the filter above. |
| Option | Description |
|---|---|
| Include status history | Adds a history attribute to each parcel โ the ordered list of status updates (timestamp, canonical status, original PostNL text), capped to the most recent 20. Off by default. The attribute is kept out of the recorder database. |
| Option | Description |
|---|---|
| Refresh every | How often the integration checks PostNL. Choices: 15 / 30 / 60 / 120 / 240 minutes โ default 30. A slower interval is gentler on PostNL's API. Changes take effect immediately, no HA restart needed. |
Standard HA removal applies: Settings โ Devices & Services โ PostNL โ โฎ โ Delete. No PostNL-side cleanup is needed; deleting the config entry stops the polling. To revoke API access entirely, change your PostNL account password โ the integration will trigger a re-auth notification, which you can then ignore.
The integration creates one device per PostNL account, named
PostNL (<your-email>). With multiple accounts each gets its own
device named after its email. The entities below show the
friendly-name pattern; their entity_ids carry the same account suffix:
| Friendly name pattern | Description |
|---|---|
PostNL (account) Incoming parcels |
Number of active incoming parcels |
PostNL (account) Parcel <barcode> |
Canonical status of a single incoming shipment |
PostNL (account) Next delivery |
Earliest expected delivery datetime |
PostNL (account) En route to PostNL Point |
Active incoming parcels destined for a PostNL Point pickup location |
PostNL (account) Delivered parcels |
Recently delivered incoming parcels (configurable window) |
PostNL (account) Outgoing parcels |
Number of active outgoing parcels |
PostNL (account) Outgoing delivered parcels |
Recently delivered outgoing parcels (same configurable window) |
PostNL (account) Letters |
Letters announced by PostNL's MyMail service over the last ~2 weeks; unread count and letters list on attributes |
PostNL (account) Letter <title> (image entity) |
Scanned photo of a single announced letter, fetched with your token and served through Home Assistant. Attributes mirror the sensor's letter dict: id, title, date, unread |
Every parcel exposed on a sensor attribute uses a carrier-agnostic shape:
| Key | Type | Meaning |
|---|---|---|
carrier |
string | "PostNL" |
barcode |
string | Parcel tracking number |
sender |
string | null | Sender name (e.g. webshop) |
receiver |
string | null | Recipient name |
status |
ParcelStatus |
Canonical status โ see the status reference |
raw_status |
string | null | Original PostNL status description (a Dutch human-readable string) |
delivered |
bool | Whether the parcel has been delivered |
delivered_at |
ISO 8601 | null | Delivery moment, if known |
planned_from |
ISO 8601 | null | Expected delivery window start |
planned_to |
ISO 8601 | null | Expected delivery window end |
pickup |
bool | Destined for a PostNL Point rather than a home address |
pickup_point |
string | null | PostNL Point name when pickup is true (always null for now โ PostNL does not expose the field) |
url |
string | null | Deep link to the parcel's tracking page on jouw.postnl.nl |
weight |
float | null | Parcel weight in kilograms. May be null for delivered parcels or when PostNL has not yet attached the data. |
dimensions |
dict | null | Parcel dimensions in centimeters: {length, width, height, text} where text is a pre-formatted "L x W x H cm" string. Same coverage as weight. |
history |
list | null | Ordered status timeline (oldest โ newest), each entry {timestamp, status, raw_status}, capped to the most recent 20. null unless the Parcel history option is enabled โ see Options. |
raw |
dict | The original PostNL payload |
Each announced letter is exposed as an image entity so the scan
loads directly in dashboards and notifications. See
examples/automations/notify_when_letter_arrives.yaml
for a ready-to-paste notification.
status on every parcel is one of the canonical ParcelStatus values
below. Use these in your automations rather than PostNL's raw Dutch
description โ the raw value stays available on raw_status for power
users.
status |
Meaning | PostNL signal that maps here |
|---|---|---|
registered |
PostNL knows about the label but the parcel is not yet in transit | statusPhase.message containing "aangemeld" or "verwacht" |
in_transit |
Picked up; somewhere in PostNL's network | statusPhase.message containing "onderweg", "ontvangen" or "gesorteerd" |
out_for_delivery |
On the delivery vehicle today | statusPhase.message containing "wordt vandaag bezorgd", "onderweg naar het bezorgadres" or "onderweg naar de bezorger" |
at_pickup_point |
Arrived at the chosen PostNL Point, ready to be collected | statusPhase.message containing "ligt klaar bij postnl punt" or similar |
delivered |
Handed over (mailbox, recipient, neighbour, picked up) | shipment.delivered == true (authoritative); fallback statusPhase.message containing "bezorgd" |
returning |
Failed delivery, on the way back to the sender | statusPhase.message containing "retour" or "teruggestuurd" |
unknown |
Raw description we have not mapped yet | anything else โ logged once at warning level with a ready-to-paste issue link so it can be added to the map |
Because PostNL's statusPhase.message is a human-readable Dutch string
(not a stable API enum), the mapping uses ordered substring matching
โ so minor wording variants still resolve correctly. If you see an
unknown for a status the integration ought to recognise, open an
issue with the raw value (visible in the integration debug logs and on
the parcel sensor under raw_status).
The coordinator fires events on the HA event bus when something interesting happens to a parcel, so automations can react without polling per-parcel sensors.
| Event | When | Payload |
|---|---|---|
postnl_parcel_registered |
A new barcode appears in the active list | The full parcel dict (see the table above) |
postnl_parcel_status_changed |
A known barcode's status value changes |
Same payload plus old_status and new_status |
postnl_parcel_delivery_time_changed |
A known barcode's expected delivery time changes to a new value | Same payload plus old_planned_from, new_planned_from, old_planned_to, new_planned_to |
postnl_outgoing_parcel_status_changed |
A known outgoing parcel (something you sent, or a return) changes status, except the final hop to delivered | Same payload plus old_status and new_status |
postnl_outgoing_parcel_delivered |
An outgoing parcel reaches the recipient | The full parcel dict |
postnl_letter_announced |
A new letter appears in the MyMail feed | The letter dict (id, title, date, unread, image_url) plus carrier: "PostNL" |
Every payload also carries a device_id identifying the PostNL account
the parcel or letter belongs to, so automations can tell two accounts apart.
Events do not fire for parcels or letters that were already in your account when HA first started.
If you build automations in the UI, these same events are also available as no-code device triggers (Settings โ Automations โ Create โ Add trigger โ Device), scoped to the selected account's device. The raw events above are there for templates and YAML automations.
See examples/automations/ for ready-to-paste
event-driven automations, or the
parcel aggregator
for a carrier-agnostic re-emit layer that fires
parcel_aggregator_parcel_* events covering every installed carrier
in one go.
Ready-to-paste automations and dashboard cards live in examples/.
Third-party cards that work with this integration's sensors:
To capture verbose information about the PostNL API responses (useful when reporting a bug or helping map a new status value), enable debug logging for the integration:
- Add this to your
configuration.yaml:logger: default: warning logs: custom_components.postnl: debug
- Restart Home Assistant.
- Wait for the next poll cycle (or reload the integration from Settings โ Devices & Services โ PostNL โ โฎ โ Reload).
- Open Settings โ System โ Logs, filter for
postnl, and copy the relevant log lines (including theShipments fetched: ...summary and anyTrack and Trace response: ...payload) into your bug report or message to the maintainer.
| Symptom | Likely cause |
|---|---|
invalid_auth error during setup |
Wrong email or password |
cannot_connect error during setup |
PostNL API is unreachable; check your network |
| Re-authentication prompt appears | PostNL session expired and could not be refreshed silently; log in again |
| Sensors disappear after delivery | Expected โ delivered parcels move to the delivered sensor (visible window controlled by the options filter) |
| Sensors not updating | Check Settings โ System โ Logs for postnl entries |
Tracking parcels from other Dutch carriers:
| Integration | Description |
|---|---|
| ha-dhl-nl | DHL eCommerce NL parcel tracker. |
| ha-dpd | DPD parcel tracker. |
| ha-gls | GLS Netherlands parcel tracker โ no account, you enter tracking numbers yourself. |
| ha-parcel-aggregator | Rolls up counts and next-delivery timestamps from all installed carrier integrations into a single set of sensors. |
This is an independent, community-built project with no affiliation, endorsement, or connection to PostNL or any of its subsidiaries. The PostNL API used here is undocumented (reverse-engineered from the mobile app and jouw.postnl.nl) and may change without notice. The maintainers have not asked PostNL for permission to use this API; installing this integration may breach PostNL's Terms of Service. You take any risk that follows โ account suspension, service disruption, etc. No warranty (see LICENSE).
This fork is maintained by @peternijssen.
The original integration is by
@arjenbos โ fixes that apply to both
forks are filed as PRs against the upstream
arjenbos/ha-postnl.
Pull requests and issues are welcome. Please open an issue before submitting a large change.
MIT