Shipping Module — Storefront
HTTP surface for customer-side shipment tracking — the timeline of provider-emitted events for a sub-order the customer placed. Read-only.
HTTP surface for customer-side shipment tracking — the timeline of provider-emitted events for a sub-order the customer placed. Read-only.
Source:
api-modules/shipping/src/controllers/store-shipping-tracking.controller.ts.Provider configuration, vendor-side shipment management, and admin operations live in sibling docs. The shipping module follows a two-layer model: the customer pays a per-vendor flat rate at cart time (plugin-free), and the vendor assigns a concrete provider at the pending→fulfilled transition (plugin-driven). Customers observe the latter via this tracking endpoint.
Conventions
Authentication
| Endpoint | Auth |
|---|---|
GET /store/shipping/orders/:id/tracking | required (customer session) |
The endpoint resolves scope through order.customer_id — sub-orders for other customers return 404 Not Found, never 403 (no row leak).
Response envelope
{
"data": <payload>,
"metadata": { "total", "limit", "offset", "hasMore" },
"message": "Success",
"statusCode": 200
}Error envelope
statusCode | errorCode examples |
|---|---|
| 400 | VALIDATION_ERROR |
| 401 | UNAUTHORIZED |
| 404 | NOT_FOUND |
| 500 | INTERNAL_SERVER_ERROR, DATABASE_ERROR |
Domain types
ShippingNormalizedStatus
The mapper normalizes every provider's event code into a small, stable set:
type ShippingNormalizedStatus =
| "pending"
| "in_transit"
| "out_for_delivery"
| "delivered"
| "failed"
| "returned";The raw statusCode from the provider is preserved alongside the normalized value so the UI can render provider-specific copy. delivered is the trigger that lets the order module auto-stamp order_vendor.delivered_at (and, for COD, the parent payment_status -> paid).
ShippingEventResponse
type ShippingEventResponse = {
id: string;
providerId: string; // e.g. "clickpost", "self-handled"
externalEventId: string | null; // provider's id for the event, when present
statusCode: string; // raw provider code
normalizedStatus: ShippingNormalizedStatus;
payload: Record<string, unknown>; // raw provider event payload (for debugging/forensics)
receivedAt: string; // ISO — when the system received the event
};The payload field is the raw provider payload as received — useful for support to introspect courier-reported timestamps, locations, and reasons. The storefront UI typically renders the timeline off normalizedStatus + receivedAt and uses payload only for an expandable "raw event" view.
Endpoints
GET /store/shipping/orders/:id/tracking — Tracking timeline for one of my sub-orders
Newest event first. Paginated.
Path params
| Name | Notes |
|---|---|
id | order_vendor.id — the sub-order id (not the parent order id) |
Query
| Name | Type | Default | Notes |
|---|---|---|---|
page | int | 1 | >= 1 |
limit | int | 50 | 1..200 |
Response 200 — paginated ShippingEventResponse[].
{
"data": [
{
"id": "01J9...",
"providerId": "clickpost",
"externalEventId": "evt_abc123",
"statusCode": "OFD",
"normalizedStatus": "out_for_delivery",
"payload": {
"city": "Bengaluru",
"remarks": "Out for delivery",
"occurredAt": "2026-05-13T08:15:00Z"
},
"receivedAt": "2026-05-13T08:16:42.122Z"
}
],
"metadata": { "total": 4, "limit": 50, "offset": 0, "hasMore": false }
}Errors
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_ERROR | page / limit out of range |
| 404 | NOT_FOUND | Sub-order does not exist or belongs to another customer |
Related modules
order— the sub-order id (order_vendor.id) comes fromOrderResponse.vendorBreakdowns[].id. ThefulfillmentStatuslifecycle that this timeline annotates lives there. Seeorder.md.shipping-clickpost/shipping-self-handled— the concrete provider plugins emit the events this endpoint reads.
Settings Module — Storefront
HTTP surface for the storefront to read public, platform-wide settings (branding, contact details, currency hints, store toggles, etc.). The whole storefront-public configuration…
Wishlist Module — Storefront
HTTP surface for the customer wishlist — one wishlist per customer ("My Wishlist"), keyed at the variant level. The default wishlist is lazy-materialized on first access so the…