Orders & fulfilment
Tracking Number Sync
Indicative price, not an offer: $199–399/year (bundled with carrier rates)
Generated from docs/plan/paid/tracking-number-sync/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
Polls carrier APIs for tracking status updates and pushes them back to orders and shipments. Customers see up-to-date tracking status in storefront. Merchants see carrier delivery confirmations in admin.
The problem
Customers ask ‘where is my package?’ and I have no idea because tracking lives only in the carrier’s system, not mine. I manually copy tracking status into emails.
What it does
- Tracking status entity: tracking_event_id, shipment_id, status (in_transit, out_for_delivery, delivered, failed, returned), carrier_timestamp, location, last_updated
- Scheduled sync: every 6 hours (or per-order on-demand), query carrier APIs for all orders with tracking_number and is_shipped=true
- Update tracking events: carrier returns array of events (picked_up, in_transit, out_for_delivery, delivered); store each as TrackingEvent
- Customer visibility: storefront displays latest tracking status and events in order detail
- Delivery confirmation webhook: when status = delivered, fire order.delivered_confirmed webhook
- Delivery photo (if carrier provides): link to carrier’s photo URL stored in tracking event
- Exception handling: if status = ‘delivery_failed’ or ‘returned_to_sender’, fire order.delivery_exception webhook for merchant action
- Retry logic: if sync fails for an order, retry up to 3 times with exponential backoff
- Admin dashboard: show all orders grouped by tracking status (in_transit, out_for_delivery, delivered_today, exceptions)
- Webhook notification to customer: send email when status changes (e.g., ‘Package out for delivery’) via email layer
What it deliberately does NOT do
Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.
- Real-time sync (WebSocket to carrier): out of scope—polling every 6h is sufficient. Reason: carrier APIs don’t offer WebSocket; polling is standard.
- Exception management workflow: out of scope—system sends webhook; merchant’s ERP handles follow-up. Reason: exception handling is merchant-specific (redeliver, return to warehouse, contact customer).
- Carrier-specific exception codes mapping: out of scope—raw carrier status stored as-is. Reason: each carrier has different codes; merchant interprets via webhook.
- Multi-carrier shipments (same order split across FedEx + UPS): out of scope—one shipment = one carrier. Reason: deferred to multi-warehouse feature.
- SMS notifications to customer: out of scope—email only. Reason: SMS requires opt-in and carrier setup.
- Signature proof upload: out of scope—carrier provides signature image URL; system stores link only. Reason: no direct integration with delivery confirmation.
Data model
Migration: new TrackingEvent table/collection (event_id, shipment_id, status, carrier_timestamp, location_city, location_zip, carrier_event_timestamp, carrier_event_id, created_at). Add last_tracking_sync, tracking_status to Shipment. Assumption: Shipment already has carrier and tracking_number fields.
API
- GET /orders/:id/tracking → {shipment_id, tracking_number, carrier, status, last_event, events: [{status, timestamp, location, carrier_info}]}
- POST /shipments/:id/sync-tracking (staff or system, manual trigger) → {status, last_sync_at, new_events_count}
- GET /shipments/:id/tracking-events → [{event_id, status, timestamp, location, carrier_event_id}]
- GET /admin/tracking-dashboard → {in_transit_count, out_for_delivery_count, delivered_today_count, exceptions_count, by_carrier: {…}}
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
Order detail shows Tracking section: carrier, tracking number (clickable link to carrier), current status, timestamp, location. Events list showing all updates with dates. Admin dashboard shows summary cards (In Transit, Out for Delivery, Delivered Today) and exception list (Failed, Returned).
The seam — why this is paid
Core owns tracking sync interface, event storage, and polling schedule. Paid pack owns: exception management workflows, SMS notifications, real-time sync (WebSocket), multi-carrier orchestration. Why: core provides honest read-only polling and event logging; exception workflows and SMS are paid.
Part of carrier integration credential flow. Requires polling carrier tracking status and reconciling against order shipments. Often bundled with rate APIs.
Dependencies
- live-carrier-rate-apis (carrier account credentials for API queries)
- shipping-label-generation (shipment has tracking_number from label generation)
- shipments-with-tracking-and-carrier-hooks (shipment entity must exist)
Acceptance checks
Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.
- Sync queries carrier API for order with tracking_number=‘1234567890’, carrier=‘ups’; receives events array with timestamps
- First sync creates 3 TrackingEvents: picked_up, in_transit, out_for_delivery; status on shipment is ‘out_for_delivery’
- Second sync (6h later) receives new event ‘delivered’; new TrackingEvent created; status updated; order.delivered_confirmed webhook fired
- GET /orders/123/tracking shows status=‘delivered’, last_event with timestamp and location
- Storefront order detail displays ‘Out for Delivery as of [time]’ and event list with all historical updates
- Sync failure (e.g., carrier API down): order retried; if all 3 retries fail, order marked tracking_sync_error; admin alert shown
- Exception event (status=‘delivery_failed’): order.delivery_exception webhook fired with reason (e.g., ‘address not found’)
- Customer receives email ‘Your package is out for delivery’ when tracking_status transitions to out_for_delivery
- Manual sync trigger POST /shipments/ship_123/sync-tracking returns new_events_count=1 if 1 new event
- Sync for order without tracking_number returns 400 with ‘shipment.no_tracking_number’
Risks
Polling frequency: every 6h might miss events if carrier delays sync; merchant sees old status. Webhook fire: if delivery_confirmed webhook fails, merchant doesn’t know order is delivered. Customer email: if email send fails, customer doesn’t get notification (retry needed but out of scope). API rate limits: if many orders sync at once, carrier API might rate-limit; backoff must be exponential. Data staleness: if sync fails and is not retried, merchant has stale tracking for days.
Commercial context
| Suggested price | $199–399/year (bundled with carrier rates) |
| Rival anchor | Magento’s carrier suite includes bidirectional tracking sync and automated customer notifications. |
The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.
Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full
catalogue and ../../AI-GUIDE.md for how to work on this repository.