Shipping and fulfilment records

The flat shipping fee and how it is charged, creating shipment records, assigning them to orders, shipping addresses, and the tracking timeline the customer sees.

5 min readUpdated 3 August 2026shipping, fulfilment, tracking, addresses

Shipping in this store is two unrelated things that share a name, and confusing them is the fastest way to mis-price your catalogue:

  1. The shipping fee — one flat amount, set once for the whole store, added to a checkout that contains a physical product. This is what the customer pays.
  2. Shipment records — your own log of a consignment: load id, carrier, vehicle, weight, status. This is what you track. Nothing in a shipment record affects what anyone was charged.

The shipping fee

Two settings, on /admin/ecommerce/settings:

Whether shipping is charged at all. Off means physical orders ship free.
The flat amount added to a checkout containing a physical product.

How it is applied:

  • Once per checkout, not once per line. A three-item cart is charged one shipping fee. This matters because a checkout produces one order per product, so the fee lands on one of those orders and the others read zero.
  • Only for physical products. A cart of downloadable items is never charged shipping, whatever the setting says.
  • A FREE_SHIPPING discount zeroes it for that checkout. See Discounts.
  • It is charged in the product's currency and wallet type, like everything else on the order.

There are no shipping zones, no per-country rates, no weight bands, no per-product overrides and no free-shipping threshold. Every physical checkout pays the same number regardless of where it is going or what is in it.

If your real costs vary, either price that variance into the products or absorb it. There is no setting that will do it for you.

Where the money goes

Shipping and tax are credited to the platform wallet separately from store revenue, and deliberately not recorded as profit. You still owe the carrier and the tax authority; counting them as margin would overstate every physical order.

They appear on the ledger as an ORDER_PASSTHROUGH credit against the order. Cancelling an order reverses that credit at the same time it refunds the buyer, so a cancellation does not leave you holding shipping you never spent.

Shipment records

Admin → E-commerce → Sales → Shipping (/admin/ecommerce/shipping).

A shipment is a free-text record of a physical consignment. Every field is typed in — there is no carrier API, no rate lookup and no label printing.

Field Required Notes
loadId Yes Your reference or the carrier's tracking number. Free text
loadStatus Yes PENDING, TRANSIT, DELIVERED or CANCELLED
shipper Yes Who is sending it
transporter Yes The carrier
vehicle Yes Vehicle or service identifier
goodsType Yes What class of goods
description Yes Free text
weight Yes Numeric, unitless — pick a unit and be consistent
volume Yes Numeric, unitless
cost No What the shipment costs you, not what the buyer paid
tax No Tax on that cost
deliveryDate No Expected or actual delivery

They are recorded for your own reconciliation and never appear on an order, an invoice or a refund. The customer's shipping charge comes from ecommerceDefaultShippingCost and is stored on the order row.

weight and volume carry no units anywhere in the product. Decide on kilograms and cubic metres (or pounds and cubic feet), write it in your internal notes, and stick to it — otherwise the column is noise.

Assigning a shipment to an order

Shipments are created independently, then linked. Open the order at /admin/ecommerce/order/<id> and assign an existing shipment. That writes the shipment's id to the order's shippingId.

The relationship is many orders to one shipment, which is genuinely useful: create one shipment for a courier run and attach every order on it. Advancing that shipment's status moves the tracking view for all of them at once.

Assigning requires edit.ecommerce.order.

Shipping addresses

Each order can hold one address: name, email, phone, street, city, state, postal code, country.

  • On checkout, the address the buyer typed is saved — but only for physical products, and only if the storefront sent one. Digital-only checkouts have no address.
  • In admin, you can add one to an order that has none or correct one that is wrong.

Phone numbers are normalised to canonical E.164 (+ followed by digits, capped at 15) on both paths, so whatever format the buyer typed is accepted and stored consistently. Do not try to pre-format it.

The checkout endpoint treats the address as optional. If an order arrives with nothing to ship to, add it from the order screen before you mark the order complete — there is no validation that will stop you completing an unaddressable order.

What the customer sees

Tracking. GET /api/ecommerce/order/{id}/track returns the order id, its status, the assigned shipment (load id, load status, shipper, transporter, vehicle) and a timeline assembled from both. Customers reach it from their order page.

The timeline is only as good as your discipline: an order marked COMPLETED with a shipment still sitting at PENDING tells the buyer nothing useful.

Their shipments list. /ecommerce/shipping shows every shipment attached to one of their orders, with the order, its items and the address. It is scoped to the signed-in user by the order join — a customer cannot see anyone else's consignments.

It is a buyer-facing route gated on authentication alone. If it ever answers 403 for ordinary customers, an admin permission has been attached to it by mistake — the User role does not hold access.ecommerce.shipping and never will.

A working fulfilment loop

  1. Batch the day's PENDING orders. Filter the orders table by status.

  2. Check every one has an address. Add missing ones now, not after the van has gone.

  3. Create one shipment per consignment. Load id from the carrier, status PENDING, real weight and volume, your own cost in cost.

  4. Assign it to each order it covers.

  5. Mark those orders COMPLETED. This sends the status-update email and closes the order. Remember it is a one-way door — do it when the goods have actually left.

  6. Advance the shipment to TRANSIT when it is collected and DELIVERED when it lands. Customers see both.

  7. If a consignment is lost, set the shipment to CANCELLED and decide separately what to do with the orders. Cancelling the order refunds the buyer in full and puts stock back — which is usually right, but it is a money decision, not a logistics one.

Turning shipping off

Set ecommerceShippingEnabled to off and physical orders stop being charged a fee. Everything else keeps working: shipment records, assignment, addresses and tracking are all independent of the setting. It is a perfectly reasonable way to run a store with free delivery priced into the products.

Next: Discounts or Store settings.