Notes
Order status mapping between a store, an ERP and a carrier
A store, an ERP and a carrier each describe an order with their own set of statuses, and none of them map one to one. Shopify splits the status into payment and fulfillment, NetSuite keeps one order status plus a ship status per fulfillment, and the carrier reports events. The integration decides per status what it does, instead of copying a label.
Updated
Three status sets that do not line up
"Shipped" in the store, "Pending Billing" in the ERP and "Out for delivery" at the carrier can all describe the same order on the same afternoon. Each system answers a different question: the store asks whether the customer paid and got the goods, the ERP asks what is left to ship and to invoice, the carrier asks where the parcel is.
The values below were read from Shopify's GraphQL Admin API reference and NetSuite's SOAP schema on October 3, 2026:
| System | Field | Values |
|---|---|---|
| Shopify, payment | displayFinancialStatus |
8: PENDING, AUTHORIZED, PAID, PARTIALLY_PAID, PARTIALLY_REFUNDED, REFUNDED, VOIDED, EXPIRED |
| Shopify, fulfillment | displayFulfillmentStatus |
11, of which 3 are replaced by newer ones: UNFULFILLED, IN_PROGRESS, PARTIALLY_FULFILLED, FULFILLED, ON_HOLD, SCHEDULED and others |
| Shopify, order | cancelledAt, cancelReason, closed |
a timestamp, 6 reasons, true or false |
| NetSuite, sales order | orderstatus |
8: _pendingApproval, _pendingFulfillment, _partiallyFulfilled, _pendingBillingPartFulfilled, _pendingBilling, _fullyBilled, _cancelled, _closed |
| NetSuite, item fulfillment | shipstatus |
3: _picked, _packed, _shipped |
| Carrier, via Shopify | FulfillmentEvent.status |
11, from LABEL_PURCHASED to DELIVERED, with DELAYED, ATTEMPTED_DELIVERY and FAILURE |
Shopify has no single field that says "shipped". Paid and shipped are two fields, and an order can be PAID and UNFULFILLED, or PARTIALLY_REFUNDED and FULFILLED. NetSuite folds shipping and billing into one field: _pendingBillingPartFulfilled means part has shipped and some of what shipped has not been invoiced. A mapping that copies one label to the other system loses half of it.
Which store orders become ERP orders
The first decision is which payment states create a sales order. The rule is an action per status, not a translation:
Shopify displayFinancialStatus |
Action in NetSuite | Why |
|---|---|---|
PAID |
create sales order | the normal route |
AUTHORIZED |
create sales order | payment is captured at fulfillment |
PARTIALLY_PAID |
create sales order, record the deposit | a manual capture below the order value |
PENDING |
per payment method: create or wait | Shopify uses it for manual payment methods and for payments still processing |
EXPIRED, VOIDED |
create nothing; cancel if created and unshipped | no money will arrive |
PARTIALLY_REFUNDED, REFUNDED |
credit memo or return authorization | money went back after the order existed |
AUTHORIZED is the one to watch. An authorization has a deadline set by the payment provider. If the order is not captured in time, Shopify moves it to EXPIRED, and a sales order that was created on the authorization now waits for money that will not come. The integration listens for orders/updated and checks the financial status again before the warehouse ships.
Shopify sends orders/create, orders/paid, orders/updated, orders/cancelled, orders/fulfilled and orders/partially_fulfilled as webhook topics. Webhooks arrive at least once and not always in order, as described in webhooks are not a guarantee, so the integration reads the current state of the order before it acts on any of them.
Partial shipments, worked through
Order 1047 has 3 lines: 10 cases of gloves, 6 cases of paper towels and 4 soap dispensers, 20 units in total. The dispensers are on backorder.
- Next morning the warehouse ships 10 + 6 = 16 units in one item fulfillment.
- Open after the first shipment: 20 - 16 = 4 units.
- Six days later the last 4 ship in a second item fulfillment.
Each NetSuite item fulfillment with shipstatus _shipped becomes one Shopify fulfillment, created with fulfillmentCreate. The input names the fulfillment order line items with their quantities and carries trackingInfo with company, number and url. After the first one Shopify shows PARTIALLY_FULFILLED with 4 units open, and the customer gets one tracking number per shipment.
On the NetSuite side the status after the first shipment depends on billing. Shipped but not yet invoiced is _pendingBillingPartFulfilled. Invoiced per shipment, it goes back to _partiallyFulfilled. After the last shipment it is _pendingBilling until the invoice, then _fullyBilled. None of those four values has a counterpart in Shopify, and none needs one: the integration reads the line quantities, quantityfulfilled against quantity, and writes fulfillments. The NetSuite status only tells it whether to look.
_picked and _packed are internal warehouse steps. They change nothing in the store, unless you want the customer to see that an order is being prepared.
Orders cancelled during a sync
A cancellation is the status change that goes wrong most often, because it races the order itself.
| Situation | What goes wrong without a rule | What the integration does |
|---|---|---|
Customer cancels at 10:04 an order placed at 10:02, while orders/create is still being processed |
the cancel finds no ERP order and is dropped; the create then writes a live sales order | events per order are processed one after the other; before writing, the integration re-reads the order and sees cancelledAt |
| Store cancels an order the ERP has already shipped | the sales order is cancelled in the integration's view while goods are in transit | no cancel in NetSuite; the order goes to the review list with the item fulfillment attached |
| ERP cancels an order for lack of stock | the store still shows the order as open and the customer waits | orderCancel with reason INVENTORY and restock set, and the customer notified |
| ERP closes the open lines of a partly shipped order | the store waits forever for the last 4 units | refund the open quantity in Shopify and record why |
Shopify documents when orderCancel is refused: an order that is already cancelled, has a pending payment authorization, has an active return, or has outstanding fulfillments that cannot be cancelled. It also states that a cancellation cannot be undone. So the integration checks those conditions before calling it, and an order that does not meet them goes to the review list instead of failing in a log. More on what happens to a message that fails is in what happens when an integration fails.
Back to the customer: tracking and carrier events
The carrier's vocabulary reaches the store through the fulfillment. A Shopify fulfillment carries its own displayStatus with 18 values, and carrier events as FulfillmentEvent.status: LABEL_PURCHASED, CARRIER_PICKED_UP, IN_TRANSIT, OUT_FOR_DELIVERY, DELIVERED, and the exceptions DELAYED, ATTEMPTED_DELIVERY and FAILURE.
NetSuite knows the shipment by its ship method, for example "UPS Ground", and Shopify by a carrier name in company. The integration maps each ship method to a carrier name and sends number and url with it, so the customer gets a working tracking link from every carrier the warehouse uses. A ship method without a mapping goes to the review list rather than out as a bare number. For retail customers on EDI, the same shipment goes out as an 856 advance ship notice instead, with its own status codes.
What this means for your integration
We write down per status what the integration does: which payment states create a sales order, which ERP events become store fulfillments, and what happens to a cancellation at each stage. Events for one order are handled in sequence, and the order's current state is read again before every write. A cancellation that comes too late, or a shipment for an order the store has cancelled, goes to the review list in the dashboard instead of disappearing.
The order flow as a whole is on orders, customers and shipping, and the NetSuite specifics on the Shopify NetSuite integration page. Tell us which store, ERP and carriers you run, and we map your statuses between them.
Frequently asked questions
Why does a store order show as unfulfilled when it has shipped?
Because the shipment was recorded in the ERP or warehouse and never sent back as a fulfillment. Shopify only changes displayFulfillmentStatus when a fulfillment is created for the line items. The integration turns each NetSuite item fulfillment with ship status _shipped into a Shopify fulfillment with the shipped quantities and the tracking number.
Should the integration create the ERP order when the payment is only authorized?
Usually yes, because the warehouse can start while the payment is captured at fulfillment. An authorization expires; Shopify then shows EXPIRED, and the integration has to cancel the sales order if nothing has shipped. Orders in PENDING are decided per payment method, because Shopify uses that status for manual payment methods as well as for payments still processing.
What happens when a customer cancels an order that is already in the ERP?
The integration receives orders/cancelled and cancels the sales order if nothing has been picked or shipped yet. If an item fulfillment exists, the cancellation goes to a review list, because goods are on their way and someone has to decide between a return and a stop at the carrier.
Can one store order become several ERP orders?
It can, for example when lines ship from two subsidiaries. The store order number then goes to each ERP order as an external reference, and the integration adds up the fulfillments of all of them before it writes the store order to fulfilled.
Why can the integration not find an order from last year?
Shopify's GraphQL Admin API returns only the last 60 days of orders by default. Reading older orders needs the read_all_orders scope, which Shopify grants on request. A status sync that has to touch older orders, such as a late return, needs that scope.
Tell us what you need
Tell us which systems you use and what needs to work better. We reply personally within one working day. We help you define the work, then agree the price and timing before you commit.
- You know the price and the timeline before you commit to anything
- We work out the connection that takes the work off your hands
- We handle the build, hosting, monitoring and maintenance
- Your team has less information to retype and fewer discrepancies to chase