Two tracking pages, one parcel, two different answers. A customer emails you a screenshot of a competitor's page showing a scan yours doesn't have, and asks why your order status is "behind".
I work on 24hTrack, a free multi-carrier tracking site, and this is the support ticket we get in every language. It is almost never a data bug. It is four assumptions that quietly break once a parcel crosses a border — and all four show up in code, not just in support.
1. A tracking record is a snapshot, not a feed
The carrier is the only writer. Everything downstream — your order page, your 3PL dashboard, any tracking site — is a copy of what the carrier had published at the moment that copy was made. Two copies made minutes apart can legitimately disagree.
So "their page is ahead of ours" usually means their copy is newer, not that one of you is wrong. Design your UI for that: show when you last looked, not just the status. A timestamp on the page kills more WISMO tickets than an extra status string, because it answers the question the customer is actually asking.
2. One shipment is often two numbers
This is the big one. On a cross-border order, one company carries the parcel out of the country it was posted in, and the postal carrier where the buyer lives delivers it — frequently under a second, local number the first carrier never sees.
The handover is visible in the data. On Belgian postal items we see lines like:
Export customs clearance started
Departure from office of exchange
Left from departure country/region
Arrival in the country of destination
After Arrival in the country of destination, the original number often goes quiet forever. Nothing is broken; the parcel simply stopped being that carrier's to move. If your UI treats "no new events for N days" as an exception, you will raise false alarms on every international order that is doing fine.
Model a shipment as one or more tracking legs, not one number. If you can only store one, at least don't present its silence as a failure.
3. The last two letters are the issuing country — not the destination
S10 is the UPU format: two letters, nine digits, two letters. The trailing pair identifies the postal operator that issued the number.
I have seen this used as a destination field more than once — routing a shipment to a "Belgium" bucket because the number ends in BE, when the parcel is going to Brazil. It also drives a support script that tells customers they have the wrong number.
// S10 (UPU) postal item number: 2 letters + 9 digits + 2 letters.
// The LAST two letters are the ISSUING country, not the destination.
const S10 = /^([A-Z]{2})(\d{9})([A-Z]{2})$/;
function parseS10(raw) {
const s = String(raw).toUpperCase().replace(/[\s-]/g, '');
const m = S10.exec(s);
if (!m) return null;
return { service: m[1], serial: m[2], issuedBy: m[3] };
}
Name the field issuedBy. Not country. Not destination. The name is the whole fix.
4. One carrier can have more than one number format
Length-based validation is the most common way a valid number gets rejected at the door. Canada Post publishes two formats on its own support pages: 16 digits for items delivered inside Canada, and 13 characters (S10) for items going to the US and internationally, plus prepaid envelopes and labels.
If your input accepts "a Canada Post number" as 16 digits, every prepaid-label and international customer hits an error on a number that is perfectly valid.
function looksLikeCanadaPost(raw) {
const s = String(raw).toUpperCase().replace(/[\s-]/g, '');
if (/^\d{16}$/.test(s)) return 'domestic-16-digit';
const p = parseS10(s);
if (p && p.issuedBy === 'CA') return 'international-s10';
return null;
}
Running the two together over a mixed bag:
1111 1111 1111 1111 | s10: no | canadaPost: domestic-16-digit
AA111111111CA | s10: AA+9+CA | canadaPost: international-s10
UL000000000BE | s10: UL+9+BE | canadaPost: no
CP000000000IE | s10: CP+9+IE | canadaPost: no
LX000000000NL | s10: LX+9+NL | canadaPost: no
1Z999AA10123456784 | s10: no | canadaPost: no
Note the last line: a UPS-shaped number is not S10 and not Canada Post, and that is the correct answer — null, not an error.
The rule underneath all four
Shape tells you very little, and never block a lookup on it. Detection should return carrier, unknown, or ambiguous — and unknown should still run the lookup rather than throwing a validation error at the customer. A person holding a real number does not care that it failed your regex.
What to show instead
- The timestamp of the last check, next to the status.
- The newest line, not just a normalised status bucket. "Electronic information submitted by shipper" means the label exists and the carrier does not have the parcel yet — a completely different support action from "In transit".
- An explicit note at the handover point: the final leg may be another company.
If you want to sanity-check a number against a lot of carriers without wiring anything up, you can paste it on 24hTrack — it detects the carrier from the number itself, free and without an account. There is also a REST API and an MCP server if you want this inside your own app or an AI assistant.
Numbers quoted here are medians and shares measured on our own platform, and the Canada Post formats come from Canada Post's published support pages.
Written with AI assistance. I work on 24hTrack.
Top comments (0)