Skip to content

For Shopify agencies

Companies, locations, catalogs and the price a Shopify B2B quote should start from

By Jahangir Alam · September 21, 2026 · 15 min read

Last verified
Shopify API
2026-07
Audience
Shopify agencies, developers and solution architects
Scope
Shopify B2B companies, locations, B2B markets, catalogs, price lists, quantity rules and volume pricing at API 2026-07, and the price a quote line should be resolved from

A Shopify B2B quote should start from the price the buyer already sees: the price Shopify resolves for their company location, at the quantity they asked for, in the currency of their market, on the day they asked. That number is derived, not stored, and a quote layer's job is to read it at the moment of the request, record it with its provenance and negotiate from it - never to keep its own copy.

The derivation runs Company → location → the catalogs that apply → the price list's fixed price or percentage adjustment → the quantity rule and volume break → one contextual unit price, and every link in that chain can change what the number is.

This page is the derivation written out: which Shopify objects sit on the chain, what happens when several catalogs apply to one location (the rules are documented, and they are not "the lowest price" alone), how quantity changes the price before anyone has negotiated, and how a quote layer should hold the result. It is for the agency asked "will a quote app conflict with catalogs?" and "if a company has negotiated catalog pricing, what should an RFQ start from?" - the two questions that decide whether a B2B build has one price list or two.

Everything about Shopify below was checked against Shopify's own pages on 21 September 2026 at API version 2026-07; sources are at the end. Where the page describes how QuotWay resolves the price, that is one implementation of the pattern.

The chain, object by object

Company and company location. The company is the account; the location is the entity actually sold to. Catalogs, payment terms, tax settings and checkout settings all attach to the location, so the price a buyer sees depends on which location they are ordering for, not on which company they belong to. A contact ordering for two locations of the same company can see two prices for the same SKU. The architecture post explains why the quote must therefore carry the location ID, not just the company's.

B2B markets. On every plan, a location belongs to the B2B markets whose conditions it meets: "all company locations in all regions", "all company locations within a region" (matched by the location's shipping address, and new locations match automatically), or "specific company locations". A market sets the currency and attaches catalogs. Below Plus, this is the only way a catalog reaches a location - Basic, Grow and Advanced allow three active catalogs across all B2B markets - and a market with no locations delivers no B2B pricing at all. A location can match more than one market, which is a design smell rather than a feature: Shopify says only that the buyer's experience follows the markets they match.

Catalogs. A catalog is two things: a publication, which decides which products the location can see, and a price list, which decides what they cost. Either half can be missing, and the documentation is precise about what happens then: with no price list, the location gets "the initial variant prices converted to the market currency"; with no publication, "customers logged into their B2B accounts won't see any products for that location". On Plus a catalog can also be assigned directly to a company or a location, a location can carry up to 25, and a store up to 10,000.

The price list. Inside it, a product's price is either a fixed price for all its variants or the result of an overall adjustment - a percentage increase or decrease on the base price. Fixed wins where both exist. Compare-at prices can be carried through or nullified. Volume pricing and quantity rules also live here, per variant.

Quantity rules and volume pricing. A variant can have a minimum, a maximum and an increment; up to ten price breaks, each at a minimum quantity; both per variant and never combined across variants. Two consequences matter for a quote. Once volume pricing is set on a product, its price is fixed and the catalog's overall adjustment no longer applies to it. And where several catalogs apply, quantity rules and breaks come "from the catalog with the lowest price per variant".

Resolution. Shopify combines all of the above into one number per variant per context. In Liquid it is variant.price, "output in the customer's local (presentment) currency", with quantity_price_breaks "for the variant in the current customer context". In the Admin API it is ProductVariant.contextualPricing(context: { companyLocationId }), which returns price - "the final price after all adjustments are applied" - plus the quantityRule and the quantityPriceBreaks for that location. That is the number a quote starts from.

How a Shopify B2B quote's starting price is derived A left-to-right chain of boxes. Company leads to company location. The location leads to two sources of catalogs: the B2B markets it matches, whose catalogs apply on every plan, and catalogs assigned directly to the location, which needs Shopify Plus. Both feed a resolution step labelled most specific first, then lowest price. The winning catalog's price list gives a fixed price or a percentage adjustment, then the quantity rule and volume break for the requested quantity apply, producing one contextual unit price in the market's currency. That price becomes the quote line's baseline, recorded with the catalog ID and the date, beside the buyer's requested price and the merchant's offered price. After negotiation the accepted price is written to the draft order line as a priceOverride. Shopify resolves the price; the quote layer reads it Company Company location B2B market catalogsevery plan · 3 active off Plus Direct location catalogsShopify Plus · up to 25 Resolutionmost specific, then lowest Price listfixed price or % adjustment Quantity rule + breakper variant, at requested qty Contextual pricemarket currency matched by region or by name contextualPricing(companyLocationId) or Liquid variant.price Quote line - three numbers kept apart Baseline = contextual price · catalog ID · date Requested (buyer) Offered (merchant, per version) Accepted → priceOverride on the draft order read at request time, never copied as a list
Everything left of the strip is Shopify's; the quote layer reads the end of the chain and records where it came from.

When more than one catalog applies

"The lowest price wins" is the sentence everyone remembers, and it is only half the rule. Shopify's Markets documentation states two rules, and the order matters. First: "if the catalogs assigned to the company location and matching B2B markets include the same product at different prices, then your B2B store displays the most specific price for that item, and for the catalog assigned to the company location" - a direct assignment beats a market assignment. Second: "if the catalogs assigned to the same company location include the same product at different prices, then your B2B store displays the lowest price for that item" - among catalogs at the same level, the lowest wins. Quantity rules and volume pricing then follow "the catalog with the lowest price per variant".

The table lists the configurations an agency meets and what the buyer sees for each. A quote that starts from anything else is starting from a price the buyer has never been shown.

Configuration What the location's buyer sees Source
One catalog, assigned through the location's B2B market That catalog's price in the market's currency help: B2B and Markets
Two market catalogs carrying the same product The lower of the two prices; quantity rules and breaks from the lower-priced catalog help: catalogs with Markets
A market catalog and a direct location catalog (Plus) carrying the same product The direct catalog's price - "the most specific" - even if the market catalog is cheaper help: catalogs with Markets
Two direct location catalogs carrying the same product The lower price help: catalogs with Markets
A catalog with a publication but no price list Base variant prices, converted to the market currency shopify.dev: manage catalogs
A B2B catalog with a price list but no publication No products at all for that location shopify.dev: manage catalogs
A product not published on any of the location's catalogs Not visible; not orderable through the storefront shopify.dev: manage catalogs
Fixed price and overall adjustment both set on a product The fixed price help: customizing B2B pricing
Volume pricing set on a product The product's price is fixed; the catalog's overall adjustment no longer applies to it; the break for the quantity reached applies per variant help: quantity rules and volume pricing
A location in no B2B market and with no direct catalog No B2B pricing - "a B2B market without company locations won't deliver B2B pricing" help: managing B2B with Markets
A location matching two B2B markets Shopify says the experience "is determined on the customizations of the markets that they match"; avoid it by making market conditions disjoint help: managing B2B with Markets

Two rows cause most of the support tickets an agency inherits. The publication-without-price-list row produces "the catalog is assigned but they're seeing retail prices"; the price-list-without-publication row produces "the catalog is assigned and they can't see anything". Both are a catalog with one half missing, and both look, from a quote layer's side, like a perfectly valid contextual price.

Quantity changes the price before anyone negotiates

A request for 500 units is not a request at the unit price. If the variant carries volume pricing, the buyer's own storefront already showed them a lower price at 500, and a quote that starts from the single-unit price is asking them to negotiate down to a number Shopify would have given them for free.

So the starting price is resolved at the requested quantity. In Liquid, the storefront has quantity_price_breaks for the variant in the buyer's context; in the API, contextualPricing(companyLocationId) returns quantityPriceBreaks, each with a minimumQuantity and the price "after reaching the minimum quantity", alongside the quantityRule. The baseline for a line of quantity Q is the highest break whose minimum Q reaches, or the contextual unit price when no break applies - and the quantity rule tells you whether Q is orderable at all: below the minimum, above the maximum, or off the increment, the line cannot become a draft-order line without a change, and the quote should say so before the merchant prices it.

Two subtleties follow from Shopify's per-variant rule. Breaks do not combine across variants, so 300 of the blue and 200 of the red do not reach a 500 break on either; a quote that pools them is offering a price the catalog does not. And once a product has volume pricing its price is fixed, so a catalog-wide 20 percent adjustment silently stops applying to it - the buyer may be seeing a higher unit price on that product than on its neighbours, and the quote should start from what they see, not from what the catalog's headline adjustment implies.

Three numbers, kept apart

A quote line needs three prices, and the failure that turns a quote layer into a second price list is storing only one of them.

The baseline is the contextual price above: resolved for the location, at the requested quantity, in the market currency, at request time. It is stored with the catalog's ID and the date, so that a year later the record says "this is what the catalog said on 21 September", never "this is the price". It is never edited.

The requested price is the buyer's ask, if they made one. It is data about the buyer, not about the catalog, and it must not overwrite the baseline: the merchant needs to see both to know how far apart the two sides are.

The offered price is the merchant's answer, set per proposal version. A counter replaces it in the next version; the previous version keeps its own. The accepted version's offered price is the one that reaches the draft order as a priceOverride on the variant line, in the presentment currency, and the draft-order post covers why nothing after acceptance edits it.

Keeping the three apart is also what keeps the quote layer out of the catalog's business. It never writes to a price list; it never keeps a table of company prices; it reads one number per line, per request, and records it. When the catalog changes next week, the quote still knows what it started from and the storefront still shows the new price. The two cannot disagree because neither is a copy of the other.

Currency follows the market

The contextual price arrives in the location's market currency, and the quote should stay in it. Shopify converts base prices to the market currency where a catalog has no price list, and a price list's fixed prices are in the price list's own currency; either way, the number the buyer sees is in one currency and the quote that starts from it must be in the same one. A quote layer that stores the baseline in the shop's base currency and reconverts later will drift by the day's rate and by rounding, and the draft order - created with a priceOverride "in presentment currency" - will not match the proposal the buyer accepted.

The practical rule: the quote's currency is fixed at creation from the location's market, every price on it is in that currency, and any exchange rate the quote layer needs for reporting is captured once as a snapshot rather than applied to the prices.

Where the quote layer sits in the chain

The chain above runs entirely inside Shopify up to the contextual price, and the quote layer's contract with it is three reads and one write.

It reads the price. On the storefront, the request already carries it: Liquid's variant.price for a signed-in company contact is the contextual price, so a quote button that submits the price the buyer saw is submitting the right baseline without an API call. For a quote created in the admin on the buyer's behalf, there is no storefront context, and the layer has to ask the API - contextualPricing(context: { companyLocationId }) per variant - or it will baseline at the default variant price and label it the catalog price, which is exactly the bug that produces "why is the quote higher than my price list".

It reads visibility. A location's catalogs each carry a publication, and a product is on the location's catalog when it is published on at least one of them. This is a separate question from price: contextualPricing.price is non-null and falls back to the base price when no catalog price exists, so a returned price never proves the product is on the catalog. A layer that infers "on catalog" from "has a price" will quote products the buyer cannot see, and the draft order will then carry a line the location was never entitled to.

It reads the quantity rule and the breaks, as above.

It writes once: the accepted price to the draft-order line as priceOverride, with the purchasingEntity so that the location's terms and tax settings apply. It never writes a price list, a catalog, a publication or a market. The evaluation checklist asks each of these as a question; this is what the right answers look like.

How QuotWay resolves it

QuotWay's company-aware quoting - on the Enterprise plan, on a store with B2B enabled - follows the chain as described up to the contextual unit price: that unit price is the baseline it records per line, beside the requested quantity, and the break a buyer already has at that quantity is the merchant's input in the editor rather than a second resolved number. Three details of the implementation are worth stating because they are where a custom build usually cuts a corner.

The storefront request carries Liquid's price. The quote button reads product.selected_or_first_available_variant.price in the buyer's context and submits it as the line's catalog price beside the quantity and any requested price, so a buyer-submitted quote baselines at what they saw. A quote created in the admin for a company location resolves the same number through contextualPricing(companyLocationId) per variant - an earlier version baselined admin quotes at the default variant price and labelled it "Catalog", and that is the bug the resolver exists to prevent. At proposal send, the baseline is stored per line in the location's currency together with the catalog's ID as provenance. The ID is the first of the location's catalogs, recorded as a best-effort marker, because contextualPricing resolves the effective price across all of them without naming the winner.

Off-catalog detection uses the publication, not the price. For each quoted product the layer asks whether it is published on any of the location's catalog publications; a product on none of them is flagged in the proposal editor and again at conversion. When the location has no catalog publication at all, availability falls to the sales channel and the test is undeterminable, so the layer does not warn rather than warn wrongly. Prices are cached for a few minutes per location so a proposal with forty lines does not make forty round trips, and every resolver fails open: if Shopify is unreachable the merchant keeps the submitted price and quoting is never blocked.

What the layer does not do is as important. It has no price table. The proposal editor shows the baseline as a label and the merchant types the offered price; the requested price sits beside both. The merchant-facing guide shows the same thing from the admin's side, and the B2B feature page lists what company-aware quoting carries onto the quote.

Designing the catalogs so the chain is short

The derivation is deterministic, but an agency can make it easy or hard to reason about. Four design rules keep it short.

  1. One catalog per location where the plan allows it. On Plus, a direct assignment makes the "most specific" rule do all the work; a location with one direct catalog has no collision to resolve. Below Plus, aim for market conditions that put each location in exactly one B2B market, so the three-catalog limit is spent on markets that do not overlap.
  2. Never split a catalog's halves without meaning to. A pricing-only catalog and a publication-only catalog on the same location is a supported pattern for scale, but it is also the pattern behind most "wrong prices" tickets. If you use it, document which catalog owns visibility and which owns price, and test both rows of the table above.
  3. Put volume pricing where the negotiation is not. A product with breaks has a fixed price, so a catalog-wide adjustment stops touching it. Use breaks for the standing tiers every buyer gets, and leave deal-specific volume to the quote, where the merchant can see the break the buyer already has and price below it deliberately.
  4. Make the baseline visible to the merchant. Whatever the quote layer resolves, the person pricing the proposal must see it as "catalog: X for this location at this quantity" next to the buyer's ask. A merchant who cannot see the baseline will either re-quote the catalog price the buyer already had or undercut it without knowing.

FAQ

What price should a Shopify B2B RFQ start from?

The price the buyer sees: the one Shopify resolves for their company location at the requested quantity, in the market's currency, at the time of the request. In Liquid that is variant.price in the buyer's context; in the Admin API it is ProductVariant.contextualPricing(context: { companyLocationId }), together with its quantityPriceBreaks for the requested quantity. Record it with the catalog ID and the date, keep the buyer's requested price in a separate field, and negotiate from there.

Which catalog applies when a company location has several?

Two rules, in order. A catalog assigned directly to the location (Shopify Plus) is "the most specific" and beats a catalog that reaches the location through a B2B market. Among catalogs at the same level that carry the same product, the lowest price wins, and quantity rules and volume pricing follow the catalog with the lowest price per variant.

Will a quote app conflict with Shopify catalogs?

Not if it never stores a price list. The conflict arises when an app keeps its own table of company prices and the storefront's catalog diverges from it. A quote layer that reads the contextual price per line at request time, records it as a dated baseline, and writes the agreed price to the draft order as a priceOverride leaves the catalog untouched and cannot disagree with it.

Why does a B2B buyer see retail prices even though a catalog is assigned?

Usually because the catalog has a publication but no price list: Shopify then shows "the initial variant prices converted to the market currency". The reverse - a price list but no publication - makes the buyer see no products at all. Check both halves of the catalog, then check the location is actually in a B2B market (below Plus) or directly assigned (Plus).

Does volume pricing change the quote's starting price?

Yes. If the variant has a price break the requested quantity reaches, the buyer already sees that lower price, and the quote should start from it. Breaks are per variant and do not combine across variants. Note also that once a product has volume pricing its price is fixed and the catalog's overall percentage adjustment no longer applies to it.

How does an app know a product is not on the buyer's catalog?

By publication membership, not by price. contextualPricing.price is non-null and falls back to the base price, so a returned price proves nothing. The product is on the location's catalog when it is published on at least one of the location's catalog publications; if the location has no publication at all, availability comes from the sales channel and the question cannot be answered from the catalog.

How do I set customer-specific prices on Shopify B2B?

With catalogs: a product selection plus a price list, assigned to a B2B market on any plan or directly to a company or location on Shopify Plus, with fixed prices or a percentage adjustment and optional quantity rules and volume pricing. The merchant guide to catalogs walks through it; this page covers what a quote should do with the result.

Sources

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

QuotWay's resolution and off-catalog behaviour are described from its source; the merchant-facing account is in company-aware B2B quoting and the catalogs guide. Plan differences are on the pricing page; the Shopify B2B reference keeps the catalog facts current.

Related articles

See how QuotWay handles this on your store.

We’d like to set analytics cookies to understand how the site is used. They’re not required — declining changes nothing about how the site works, and you can change your mind any time on our privacy page.