---
title: "A Shopify draft order is not a quote: the state machine each one models"
description: "A draft order's three states all start after the price is agreed. The quote states Shopify has no object for, and where the draft order fits."
url: "https://www.quotway.com/blog/shopify-draft-order-vs-quote"
type: "blog post"
category: "For Shopify agencies"
published: "2026-09-21"
verified: "2026-09-21"
shopify_api_version: "2026-07"
audience: "Shopify agencies, developers and solution architects"
scope: "Shopify's DraftOrder object and statuses at API 2026-07, the B2B submit-for-approval setting, and the quote states a negotiation needs before a draft order exists"
locale: "en"
source: "QuotWay - B2B Quote & Negotiation App for Shopify"
---

# A Shopify draft order is not a quote: the state machine each one models

A Shopify draft order is the record of an order that is waiting to be paid. It has three states - open, invoice sent, completed - and every one of them assumes the price is already agreed. A quote is the record of how the price gets agreed: a request, a proposal, revisions, counter-offers, approvals and an acceptance.

Shopify has an object for the first and none for the second, so a B2B build that needs negotiation has to model the quote's states somewhere else and create the draft order at the end.

That is the whole answer; the rest of this page is the evidence. It walks through what Shopify's draft order actually models, field by field, then the states a negotiation needs that no draft-order field can hold, then where the draft order fits once the two are kept apart. It is written for the agency developer whose first instinct on a B2B quoting project is "why don't we just use draft orders?" - a good instinct, because draft orders are the right ending, and a costly one when they are asked to be the middle as well.

Everything about Shopify below was checked against Shopify's own documentation on 21 September 2026 at API version 2026-07; the sources are listed at the end. Where the page describes how QuotWay models the states, that is one implementation of the pattern, and the pattern holds for a custom build too.

## What a draft order models

Shopify's `DraftOrderStatus` enum has three values, and their definitions are the shortest description of what a draft order is for. `OPEN`: "the draft order is open. It has not been paid, and an invoice hasn't been sent." `INVOICE_SENT`: "an invoice for the draft order has been sent to the customer." `COMPLETED`: "the draft order has been paid." There is no fourth value. Nothing in the enum describes a price being asked for, offered, revised or refused, because the object begins after that has happened.

The fields around the status say the same thing. `invoiceSentAt` is the time the invoice was *last* emailed, a single timestamp that is overwritten on each send. `completedAt` is when the order was created from the draft. `ready` says whether the draft can be completed at all. `order` points at the order once it exists. `paymentTerms`, `deposit`, `amountDueNowSet` and `amountDueLaterSet` describe how the agreed total will be collected. `anyVariantPricesOverridden` says whether a line carries a price other than the catalog's - it records *that* a price was changed, not what it was changed from, by whom, or whether the buyer agreed.

A draft order is also mutable for its whole life. `draftOrderUpdate` takes a complete `DraftOrderInput` and replaces what is there - the line items are swapped wholesale, not patched - and the documentation puts no restriction on updating a draft whose invoice has already gone out. It does warn what happens if you do: an update after a checkout has started unlinks that checkout, and the order can then be created while the draft stays open. The draft's timeline (`events`) records each of these changes as a `BasicEvent`, whose `message` field is "human readable text that describes the event". It is a log that an edit happened; it is not a numbered version you can reopen, and there is no field that returns the lines as they stood before the edit.

For B2B, Shopify adds the parts a business order needs and still nothing a negotiation needs. A draft order can start in the admin, or from the storefront: when a company location is set to "Submit all orders as drafts for review" (Shopify's checkout overview still calls the same setting "Only allow draft orders at checkout"), its buyers see a **Submit for approval** button at checkout instead of a payment step, and the draft lands on the merchant's Drafts page with its prices locked. The merchant can edit products and quantities, add a PO number, set payment terms and a deposit, lock or unlock the prices, send the invoice, or create the order directly. Price lock is worth reading precisely: it "prevents prices from being raised, but they also prevent prices from being automatically lowered" - it freezes the price the draft already has, which is not the same as recording a price two parties settled on. When the buyer pays from the invoice link the draft becomes an order marked paid; with payment terms the order starts as pending and moves through partially paid and paid as Shopify collects against the schedule. And a draft that nobody edits for a year is deleted - the clock resets on any edit, which is one more reason the draft should not be the place a slow negotiation lives.

Shopify's own changelog frames the sequence exactly. The July 2024 note that brought payment terms to draft-order invoices says the invoice gives customers "the opportunity to review their negotiated cart and prices before placing an order". Negotiated, past tense, before the draft order is placed. The negotiation happened somewhere else.

## What a draft order cannot model

The table lists the things a B2B negotiation has to record and what the draft order offers for each. The right-hand column is the honest one: where the draft order has a field, it is named; where it has none, the row says so.

| A negotiation needs to record | Draft order | What is actually needed |
| --- | --- | --- |
| The price the buyer asked for | No field. The line has a price; it does not know whether the buyer or the seller set it | Requested price per line, kept separately from the offered and final prices |
| The seller's proposal as sent | The current lines. `draftOrderUpdate` replaces them wholesale | A numbered version that is locked the moment it goes out |
| Revision history | A timeline of human-readable event text | Every version, reopenable, with a diff between any two |
| A counter-offer from the buyer | Nothing. The buyer's actions on a draft are "pay the invoice" or "do nothing" | A buyer-authored version, with the requested price per line |
| A merchant approval gate before the offer goes out | Nothing. Staff permissions scope which orders a user can see, not whether an offer may go out | A policy that holds a proposal, and the decision with its author |
| A buyer-side approval chain | Nothing. "Submit for approval" asks the *merchant* to approve the buyer's order | A state the buyer's own approvers can act on, with each step recorded |
| Accepting some lines and leaving the rest open | Nothing. A draft is paid whole or not at all | Per-line acceptance, with the lines not chosen still open for a later round |
| Expiry of an offer | The one-year inactivity purge, which is a housekeeping deadline, not a commercial one | A validity date the seller sets, after which accept and counter are withdrawn |
| Immutability once sent | None. An invoice-sent draft can still be edited, which unlinks any checkout in progress | A sent version that cannot be edited in place; a change is a new version |
| Who may make each move | The admin user's permissions | Each transition allowed to a named actor - buyer, merchant, or the system - and refused to anyone else |
| Where the price came from | `anyVariantPricesOverridden` says a line was overridden, not from what | The catalog price the negotiation started from, resolved for the buyer's location, with the catalog ID as provenance |
| Terminal states | `COMPLETED` (paid), or deleted | Declined, expired, cancelled, refunded and closed as distinct ends, because each is reported and followed up differently |

None of this is a criticism of the draft order. It is a precise object for a precise job. The failure is only in asking it to do the job before its own.

> **Two state machines: the quote and the draft order**
>
> Left, the quote layer's states run top to bottom: requested, in review, then an optional merchant approval, then proposal sent. From proposal sent the buyer may counter, which returns the quote to review for a new version, or route it to their own approvers, or accept all lines or some of them. Accepted leads to the single hand-off arrow on the right. Right, Shopify's draft order has three states: open, invoice sent, completed, and completed becomes an order whose payment status runs pending, partially paid, paid. Terminal quote states - declined, expired, cancelled, refunded, closed - sit at the bottom left. The caption notes that Shopify's machine starts where the negotiation ends.
>
> Shopify's machine starts where the negotiation ends: one arrow crosses from "accepted" to "open", and nothing crosses back.

## The quote states Shopify has no object for

A quote state machine does not need to be large, but it needs to be explicit, because each state answers a question someone will ask later: what could the buyer do at this point, what could the merchant do, and who did what. The states below are the ones QuotWay uses; a custom build would name them differently and need the same set.

**Requested.** The buyer has asked. Lines, quantities, the prices they hope for, a note, a PO number, custom fields, attachments. The merchant has done nothing yet. This state exists so that the request is a record before anyone has priced it - the [architecture post](/blog/shopify-b2b-quote-architecture) explains why the request itself is data Shopify never holds.

**In review.** A merchant has opened it. Here the merchant prices the lines from the buyer's catalog price - resolved for their company location, not copied from a list - adds or removes lines, adds shipping, and writes the proposal.

**Awaiting merchant approval.** A policy has held the proposal because of something about it: its total, the products on it, the company, the buyer's tag. The quote does not move to the buyer until the approver acts, and the approver's decision is recorded with their name. Shopify staff permissions cannot express "a proposal over this value needs a manager before it goes out"; this state is where that rule lives.

**Proposal sent.** The offer is out. It is a numbered version, and the version is now locked: the document the buyer received is a commercial record, and the audit trail has to be able to say what it contained. A change is a new version, never an edit. The buyer's options from here are to accept, to accept some of it, to counter, to decline, or to route it to their own approvers.

**Awaiting buyer approval.** The buyer's organisation has its own sign-off - a manager, a finance contact. Shopify's "submit for approval" is not this; it asks the merchant to approve the *buyer's* order, not the buyer's manager to approve the *seller's* offer. When the chain finishes, the quote returns to proposal sent with the chain's decisions on file.

**Countered.** The buyer has answered with different numbers. The counter is itself a version, authored by the buyer, with the requested price per line, and the merchant opening it returns the quote to review. The next proposal is version n+1. A draft order has no way to represent this state at all: the buyer's only actions on a draft are to pay or to wait.

**Partially accepted and fully accepted.** The buyer has taken all of the lines, or a strict subset. Lines not taken are still open - they can be accepted in a later round or re-proposed - and the accepted lines' prices are now sealed; nothing after this point edits a price. On the Professional plan and up the buyer decides per line ([how partial acceptance works](/blog/partial-order-acceptance-b2b)); on every plan the whole-proposal accept exists.

**Partially converted, fully converted, invoice sent, order completed.** The draft order exists now. One draft order moves the quote to partially converted, because an accepted subset can become one draft order while the rest is re-negotiated; fully converted when every accepted line has a draft order; invoice sent when each draft's invoice has gone; order completed when Shopify's `orders/create` webhook says the order exists. These states mirror Shopify's three plus the order, and they are driven by Shopify's events rather than the quote layer's assumptions.

**Declined, expired, order cancelled, order refunded, closed.** The ends. They are distinct because they are reported differently - a declined proposal is a pricing signal, an expired one is a follow-up signal, a refunded order is finance's - and because "closed" is the operator's explicit archive of any of the others.

## Submit for approval is the merchant's review, not the buyer's negotiation

The one place Shopify's native B2B puts a state between "buyer wants this" and "paid" is the company-location setting "Submit all orders as drafts for review". It deserves a clear description because it is often mistaken for a quote workflow.

When the setting is on, every order from that location arrives as a draft: the buyer builds a cart at catalog prices, sees a banner that payment is due when the order is confirmed, and presses **Submit for approval**. The draft appears with prices locked. The merchant reviews it, may edit products and quantities, may set terms, and either creates the order or sends the invoice. It can be switched on per location, per company, in bulk, or from a Shopify Flow action when a location is created.

What it models is a *hold*: the merchant's chance to check an order before it is confirmed. The buyer proposes nothing about price; the price is the catalog's. The merchant's edits are made to the draft, not offered back for agreement. There is no version, no counter, no acceptance step - the buyer's next action is to pay. For a merchant whose deals are "check the order, then invoice it", this is the right tool and a quote layer would be overhead; [when you shouldn't use a quote app](/blog/when-not-to-use-a-shopify-quote-app) lists that case among others. The moment a deal has a second price in it, the hold stops being enough.

## How the transitions are enforced

A list of states is not a state machine until the transitions between them are enumerated and everything else is refused. This is the engineering detail that separates a quote layer from a status column, and it is where QuotWay's implementation is worth describing as a pattern.

Every allowed move is one row in a table: the state it leaves, the state it enters, which actors may make it - the buyer, the merchant, or the system - and the event it writes. "Proposal sent → countered" is allowed to the buyer and writes a *countered* event. "Countered → in review" is allowed to the merchant and writes a *reviewed* event. "Fully accepted → partially converted" is allowed to the merchant or the system, because a conversion may run from a queue with no signed-in user, and writes a *conversion group created* event. A transition that is not in the table throws, and because the state change and its event are written in one database transaction, the throw rolls both back: the quote cannot be left in a state its history does not explain.

Two rules in the table are wildcards, and they are the ones a custom build most often forgets. Any non-terminal state can move to *expired* when the validity date passes - that is one rule applied to every state, run by a daily sweep, so a quote cannot escape expiry by being in an unusual state. And any terminal state can move to *closed*, the operator's archive. Five states are terminal: declined, expired, order cancelled, order refunded, closed. *Order completed* is deliberately not among them, because Shopify's `orders/updated` webhook can still report a cancellation or a refund, and the quote has to be able to follow the order there.

The conversion states are the other place the pattern matters. The quote does not decide that it is "converted" when it calls `draftOrderCreate`; it records the draft order's ID and waits. It moves to order completed when `orders/create` arrives - including the case where the buyer pays from the invoice URL before the merchant has explicitly sent the invoice, which the machine allows as a direct transition from fully converted. A build that advances its own state on the API call's success, rather than on Shopify's webhook, will one day show "order completed" for a checkout that was abandoned.

The same table is what makes the audit trail answer questions a year later. Because every transition names its actor and writes its event, "who approved this price", "did the buyer ever see version 2", and "why is this quote expired" are lookups, not investigations.

## Where the draft order fits

Kept to its job, the draft order is the best part of the design, because Shopify does the collecting. The pattern is: create it once, at acceptance, with everything the negotiation settled written onto it.

The negotiated unit price goes on each variant line as a `priceOverride` in the presentment currency, so the order matches the accepted version to the cent; custom lines carry their own price. The `purchasingEntity` - company, location, contact - makes it a B2B draft, so the location's tax exemption and checkout settings apply. The payment terms template the deal settled on is set on the draft, and where the deal included a deposit - on Shopify Plus stores, from QuotWay's Professional plan, 1-99 percent - it rides the draft too, so checkout collects the deposit and Shopify schedules the balance. The PO number and the buyer's note go into the draft's fields. Before creating it, `draftOrderCalculate` previews the totals so that tax, shipping and stock changes since the proposal are detected and shown rather than discovered on the invoice; the [architecture post](/blog/shopify-b2b-quote-architecture) covers the price hand-off in detail and the [conversion feature page](/features/convert-to-orders) shows it from the merchant's side.

A partial acceptance becomes a draft order for the accepted lines only; the still-open lines stay on the quote for the next round, and a later acceptance becomes a second draft order. That is why the quote has a *partially converted* state and why a conversion is idempotent per accepted set - retrying a failed creation must not produce two drafts for the same lines.

From here Shopify's machine runs on its own. `draftOrderInvoiceSend` moves the draft to invoice sent and emails the checkout link; the buyer pays, or pays later under the terms from their customer account; the draft completes; the order exists with a financial status of pending, partially paid or paid. The quote layer listens and mirrors. It does not edit the draft after sending - the negotiation is over, and an edit would unlink a checkout in progress - and it never treats the draft as the place to reopen a price. If the price has to change after acceptance, that is a new proposal on the quote, a new acceptance, and a new draft order; the merchant-side mechanics are in [Shopify draft orders for B2B](/blog/shopify-draft-orders-for-b2b), and the draft order's own gotchas - stock not reserved by default, the 500-line ceiling, the one-year purge - in [the limits post](/blog/shopify-draft-order-limits).

## Five signs a draft order is being used as a quote

These are what an agency finds on a rescue engagement, listed so they can be recognised early.

1. **The Drafts page is the pipeline.** Dozens of open drafts, some months old, with notes like "v3 - waiting on buyer". The one-year purge will eventually delete the oldest of them, and there is no report of which stage each is at because the object has no stage.
2. **Prices are changed in place.** A draft edited four times to reflect four rounds of haggling, with no record of rounds one to three except the timeline's "edited" entries. When the buyer disputes the final number, nobody can show what was offered before.
3. **Invoices are sent as offers.** The invoice is the proposal, and "the buyer hasn't paid" is the only signal that they did not accept. Payment terms make this worse, because an unpaid invoice under net 30 is indistinguishable from a rejected one for a month.
4. **The buyer's counter arrives by email.** Because the draft has no buyer-authored state, the negotiation runs in an inbox and the draft is updated afterwards by hand - the moment the two diverge, the order is wrong.
5. **Approval is a Slack message.** A rep asks a manager whether a discount is fine, gets a thumbs-up, and edits the draft. Nothing ties the approval to the price it approved.

Each is a state the draft order cannot hold being held somewhere informal instead. The fix is not a bigger draft order; it is a quote object in front of it.

## FAQ

### What is the difference between a draft order and a quote on Shopify?

A draft order is Shopify's record of an order waiting for payment: it has three statuses - open, invoice sent, completed - and its lines carry one price each. A quote is the record of reaching that price: the buyer's request, the seller's numbered proposals, counter-offers, approvals and the acceptance. Shopify has no quote object, so a quote layer holds those states and creates the draft order once the price is agreed.

### Why not just use draft orders as quotes?

Because the draft order has no field for anything before agreement. It cannot hold a requested price, a counter-offer, a locked version, an approval decision or a per-line acceptance, its lines are replaced wholesale on every edit, and its timeline is text rather than reopenable versions. A deal with one round - buyer submits, merchant checks, buyer pays - fits it; a deal with a second price does not.

### What are the Shopify draft order statuses?

Three, from the `DraftOrderStatus` enum at API 2026-07: `OPEN` (not paid, no invoice sent), `INVOICE_SENT` (an invoice has been emailed to the customer) and `COMPLETED` (paid). Once completed, the order it created has its own financial status - pending, partially paid, paid, refunded and so on.

### Is "Submit for approval" a quote workflow?

No. It is a company-location setting that turns every order from that location into a draft the merchant reviews before it is confirmed. The buyer submits a cart at catalog prices; the merchant may edit it and then invoices it or creates the order. There is no price proposal from the buyer, no version and no acceptance step. It models a hold on the order, which is exactly right for merchants who only need to check orders before invoicing.

### Can a draft order be edited after the invoice is sent?

Yes. `draftOrderUpdate` has no restriction on invoice-sent drafts, and any edit resets the one-year inactivity purge. Shopify does warn that an update after a checkout has started unlinks that checkout - the order can then be created while the draft stays open. That mutability is what makes a draft order the wrong record of an offer: a sent proposal must not be editable in place.

### When should the draft order be created?

At acceptance, once, with the negotiated prices as `priceOverride` on the lines, the purchasing entity, the payment terms, any deposit and the PO number already on it. Creating it earlier - at proposal - puts a mutable, purgeable object in the middle of the negotiation; creating it later leaves the buyer with nothing to pay against. A partial acceptance creates a draft for the accepted lines and leaves the rest open on the quote.

### Do quotes expire, and do draft orders?

Differently. A quote's expiry is a commercial date the seller sets - after it, accept and counter are withdrawn and the buyer needs a fresh proposal. A draft order's only clock is housekeeping: drafts created on or after 1 April 2025 are deleted after a year without an edit. Neither should be relied on to do the other's job.

## Sources

Shopify pages, all read on 21 September 2026 at API version 2026-07:

- [DraftOrder object](https://shopify.dev/docs/api/admin-graphql/latest/objects/DraftOrder), [DraftOrderStatus enum](https://shopify.dev/docs/api/admin-graphql/latest/enums/DraftOrderStatus) and [BasicEvent](https://shopify.dev/docs/api/admin-graphql/latest/objects/BasicEvent)
- [draftOrderUpdate](https://shopify.dev/docs/api/admin-graphql/latest/mutations/draftOrderUpdate) and [draftOrderComplete](https://shopify.dev/docs/api/admin-graphql/latest/mutations/draftOrderComplete)
- [Draft orders for B2B apps](https://shopify.dev/docs/apps/build/b2b/draft-orders)
- [Creating B2B orders using draft orders](https://help.shopify.com/en/manual/b2b/checkout-and-orders/draft-orders) and [managing checkout settings in B2B](https://help.shopify.com/en/manual/b2b/checkout-and-orders/checkout-settings)
- [Setting up payment terms in B2B](https://help.shopify.com/en/manual/b2b/checkout-and-orders/payment-terms), [PaymentTerms object](https://shopify.dev/docs/api/admin-graphql/latest/objects/PaymentTerms) and [OrderDisplayFinancialStatus](https://shopify.dev/docs/api/admin-graphql/latest/enums/OrderDisplayFinancialStatus)
- [Sending invoices for draft orders](https://help.shopify.com/en/manual/fulfillment/managing-orders/create-orders/send-draft) and [draft orders and invoices](https://help.shopify.com/en/manual/fulfillment/managing-orders/create-orders)
- [Flow action: update checkout to draft for company location](https://help.shopify.com/en/manual/shopify-flow/reference/actions/update-checkout-to-draft-for-company-location)
- [Changelog, 8 July 2024: send draft order invoices with payment terms](https://changelog.shopify.com/posts/send-draft-order-invoices-with-payment-terms)

QuotWay's state machine, transition rules and conversion behaviour are described from its source; the merchant-facing account is in [build and send a proposal](/docs/negotiation/proposals-and-versions), [how approval enforcement works](/docs/approvals/how-approval-enforcement-works) and [convert a quote to a draft order](/docs/converting-to-orders/convert-a-quote-to-a-draft-order). Plan differences are on the [pricing page](/pricing).
