OptiWMS API
Order rules
When an order can change, every 409, GET statuses, and how to release a pick.
Order rules
Send an order with PUT /store/v1/orders/{orderId} once it is paid and ready to ship, and again whenever it changes. The body is always the whole current order, never a change list. Cancel with POST /store/v1/orders/{orderId}/cancel. Read status with GET /store/v1/orders/{orderId}.
The path {orderId} is your store's id, percent-encoded (#1001 as %231001). Sign the encoded path.
What counts as a change
Only what the warehouse acts on counts as a change against the last version you sent:
- Lines: id, SKU or variantId, quantity, and
setIdandsetTitlewhen you send them - Delivery address
- Customer's name, email and phone
- Shipping method code and name
A different note, price, total or updatedAt alone answers 200 unchanged and leaves the order as it is. The comparison is with the last version you sent, not with the order as OptiWMS holds it after floor edits.
PUT outcomes
| Situation | Status | Body status |
|---|---|---|
| New order | 201 | created |
| Same warehouse fields as last send | 200 | unchanged |
Older updatedAt than the version OptiWMS applied |
200 | stale |
| Changed, warehouse has not started | 200 | updated |
| Changed, warehouse has started | 409 | error ORDER_IN_FULFILLMENT |
| Changed, order shipped | 409 | error ORDER_SHIPPED |
| Changed, order canceled in OptiWMS | 409 | error ORDER_CANCELED |
| Previously canceled before OptiWMS saw a create | 200 | canceled (not imported) |
"Warehouse has started" means the order is in a pick, wave or pack, has a shipping label, or has been handed to a shipping platform (shipmentWorkStarted includes a platform order id).
Example new-order request (OpenAPI created):
{
"number": "#1001",
"createdAt": "2026-10-27T09:30:00+11:00",
"updatedAt": "2026-10-27T09:30:00+11:00",
"currency": "AUD",
"customer": { "name": "Jo Citizen", "email": "[email protected]", "phone": "+61 400 000 000" },
"shipTo": {
"name": "Jo Citizen",
"phone": "+61 400 000 000",
"company": "",
"line1": "1 George St",
"line2": "Level 2",
"city": "Sydney",
"provinceCode": "NSW",
"postalCode": "2000",
"countryCode": "AU"
},
"shipping": { "code": "express", "name": "Express Post", "price": "12.00" },
"totals": {
"subtotal": "99.85",
"shipping": "12.00",
"tax": "10.17",
"total": "111.85",
"taxesIncluded": true
},
"notes": "Leave at the door",
"lines": [
{
"id": "11",
"productId": "201",
"variantId": "301",
"sku": "TEE-BLK-M",
"title": "Organic tee - Black / M",
"quantity": 2,
"unitPrice": "29.95"
},
{ "id": "12", "sku": "CAP-RED", "title": "Cap", "quantity": 1, "unitPrice": "39.95" }
]
}
Sets
A variant can list components on PUT /products: each component is another variant's SKU and how many of it make one of this variant. An order line for that SKU is picked as those products. Leave components out of a later send and the set stays as it is. An empty list clears a set this integration defined. A set defined by another store, or in OptiWMS, is left as it is.
When your store has already split a set into its products, send the same setId and setTitle on each of those lines. OptiWMS keeps them together and does not split the product again.
GET /orders/{orderId} and the shipments call report a set line once. For a set, this is the number of sets, not the component units. Three of a set whose components are one soap and two tins is quantity 3 on that line, not 9 units.
Example 201 response:
{ "status": "created" }
Example 409 while picking (OpenAPI inFulfillment response shape):
{
"error": {
"code": "ORDER_IN_FULFILLMENT",
"message": "The warehouse has started on this order, so the change cannot apply yet. An Order edited task now tells the warehouse what your store wanted changed (the lines); when staff release the order (GET /orders/{orderId} shows pending), send it again.",
"details": [],
"requestId": "3f6c2b1e-8d4a-4c1f-9b7e-2a5d6e8f9a10"
}
}
The 409 commits so the Order edited task survives. When the order has no fulfillment item yet (label or shipping platform only), the message asks the warehouse to release the order instead of promising a task.
Release the pick
- Your store sends a change while the order is being picked or packed: OptiWMS answers 409
ORDER_IN_FULFILLMENTand raises an Order edited task for the warehouse. - Staff cancel the pick in OptiWMS (or otherwise release the order).
GET /orders/{orderId}answerspendingagain.- Send the same
PUTagain: it applies as 200updated, and the Order edited task is resolved.
Do not keep retrying the PUT while the order stays in fulfillment; wait until GET shows pending (or handle the 409 by notifying operators).
Cancel
POST /orders/{orderId}/cancel with an optional reason. Example body from the OpenAPI document:
{ "reason": "Customer asked to cancel" }
| Situation | Result |
|---|---|
| Order pending in OptiWMS | Canceled; 200 |
| OptiWMS never received the order | Tombstone only; later PUT answers 200 canceled and does not import |
| Already canceled | 200, idempotent |
| In fulfillment | 409 ORDER_IN_FULFILLMENT |
| Shipped | 409 ORDER_SHIPPED |
GET status
GET /orders/{orderId} returns what this integration sent. Another store's order id answers 404 ORDER_NOT_FOUND, never 403.
status is one of:
| Status | When |
|---|---|
canceled |
Canceled in OptiWMS (even if shipments exist). |
shipped |
Fully shipped. |
partially_shipped |
At least one parcel left, more still owed. |
packing |
Order status PACKING or UNFULFILLED, or shipment work started (label or shipping platform) with no open pick. |
picking |
Order status PICKING, or a fulfillment action is allocated (PENDING). |
pending |
Waiting for the warehouse. Never returned while a PUT would answer 409. |
shipments lists parcels that have a tracking number whose label is not voided, or a shipped-without-tracking mark. Example shipped body from the OpenAPI document:
{
"orderId": "5001",
"number": "#1001",
"status": "shipped",
"shipments": [
{
"shipmentId": "8812",
"carrier": "Australia Post",
"trackingNumber": "33ABC1234567",
"trackingUrl": "https://auspost.com.au/mypost/track/details/33ABC1234567",
"shippedAt": "2026-10-27T15:20:00+11:00",
"lines": [
{ "lineId": "11", "quantity": 2 },
{ "lineId": "12", "quantity": 1 }
]
}
],
"updatedAt": "2026-10-27T15:20:00+11:00"
}
Error codes for orders
These codes appear on order calls (and elsewhere as listed in Signing):
| Code | Status | What to do |
|---|---|---|
VALIDATION_ERROR |
400 | Fix the fields in details and send again. |
NOT_FOUND |
404 | Wrong method or path. |
ORDER_NOT_FOUND |
404 | No order with this id for this integration; send it with PUT. |
ORDER_IN_FULFILLMENT |
409 | Release the pick (or ask the warehouse to release), then send again. |
ORDER_SHIPPED |
409 | The order has shipped; it can no longer change. |
ORDER_CANCELED |
409 | The order is canceled; it can no longer change. |
RATE_LIMITED |
429 | Wait Retry-After seconds. |
INTERNAL |
500 | Retry later; quote the request id if it continues. |
Lines whose SKU OptiWMS does not know are imported as held lines and raised as product questions; the rest of the order goes ahead. An order carries at most 250 lines.
Connect your own store
Register the integration in the dashboard, then start with GET /ping.