Start here. Every create names a facility, and this is where the identifier comes from. Facility ids are stable, so this is a setup step — store the mapping rather than calling it on every write.
FACILITY_NOT_CONFIGURED.capabilities field to check and no
facility you may use for one direction but not the other. The same facilityId
works on an inbound create and an outbound one.1–200, default 50. Echoed back in page.limit so you can see what was
applied.
Opaque. Pass page.nextCursor back here for the next page. There is no
page number — a cursor encodes where you were in the sort, so rows inserted ahead of
you cannot move you. An expired or unparseable cursor is refused, never silently
restarted at page one.
This is the value you put on an order. Stable for the life of the facility, and the only facility identifier the API accepts.
The facility's name, for display.
The operator's own short code — the same value your EDI carries, which is
what makes this list matchable against the facility codes you already use. Match on it
once at setup; submit the facilityId.
line1, line2, city, state,
postalCode, country — each nullable. A nested object,
not flattened fields.
The facility's own zone. A requested ship date is a calendar date and means that day here — which is why it is never sent as an instant.
GET /mdm/ext/v1/facilities?limit=50 Authorization: Bearer <token>
{
"data": [
{
"facilityId": "019faa4e-4b53-7594-9b19-8f717ebf5b6d",
"name": "Distribution Center 1",
"code": "DEV-SHIP-01-DC1",
"address": {
"line1": "4400 Fisher Road",
"line2": null,
"city": "Columbus",
"state": "OH",
"postalCode": "43228",
"country": "US"
},
"timezone": "America/New_York"
},
{
"facilityId": "019faa4e-9c21-70b8-a4e7-2d1f6b83c907",
"name": "Distribution Center 4",
"code": "DEV-SHIP-01-DC4",
"address": {
"line1": "1200 Vista Boulevard",
"line2": "Building C",
"city": "Sparks",
"state": "NV",
"postalCode": "89434",
"country": "US"
},
"timezone": "America/Los_Angeles"
}
],
"page": {
"limit": 50,
"nextCursor": null,
"hasMore": false
}
}
Add a SKU to your item master so orders and ASNs can name it. Until an item exists here, every line referencing it is refused.
Your item number, exactly as you will send it on orders. Max 80 characters.
Short description, shown to warehouse staff picking it. Max 50 characters.
longDescription takes the full text.
The smallest unit you count this item in. Everything in units nests above
it. It cannot be changed once stock exists — the ledger is denominated in it.
How the item packs above its base unit. Omit it and the item can only be ordered in
baseUom.
The unit code — CS, PL, BX.
How many of parentUom are in one of these.
The unit this one contains. A chain of these forms the hierarchy — PL
holds CS holds EA.
Scope this packing to one facility. Omit it to apply everywhere — packing often differs by site.
The unit goods normally arrive in. isStorageUnit is the unit they are
put away in.
What the warehouse must capture when handling this item.
Capture a lot on receipt and pick. Required if you want lot-level traceability.
Capture an expiry date, and pick earliest-expiry-first.
Capture a serial per unit. Slows receiving considerably — only for items that need it.
Used to flag stock approaching expiry.
Storage and handling constraints the warehouse honours.
With tempRangeMin, tempRangeMax and tempUom
to state the band.
Alongside stackable, keepDry and
orientationLock.
Hazmat. fdaRegulated, controlledSubstance and
deaSchedule cover the regulated cases.
Barcode, if the warehouse scans it.
With currency and unitCostUom. Used for valuation reporting,
never for pricing an order.
| Code | Meaning |
|---|---|
| 201 | Created. |
| 200 | The SKU already existed and was updated. |
| 400 | A field is wrong, or a unit chain does not resolve to baseUom. |
POST /mdm/ext/v1/items Authorization: Bearer <token> { "sku": "2117", "description": "Threadlock adhesive, 50ml", "baseUom": "EA", "units": [ { "uom": "CS", "parentUom": "EA", "quantityPerParent": 40, "isReceivingUnit": true }, { "uom": "PL", "parentUom": "CS", "quantityPerParent": 24, "isStorageUnit": true } ], "tracking": { "lotControlled": true, "expiryTracked": true, "shelfLifeDays": 365 }, "handling": { "keepDry": true } }
{
"sku": "2117",
"baseUom": "EA",
"status": "ACTIVE",
"units": [
{ "uom": "CS", "contains": "40 EA", "verified": true },
{ "uom": "PL", "contains": "24 CS", "verified": true }
]
}
Your item master, with the units each SKU can be ordered in. Use it to check a SKU exists before you submit, rather than discovering it in a refusal.
units list and is verified. Anything else
is refused, because converting on an unconfirmed factor ships the wrong quantity.Return the single item with this SKU. Omit for a page of your items.
Only items changed since this instant — for keeping a local cache in step.
Page marker from the previous response.
GET /mdm/ext/v1/items?sku=2116 Authorization: Bearer <token>
{
"data": [
{
"sku": "2116",
"description": "Threadlock adhesive, 250ml",
"baseUom": "EA",
"lotControlled": true,
"expiryTracked": true,
"units": [
{ "uom": "EA",
"perParent": null,
"verified": true },
{ "uom": "CS",
"contains": "40 EA",
"verified": true },
{ "uom": "PL",
"contains": "24 CS",
"verified": false }
],
"status": "ACTIVE"
}
]
}
Send an item's complete new state. Use this to correct a description, add packaging, or change how an item is tracked.
PATCH.sku and
baseUom. The ledger is denominated in the base unit, so changing it would
silently reinterpret every quantity already on hand. A 409 says so
explicitly.Identical to POST /items, with
sku required as the identifier.
| Code | Meaning |
|---|---|
| 200 | Replaced. |
| 404 | No item with that SKU. Nothing is created. |
| 409 | You changed baseUom on an item that has stock. |
PUT /mdm/ext/v1/items Authorization: Bearer <token> { "sku": "2116", "description": "Threadlock adhesive, 250ml", "baseUom": "EA", // adding the PL level that was missing "units": [ { "uom": "CS", "parentUom": "EA", "quantityPerParent": 40 }, { "uom": "PL", "parentUom": "CS", "quantityPerParent": 24 } ], "tracking": { "lotControlled": true, "expiryTracked": true } }
{
"sku": "2116",
"status": "ACTIVE",
"units": [
{ "uom": "CS", "contains": "40 EA", "verified": true },
{ "uom": "PL", "contains": "24 CS", "verified": true }
]
}
Retire a SKU you no longer ship, or bring one back.
Which item.
ACTIVEINACTIVE
Why, for your own audit trail.
| Code | Meaning |
|---|---|
| 200 | Status changed. |
| 400 | Unrecognised status, or the body carried item content. |
| 404 | No item with that SKU. |
PATCH /mdm/ext/v1/items Authorization: Bearer <token> { "sku": "2612", "status": "INACTIVE", "reasonCode": "DISCONTINUED" }
{
"sku": "2612",
"status": "INACTIVE",
"onHandRemaining": 180,
"message": "No new orders accepted. Existing stock is unaffected."
}
Submit an outbound order for fulfilment. We accept it, return the order id synchronously, and dispatch it to the warehouse. One call is the whole handoff — there is nothing else to send.
orderNumber is required and must be unique for your account. Re-posting
the same reference replaces the order rather than creating a second one.Your order number. Max 128 characters. This is the order's identity — everything you do later refers to it.
The facility to ship from. Must be one your account is assigned to, or the order is
refused with a 403.
What to ship. At least one line.
Must exist in your item master. An unknown SKU is refused immediately, naming the line.
Quantity, expressed in uom.
The unit you are ordering in. EACSPL Defaults to the item's own unit. We never convert it — the unit you send is the unit we track.
Ask for a specific lot. Unusual — leave it empty and the warehouse picks the lot at pick time. Split one SKU across several lines to request more than one lot.
Expiry of the requested lot. Only meaningful alongside lot.
Your line number. A string, not an integer — we echo it back on every event about this line.
Price per uom.
Item text as you sent it. Kept verbatim and never overwritten from our records.
Where it ships to. Supply exactly one — an order ships to a single destination.
Consignee name.
Street address. line2 takes a suite or unit.
Along with region, postal and country,
completes the address.
Business or residential. Affects how the shipment is tendered.
Delivery contact, with phone and email.
When the consignee wants it delivered.
When the order was actually raised. Reporting sorts on this, not on when we received it — so a backdated order reads correctly.
When you want it to ship.
Do not ship after this date.
Your customer's purchase order number. customerReference carries their own
reference and is not part of order identity.
Free text.
Whether the warehouse may substitute an equivalent item. Defaults to false.
Free text for the warehouse.
| Code | Meaning |
|---|---|
| 202 | Accepted. Order id returned immediately; fulfilment continues asynchronously. |
| 400 | A field or line is wrong. The response names the offending line. |
| 403 | Your credential cannot write orders, or names a facility you are not assigned to. |
| 429 | Over your rate limit. Wait for Retry-After seconds. |
POST /outbound/ext/v1/orders Authorization: Bearer <token> { "orderNumber": "SO-884213", "facilityId": "019faa4e-4b53-…-8f717ebf5b6d", "customerPo": "PO-55120", "orderedAt": "2026-08-14T08:03:11Z", "requestedShipDate": "2026-08-18", "lines": [ { "sku": "2116", "orderedQty": 480, "uom": "EA", "lineNumber": "1", "lot": "L2026-0814-A", "lotExpiry": "2027-02-28" }, { // no lot — the warehouse picks it "sku": "2020B", "orderedQty": 12, "uom": "CS", "lineNumber": "2" } ], "destinations": [ { "name": "Northgate Motors", "line1": "1400 Dock Street", "city": "Columbus", "region": "OH", "postal": "43215" } ] }
{
"orderId": "019ffd66-a1c4-7f2e-b3d1-4e8554c14a1f",
"orderNumber": "SO-884213",
"status": "OPEN"
}
Read an order's current state, its lines, and where it has got to. This is the
response you fetch, modify and send back to PUT — the two shapes round-trip.
GET /orders with
modifiedSince and page by cursor. Paging by page number through a
list that is being written to will skip orders.Return the single order with this reference. Omit it and you get your orders as a page, newest change first.
Only orders changed since this instant. A timestamp, not a date — this is the filter a poller runs on.
Opaque page marker from the previous response. Page with this, never with a page number.
| Code | Meaning |
|---|---|
| 200 | A page of orders, or a single-entry page when you filtered on a reference. |
| 404 | No order with that reference on your account. |
GET /outbound/ext/v1/orders?orderNumber=SO-884213 Authorization: Bearer <token>
{
"orderId": "019ffd66-a1c4-…-4e8554c14a1f",
"orderNumber": "SO-884213",
"status": "PICKING",
"orderedAt": "2026-08-14T08:03:11Z",
"lastModifiedAt": "2026-08-14T09:41:02Z",
"lines": [
{
"sku": "2116",
"orderedQty": 480,
"uom": "EA",
"lot": "L2026-0814-A",
"allocatedQty": 480,
"shippedQty": 0
}
]
}
Amend an order by sending its complete new state. Fetch it, change what you need, send the whole thing back.
POST /orders with the same reference, which creates the order if it does not
exist — so a typo in the reference silently produces a duplicate. This verb exists to make
that a 404.| Code | Meaning |
|---|---|
| 202 | Accepted. If the warehouse already holds the order the change is staged. |
| 404 | No order with that reference. Nothing is created. |
| 409 | The order has shipped and can no longer be amended. |
PUT /outbound/ext/v1/orders Authorization: Bearer <token> { "orderNumber": "SO-884213", "facilityId": "019faa4e-4b53-…-8f717ebf5b6d", "requestedShipDate": "2026-08-19", // line 1 reduced 480 → 400 "lines": [ { "sku": "2116", "orderedQty": 400, "uom": "EA", "lineNumber": "1" } ] }
{
"orderNumber": "SO-884213",
"appliedState": "STAGED",
"message": "The warehouse already holds this order. Your change is staged and may be refused."
}
Cancel an order without touching its contents. Cancelling is the only lifecycle change this API offers.
400, not silently ignored. To change contents use PUT.| Field | Type | Description |
|---|---|---|
| statusrequired | string | Only CANCELLED is accepted. |
| reasonCoderequired | string | Why. Max 64 characters. A cancellation without a reason cannot be audited, so it is refused. |
| Code | Meaning |
|---|---|
| 202 | Accepted. If the warehouse holds the order it may still refuse. |
| 400 | Missing reasonCode, or the body carried order content. |
| 409 | The order has shipped. It cannot be cancelled. |
202 means we asked.PATCH /outbound/ext/v1/orders Authorization: Bearer <token> { "orderNumber": "SO-884213", "status": "CANCELLED", "reasonCode": "CUSTOMER_REQUEST" }
{
"orderNumber": "SO-884213",
"status": "CANCEL_REQUESTED",
"message": "Cancellation sent to the warehouse."
}
Tell us what is arriving, so the warehouse can expect it and check it in against what you said. An ASN is an advance shipping notice — the inbound counterpart of an order.
asnNumber is
required and must be unique for your account. Re-posting the same number revises the ASN
rather than creating a second one.Your ASN number. Max 128 characters. Everything you do later refers to it.
What is on the truck.
Must exist in your item master.
How much you are sending, in uom. The receipt reports what actually
arrived, which is frequently not this.
The unit you are advising in. EACSPL
The lot you are sending, when you know it in advance.
Expiry of that lot. Drives FEFO picking once it is on hand.
Your line number. A string, the same as outbound — we echo it back on every event about this line.
Cost per uom.
Per-line purchase order, when one ASN covers several POs.
When you expect it to arrive. The warehouse plans dock time from this.
Who it is coming from, as you know them. Kept verbatim.
Purchase order for the whole ASN.
| Code | Meaning |
|---|---|
| 202 | Accepted. The ASN id is returned immediately. |
| 400 | A field or line is wrong. The response names the offending line. |
| 403 | Your credential cannot write inbound. |
| 429 | Over your rate limit. |
POST /inbound/ext/v1/orders Authorization: Bearer <token> { "asnNumber": "ASN-30514", "poNumber": "PO-77120", "supplierName": "Tamsen Metalworks", "expectedDeliveryDate": "2026-08-19T14:00:00Z", "lines": [ { "sku": "2116", "expectedQty": 4800, "uom": "EA", "lineNumber": "1", "lot": "L2026-0819-C", "expirationDate": "2027-03-31" } ] }
{
"inboundOrderId": "019ffd80-2c41-…-b7a9e2f10c33",
"asnNumber": "ASN-30514"
}
Where the ASN has got to: whether the warehouse has it, whether it has arrived, and what state it is in.
Return the single ASN with this number. Omit it for a page of your ASNs.
Only ASNs changed since this instant.
| Code | Meaning |
|---|---|
| 200 | The ASN, or a page of them. |
| 404 | No ASN with that number on your account, or it has not been projected yet. |
GET /inbound/ext/v1/orders?asnNumber=ASN-30514 Authorization: Bearer <token>
{
"asnNumber": "ASN-30514",
"poNumber": "PO-77120",
"currentStatus": "IN_TRANSIT",
"supplierName": "Tamsen Metalworks",
"expectedDeliveryDate": "2026-08-19T14:00:00Z",
"lastModifiedAt": "2026-08-14T09:12:40Z",
"lines": [
{
"sku": "2116",
"expectedQty": 4800,
"receivedQty": 0,
"uom": "EA",
"lot": "L2026-0819-C"
}
]
}
What actually arrived, as the warehouse counted it. Newest first.
correctsReceiptId rather than replacing anything, so the history stays
intact.Which ASN's receipts to return.
Order of receipts against this ASN. Count up, never reused.
Whether the warehouse considers the ASN closed. Read this, not the receipt count — more can still arrive until it is true.
Set when this receipt is a re-count of an earlier one. The earlier receipt stays readable.
When it was checked in. putAwayCompletedAt is when it reached its
location — stock is not available until then.
GET /inbound/ext/v1/receipts?asnNumber=ASN-30514 Authorization: Bearer <token>
[
{
"receiptId": "019ffd91-8b02-…",
"receiptSeq": 2,
"status": "RECEIVED",
"isFinal": true,
"correctsReceiptId": "019ffd91-4a77-…",
"receivedAt": "2026-08-19T15:22:10Z",
"putAwayCompletedAt": "2026-08-19T17:04:55Z",
"lines": [
{ "sku": "2116",
"receivedQty": 4760,
"uom": "EA",
"lot": "L2026-0819-C" }
]
}
]
Withdraw an ASN the warehouse has not received yet.
Which ASN to cancel. The identifier travels in the body, never in the URL.
Only CANCELLED is accepted.
Why. Max 64 characters. A cancellation without a reason cannot be audited, so it is refused.
| Code | Meaning |
|---|---|
| 202 | Accepted. If the warehouse already holds it, they may still refuse. |
| 400 | Missing reasonCode. |
| 409 | Already received. It cannot be cancelled. |
PATCH /inbound/ext/v1/orders Authorization: Bearer <token> { "asnNumber": "ASN-30514", "status": "CANCELLED", "reasonCode": "SUPPLIER_DELAY" }
{
"asnNumber": "ASN-30514",
"status": "CANCEL_REQUESTED",
"message": "Cancellation sent to the warehouse."
}