Shipping
The shipping API covers the merchant's shipping origin, available couriers, rate quotes, and shipment lifecycle. The whole surface is a thin proxy over Fulkruma, which owns the Biteship integration; Storlaunch keeps no local shipment rows.
This API requires the fulfilment module to be enabled on the workspace. Without it, every call returns 409 fulfillment_module_disabled.
All endpoints require an sk_* key.
Endpoints
| Method | Path | Purpose |
|---|---|---|
GET |
/v1/shipping/origin |
Read the workspace's default ship-from origin |
PATCH |
/v1/shipping/origin |
Update the origin |
GET |
/v1/shipping/couriers |
List supported couriers |
POST |
/v1/shipping/rates |
Quote shipping rates for a parcel |
GET |
/v1/shipping/shipments |
List shipments |
GET |
/v1/shipping/shipments/:id |
Retrieve one shipment |
POST |
/v1/shipping/shipments/:id/cancel |
Cancel a shipment |
GET |
/v1/shipping/shipments/:id/label |
Get the printable label URL |
Read / update the origin
GET /v1/shipping/origin
PATCH /v1/shipping/origin
The origin is what every rate quote and shipment defaults to as the "ship-from" address. One per workspace.
PATCH body
| Field | Type | Required | Description |
|---|---|---|---|
address |
string (1–500) | yes | Free-form address. |
province / city / district / village |
string | no | Administrative subdivisions. |
postal |
string | no | Postcode. |
areaId |
string | no | The Biteship-area ID. Required for some couriers' instant-pickup tiers. |
lat / lng |
number | no | Decimal degrees. Used for distance-based couriers. |
note |
string | no | Free text (driver notes). |
contactName |
string (1–100) | yes | Pickup contact. |
contactPhone |
string (1–30) | yes | Pickup phone. |
couriers |
array of strings | no | Whitelist of courier codes to enable; everything else is hidden from rate quotes. |
List couriers
GET /v1/shipping/couriers
Returns the list of couriers available from the merchant's origin. Includes per-courier service types (instant, same-day, regular, cargo), the courier's logo, and whether each service supports insurance and COD.
Quote rates
POST /v1/shipping/rates
The rate quote takes a destination, parcel dimensions, and item list; returns one entry per courier-service-type combo with the price and ETA.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
origin |
object | no | Override the workspace origin (areaId or full address). |
destination |
object | yes | Address or areaId. |
couriers |
array of strings | no | Filter to these courier codes. |
items |
array | yes | [{ name, value, weight, length, width, height, quantity }]. |
insurance |
boolean | no | Force insurance — some couriers refuse if value > N. |
Response — 200 OK
{
"data": {
"pricing": [
{
"courierCode": "jne",
"courierName": "JNE",
"courierServiceCode": "reg",
"courierServiceName": "Regular",
"courierType": "regular",
"price": 18000,
"currency": "IDR",
"etaMin": 2,
"etaMax": 3,
"etaUnit": "days",
"insuranceFee": 0,
"supportsCod": true
}
]
},
"error": null,
"meta": { "requestId": "req_01HX...", "timestamp": "2026-05-13T10:42:00Z" }
}
List shipments
GET /v1/shipping/shipments?status=…
Returns shipments newest-first. Filter by status: pending, confirmed, picked_up, in_transit, delivered, cancelled, returned.
Retrieve a shipment
GET /v1/shipping/shipments/:id
The shipment object includes the chosen courier, the waybill ID once issued, the courier-side tracking URL, the current status, and timestamps for each lifecycle transition.
{
"data": {
"id": "shp_01HX...",
"waybillId": "JNE001234567",
"courierCode": "jne",
"courierServiceCode": "reg",
"status": "in_transit",
"price": 18000,
"currency": "IDR",
"origin": { /* full origin snapshot */ },
"destination": { /* full destination snapshot */ },
"items": [ /* line items */ ],
"labelUrl": "https://cdn.fulkruma.com/labels/shp_01HX.pdf",
"trackingUrl": "https://www.jne.co.id/track?awb=JNE001234567",
"externalSource": "storlaunch",
"externalRef": "cs_01HX...",
"createdAt": "2026-05-13T10:42:00Z",
"updatedAt": "2026-05-13T10:55:33Z"
},
"error": null,
"meta": { "requestId": "req_01HX...", "timestamp": "2026-05-13T10:42:00Z" }
}
Shipments are created server-side at checkout-completion time — there is no POST /v1/shipping/shipments. The checkout's chosen courier-service combo (captured in metadata.courierCode / courierService on the session) becomes the shipment's parameters.
Cancel a shipment
POST /v1/shipping/shipments/:id/cancel
Cancels with Biteship. Refund to the buyer is your responsibility — cancelling doesn't auto-refund.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
reason |
string | no | Free text. Defaults to Merchant cancelled. |
Get the label
GET /v1/shipping/shipments/:id/label
Returns { url } — the signed PDF URL for the courier waybill. 404 LABEL_NOT_AVAILABLE if the courier hasn't generated it yet (usually within seconds of confirmation; up to minutes for some carriers).
Public tracking
There isn't a public tracking endpoint under Storlaunch's API any more — Fulkruma owns it. GET /v1/shipping/track/:waybillId returns 410 MOVED with a pointer to fulkruma.com/track/:waybillId. The buyer-facing order page (/v1/storefront/public/order/:deliveryId) embeds a tracking widget that hits Fulkruma directly.
Events
Shipment-lifecycle events fire from Fulkruma, not Storlaunch. To subscribe to shipment.confirmed, shipment.picked_up, shipment.in_transit, shipment.delivered, register a webhook endpoint in Fulkruma against your merchant context there. The Storlaunch outbox stays catalogue + checkout focused.
The closest Storlaunch-native shipping signal is the metadata.hasShipping flag on product.purchased — treat that as "a shipment is about to exist".
Next
- Inventory — the variants that drive
itemsin rate quotes. - Deliveries — the buyer-facing record that wraps the shipment.
- Public storefront — how rates are surfaced at checkout.