Tracking
Carriyo keeps shipment statuses fresh so you always know where a
parcel is. From the moment a shipment leaves the warehouse through
to a terminal state (delivered, returned, cancelled),
Carriyo tracks it in the background. The recommended way to stay in
sync is webhooks, Carriyo pushes each status change to your endpoint;
you can also read the current state from the shipment record at any
time.
Two methods
Carriyo uses whichever method the carrier supports, with the real-time path preferred where available.
Carrier callbacks (real-time)
Where the carrier supports it, the carrier pushes status events to Carriyo as the shipment moves (pickup, in-transit scans, delivery), and Carriyo translates each into a Carriyo-canonical status. This is the preferred method: latency is seconds rather than minutes, with no polling overhead.
How those callbacks get wired up depends on the carrier:
- Automatic. Carriyo registers the callback with the carrier itself, nothing for you to do.
- One-time. A single configuration step, then it's live.
- Manual. You add Carriyo's callback URL on the carrier side, it's on the Carrier account under Callback Settings.
For carriers that support real-time updates, Carriyo additionally runs scheduled refreshes as a safety net to catch any events the carrier missed pushing.
Scheduled proactive refreshes
For carriers that don't expose webhook callbacks, Carriyo polls the carrier at scheduled intervals to fetch the current status. This is the fallback method: it works for any carrier with a status query API, but introduces refresh-cadence latency.
The refresh schedule for scheduled-only carriers is age-based:
| Shipment age (from booking) | Refresh frequency | What's tracked |
|---|---|---|
| 0–7 days | Hourly | Every open shipment |
| 8–14 days | Daily | Every open shipment |
| 15–60 days | Daily | Only collected shipments |
Two implications worth knowing:
- Uncollected after 14 days. If a shipment hasn't been collected by the carrier within 14 days of booking, scheduled tracking stops. This is a long-tail efficiency cut (most abandoned shipments fall into this bucket). If you ship via a scheduled-only carrier, your code should monitor for shipments stuck in pre-collection states.
- 60-day cap. Tracking ends 60 days after booking regardless of state. Long-tail returns or post-delivery exceptions after that window won't be reflected automatically.
These limits don't apply to real-time carriers. Those keep pushing for as long as the carrier itself produces events.
Consuming status updates
Two ways to read status, in order of preference:
- Webhooks (push) — recommended. Subscribe a webhook in the Dashboard to the events you care about (status changes, reason-code changes, label updates), and Carriyo pushes the full updated Shipment object to your endpoint as each event fires. This is the recommended, and effectively the primary, way to stay in sync. See Webhooks.
GET /shipments/{shipment_id}(pull) — fallback. Fetch the full Shipment object on demand. The current status is inpost_shipping_info.status, andpost_shipping_info.key_milestonesrecords when each status was reached.
What key_milestones does and doesn't capture
key_milestones is a map of status → timestamp, so it records
only the first time each status was set. If a status repeats, the
later timestamp isn't recorded, a shipment that goes
out_for_delivery → failed_delivery_attempt → out_for_delivery
keeps only the first out_for_delivery time. It also doesn't preserve
the full history: the carrier's raw status and the reason code at the
moment of each transition aren't kept.
So key_milestones answers "did this shipment reach status X, and
when did it first get there?", not "what was the full sequence of
events?" For the complete, ordered history, consume the webhook stream
and record events on your side.
How it fits with other modules
- Shipment lifecycle. The state machine that tracking events drive transitions through.
- Webhooks. The push side of tracking-event delivery.
- Pre-booked shipments.
Pre-booked shipments use the same tracking machinery via the
input_carrierto carrier-account mapping. - Carrier configuration. Whether a carrier supports real-time callbacks is per-carrier configuration.