How live tracking gets pulled
A matched order has a carrier and a tracking number, but those are just a promise until something checks them. This post is about the lookup: how the responder calls the carrier’s live tracking, turns a dozen carrier-specific status codes into one honest picture, and refuses to make anything up when the carrier doesn’t answer.
Key takeaways
- A matched order carries a carrier and a tracking number; the tracking Lambda turns those into a live status.
- Each carrier’s API speaks its own dialect; the Lambda normalises every response into one small set of statuses.
- Carrier keys live in Secrets Manager, one per carrier, never in code or the sheet.
- Results are cached briefly in DynamoDB so ten “any update?” texts about one parcel make one carrier call, not ten.
- If the carrier is slow, errors, or has no scan, the responder reports the truth or escalates — it never fills the gap with a guess.
From an order to a status
By the time this step runs, the matcher has handed over one order, and that order has two fields that matter here: a carrier (“Royal Mail”, “DPD”, “Evri”) and a tracking number. Everything else — the items, the customer, the order date — is context for the reply, not for the lookup. The tracking Lambda’s whole job is to take that carrier-and-number pair and come back with one honest answer: where is this parcel, and when will it arrive?
It does that by calling the carrier’s own tracking API. That call is the only thing in the entire system that knows the real state of a parcel. The sheet might say “shipped”; that just means a label was printed. The carrier’s API knows whether it was actually scanned, whether it’s sitting in a depot, whether it went out for delivery this morning, or whether it was handed over three days ago and signed for. The responder treats that API response as the single source of truth and reports nothing the API didn’t say.
Many dialects, one vocabulary
Every carrier’s API is different. They authenticate differently, they shape their JSON differently, and — most awkwardly — they all have their own list of status codes. One carrier’s “OFD” is another’s “Out for delivery” is another’s numeric 17. If that mess leaked into the reply, customers would get a different-sounding answer depending on which courier you happened to use.
So the Lambda has a thin adapter per carrier that does two things: makes the authenticated call, and maps the carrier’s status onto one small shared vocabulary. The reply writer in Part 4 only ever sees that shared vocabulary:
- In transit — accepted and moving, with the last scan location and time, and the carrier’s estimated delivery date if it gives one.
- Out for delivery — on a vehicle for delivery today.
- Delivered — with the delivery time and, where the carrier provides it, who signed or where it was left.
- Exception — the carrier is flagging a problem: a failed delivery, a held parcel, an address issue, a return to sender.
- No movement — the label exists but the carrier has no scan, or hasn’t scanned it in days.
Adding a carrier means writing one more adapter — one function that knows that carrier’s auth and its status table — and nothing downstream changes. The reply writer and the escalation logic only ever reason about the five normalised states, never about a particular courier’s codes.
Caching, keys, and silence
Two practical things make this lookup cheap and safe. First, a short cache. The same parcel often gets asked about more than once — the customer texts, then emails an hour later, then a partner asks too. Each carrier-and-number result is written to a small DynamoDB table with a short time-to-live (a handful of minutes). A second enquiry about the same parcel inside that window reads the cache instead of calling the carrier again. Carriers update their tracking every few hours, not every few seconds, so a few minutes of cache costs the customer nothing in accuracy and saves a pile of API calls on a busy day.
Second, every carrier credential lives in Secrets Manager, one secret per carrier, fetched at call time and never written into code, environment variables, or the sheet. Rotating a key is a Secrets Manager update, not a deploy.
And then the part that matters most — what happens when the carrier doesn’t answer. APIs time out, return errors, or come back with a tracking number they’ve simply never scanned. In every one of those cases the responder does the same thing: it does not invent a status. A timeout or error escalates with a note that the carrier couldn’t be reached. A “no movement” result — a label with no scan, or no scan in days — is exactly the kind of stuck order that should reach a person, so it escalates too (Part 5). The one thing the responder will never do is tell a customer “it’s on its way” when the carrier never said so.
Design rules that shaped the tracking lookup
- The carrier API is the only source of a status. Nothing else — not the sheet, not a model — gets to say where a parcel is.
- Normalise to a small shared vocabulary. Five states, so the reply reads the same whichever courier carried it.
- One adapter per carrier. Adding a courier is one function; nothing downstream changes.
- Cache briefly. Repeat asks about one parcel make one carrier call, not ten.
- Keys in Secrets Manager. One secret per carrier, rotated without a deploy.
- Silence is not a status. Timeouts, errors, and no-scan parcels escalate — the responder never fills the gap.