Customer Shipments
Customer shipments are outbound orders assigned to the 3PL's warehouse. The integration polls for new shipments, acknowledges them, updates picking status, and marks them shipped with tracking. All endpoints scope to the warehouse in your JWT — see Authentication.
List customer shipments 🔗
1
GET /v1/customer-shipments.json
Returns the shipments assigned to your warehouse, filtered by lifecycle status. Use this as the polling endpoint to discover work.
Query parameters
| Name | Type | Description |
|---|---|---|
status |
string | pending (default), open, done, canceled |
page |
integer | 1-indexed page number (100 records per page) |
ids |
string | Comma-separated internal ids to filter by |
numbers |
string | Comma-separated human-readable shipment numbers |
planned_date_min |
date | Lower bound (inclusive) on planned_date |
planned_date_max |
date | Upper bound (inclusive) on planned_date |
updated_at_min |
datetime | Lower bound on last modification time |
updated_at_max |
datetime | Upper bound on last modification time |
carrier_service |
string | Filter by carrier service code |
Status meanings
pending— assigned to the warehouse, not yet acknowledged by the 3PLopen— acknowledged, still being processeddone— shippedcanceled— cancelled before shipment
Response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{
"data": [
{
"id": 100123,
"number": "CS-100123",
"company_code": "ACME",
"warehouse": { "id": 3, "code": "LAX", "name": "Los Angeles" },
"priority": "3",
"planned_date": "2026-06-20",
"customer": {
"name": "Jane Doe",
"email": "jane@example.com",
"code": "CUST-42"
},
"delivery_address": {
"address1": "123 Main St",
"city": "Los Angeles",
"zip": "90012",
"country_code": "US",
"subdivision_code": "CA"
},
"carrier_code": "ups",
"service_code": "ground",
"lines": [
{
"id": 5001,
"product": { "code": "SKU-100", "name": "Widget" },
"quantity": 2,
"uom": "u"
}
]
}
],
"has_more": false
}
Count customer shipments 🔗
1
GET /v1/customer-shipments/count.json
Same filters as the list endpoint. Returns {"count": <n>}.
Acknowledge customer shipments 🔗
1
PUT /v1/customer-shipments/acknowledge.json
Signals to Fulfil that the 3PL has received the request and committed to
fulfil it. The shipment must be in the assigned state and not already
acknowledged.
Request body
1
2
3
4
5
6
{
"shipments": [
{ "id": 100123 },
{ "id": 100124 }
]
}
Response
1
2
3
4
5
6
{
"shipments": [
{ "id": 100123, "acknowledged_at": "2026-06-17T18:33:00+00:00", "errors": null },
{ "id": 100124, "acknowledged_at": null, "errors": ["The shipment is cancelled"] }
]
}
Returns 200 if every item succeeded, 207 if some failed.
Update picking status 🔗
1
2
PATCH /v1/customer-shipments/change-status.json
POST /v1/customer-shipments/change-status.json
Update the picking status of one or more shipments. POST is a convenience alias for callers that cannot send PATCH.
Request body
1
2
3
4
5
6
{
"shipments": [
{ "id": 100123, "picking_status": "in-progress" },
{ "id": 100124, "picking_status": "done" }
]
}
Allowed transitions
in-progress— picker has starteddone— picking completeexception— picking blocked
Shipments already shipped or cancelled cannot transition. Returns 200 /
207 with per-item errors.
Mark customer shipments as shipped 🔗
1
PUT /v1/customer-shipments/ship.json
Mark shipments as shipped, attaching carrier, service, tracking, and per-line
quantities. Unlike acknowledge, this endpoint fails fast: if any item has
a problem, the entire request returns 400 and no shipments are marked
shipped.
The payload accepts either a flat lines list (converted internally into a
default single package) or a full packages[] structure supporting per-package
tracking, medium/material, and nested inner packages.
Request body
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{
"shipments": [
{
"id": 100123,
"carrier": "ups",
"carrier_service": "ground",
"ship_strategy": "ship_as_available",
"cost": 12.50,
"cost_currency_code": "USD",
"packages": [
{
"tracking_number": "1Z999AA10123456784",
"tracking_url": "https://www.ups.com/track?tracknum=1Z999AA10123456784",
"carrier_identifier": "ups",
"medium": "box",
"material": "cardboard",
"reference": "WH-PKG-1",
"override_sscc": "00614141234567890",
"items": [
{
"product_code": "SKU-100",
"quantity": 2,
"lot": { "number": "LOT-A" },
"serial_numbers": ["SN-001", "SN-002"]
}
]
}
]
}
]
}
Root-level fields
| Field | Type | Notes |
|---|---|---|
id |
integer | Required — customer shipment id |
carrier |
string | Carrier code (also accepted as carrier_code) |
carrier_service |
string | Carrier service code (also accepted as carrier_service_code) |
carrier_identifier |
string | Optional carrier identifier |
tracking_number |
string | Shipment-level tracking number (or omit and set per-package) |
cost |
number | Optional shipping cost |
cost_currency_code |
string | Required when cost uses a currency other than the company default |
ship_strategy |
string | defer_unshipped, ship_as_available (default), or ship_all_together |
lines |
array | Flat item list — mutually exclusive with packages |
packages |
array | Full package structure — mutually exclusive with lines |
Passing both lines and packages returns 400.
ship_strategy values
ship_as_available(default) — available items are shipped immediately in their own split shipment with a unique tracking number; unshipped items remain in the original awaiting more advicesdefer_unshipped— ship what's available now; move the rest into a new deferred shipmentship_all_together— hold everything until all items are ready; tracking numbers accumulate on the same shipment, no splits
Package fields
| Field | Type | Notes |
|---|---|---|
tracking_number |
string | Package-level tracking |
tracking_url |
string | Optional URL for the tracking page |
carrier_identifier |
string | Optional carrier identifier for the package |
medium |
string | Package medium (see supported values in Fulfil) |
material |
string | Package material |
reference |
string | Optional external reference |
override_sscc |
string | Override the auto-generated SSCC label |
items |
array | Line items in the package (required) |
consumables |
array | Non-inventory consumables used |
children |
array | Nested inner packages — makes this a parent/pallet package. Mutually exclusive with items at this level. |
Item fields
At least one of product_code, product_id, or sale_line_id is required.
quantity is always required and must be ≥ 1.
| Field | Type | Notes |
|---|---|---|
product_code |
string | SKU |
product_id |
integer | Internal product id |
sale_line_id |
integer | Internal sale line id (for cross-dock, options) |
quantity |
number | Required, ≥ 1 |
lot.number |
string | Lot number for lot-tracked SKUs |
serial_numbers |
array of string | Serial numbers for serial-tracked SKUs |
sale_line_cross_dock_location_code |
string | Overrides the shipment's cross-dock location for this line |
Consumable fields (per-package, non-inventory items like dunnage, labels)
| Field | Type | Notes |
|---|---|---|
product_code |
string | Required |
quantity |
number | Required, ≥ 1 |
Response
1
2
3
4
5
{
"shipments": [
{ "id": 100123, "shipped_at": "2026-06-17T18:33:00+00:00" }
]
}
Deprecated fields
split(boolean) — legacy alternative toship_strategy.falsemaps toship_all_together,truemaps toship_as_available. Logs a deprecation warning. Useship_strategyin new integrations.
Report a failure 🔗
1
2
PATCH /v1/customer-shipments/failed.json
POST /v1/customer-shipments/failed.json
Tell Fulfil that the 3PL could not process specific customer shipments (for
example: integration error, missing inventory). Pass an error string per
item to surface the reason in Fulfil's UI.
Request body
1
2
3
4
5
{
"shipments": [
{ "id": 100123, "error": "Out of stock at picking station" }
]
}
Response
1
2
3
4
5
{
"shipments": [
{ "id": 100123, "tpl_status": "sending_error", "errors": null }
]
}
Returns 200 / 207 with per-item errors.
Get SSCC labels 🔗
1
GET /v1/customer-shipments/{shipment_id}/sscc.json
Generate SSCC (Serial Shipping Container Code) labels for the shipment and each of its packages. Returns signed URLs to PDF labels.
Response
1
2
3
4
5
6
7
{
"id": 100123,
"label_url": "https://.../sscc-shipment-100123.pdf",
"packages": [
{ "id": 88, "label_url": "https://.../sscc-package-88.pdf" }
]
}