Fulfillment orders
A fulfillment order captures one allocation decision: a subset of an Order's lines, the location those lines ship from, and the destination they're going to. It sits between the Order (what the customer bought) and the Shipment (what actually ships).
An Order can produce one or many fulfillment orders. The split happens whenever the lines diverge on any of three dimensions:
- Fulfillment location. Different warehouses, stores, or dark stores.
- Delivery method.
DELIVERY(carrier delivery to an address),COLLECTION(customer pickup from a collection point), orDIGITAL(no physical delivery; fulfilled digitally). - Destination. Different delivery or collection addresses, even within the same delivery method.
Each fulfillment order then produces one or more Shipments when it's booked.
Why this exists as a distinct object
It would be possible to model directly from Order to Shipment, skipping the middle layer. Carriyo doesn't. Real operations have a step between "customer wants this" and "this is being shipped": the moment when allocation decisions are made and locked.
The fulfillment order is that decision. It has:
- A reference back to the Order.
- A subset of the order's lines (the ones for this location).
- A fulfillment location.
- A status of its own (
open,allocated,processing,fulfilled,closed,cancelled). - A reference forward to the Shipment(s) that fulfil it.
Putting allocation in its own object means:
- Allocation can be revised before booking. Change the fulfillment location, move lines between fulfillment orders.
- Pre-booking holds work. The fulfillment order can sit in
allocatedwhile ops review or while inventory is reserved. - Reporting is clean. Fulfillment-side metrics (per-location throughput, allocation accuracy) are held on fulfillment orders, separate from shipment-side metrics.
Delivery method and destination
Each fulfillment order carries a delivery method and a destination. The destination field name depends on the method:
| Delivery method | Destination field |
|---|---|
DELIVERY | delivery_address: where the carrier delivers the parcel. |
COLLECTION | customer_collection_address: where the customer picks up the parcel. |
DIGITAL | (no physical destination; the order is fulfilled digitally) |
Two fulfillment orders on the same Order can use the same
delivery method with different destinations (for example, two
DELIVERY FOs going to two different addresses), or different
methods entirely (one DELIVERY, one COLLECTION).
Delivery option and allocation
A fulfillment order can carry a delivery_option, typically
the shipping option the customer chose at checkout. You pass it
back to Carriyo when the Order is created (see POST /orders).
The allocation engine uses this as input when picking a
fulfillment location.
When a delivery option has associated fulfillment locations, allocation narrows its search to that subset rather than scanning the full pool of locations with inventory. The customer's choice at checkout therefore constrains where the order can be fulfilled from.
What a fulfillment order produces
What a fulfillment order produces depends on its delivery method and, for collections, on whether the customer is collecting from the fulfillment location itself or from a different location.
| Delivery method | Scenario | Entities created |
|---|---|---|
DELIVERY | Standard | One Shipment (sometimes more, when an FO needs to split across consignments). |
COLLECTION (local) | Customer collects from the fulfillment location. | Customer collection only; the parcel doesn't move. |
COLLECTION (remote) | Customer collects from a different location. | Shipment (fulfillment location → collection point) plus customer collection (handover at the collection point). |
DIGITAL | Any | No Shipment or customer collection; the fulfillment is digital. |
Customer collections are created automatically as part of the fulfillment process when the client uses the Fulfillment App. For the Collection lifecycle (ready → OTP → collected, plus expiry, unexpire, and cancel), see Customer collection.
A Shipment, when one is produced, inherits:
- The fulfillment location (as the Shipment's pickup location).
- The lines (as items on the shipment).
- The destination:
delivery_addressforDELIVERY, orcustomer_collection_address(the collection point) for a remoteCOLLECTION. - The merchant.
The Shipment has data the fulfillment order doesn't: tracking number, label, carrier-specific references. Those come from the booking call, which is what turns a fulfillment order into a Shipment.
Status flow
A fulfillment order's status is computed from the statuses of its line items.
open → allocated → processing → fulfilled → closed
↘ cancelled (alternative terminal)
| Status | Meaning |
|---|---|
open | No fulfillment location assigned yet. |
allocated | Fulfillment location has been assigned. |
processing | At least one line item is in pick / pack / fulfilled. |
fulfilled | All line items reached a terminal state, with at least one fulfilled. |
closed | All line items are closed (typically once the Shipment is delivered). |
cancelled | All line items were cancelled. |
For the full criteria see Fulfillment order status codes; the leaf state that drives this is documented in Line item status codes.
Pick and pack
Inside Carriyo's Fulfillment App, the journey from allocated
to fulfilled runs through two intermediate workflows:
- Pick. Staff retrieve the line items from inventory at the fulfillment location.
- Pack. Picked items are assembled into one or more packages and prepared for handover.
Both workflows produce their own line-item states
(pick_in_progress, picked, pack_in_progress) before the
line item reaches fulfilled.
These workflows are internal to the Fulfillment App. If you
don't use the app (for example, your warehouse runs on a WMS),
you bypass them by recording fulfillment directly via
POST .../fulfill.
Line items move straight from allocated to fulfilled and
the Pick and Pack states are skipped.
For the full set of intermediate states, see Line item status codes.