For Shopify agencies
When the quote and the Shopify order disagree: what drifts after conversion
By Jahangir Alam · October 9, 2026 · 12 min read
- Last verified
- Shopify API
- 2026-10
- Audience
- Shopify agencies and developers keeping a quote layer, Shopify and an ERP or CRM in agreement after conversion
- Scope
- After conversion at API 2026-10: draft edits and unlinked checkouts, deleted and purged drafts, unpaid invoices, missed order webhooks, order edits, buyer-requested edits, cancellations, refunds and fulfilment, split deals, currency, linkage keys and tag overwrites, identity changes, field ownership, Events versus webhooks, and a reconciliation job
After a quote converts on Shopify, four records describe the same deal: the accepted quote, the draft order, the order, and whatever the ERP or CRM holds. They stop agreeing in a small number of documented ways - a draft edited after conversion, a draft still open after its order exists, a draft deleted or purged after a year, an order edited, cancelled or refunded, a buyer-requested change, a tag list overwritten by a connector. Each drift is detectable by comparing two specific fields, and each field has one owner. Reconciliation is choosing those owners and checking the pairs on a schedule.
This page is the catalogue of what drifts after the draft exists, how to detect each case, how to repair it, and who owns which field from conversion on. Creating exactly one draft is covered in how one accepted quote becomes exactly one draft order; what breaks at conversion is in the 17 failure modes; a price that was wrong from the start is diagnosed in why a quote converts at the wrong price. "Reconciliation" here means quote-to-order data, not payout or bank reconciliation.
Everything about Shopify below was checked against Shopify's own pages on 9 October 2026 at API version 2026-10; sources are at the end.
The four comparisons
What drifts, and how to catch it
Grouped by when it happens. "Source of truth" is the record that wins from conversion on.
Before the order exists
| # | What drifts | Cause in Shopify | Detect | Repair | Source of truth |
|---|---|---|---|---|---|
| 1 | The draft's lines or totals versus what was accepted | Staff edit the draft after conversion; updating tags overwrites the whole list |
draft_orders/update payload totals against the totals stored at conversion; DraftOrder.tags against the tags you wrote |
A commercial change is a new proposal or counter-offer and a new draft, with the old draft deleted; a logistics change is recorded | The accepted version, until payment |
| 2 | The draft is still open although its order exists | An update while a checkout was in progress unlinked the checkout: the order is created, and "the draft will remain open even after the order is created" | An order carries your conversion attribute, but DraftOrder.order is null or the status isn't COMPLETED |
Link the order by the attribute; a person deletes the stale draft | The order |
| 3 | The quote points at a draft that no longer exists | Staff deleted it | draft_orders/delete; as a backstop, draftOrder(id) returns nothing |
Mark the draft lost; confirm no order carries the attribute; re-create or close | Shopify |
| 4 | The same, after a year | Drafts created on or after 1 April 2025 are deleted after a year without an edit | draftOrder(id) returns nothing - Shopify doesn't document whether the purge sends a delete webhook |
Decide well before the year whether the deal is lost or re-quoted | Shopify |
| 5 | The invoice went out and was never paid | An order exists only once the draft is paid or completed | draftOrders(query: "tag:… status:invoice_sent") plus age |
Chase, extend, or delete the draft and close the quote - together | The draft's status |
| 6 | The quote expired, the draft is still payable | A draft has no deadline other than the purge | The quote is expired while DraftOrder.status is OPEN or INVOICE_SENT |
Delete the draft when the quote expires, or don't expire quotes after the invoice | Choose one - but only one |
| 7 | A paid order the quote layer or ERP doesn't know about | Webhook delivery isn't guaranteed | draftOrder.order is set but no order id is stored; or orders(query: "tag:… updated_at:>…") against stored ids |
Replay through the same webhook handler (the sweep) | The order |
After the order exists
| # | What drifts | Cause in Shopify | Detect | Repair | Source of truth |
|---|---|---|---|---|---|
| 8 | The order's lines or total versus the accepted version | The merchant or another app edits the order: Shopify says an app-created draft's order "can be edited in your Shopify admin, and by other apps" | orders/edited; Order.edited; originalTotalPriceSet against currentTotalPriceSet |
Record the reason on the order; don't rewrite the quote; the ERP follows the order | The order for fulfilment and invoicing; the quote for what was agreed |
| 9 | A pending buyer change the ERP or warehouse doesn't see | From 2026-10, a buyer can request removal of unfulfilled lines | requested_order_edit/* webhooks; Order.requestedOrderEdits |
Hold fulfilment while it's pending; accept or decline; an accepted request arrives as an order edit | The order |
| 10 | The quote shows a live deal, the order is cancelled | Cancellation is "irreversible" | orders/cancelled; cancelledAt |
Record it per order; the quote stays the record; a re-quote is new | The order |
| 11 | Amounts after refunds and returns | Refunds per line or shipping; returns | refunds/create; returns/*; currentSubtotalPriceSet |
The ERP's credit note comes from the refund, not from the quote | The order's refunds |
| 12 | Fulfilment state copied in several places | A warehouse or ERP writes fulfilments into Shopify | fulfillments/*; displayFulfillmentStatus |
Don't mirror fulfilment into the quote layer | The warehouse or ERP, through Shopify |
The shape of the deal and its keys
| # | What drifts | Cause | Detect | Repair | Source of truth |
|---|---|---|---|---|---|
| 13 | "The quote has one order, it should have two" | Partial acceptance or split conversion makes one draft per group; lines left for later are still open, not declined | Converted groups against stored order ids; each order's subtotal against its accepted lines | Reconcile per group; open lines aren't missing | The quote layer for which lines are open; the orders for what was bought |
| 14 | The quote total doesn't match the ERP's amount | The draft and order are in the presentment currency; shopMoney is converted |
Compare the quote with presentmentMoney only |
Book from one side deliberately | Presentment money |
| 15 | The order is no longer recognisable as from the quote | Tags overwritten by a full-list write; the attribute or note edited | orders(query: "tag:…") against stored ids; Events triggers on order.tags and order.customAttributes |
Restore from the quote record; integrations add tags with tagsAdd, never by rewriting the list |
The quote layer, which wrote the keys once |
| 16 | The buyer's identity changed after conversion | A contact removed, a location deleted or changed, customers merged | company_contacts/delete, company_locations/delete, customers/merge against stored ids |
Before payment, re-create the draft for the right location; after, fix the customer master, not the order | The ERP's customer master, or Shopify - one of them |
| 17 | A connector rewrites fields the quote layer owns | Two writers on one field | Field-level Events triggers; diff against what the quote layer wrote | Restrict the connector to its own fields | One writer per field |
| 18 | An integration missed quote-layer events | At-least-once, out-of-order delivery; an endpoint down | Gaps in the event sequence; deliveries the sender gave up on | Replay from the events feed, then from the API | The quote layer's event log |
| 19 | A reconciliation job reports "all good" on old orders | Apps see the last 60 days of orders unless they have read_all_orders |
The job's scope against the order's age | Request read_all_orders, or reconcile within the window |
- |
Who owns which field after conversion
ERP, CRM and PIM integration patterns has the ownership table for the life of an integration. This one starts at the moment the draft exists:
| Field | Owner after conversion | Everyone else | Why |
|---|---|---|---|
| Agreed lines, quantities, unit prices, terms, version | The quote layer - the sealed accepted version, never edited | Read for audit | It is the record of the agreement; a sent proposal is locked |
| Which lines are still open | The quote layer | The CRM reads it as pipeline | Not converted, and not lost |
| The draft's content until payment | The quote layer wrote it once; staff edits are exceptions to record | The ERP ignores drafts | The draft is payable - changing it changes the deal |
| The order's lines, quantities and totals | The Shopify order | The ERP and warehouse read; the quote layer doesn't mirror | Edits, refunds and returns happen on the order |
| Payment and refunds | Shopify | The ERP books from refunds; the quote layer records end states only | Money moves on the order |
| Fulfilment | The warehouse or ERP, writing into Shopify | The quote layer: nothing | One writer |
| Invoice, ledger, credit notes | The ERP or accounting system | - | Shopify isn't the ledger |
| Linkage keys - the quote's tags, conversion attribute, note line | The quote layer writes them once | Others append with tagsAdd |
Updating tags overwrites the list |
| Company, location, contact | The ERP's customer master or Shopify - one of them | The quote layer reads | One writer |
| Quote status after conversion | The quote layer, derived from Shopify's order events | Integrations read | Derived, never the other way round |
The rule that falls out of it: never write order changes back into the quote. The quote is what was agreed; the order is what is being fulfilled and paid. When they differ, record why on the order.
Events or webhooks for this
Shopify's Events became generally available with API 2026-10 as the successor to classic webhooks, and classic webhooks keep working. Events has topics for orders, refunds, returns, fulfilment orders, companies and customers, with triggers on individual fields - order.tags, order.customAttributes, order.lineItems.quantity among them - which suits rows 8, 15 and 17. It has no draft-order topic, so rows 1 to 6 still need the classic draft_orders/* webhooks. Shopify doesn't document Events' delivery guarantees, so keep the reconciliation job below whichever you use.
A reconciliation job you can copy
Run it on a schedule, against the stored ids, inside the order-access window:
- Drafts you created and haven't seen paid:
draftOrders(query: "tag:<your-tag> updated_at:>…"). For each,statusandorder: an order without a stored id is row 7; a missing draft is row 3 or 4; an oldINVOICE_SENTdraft is row 5 or 6. - Orders that carry your key:
orders(query: "tag:<your-tag> updated_at:>…"). CompareoriginalTotalPriceSetwithcurrentTotalPriceSet(row 8), checkcancelledAtand the financial status (rows 10, 11),requestedOrderEdits(row 9), and that the tags and conversion attribute are still there (row 15). - Outside systems: for each order, the ERP's amount against
presentmentMoney(row 14), the number of orders against converted groups (row 13). - Scope: without
read_all_orders, only the last 60 days of orders are visible (row 19).
The create side - the sweep that finds a conversion whose outcome is unknown - is in the idempotent conversion post.
How QuotWay behaves
One implementation, described as it works today:
- What it matches on. Every draft QuotWay creates carries the tags
quotwayandquotway-<quote reference>, a merchant-only note with the quote reference and version, and one conversion attribute that links the order back to its conversion group. QuotWay never edits, completes or deletes a draft after creating it, and never edits orders. - What it records. A change to a converted draft's totals is recorded on the quote's activity log, with the before and after amounts. Once a quote is at Order completed, QuotWay records it on the quote when its order is cancelled or fully refunded. Partial refunds, returns, order edits and fulfilment stay on the Shopify order - check the order for those (converting to a draft order).
- Split and partial conversions. Each accepted group becomes its own draft and order; lines the buyer left for later stay open on the quote and can be converted later (partial acceptance and split conversion).
- Missed order webhooks. A sweep checks invoiced quotes after seven days without an order and, if Shopify shows one, replays it through the same handler.
- For integrations. On the Enterprise plan - including during the 14-day free trial - the API's quote object lists the quote's Shopify draft-order and order ids, and a 30-day events feed is the recovery path for missed webhooks (the events feed). Plans are on the pricing page; conversion is described on convert quotes to orders.
FAQ
Why is a Shopify draft order still open after the customer paid?
An update was made to the draft while its checkout was in progress, which unlinks the checkout: the order is created, but "the draft will remain open even after the order is created". Check DraftOrder.order, link the order by your own key, and delete the stale draft.
Is there a webhook for when a draft order is paid?
Not a draft-order "paid" topic. Payment creates an order, so listen to orders/create and match it to the draft with your own attribute or tag; the draft's status becomes COMPLETED and its order field is set. draft_orders/update covers changes to the draft, and Shopify's Events has no draft-order topic at 2026-10.
Should the quote change when the order is edited?
No. The quote is the record of what was agreed; the order is what is fulfilled and paid. Record the reason for an edit on the order and let the ERP follow the order.
How do I detect order edits for my ERP?
The orders/edited webhook, the order's edited field, and a difference between originalTotalPriceSet and currentTotalPriceSet. With Events, a trigger on order.lineItems.quantity does the same for quantities.
What are buyer-requested order edits?
From API 2026-10, a buyer can ask to remove unfulfilled lines from an order. The request arrives as requested_order_edit/* webhooks and is visible on Order.requestedOrderEdits; hold fulfilment while it's pending, and an accepted request becomes an ordinary order edit.
Do unpaid draft orders expire?
Drafts created on or after 1 April 2025 are deleted after a year without an edit. Nothing else expires them, so a quote that expires earlier leaves its draft payable unless you delete it.
Does Shopify Events replace webhooks for reconciliation?
Events is the successor to classic webhooks and classic webhooks keep working, but it has no draft-order topic, so drafts still need draft_orders/*. Shopify doesn't document Events' delivery guarantees, so keep a reconciliation job either way.
Why doesn't the ERP total match the quote?
The quote, the draft and the order are in the buyer's presentment currency; shopMoney is converted. Refunds and order edits change the order's current total. Compare the quote with the order's presentment money and its original total.
Sources
Shopify pages, read on 9 October 2026 at API version 2026-10:
- draftOrderUpdate - an update unlinks a checkout in progress and the draft stays open
- DraftOrder and DraftOrderInput - status,
order, tags - Draft orders - deletion after a year without an edit for drafts created on or after 1 April 2025; payment creates the order
- Order - original and current totals,
edited,cancelledAt,requestedOrderEdits - Edit existing orders - app-created orders can be edited by the admin and other apps; the 60-day order window
- orderCancel - cancellation is irreversible
- tagsAdd - adding tags without overwriting
- Webhook topics and HTTPS webhook delivery - topics; delivery isn't guaranteed
- Events are now generally available and events and webhooks - topics, field triggers, classic webhooks continue
- Release notes 2026-10 - buyer-requested order edits
QuotWay behaviour is described from its public documentation and the app's source, read on 9 October 2026.
Related articles
- For Shopify agenciesIs Shopify good for B2B? When it's the right platform, and when it isn't14 min read
- For Shopify agenciesWhy a quote converts at the wrong price on Shopify: diagnose it by the size of the difference13 min read
- For Shopify agenciesBuild vs buy: what a custom Shopify RFQ app has to run in 202614 min read
See how QuotWay handles this on your store.