Endpoints
Operation reference for purchase orders, orders, shipments, inventory, and returns — with sample requests and responses.
All paths below are relative to your environment base URL (e.g.
https://api.staging.canopywms.com) and require a bearer token with the listed
scope. See Authentication and
Conventions first.
Purchase orders
Create purchase orders with your own external PO number, update them, cancel
them, and retrieve their received quantities and status. Purchase orders are
modeled as inbound shipments under /api/receiving.
| Operation | Method & path | Scope |
|---|---|---|
| Create PO | POST /api/receiving/shipments | receiving:write |
| List / search POs | GET /api/receiving/shipments | receiving:read |
| Retrieve PO | GET /api/receiving/shipments/:id | receiving:read |
| Update PO | PATCH /api/receiving/shipments/:id | receiving:write |
| Cancel PO | POST /api/receiving/shipments/:id/cancel | receiving:write |
| PO history | GET /api/receiving/shipments/:id/history | receiving:read |
curl -X POST https://api.staging.canopywms.com/api/receiving/shipments \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Idempotency-Key: 4d8f...-create-po-PO-2026-00184" \
-H "Content-Type: application/json" \
-d '{
"externalPoNumber": "PO-2026-00184",
"vendorName": "Sunbelt Co-Packers",
"expectedArrival": "2026-07-05",
"expectedCartons": 40,
"lines": [
{ "skuId": "sku_sriracha17", "expectedQuantity": 240 },
{ "skuId": "sku_ketchup20", "expectedQuantity": 300 }
]
}'{
"success": true,
"data": {
"id": "inb_9f2a...",
"shipmentNumber": "RCV-20260629-00031",
"externalPoNumber": "PO-2026-00184",
"status": "PENDING",
"vendorName": "Sunbelt Co-Packers",
"expectedArrival": "2026-07-05T00:00:00.000Z",
"totalExpected": 540,
"totalReceived": 0,
"totalDamaged": 0,
"lines": [
{ "skuId": "sku_sriracha17", "expectedQuantity": 240, "receivedQuantity": 0, "status": "PENDING" },
{ "skuId": "sku_ketchup20", "expectedQuantity": 300, "receivedQuantity": 0, "status": "PENDING" }
]
}
}Retrieve by external PO number via the list filter, and by internal id via the path:
curl "https://api.staging.canopywms.com/api/receiving/shipments?externalPo=PO-2026-00184" -H "Authorization: Bearer $ACCESS_TOKEN"
curl "https://api.staging.canopywms.com/api/receiving/shipments/inb_9f2a..." -H "Authorization: Bearer $ACCESS_TOKEN"As the warehouse receives the PO, the retrieved record reflects progress:
status moves PENDING → IN_PROGRESS → COMPLETED, and totalReceived /
per-line receivedQuantity fill in. A purchase_order.received webhook fires
when receiving completes (see Webhooks).
Each list row also reports totalExpected, totalReceived, and totalDamaged,
and the available filters are externalPo, shipmentNumber, status,
vendorName, dateFrom/dateTo, updatedSince, and search.
Orders
Ingest orders, manage their lifecycle (hold / release / cancel), and retrieve them by your order number or sync them incrementally.
| Operation | Method & path | Scope |
|---|---|---|
| Create / ingest order | POST /api/orders | orders:write |
| List / search orders | GET /api/orders | orders:read |
| Retrieve order | GET /api/orders/:id | orders:read |
| Update order | PATCH /api/orders/:id | orders:write |
| Hold / release | PATCH /api/orders/:id/hold · /release | orders:write |
| Cancel / requeue | PATCH /api/orders/:id/cancel · /requeue | orders:write |
| Mark shipped / delivered | POST /api/orders/:id/mark-shipped · /mark-delivered | orders:write |
| Add / update / remove line | POST·PATCH·DELETE /api/orders/:id/lines/... | orders:write |
| Order history | GET /api/orders/:id/history | orders:read |
curl -X POST https://api.staging.canopywms.com/api/orders \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Idempotency-Key: 7a2c...-order-CC-10042" \
-H "Content-Type: application/json" \
-d '{
"orderNumber": "CC-10042",
"priority": "STANDARD",
"customerName": "Emma Thompson",
"customerEmail": "emma.thompson@example.com",
"shippingMethod": "Standard",
"shippingAddress": { "name": "Emma Thompson", "line1": "210 Congress Ave", "city": "Austin", "state": "TX", "zip": "78701", "country": "US" },
"lines": [ { "skuId": "sku_sriracha17", "quantity": 2, "unitPrice": 6.49 } ]
}'Retrieve by your order number, filter by status, or pull everything changed since your last sync:
curl "https://api.staging.canopywms.com/api/orders?orderNumber=CC-10042" -H "Authorization: Bearer $ACCESS_TOKEN"
curl "https://api.staging.canopywms.com/api/orders?status=SHIPPED&updatedSince=2026-06-29T00:00:00Z&sortBy=updatedAt&sortOrder=asc" -H "Authorization: Bearer $ACCESS_TOKEN"The retrieved order exposes its status, lines (with
quantityAllocated/quantityPicked/quantityPacked), tracking events, and a
full edit history. An order.created webhook fires on ingest and order.shipped
when it ships.
Orders that originate from a connected channel (e.g. Shopify) also carry the
channel's order id, which is queryable via ?externalOrderId=.
Shipments
Buy carrier labels and retrieve shipment identifiers, tracking, carrier/service, and the actual historical label cost.
| Operation | Method & path | Scope |
|---|---|---|
| Create label | POST /api/shipping/labels | shipping:write |
| List shipments | GET /api/shipping/shipments | shipping:read |
| Retrieve shipment (+ live tracking) | GET /api/shipping/shipments/:id | shipping:read |
| Live tracking | GET /api/shipping/shipments/:id/track | shipping:read |
| Void label | POST /api/shipping/shipments/:id/void | shipping:write |
| Shipped items (per-SKU) | GET /api/shipping/shipped-items | shipping:read |
{
"success": true,
"data": {
"id": "shp_3c1b...",
"shipCode": "SHP-10231",
"orderId": "ord_9a8b...",
"orderNumber": "CC-10042",
"trackingNumber": "1Z999AA10123456784",
"carrierService": "UPS Ground",
"shippingCost": 8.42,
"rate": 8.02,
"tax": 0.40,
"totalPaid": 8.42,
"weight": 52,
"shipDate": "2026-06-28T15:10:00.000Z",
"status": "IN_TRANSIT",
"deliveredAt": null,
"tracking": {
"status": "in_transit",
"events": [ { "status": "in_transit", "description": "Departed facility", "occurredAt": "2026-06-28T22:01:00Z" } ]
}
}
}The GET /api/shipping/shipped-items report breaks shipping down per SKU,
exposing quantityOrdered, quantityShipped, trackingNumber,
carrierService, and the actual shippingCost per shipment — useful for
reconciling outbound volume and freight spend.
Inventory
Read stock levels by SKU and by location.
| Operation | Method & path | Scope |
|---|---|---|
| List SKUs with stock | GET /api/inventory/skus | inventory:read |
| Retrieve a SKU | GET /api/inventory/skus/:id | inventory:read |
| Stock by location | GET /api/inventory/stock | inventory:read |
GET /api/inventory/skus returns each SKU's totalStock, allocated,
available, backordered, locationCount, dailyVelocity, and daysOfStock.
GET /api/inventory/stock returns one row per bin — binLabel, zoneName,
warehouseName, quantity, isPickable, lotNumber, and expiresAt.
Returns
Read returns (RMAs) and their lines.
| Operation | Method & path | Scope |
|---|---|---|
| List returns | GET /api/returns | returns:read |
| Retrieve a return | GET /api/returns/:id | returns:read |
Each return exposes its rmaNumber, status, linked orderNumber, and per-line
status. return.completed and return.cancelled webhooks fire on those
transitions.