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 setId and setTitle when 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

  1. Your store sends a change while the order is being picked or packed: OptiWMS answers 409 ORDER_IN_FULFILLMENT and raises an Order edited task for the warehouse.
  2. Staff cancel the pick in OptiWMS (or otherwise release the order).
  3. GET /orders/{orderId} answers pending again.
  4. Send the same PUT again: it applies as 200 updated, 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.