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.

Response200 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 items in rate quotes.
  • Deliveries — the buyer-facing record that wraps the shipment.
  • Public storefront — how rates are surfaced at checkout.