Skip to content

For Shopify agencies

ERP, CRM and PIM integration patterns for Shopify B2B quoting

By Jahangir Alam · September 22, 2026 · 18 min read

Last verified
Shopify API
2026-07
Audience
Shopify agencies, solution architects and integration developers
Scope
Ownership of each field across PIM, ERP, CRM, Shopify and a quote layer; company externalId, catalog price lists, Flow and the orders/* webhooks at API 2026-07; what a quote-originated order carries; patterns, not connectors

A Shopify B2B quote layer should not be wired to the ERP, the CRM or the PIM directly. It should be wired to Shopify, and Shopify to the rest: the quote reaches the ERP as the order it becomes, it reaches the CRM as the events it emits, and it never reaches the PIM at all, because it reads product data from Shopify like every other part of the store. That is not a shortcut. It is the only arrangement in which every field has one writer, and it is the arrangement the Shopify-to-ERP connector the store already runs was built for.

This page sets out the pattern for the agency scoping the integration: which system owns which field, the identity keys to settle before any sync runs, the three doors a quote can leave through today and what each one carries, the ERP-to-Shopify price-list flow the quote layer depends on, the failure modes specific to quoting, and ten questions to answer in the statement of work. It is about patterns, not connectors: it names no product as "integrated" and, where a capability does not exist yet, says so.

Everything about Shopify below was checked against Shopify's own pages on 22 September 2026 at API version 2026-07; sources are at the end. Where the page describes what QuotWay writes and exposes, that is one implementation of the pattern, labelled as such.

Who owns what

An integration is a list of fields and, for each, the one system allowed to write it. Everything else about the project - cadence, transport, error handling - follows from that list, and most integration incidents trace back to a field with two writers. For a Shopify B2B store with a quote layer, the list is short enough to fit in one table.

Field Writer (system of record) Where it lands in Shopify Who reads it Typical cadence
Product content: titles, descriptions, media, attributes PIM (or Shopify itself when there is no PIM) Product, ProductVariant, metafields Storefront, quote layer, ERP by SKU Batch, on change
Variant existence and SKU PIM or ERP item master ProductVariant.sku Everything Batch, on change
Base price ERP ProductVariant.price Storefront, catalogs, quote layer Batch, daily or on change
Customer-specific prices and quantity rules ERP Catalog price lists (PriceList), quantity rules Storefront for the logged-in location, quote layer as the starting price Batch, on change
Stock ERP or WMS InventoryLevel per location Storefront, checkout, conversion pre-flight Near real time
Company, locations, addresses, tax ID, exemptions, payment terms, checkout settings ERP customer master Company, CompanyLocation (with externalId), BuyerExperienceConfiguration Storefront, checkout, quote layer through purchasingEntity Batch, on change
Contacts and their roles CRM or ERP, one of them CompanyContact, Customer Sign-in, quote layer On change
The negotiated price and terms of one deal The quote layer - the sealed accepted version The draft order it creates at conversion Shopify checkout, then the ERP as an order Once, at acceptance
The order and its state Shopify Order ERP through the connector; CRM as the outcome of the deal Event, orders/* webhooks
Fulfilment and capture status ERP or WMS writing back into Shopify Fulfillment, transactions Storefront account, notifications Event
Pipeline: deal, stage, owner, forecast CRM Nothing - it stays in the CRM Sales management Event, from quote events

Two things in that table do the work. The quote layer owns exactly one row - the record of what the two sides agreed - and reads everything else. And Shopify is the meeting point for every other row: the PIM writes products into it, the ERP writes prices, stock and companies into it, and the ERP reads orders out of it. Nothing writes into the quote layer, and the quote layer writes into nothing but Shopify. A quote is not something the ERP receives; an order is.

That also answers a question the search data for this topic keeps surfacing: Shopify is not an ERP. It is the system of record for products as sold, customers as buyers, and orders as placed, and it deliberately holds nothing about purchasing, general ledger, costing or manufacturing. The ERP remains the master for prices, stock and the customer's commercial identity, and the table above has it writing those into Shopify, never the other way round.

Identity keys before anything else

A sync that cannot say "this Shopify company is that ERP customer" ends up with a lookup table nobody maintains. Shopify gives each B2B object a field for exactly this, so settle the keys first.

  • Companies and locations. CompanyInput.externalId is "a unique externally-supplied ID for the company", and CompanyLocationInput.externalId the same for the location. Put the ERP customer number on the company and the ERP ship-to or bill-to id on each location. The companies query filters on external_id, so the connector can find the record it needs without a table of its own. For anything the ERP needs beyond one id - a price group, a sales-rep code, a credit limit - use metafields: COMPANY and COMPANY_LOCATION are metafield owner types, and the same query filters on metafields.{namespace}.{key}.
  • Products. Match on SKU only if the ERP guarantees the SKU is unique across every variant it exports; if it does not, carry the ERP item id in a variant metafield and match on that. A quote line references a Shopify variant, so whatever key the ERP uses for the item must resolve to a variant before the quote layer is involved.
  • Orders that came from quotes. This is the key the quote layer has to provide, because nothing in Shopify marks an order as quote-originated. QuotWay writes two tags on every draft order it creates - quotway and quotway-<reference>, for example quotway-QW-1042 - and Shopify carries draft-order tags onto the order "when you create an order from a draft order". Tags on orders are limited to 40 characters and to letters, numbers and hyphens, which a default reference respects; a custom reference prefix should too. The draft also carries a buyer-visible order attribute, quotway_conversion_group_id, which is what QuotWay itself reads back from the orders/create payload to match the order to the quote, so it is the key to use for the same purpose in the ERP.

The one key not to use is sourceName. An order completed from the admin's draft section reports shopify_draft_order; the same draft completed by the buyer from the invoice reports web; completed by an app, it reports the app's id. That is Shopify's own description of the field's behaviour, and it means a filter on sourceName will find some quote-originated orders and miss the rest depending on who clicked.

The three doors

One writer per field: how the PIM, ERP, CRM and quote layer connect through Shopify Shopify sits in the centre holding products, catalogs and price lists, inventory, companies and locations, and orders. On the left, the PIM writes product content into Shopify products and the ERP writes prices into catalogs, stock into inventory and the customer master into companies and locations. At the bottom, the quote layer reads variants, catalog prices and companies from Shopify and writes one thing: a draft order at conversion, tagged with the quote reference. On the right, three doors leave the store. Door 1: Shopify orders flow to the ERP through the existing connector, carrying the tags, note, attributes, purchasing entity, payment terms and currency. Door 2: quote events flow through Shopify Flow to the CRM or any HTTP endpoint, carrying quote-level fields but no lines. Door 3: a CSV export of quotes goes to finance or BI in batches. A dashed arrow marked "needs an API - not yet" runs from the quote layer's lines to the ERP. Shopify - the meeting point Products and variants Catalogs - publication + price list Inventory levels Companies, locations (externalId), contacts Draft order → order (tags, note, attributes) Shopify Flow PIM product content, attributes, media writes products ERP item master, price lists, stock, customer master, receivables one writer for prices, stock, companies price lists stock companies, terms Quote layer - owns the sealed accepted version reads variants, catalog prices, companies writes one draft order per conversion, tagged quotway-<ref> writes draft reads Door 1 - ERP, as an order the connector you already run; orders/* webhooks; carries tags, note, attributes, purchasing entity, terms, currency - everything the ERP needs Door 2 - CRM, as events Shopify Flow triggers → CRM connector or Send HTTP request; quote-level fields: number, status, total, buyer, admin link - no lines Door 3 - finance or BI, as a file CSV export of quotes, batch quote lines to the ERP - needs an API, not yet
The PIM and the ERP write into Shopify; the quote layer reads from it and writes one draft order; the ERP, the CRM and finance each get the quote through the door that matches what they need.

A quote can leave the store through three doors today. Each carries a different shape of data, and picking the wrong door for a requirement is how a project ends up building something the store cannot support.

Door 1: the ERP receives the order

The ERP needs the quote at exactly one moment - when it becomes something to fulfil and invoice - and at that moment it is a Shopify order. So the ERP takes it through whatever already moves orders: a certified connector, an iPaaS, or a subscription to the orders/create, orders/updated, orders/edited, orders/paid, orders/cancelled and orders/fulfilled webhooks. Nothing about the quote layer changes that path; what the quote layer adds is the identification on the order, so the ERP can tell a quote-originated order from a storefront one and can link it to the deal.

What a QuotWay-originated order carries, as created by its conversion at API 2026-07:

On the order Value Visible to Use in the ERP
Tag quotway Admin, connector Route quote-originated orders to their own flow
Tag quotway-<reference>, e.g. quotway-QW-1042 Admin, connector The quote number, as a key
Note (merchant-only) QuotWay quote QW-1042 v3, then any internal note Admin, connector The accepted version number, for audit
Order attribute quotway_conversion_group_id Buyer and admin The idempotency key for one conversion; dedupe on it
Order attributes Special requests, then each answered form field as label: value Buyer and admin Buyer instructions and any field the form collects, such as a reference the buyer typed
Line property Customer note on lines the buyer annotated Buyer and admin Per-line instructions
purchasingEntity Company, location, contact - for company-aware quotes Connector The ERP customer, through the location's externalId
paymentTerms The location's terms as applied to the draft Connector Due date and receivables
presentmentCurrencyCode, currencyCode, totalPriceSet The quote's currency, the shop currency, totals in both Connector Book in the right currency (see failure modes)
poNumber Only if the merchant added it to the draft or the buyer entered it at checkout; the conversion does not write it Connector Purchase order matching

Two notes on that table. The quote layer does not set poNumber. QuotWay's request form has a built-in PO number field, and the value is kept on the quote and shown in the admin, but the conversion does not pass it to the draft today - neither into DraftOrderInput.poNumber nor as an attribute; only a custom form field reaches the order, as an attribute under its label. A merchant who needs the PO on the Shopify order adds it to the draft before sending the invoice, and an ERP mapping should not expect it from the quote. And the attributes are the mechanism QuotWay relies on for its own bookkeeping - its handler reads quotway_conversion_group_id from the orders/create payload to mark the quote converted - so an ERP mapping built on the same attribute is built on the same behaviour the app depends on in production.

Which connector carries the order is the store's decision, not the quote layer's. Shopify's Global ERP Program lists five certified apps today - Dynamics 365 Business Central, Brightpearl by Sage, NetSuite ERP Connector, Acumatica Cloud ERP and the Infor eCommerce Connector - and described the programme's scope at launch as "inventory, products, orders, and customer information". Whether a given connector also writes companies, locations and catalog price lists is a per-connector question, and the answer decides whether the ERP-to-Shopify rows of the ownership table run through the connector or through the Admin API. Shopify's own Spring '26 note that QuickBooks syncs B2B orders, PO numbers and company details is the kind of sentence to look for in a connector's documentation before assuming it.

Door 2: the CRM receives events

A CRM's object is the deal, and a deal changes on events: created, proposal out, negotiating, won, lost. That is the shape of a quote's lifecycle, and it is what Shopify Flow carries. QuotWay exposes ten triggers to Flow from the Professional plan - submitted, proposal sent, countered, accepted, declined, expired, converted, approval requested, granted and rejected - each with quote-level fields: the id, the number, a deep link to the quote in the admin, the status, the total and currency, who acted and when, the Shopify customer id (empty for guest requests) and the buyer's email, which is always present. Specific triggers add the round number, whether acceptance was full or partial and the accepted total, the draft order's id on conversion, and whether an approval was the merchant's or the buyer's.

From a trigger, the workflow reaches the CRM in one of two ways. Flow's built-in connectors (Slack, email, Google Sheets and the apps that have registered actions) cover the notification cases directly. For a CRM without a Flow action, Send HTTP request posts the trigger's fields to any endpoint - the CRM's API or an iPaaS webhook - with GET, POST, PUT, PATCH and DELETE, a 30-second window for the response, and a per-status retry policy that can keep trying for up to 24 hours. Flow itself is free on Basic, Grow, Advanced and Plus; the HTTP action needs Grow, Advanced or Plus.

The mapping, written as a table the CRM administrator can check:

Quote event CRM effect
Quote submitted Create or update the deal; attach the contact by email, or by customer id when present
Quote proposal sent Stage: proposal; amount: the proposal total
Quote countered Stage: negotiation; note the round number
Quote approval requested / granted / rejected Activity on the deal; branch on approval kind for the merchant's own chain versus the buyer's
Quote accepted Stage: won, amount: the accepted total; a partial acceptance is a win for the accepted lines, with the rest still open
Quote declined, quote expired Stage: lost, with the reason
Quote converted Attach the draft order id; the order number follows from Door 1 when the buyer pays

What Door 2 cannot carry is lines. The triggers hold no line items, quantities or per-line prices, and a workflow that wanted them would have nothing to fetch them from yet. A CRM that must show the quoted lines needs the API below, not a workaround on Flow.

Door 3: finance receives a file

Some requirements are reporting, not integration: finance wants the month's quotes with their status and totals against the orders they became; a BI tool wants pipeline value over time. QuotWay's analytics export (Professional and above) downloads the quotes behind the metrics as a CSV for the range and filters applied. It is batch, it is quote-level, and it is the right door when nobody needs the data before the month ends. It is the wrong door whenever a system, rather than a person, is waiting on the other side.

What waits for the API

Three requirements do not fit any door today, and the honest scoping answer is to say so rather than approximate them: pushing a quote's lines into the ERP before it converts, creating a quote from the ERP or CRM side, and two-way status between the quote and a deal. Each needs an API that lets a system read and write quotes, and QuotWay has no public API and no outbound webhooks yet. Until it does, the mapping in Door 1 is where the ERP meets the quote, and it is worth designing that mapping so it does not need to change when the API arrives: the quote number, the version number and the conversion group id will be the same keys.

Price lists: the ERP writes, Shopify resolves, the quote reads

The row in the ownership table that most often goes wrong is customer-specific pricing, because three systems have an opinion about it. The pattern that holds is one direction: the ERP writes prices into Shopify catalogs, Shopify resolves which price a given location sees, and the quote layer takes that resolved price as the starting point of the negotiation.

On the Shopify side, a catalog is a publication (which products) plus a price list (what they cost). A price list can take fixed prices per variant through the Admin API - priceListFixedPricesAdd "creates or updates fixed prices on a PriceList", with siblings to update by product, delete and manage the list - or through the admin's CSV import, which carries fixed prices only and removes a variant's fixed price when the row is imported with the price blank. Fixed prices override the catalog's percentage adjustment, so an ERP that exports customer price groups as fixed amounts and leaves the percentage rules in Shopify has a clean split. When a location reaches the same variant through more than one catalog, Shopify applies the most specific catalog first and then the lowest price, with quantity rules and volume pricing following the winning catalog; off Plus, three active catalogs across all B2B markets is the cap. The current rows are kept in the Shopify B2B reference.

Two rules make this row safe. Never let the quote layer hold a price of its own for a product - its starting price should be derived from what Shopify shows the logged-in buyer, so a price change in the ERP reaches the next quote through the catalog without a second sync. And never write a negotiated price back into a price list automatically: a deal price is one deal's outcome, and a job that promotes it to the customer's list price has turned every salesperson's concession into a permanent discount without anyone deciding so.

Companies and locations: the customer master, in one direction

The second row that attracts two writers is the company. Shopify lets a merchant create companies in the admin or through the Admin API, and a storefront sign-up flow can feed the same object; the ERP has a customer master with numbering, credit and terms. Pick one. If the ERP is the master, it creates the Company and each CompanyLocation with externalId set, the location's taxRegistrationId, taxExempt and taxExemptions, and a buyerExperienceConfiguration carrying the payment terms template, whether the location's orders go to draft for review and whether the buyer may enter a one-time shipping address; and it updates them on change. If Shopify is the master - typical when accounts come from a sign-up flow - the ERP subscribes to companies/create, company_locations/create and their update topics and creates its customer from the webhook, writing the ERP number back as externalId so the next event can be matched. Either way, one system creates; the other reacts.

Contacts follow the same rule with a different candidate: a CRM often holds the people while the ERP holds the accounts. Whichever system owns the person writes the CompanyContact; the company_contacts/* and company_contact_roles/* topics tell the other side what happened. A quote layer with company-aware quoting then reads the buyer's location at request time and carries it into the draft as purchasingEntity, which is how the location's terms, tax settings and identity reach the order and, through Door 1, the ERP.

Failure modes specific to quoting

The ordinary Shopify-to-ERP failure modes - duplicate webhook deliveries, out-of-order arrival, a connector that maps a field wrongly - apply here as everywhere. These are the ones the quote adds.

Failure mode What happens Design
Filtering on sourceName The value depends on who completed the draft: shopify_draft_order from the admin, web from the invoice, an app id from an app Identify quote-originated orders by the quotway tag or the reference tag
One quote, several orders Partial acceptance and split conversion create one draft per conversion group, so a quote can become two or more orders while unaccepted lines stay open Dedupe on quotway_conversion_group_id, not on the quote number; expect N orders per reference
Currency booked wrong The draft is created in the quote's currency (presentmentCurrencyCode); currencyCode is the shop currency; totalPriceSet carries both Decide which currency the ERP books and read that side of the money set; see multi-currency B2B on Shopify
Order edited after conversion The merchant edits the order; orders/edited fires; the quote's sealed version does not change After conversion the order is the truth for fulfilment and invoicing; the quote is the record of what was agreed, not a second copy to reconcile
Reference tag rejected or truncated Order tags are limited to 40 characters and to letters, numbers and hyphens Keep a custom reference prefix within those rules
Webhook arrives twice, late or never Delivery is at least once with eight retries over four hours; ordering is not guaranteed Idempotent handlers keyed on the order id; a reconciliation job that asks Shopify, the same design the conversion itself needs
Drafts-for-review locations Under "submit all orders as drafts for review", every storefront order from the location also arrives as a draft, next to the drafts the quote layer creates Tell them apart by tag; do not treat every draft as a quote
Guest quotes A request from a buyer with no Shopify customer carries an empty customer id in the Flow trigger Match the CRM contact on the buyer's email, which the trigger always carries
Company created on both sides Sign-up in Shopify and a customer created in the ERP for the same buyer One master; the other checks externalId before creating
Price re-import blanks a price A catalog CSV row imported with Fixed Price and Compare At both empty removes that variant's fixed price Export before import; never round-trip a partial file
PO number expected on the order The conversion does not write poNumber; the built-in PO field stays on the quote, and only a custom form field lands as an attribute Add the PO to the draft before the invoice, or collect it as a custom field and map the attribute

Ten questions before the statement of work

  1. For each row of the ownership table, which system writes it, and does the client agree in writing?
  2. What is the ERP key for a customer, a ship-to and an item, and which Shopify field carries each (externalId, a metafield, the SKU)?
  3. Does the connector the store runs, or will run, write companies, locations and catalog price lists, or only products, stock, customers and orders?
  4. How do customer-specific prices leave the ERP - as fixed amounts per variant, as percentage groups, or both - and does the three-active-catalog cap off Plus fit that?
  5. Which currency does the ERP book, and does the quote layer quote in the location's market currency?
  6. Is the ERP prepared for several orders per quote reference, and does it dedupe on the conversion group?
  7. Which quote events does the CRM need, and does the store's plan allow Send HTTP request if the CRM has no Flow action?
  8. Does anyone need quote lines in another system before conversion? If yes, that is the requirement waiting on an API, and the statement of work should say so.
  9. Who owns company creation, and how does the other side learn about it?
  10. What does the launch test plan need to add for the integration - one quote through each door, on a development store, before go-live?

How QuotWay fits the pattern

Labelled as one implementation, so the pattern above can be checked against something concrete. QuotWay owns the sealed accepted version and nothing else: it reads variants, catalog-resolved prices and, on the Enterprise plan, the buyer's company location from Shopify, and it writes one draft order per conversion, tagged and attributed as in the Door 1 table, with purchasingEntity and the location's payment terms when the quote is company-aware and the quote's currency as the presentment currency. Its conversion is idempotent and reconciled, which is what makes the N-orders-per-quote rule safe to build on. It exposes the ten Flow triggers from Professional and three actions on Enterprise, and a CSV export of quotes from Professional. It has no ERP or CRM connector of its own, and no public API or outbound webhooks yet. Plan lines are on the pricing page, the Flow surface on the Shopify Flow feature page, and the field-level trigger reference in the docs.

The Shopify facts in this post are kept current in the Shopify B2B reference.

FAQ

Is Shopify an ERP?

No. Shopify is the system of record for products as sold, customers as buyers and orders as placed. It holds no purchasing, general ledger, costing or manufacturing data, which is what an ERP is for. In a B2B store the ERP stays the master for prices, stock and the customer's commercial identity and writes those into Shopify; Shopify stays the master for the order and hands it to the ERP.

Which ERPs have a certified Shopify connector?

Shopify's Global ERP Program lists five certified apps on 22 September 2026: Dynamics 365 Business Central, Brightpearl by Sage, NetSuite ERP Connector, Acumatica Cloud ERP and the Infor eCommerce Connector. Other ERPs connect through partner-built apps, an iPaaS or the Admin API. Whether any of them also writes B2B companies and catalog price lists is a per-connector question to check in its documentation.

How does a quote get into NetSuite, Business Central or SAP?

As the order it becomes. The quote layer converts the accepted quote into a Shopify draft order tagged with the quote reference; when the buyer pays or the merchant completes it, the order flows to the ERP through the connector the store already uses, and the ERP recognises it by the tag and links it to the deal by the reference. Nothing pushes an open quote into the ERP, and nothing should.

Can Shopify Flow send quote data to HubSpot or Salesforce?

Yes, at the quote level. Flow triggers fire on submitted, proposal sent, countered, accepted, declined, expired, converted and the three approval events, with the number, status, total, currency, buyer email and admin link. A CRM with a Flow action receives them directly; any other CRM receives them through Send HTTP request on the Grow, Advanced and Plus plans. Line items are not in the triggers.

How do I sync ERP price lists into Shopify B2B?

Write them into catalog price lists, either through the Admin API's fixed-price mutations or the admin's catalog CSV import, and keep the direction one way. Fixed prices override the catalog's percentage adjustment, an empty price on import removes the fixed price, and off Plus three active catalogs across all B2B markets is the limit. The quote layer then takes the price Shopify resolves for the location as its starting price.

Do I need Shopify Plus for any of this?

Not for the pattern. Flow is on every plan, the Send HTTP request action needs Grow, Advanced or Plus, and the catalog cap off Plus is three active catalogs across B2B markets. Plus adds direct catalog assignment to companies and locations, unlimited active catalogs and deposits.

Where does a PIM fit?

Upstream of Shopify and nowhere near the quote. The PIM writes product content, attributes and media into Shopify products; the quote layer references Shopify variants and never holds product data of its own. If the PIM and the ERP disagree about whether a variant exists, that has to be settled before it reaches Shopify, because the quote line needs a variant to point at.

Sources

Shopify pages, all read on 22 September 2026 at API version 2026-07 unless dated otherwise:

What QuotWay writes on a draft order, reads back from the order and exposes to Flow is described from its source and its tests; the merchant-facing account is in the docs linked above. Market phrasing for this topic came from the autocomplete probe in the site's keyword research on 22 September 2026.

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.