Stock changes and sync state
Stockroom records the change immediately and pushes it to Shopify in the background. The response tells you which half has happened.
Why there are two halves
Receiving a delivery, or applying a stock adjustment, changes two things: Stockroom's own record, and the inventory level in Shopify. The Stockroom record is written inside the request. The Shopify write is queued, because Shopify's API has its own rate budget and its own bad days, and a request that waited on it would hang on you or fail on you for a reason that has nothing to do with your call.
So a 201 from POST /v2/purchase_orders/1024/receipts means the receipt exists, is final, and is what the merchant now sees in Stockroom. It does not mean Shopify has heard about it yet.
The sync block
Anything whose stock reaches Shopify carries shopify_sync:
"shopify_sync": {
"state": "pending",
"attempted_at": null,
"error": null
}| State | Means |
|---|---|
pending | Queued, or in flight. This is what a fresh write returns. |
synced | Shopify accepted the change. attempted_at is when. |
failed | Every attempt failed, and attempted_at is the last one. The Stockroom record still stands - the two are out of step until someone acts. |
In the normal case pending becomes synced within seconds. Under a Shopify incident or a large burst it can take minutes.
Read the block on the record that moved the stock - the receipt, or the adjustment. A purchase order carries one too, but it answers a narrower question: whether the incoming quantity it pushed to Shopify has landed. Its attempted_at anderror are always null, and it never reads failed.
A record that never touches Shopify stock is synced from the moment it exists, because there was nothing to wait for: imported history, a drop-ship order, a receipt on an order marked not to update stock.
Learning the outcome
Subscribe to purchase_order.updated (orstock_adjustment.updated) and read shopify_sync.state off the payload. The state settling fires that event, so a consumer subscribed to it sees the whole lifecycle without a separate "synced" event to learn.
If you are not using webhooks, re-read the record. Do it on a short backoff rather than in a tight loop, and treat pending as normal rather than as a problem: a few seconds is the expected case, not a symptom.
What to do about failed
This is rare, and it is not something your integration can fix by retrying the write. Receiving the same delivery again would double the stock in Stockroom; what failed is only the Shopify half.
error is a fixed sentence rather than Shopify's own words, because the useful detail is in the store's own record - the merchant can see what failed on the purchase order or the adjustment in Stockroom and retry the push from there. The usual causes are an item Shopify no longer tracks and a location that was removed, and both need a person.
So on failed: surface it to whoever watches your integration, with the record id, and let someone look at it in Stockroom. Do not re-send the write.
Everything else is synchronous
Only stock movement is queued. Creating a purchase order, editing lines, recording a payment, archiving a supplier - all of those are done when the response arrives, and carry no sync block.
Two writes are queued for a different reason and say so in their own response:sending a purchase order hands the email to the mail provider in the background, and marking an order ordered pushes the incoming quantity to Shopify the same way a receipt pushes stock.