The quote object
Read time: 10 minutes. Who it's for: Developers mapping QuotWay quotes into another system.
GET /v1/quotes, GET /v1/quotes/{id}, POST /v1/quotes and POST /v1/quotes/{id}/send-proposal
all return quotes in this shape. It's the merchant's view of the quote: it includes lines the
buyer can't see (flagged as such) and never includes internal notes.
Conventions
- Keys are
snake_case; enum values are lowercase (proposal_sent,b2b). - Money is a decimal string with exactly two places (
"1250.00"), always next to an ISO 4217 currency code. Parse it with a decimal type, not a float. - Timestamps are ISO-8601 in UTC (
"2026-09-29T14:12:00.000Z"). - Shopify references are Shopify GIDs (
gid://shopify/Customer/7012345678). - New fields may be added to this object at any time; your code should ignore fields it doesn't know. See Versioning.
Example
A wholesale quote where the buyer accepted one line, declined one and left one open:
{
"object": "quote",
"id": "clz8k2x9f0001qw7h3m4n5p6r",
"number": "QW-1042",
"status": "partially_accepted",
"mode": "wholesale",
"source": "product_page",
"currency": "USD",
"version_number": 2,
"negotiation_round": 1,
"po_number": "PO-88213",
"requested_delivery_date": "2026-11-01T00:00:00.000Z",
"expires_at": "2026-10-14T00:00:00.000Z",
"created_at": "2026-09-22T09:31:05.412Z",
"updated_at": "2026-09-29T14:12:00.000Z",
"first_accepted_at": "2026-09-29T14:12:00.000Z",
"fully_converted_at": null,
"invoice_sent_at": null,
"totals": {
"currency": "USD",
"subtotal": "2300.00",
"discount_total": "115.00",
"shipping_total": "40.00",
"tax_estimate": "0.00",
"total": "2225.00",
"accepted_total": "1187.50",
"still_open_total": "450.00",
"headline": "1187.50",
"headline_mode": "open",
"base_currency": {
"currency": "USD",
"total": null,
"accepted_total": null,
"exchange_rate": null
}
},
"lines": [
{
"id": "clz8m0v1a0010qw7hk2l3m4n5",
"quote_line_item_id": "clz8k2xa00002qw7h9a8b7c6d",
"position": 0,
"product_id": "gid://shopify/Product/8123456789",
"variant_id": "gid://shopify/ProductVariant/4455667788",
"sku": "NW-CUP-12OZ",
"title": "Compostable cup, 12 oz",
"variant_title": "Case of 50",
"quantity": 100,
"catalog_price": "14.00",
"requested_price": "11.50",
"unit_price": "12.50",
"line_total": "1250.00",
"is_custom": false,
"requires_shipping": true,
"taxable": true,
"buyer_visibility": "show",
"kit": null,
"decision": "accept",
"accepted": true,
"still_open": false
},
{
"id": "clz8m0v1a0011qw7hp6q7r8s9",
"quote_line_item_id": "clz8k2xa00003qw7hd5e4f3g2",
"position": 1,
"product_id": "gid://shopify/Product/8123456790",
"variant_id": "gid://shopify/ProductVariant/4455667790",
"sku": "NW-LID-12OZ",
"title": "Lid, 12 oz",
"variant_title": null,
"quantity": 20,
"catalog_price": "32.00",
"requested_price": null,
"unit_price": "30.00",
"line_total": "600.00",
"is_custom": false,
"requires_shipping": true,
"taxable": true,
"buyer_visibility": "show",
"kit": null,
"decision": "reject",
"accepted": false,
"still_open": false
},
{
"id": "clz8m0v1a0012qw7ht1u2v3w4",
"quote_line_item_id": "clz8k2xa00004qw7hh1i2j3k4",
"position": 2,
"product_id": null,
"variant_id": null,
"sku": null,
"title": "Custom logo print setup",
"variant_title": null,
"quantity": 10,
"catalog_price": null,
"requested_price": null,
"unit_price": "45.00",
"line_total": "450.00",
"is_custom": true,
"requires_shipping": false,
"taxable": true,
"buyer_visibility": "show",
"kit": null,
"decision": "defer",
"accepted": false,
"still_open": true
}
],
"proposal": {
"version_number": 2,
"sent_by": "merchant",
"created_at": "2026-09-24T16:02:44.108Z",
"shipping_status": "final",
"shipping_label": "UPS Ground",
"payment_terms": "Net 30",
"deposit_percentage": null,
"expires_at": "2026-10-14T00:00:00.000Z"
},
"approval": { "active": false, "pending_domain": null },
"shopify": {
"customer_id": "gid://shopify/Customer/7012345678",
"company_id": null,
"company_location_id": null,
"company_contact_id": null,
"draft_order_ids": [],
"order_ids": []
},
"assigned_staff": { "id": "clx3q9r8s0000ab12cd34ef56", "email": "sam@northwind-supply.com" },
"kit_conversion_mode": "collapse",
"based_on_quote_id": null,
"links": {
"admin_url": "https://admin.shopify.com/store/northwind-supply/apps/<app-client-id>/app/quotes/clz8k2x9f0001qw7h3m4n5p6r"
}
}
With the read_customer_data scope, the quote also carries a customer object and each line a
buyer_note - see Buyer personal data.
Top-level fields
| Field | Type | Description |
|---|---|---|
object |
"quote" |
Always quote. |
id |
string | The quote's id. Use it in API paths. |
number |
string | The human reference buyers and staff see, e.g. QW-1042 (the prefix is the store's setting). Not usable in API paths. |
status |
string | Where the quote is in its life cycle - see Status. |
mode |
string | wholesale (a standard quote) or b2b (a company-aware quote tied to a Shopify B2B company location). |
source |
string | Where the request came from: product_page, cart, buyer_portal, admin (created by staff), guest, customer_account (buyer quick order in the Shopify customer account) or api. |
currency |
string | ISO currency of the quote's money. |
version_number |
integer | The current proposal version (0 before any proposal exists). |
negotiation_round |
integer | How many negotiation rounds the quote has been through. |
po_number |
string | null | The buyer's purchase-order number. |
requested_delivery_date |
timestamp | null | The delivery date the buyer asked for. |
expires_at |
timestamp | null | When the current offer expires. |
created_at / updated_at |
timestamp | Created / last changed. updated_at is the field to sync on. |
first_accepted_at |
timestamp | null | When the buyer first accepted any part of the quote. |
fully_converted_at |
timestamp | null | When every accepted line had become a Shopify draft order. |
invoice_sent_at |
timestamp | null | When the draft-order invoice was sent. |
totals |
object | See Totals. |
lines |
array | See Lines. |
proposal |
object | null | The current proposal version; null before the first proposal. See Proposal. |
approval |
object | active - an approval chain is in progress; pending_domain - merchant (your team's approval) or buyer (the buyer's internal approval), or null. |
shopify |
object | Shopify ids: customer_id, company_id, company_location_id, company_contact_id (each may be null), plus draft_order_ids and order_ids (arrays of GIDs). These are identifiers, not personal data. |
assigned_staff |
object | null | The staff member the quote is assigned to (id, email). |
kit_conversion_mode |
string | How kits become draft-order lines: collapse (one line at the kit price) or expand (the components). |
based_on_quote_id |
string | null | Set when staff created the quote with Duplicate quote; the id of the original. (A buyer's reorder is a fresh request and doesn't set it.) |
links.admin_url |
string | Opens the quote in the QuotWay admin inside Shopify. Staff need to be logged in to Shopify. |
customer |
object | Only with read_customer_data. See Buyer personal data. |
Status
status is one of:
| Value | Meaning |
|---|---|
submitted |
A new request, not yet opened. Quotes created with POST /v1/quotes start here. |
in_review |
Your team has opened the quote and is working on it. |
awaiting_info |
Waiting for information from the buyer. |
awaiting_merchant_approval |
A proposal is waiting for your team's internal approval before it goes out. |
proposal_sent |
A proposal is with the buyer. |
awaiting_buyer_approval |
The buyer's own internal approval chain is reviewing the proposal. |
countered |
The buyer made a counter-offer. |
partially_accepted |
The buyer accepted some lines. |
fully_accepted |
The buyer accepted the whole proposal. |
partially_converted |
Some accepted lines have become a Shopify draft order. |
fully_converted |
Every accepted line has become a Shopify draft order. |
invoice_sent |
The draft-order invoice has been sent to the buyer. |
order_completed |
The buyer paid and the order was placed. |
order_cancelled |
The resulting order was cancelled. |
order_refunded |
The resulting order was refunded. |
declined |
The quote was declined. |
expired |
The offer passed its expiry date. |
closed |
The quote was closed. |
New statuses may be added in future; treat an unknown value as "in progress" rather than failing.
Totals
totals answers two different questions - what was offered and what was agreed - and gives
you one field, headline, that is always the right figure to act on.
| Field | Description |
|---|---|
currency |
ISO currency of every amount in totals (except base_currency). |
subtotal |
Sum of the current proposal's lines, before discount. |
discount_total |
The quote-level discount. |
shipping_total |
Shipping on the proposal. |
tax_estimate |
Estimated tax. Shopify computes the final tax on the draft order. |
total |
What was offered on the current version: subtotal − discount + shipping + tax estimate. Never rewritten by acceptance. |
accepted_total |
What the buyer committed to on a mixed acceptance - the accepted lines, net of their share of the quote discount, excluding shipping and tax. null when nothing has been accepted, and also null on a full acceptance (use total). |
still_open_total |
The value of the lines the buyer left undecided (the sum of their line totals, before any discount share). null unless some lines are still open. |
headline |
The figure to act on. See headline_mode. Always present. |
headline_mode |
Why headline is what it is - see below. |
base_currency |
The store's base currency view: currency, total, accepted_total and the exchange_rate captured when the quote was requested. The amounts are null when no exchange-rate snapshot was captured - fall back to totals.total when the quote's currency equals the base currency. |
headline_mode
| Value | Situation | headline equals |
|---|---|---|
none |
Nothing accepted yet. | total |
full |
Accepted in full. | total |
decided |
Every line was either accepted or declined - nothing is pending. | accepted_total |
open |
Some lines accepted, some still undecided. | accepted_total (what's agreed so far); still_open_total is what's still in play |
If you only store one number, store headline. Reconstructing it yourself from total and
the lines is how quote values end up wrong in a CRM: on a partial acceptance total still shows the
whole original offer.
Lines
Before the first proposal, lines are the buyer's requested lines. Once a proposal exists, they are
the current proposal version's lines, and quote_line_item_id links each one back to the
request line it came from.
| Field | Description |
|---|---|
id |
The line's id (a proposal-version line once a proposal exists). |
quote_line_item_id |
The request line this proposal line came from; null before a proposal. |
position |
Sort order, starting at 0. |
product_id / variant_id |
Shopify GIDs; null for custom lines. |
sku, title, variant_title |
As captured from Shopify. A kit's parent line shows the kit's name. |
quantity |
Integer. |
catalog_price |
The Shopify price when the line was captured. |
requested_price |
The unit price the buyer asked for, if any. |
unit_price |
The price you offered; null until priced. |
line_total |
unit_price × quantity; null until priced. |
is_custom |
true for a custom line (not a Shopify product). |
requires_shipping, taxable |
How the line converts to the draft order. |
buyer_visibility |
What the buyer sees: show, price_hidden (shown as "Included", no price) or hidden (not shown to the buyer at all). |
kit |
For kit lines: key (groups a kit's lines), role (parent or component) and title. null otherwise. |
decision |
The buyer's per-line decision on a mixed acceptance: accept, reject (declined) or defer (decide later). null when there's nothing to mark - not yet accepted, or accepted in full. |
accepted |
true when the line is part of an accepted set. |
still_open |
true when the line is undecided and still negotiable. |
buyer_note |
The buyer's note on this line. Only with read_customer_data. |
still_open is not !accepted
A declined line is neither accepted nor still open. Use the pair like this:
accepted |
still_open |
The line is… |
|---|---|---|
false |
true |
not yet decided - before acceptance, or deferred by the buyer |
true |
false |
accepted |
false |
false |
declined |
Kits
A kit is sold as one line at one price. Its parent line carries the price; its component
lines are 0.00 and exist so stock and fulfilment see what's in the box. All of a kit's lines share
the same kit.key. To total a quote yourself, sum parents and ordinary lines - the components add
nothing. Component lines are often buyer_visibility: "hidden"; they're included here because this
is the merchant's view.
Proposal
proposal describes the current version, or is null before the first one.
| Field | Description |
|---|---|
version_number |
The version this describes. |
sent_by |
Who created it: merchant, buyer (a counter-offer) or system (automation). |
created_at |
When the version was created. |
shipping_status |
pending (shipping still to be confirmed) or final. |
shipping_label |
The shipping method label, if set. |
payment_terms |
Payment-terms name, e.g. Net 30, if set. |
deposit_percentage |
The deposit asked for, e.g. "50.00", or null. |
expires_at |
When this version's offer expires. |
Buyer personal data
With the read_customer_data scope, quotes include:
"customer": {
"email": "buyer@example.com",
"name": "Dana Ortiz",
"phone": "+1 303 555 0142",
"company_name": "Acme Restaurant Group",
"shipping_address": {
"firstName": "Dana",
"lastName": "Ortiz",
"name": null,
"company": "Acme Restaurant Group",
"address1": "1200 Larimer St",
"address2": null,
"city": "Denver",
"province": null,
"provinceCode": "CO",
"countryCode": "US",
"zip": "80204",
"phone": null
},
"billing_address": null,
"custom_fields": { "industry": "Hospitality", "weekly_volume": "500+" },
"notes": "Delivery to our Denver kitchen, please."
}
shipping_address/billing_addressare the stored address objects - note their keys are camelCase (address1,provinceCode,countryCode), unlike the rest of the API - ornull.custom_fieldsholds the answers to the store's custom quote-form fields, keyed by field key.notesis the buyer's note on the request.
Without the scope, customer and every buyer_note are absent (not null). Nothing else
changes, so the same code can handle both.
Internal notes your team writes are never returned by the API.
Related
- Pagination - listing and syncing quotes.
- Webhooks - the smaller, PII-free quote snapshot sent with each event.
- Partial acceptance - how buyers accept line by line.
Brauchen Sie noch Hilfe? Das Team hilft gern weiter.