The Order Intake API lets an order platform (a checkout, CRM or call-center system) send orders to a warehouse that runs on Warehance and read status and tracking back. Your platform pushes each order to Warehance, the warehouse picks, packs and ships it, and your platform polls Warehance for the shipment and tracking number.
This guide is for developers building or maintaining that connection. It covers authentication, every endpoint, the recommended workflow, and the edge cases you need to handle.
If your platform already sends orders to a fulfillment house over an API with these paths (/stores/{store_id}/orders/new.json,/shipments.jsonand so on), it works with Warehance unchanged. Point it at the Warehance Base URL and use the credentials the warehouse gives you.
How It Works
- You push orders. Warehance never pulls from your platform. Each new order is one
POSTto Warehance. - The warehouse fulfills them. The order lands in the warehouse's Warehance account like any other store order, where its usual rules, holds and routing apply.
- You poll for tracking. Warehance doesn't call your platform back. Read shipments from Warehance on a schedule and write the tracking into your platform.
- Edits are allowed until work starts. You can change the address, items or cancel the order while its status is
unverified. Once the warehouse starts working on it, edits are refused.
| Step | Request | Purpose |
|---|---|---|
| Send an order | POST /orders/new.json | Create the order in Warehance |
| Change it | PUT /orders/{id}/update.json, item endpoints, DELETE /orders/{id}.json | Edit or cancel while still unverified |
| Read it | GET /orders/{id}.json, GET /orders.json | Status, items and shipments |
| Get tracking | GET /shipments.json?updated_at_start=… | New shipments since your last poll |
Get Your Credentials
The warehouse creates the connection in Warehance and gives you four values. Each store you connect has its own set.
| Value | Example | Used as |
|---|---|---|
| Base URL | https://app.warehance.com/api/v1/fulfillment-api/api | Prefix for every request |
| Username | api-1a2b3c4d5e6f7a8b@stores.warehance.com | HTTP Basic username |
| API Key | 48 characters | HTTP Basic password |
| Store ID | 7f3c2a10-5b8e-4d21-9c6f-2e4a8b9d0c11 | {store_id} in every path |
For the warehouse: set up the store
Step 1: In Warehance, go to Stores. Under Connect Store, click Connect on your platform (for example BuyGoods or HelpGrid).
Step 2: Enter a Store Name (and choose the Client, if you're a 3PL), then pick how to get credentials:
- New connection: Warehance generates the username and store ID.
- Moving an existing store: enter the Store ID, Username and API Key the platform already sends to its current fulfillment house. The platform then only changes its URL. The Store ID must be a UUID, and the same credentials can't be used on two Warehance stores.
Step 3: Open the store. The General settings tab shows the Base URL, Username and Store ID. For a new connection, click Generate API Key.
Step 4: Copy the key from the dialog and send all four values to the platform securely. Warehance shows the key only once. Afterwards the page shows only its last four characters.
Regenerate API Key replaces the key immediately. The old key stops working right away, so the platform can't send orders until it has the new one. The username and store ID don't change.
Authentication
Every request uses HTTP Basic authentication with the username and API key, and includes the store ID in the path:
GET https://app.warehance.com/api/v1/fulfillment-api/api/stores/{store_id}/orders.json
Authorization: Basic base64(username:api_key)| Situation | Response |
|---|---|
| Missing, unknown or wrong credentials | 401 with an empty body and WWW-Authenticate: Basic realm="API Authorization" |
| No API key generated yet | 401 (as above) |
| Store deactivated in Warehance | 401 (as above), until the warehouse reactivates it |
| Correct credentials, wrong {store_id} | 404 {"errors": ["Please check your store number"]} |
A 401 never has a JSON body. Check the status code before parsing.Conventions
- Paths. Every path works with or without
.json:/orders/new.jsonand/orders/neware the same endpoint. - IDs are strings. Order, shipment and item IDs are JSON strings, even when they look numeric.
- Request bodies. You can wrap the body (
{"order": {...}},{"order_item": {...}}) or send the fields at the top level. - Flexible types. Text fields also accept numbers, so
"order_number": 10001works. Quantities also accept numeric strings. - Content type. Send
Content-Type: application/json.
Create an Order
POST /stores/{store_id}/orders/new.json{
"order": {
"order_number": "10001",
"order_date": "2026-10-26T14:30:00Z",
"requested_shipping_service": "Standard",
"ship_name": "Jane Doe",
"address1": "1600 SW 4th Ave",
"address2": "Suite 300",
"city": "Portland",
"state": "OR",
"postal_code": "97201",
"country": "US",
"phone": "555-010-0000",
"email": "jane@example.com",
"items": [
{"sku": "TEA-001", "quantity": 2},
{"sku": "MUG-RED", "quantity": 1}
]
}
}| Field | Required | Notes |
|---|---|---|
| order_number | Yes | Your order number. It's unique per store and can't be changed later. |
| ship_name | Yes | Recipient's full name. |
| address1 | Yes | |
| city | Yes | |
| country | Yes | Two-letter code (US) recommended. A country name also works. |
| address2, state, postal_code, phone, email | No | Send state and postal_code wherever the country uses them. |
| requested_shipping_service | No | The shipping service name your platform uses, such as Standard or Expedited. See Shipping Services below. |
| order_date | No | 2026-10-26T14:30:00Z (RFC 3339) or 2026-10-26 14:30:00 -0700. If it's missing or can't be read, the time Warehance received the order is used. |
| items | No | One entry per line: sku (required) and quantity (1 or more). You can create an order with no items and add them later. |
A new order returns 201:
{
"order": {
"id": "231185181863",
"status": "unverified",
"order_number": "10001",
"ship_name": "Jane Doe",
"address1": "1600 SW 4TH AVE",
"address2": "SUITE 300",
"city": "PORTLAND",
"state": "OR",
"postal_code": "97201",
"country": "US",
"shipments": [
{
"id": "pending-231185181863",
"order_number": "10001",
"tracking_number": null,
"status": "pending_shipment",
"carrier_code": null,
"service_code": null,
"ship_date": null,
"orders": [{"id": "231185181863", "order_number": "10001"}],
"ship_name": "Jane Doe",
"address1": "1600 SW 4TH AVE",
"address2": "SUITE 300",
"city": "PORTLAND",
"state": "OR",
"postal_code": "97201",
"country": "US"
}
],
"items": [
{"id": "54a1e612-ab3d-45b4-aec5-abc064650bce", "sku_id": 231185181506, "quantity": 2, "sku": "TEA-001"},
{"id": "92cc21b0-7d0e-4f8a-9b51-0c8d3e6f2a47", "sku_id": 231185181508, "quantity": 1, "sku": "MUG-RED"}
]
}
}Store the order id and each item id; you need them for edits.
- Sending the same order twice is safe. If an order with that
order_numberalready exists in the store, Warehance returns it with200and creates nothing. Retry a create after a timeout without worrying about duplicates. - The address may come back corrected. Warehance verifies and standardizes the ship-to address, so responses can differ from what you sent (for example, uppercased or with a corrected ZIP code).
- Validation errors return
404, not400, with one message per problem:{"errors": ["...", "..."]}. Malformed JSON also returns404. This matches the API your platform was originally built against. - Import filters. If the warehouse filters this store's orders, a filtered order is rejected with
404{"errors": ["Order was rejected by the store's import filter"]}.
Order Statuses
| Status | Meaning | Can you edit or cancel it? |
|---|---|---|
| unverified | Received. The warehouse hasn't started on it yet. | Yes |
| pending_shipment | The warehouse is working on it, or part of it has shipped. | No |
| shipped | Every part of the order has shipped. | No |
| cancelled | Cancelled by you or the warehouse. | No |
unverifieddoesn't mean the order is stuck or waiting for you. The warehouse fulfillsunverifiedorders normally; the status only tells you edits are still accepted.
An order can also go to pending_shipment because the warehouse split it into several shipments or merged it with another order. See Split and Merged Orders below.
Read Orders
Get one order
GET /stores/{store_id}/orders/{id}.json
GET /stores/{store_id}/orders/{id}.json?order_number=10001On any order endpoint, the order_number query parameter takes priority over the {id} in the path. If you only stored your own order number, put any placeholder in {id} and pass ?order_number=.
An unknown order returns 404 {"error": "could not find order"}. Note the singular error key.
List orders
GET /stores/{store_id}/orders.json?updated_at_start=2026-10-26T00:00:00Z&page=1&per_page=100| Parameter | Notes |
|---|---|
| page | Starts at 1. |
| per_page | Default 25, maximum 500. |
| updated_at_start, updated_at_end | RFC 3339, such as 2026-10-26T14:30:00Z. Returns orders changed in that window, including status changes from the warehouse. |
| order_number | Returns just that order. |
The response is {"total": 120, "pages": 2, "orders": [...]}, oldest order first, one entry per order number. A bad updated_at value returns 400 {"errors": "supplied updated time is invalid"}; here errors is a string, not a list.
Change an Order
All changes are accepted only while the order is unverified. After that, every change returns 403 {"errors": ["Order can no longer be modified"]}. If you get that error, contact the warehouse about the change.
Update order fields
PUT /stores/{store_id}/orders/{id}/update.json{"order": {"address2": "Suite 300", "city": "Beaverton"}}Only the fields you send change. You can update requested_shipping_service, ship_name, address1, address2, city, state, postal_code, country, phone, email and order_date. The order_number can't change. Change items through the item endpoints below. Returns 200 with the order.
Items
| Request | Body | Returns |
|---|---|---|
| GET /orders/{id}/items.json | 200 {"items": [...]} | |
| GET /orders/{id}/items/{item_id}.json | 200 {"order_item": {...}} | |
| POST /orders/{id}/items/add.json | {"order_item": {"sku": "TEA-002", "quantity": 1}} | 201 {"order_item": {...}} with the new item id |
| PUT /orders/{id}/items/{item_id}.json | {"order_item": {"quantity": 4}} | 200 {"order_item": {...}} |
| DELETE /orders/{id}/items/{item_id}.json | 200 {"items": [...]}: the remaining items |
- Item IDs. Warehance issues each item
id(a UUID) when the line is created. You can't choose it. - SKU changes. Only the quantity can change. A different SKU is a different line: delete the item and add a new one. Sending a different
skureturns400{"errors": ["Item sku can't be changed; delete the item and add a new one"]}. - Quantities. A quantity must be 1 or more. To remove a line, delete it.
- Deleted items disappear from every response. Requesting them afterwards returns
404{"errors": ["could not find item"]}. sku_idis the warehouse's product ID for the SKU. It isnullwhile the warehouse still has to match your SKU to a product. See SKUs and Products below.
Cancel an order
DELETE /stores/{store_id}/orders/{id}.jsonReturns 200 with the order, now cancelled. Cancelling also follows the editing rule: once the order isn't unverified (including when it's already cancelled), you get 403. In that case, ask the warehouse to cancel it if it hasn't shipped.
Get Shipments and Tracking
Each order carries a shipments list. Until a label is bought, an open order has one placeholder shipment:
idispending-{order id}statusispending_shipmenttracking_number,carrier_code,service_codeandship_datearenull
Once the warehouse ships, the placeholder is replaced by the real shipment:
{
"id": "231185181467",
"order_number": "10001",
"status": "shipped",
"tracking_number": "9400111899223197428490",
"carrier_code": "usps",
"service_code": "usps_ground_advantage",
"ship_date": "2026-10-27",
"orders": [{"id": "231185181863", "order_number": "10001"}],
"ship_name": "Jane Doe",
"address1": "1600 SW 4TH AVE",
"address2": "SUITE 300",
"city": "PORTLAND",
"state": "OR",
"postal_code": "97201",
"country": "US"
}carrier_codeandservice_codeare Warehance's lowercase carrier and service codes, such asusps,ups,fedexandusps_ground_advantage.ship_dateisYYYY-MM-DD.- A real shipment
idis always numeric. Only placeholders start withpending-.
Recommended: poll the shipments feed
GET /stores/{store_id}/shipments.json?updated_at_start=2026-10-26T14:00:00Z&per_page=500The response is {"total": 3, "pages": 1, "shipments": [...]}, oldest first, with real shipments only. It accepts the same page, per_page, updated_at_start, updated_at_end and order_number parameters as the orders list. Each shipment's orders tells you which of your orders it ships.
Other ways to read shipments:
GET /orders/{id}/shipments.jsonreturns{"shipments": [...]}for one order, including the placeholder while the order is open.GET /shipments/{shipment_id}.jsonreturns{"shipment": {...}}. An unknown ID returns200with{"shipment": null}, not404.
Split and Merged Orders
The warehouse can split one of your orders into several shipments, or merge several of your orders into one shipment (for example, two orders for the same customer). Your view stays the same:
- Your order keeps its
idandorder_number. Both still work after a split or merge, and the orders list still has one entry per order number. - Split orders. The order shows every shipment made so far. It stays
pending_shipmentuntil the last part ships, then becomesshipped. Your items list is unchanged. - Merged orders. Each of your orders shows the shared shipment and its tracking, and that shipment's
orderslist includes every order it ships. Write the same tracking number to each of them. - No more edits. Split or merged orders can't be edited or cancelled through the API (
403).
Always handle an order with more than one shipment, and a shipment with more than one order, even if your platform rarely sees them.
Orders Shipped Without a Label
Sometimes a warehouse marks an order as shipped without buying a label in Warehance, for example a local pickup or freight handled outside Warehance. The order becomes shipped but has no shipments and no tracking number. Treat shipped with an empty shipments list as fulfilled without tracking.
SKUs and Products
- Matching SKUs. Send the SKU exactly as the warehouse knows the product. A matching SKU goes straight to fulfillment.
- SKUs the warehouse doesn't recognize are still accepted; the order isn't rejected. Depending on the warehouse's settings, either a new product is created for the SKU, or the order waits until the warehouse maps your SKU to one of its products (
sku_idstaysnulluntil then). Either way, the order can't ship until the warehouse resolves it. - Bundles. If the warehouse maps your SKU to a bundle or multi-pack, it ships the components. You keep seeing your single line.
Shipping Services
requested_shipping_service is the name of the service your customer chose. The warehouse maps each name your platform sends to a carrier service once. Until a new name is mapped, orders using it wait in the warehouse. Send a small, consistent set of names (Standard, Expedited, Overnight) rather than free text, and tell the warehouse which names to expect.
Concurrent Requests
Warehance processes one change at a time per order. If another request is still updating the same order, you may get 409 {"errors": ["Order is being updated, try again"]}. Wait a moment and retry. Send changes to the same order one after another, not in parallel. Requests for different orders can run in parallel.
Recommended Workflow
- Create each order once it's final (paid and approved). Store the returned order
idand itemids. If the request times out, send it again: the sameorder_numberwon't create a duplicate. - Send edits only while
unverified. If an edit returns403, the warehouse has started work. Contact them about the change. - Poll
GET /shipments.jsonevery 15–60 minutes withupdated_at_startset a few minutes before your last successful poll. Read all pages and skip shipment IDs you've already processed. - Write tracking back for every order listed in each shipment's
orders. - Close out orders when they reach
shippedorcancelled. To check a few specific orders, useGET /orders.json?order_number=…orGET /orders/{id}.json. Don't page through every order.
Errors
| Status | Body | Meaning | What to do |
|---|---|---|---|
| 401 | empty | Credentials missing or wrong, no key yet, or store deactivated | Check the username and key. Ask the warehouse whether the key was regenerated or the store deactivated. |
| 404 | {"errors": ["Please check your store number"]} | {store_id} doesn't match these credentials | Use the store ID that goes with this username. |
| 404 | {"errors": [...]} on create | Missing or invalid fields, or malformed JSON | Fix the fields listed and resend. |
| 404 | {"error": "could not find order"} | No order with that id or order_number in this store | Check the id, or use ?order_number=. |
| 404 | {"errors": ["could not find item"]} | No such item on the order, or it was deleted | Re-read the order's items. |
| 400 | {"errors": [...]} | Invalid edit, such as a SKU change or quantity below 1 | Fix the request. |
| 400 | {"errors": "supplied updated time is invalid"} | updated_at_start or updated_at_end isn't RFC 3339 | Use a format like 2026-10-26T14:30:00Z. |
| 403 | {"errors": ["Order can no longer be modified"]} | The order isn't unverified anymore | Don't retry. Contact the warehouse. |
| 409 | {"errors": ["Order is being updated, try again"]} | Another change to this order is in progress | Retry after a short wait. |
| 500 | {"error": "...", "request_id": "..."} | Unexpected error | Retry with backoff. If it persists, send the request_id to the warehouse. |
Troubleshooting Checklist
- Base URL. Is it exactly the Base URL from the store's General tab, including
/api/v1/fulfillment-api/api? - Credentials. Are you sending the store's username and current API key as HTTP Basic auth? A regenerated key replaces the old one immediately.
- Store ID. Does
{store_id}in the path belong to the same store as the username? - Edits refused. A
403on an edit means the warehouse has started on the order, split or merged it, or it's cancelled. - No tracking yet. The order is still open: it shows a
pending-…placeholder shipment. Ask the warehouse whether it's waiting on a SKU or shipping-service mapping, or on stock. - Shipped with no tracking. The warehouse marked it shipped without a Warehance label. See Orders Shipped Without a Label above.
- Errors you can't explain. Send the warehouse the request time, the endpoint, your order number and any
request_id.