For Shopify agencies
Shopify B2B technical debt: the decisions that get expensive later
By Jahangir Alam · September 24, 2026 · 13 min read
- Last verified
- Shopify API
- 2026-07
- Audience
- Shopify agencies and developers scoping, auditing or inheriting a B2B build
- Scope
- Structural decisions that are hard to reverse (store type, company tree, where prices live, identity keys, deal state, Plus dependencies), surfaces with a Shopify retirement date (checkout.liquid, Scripts, legacy customer accounts, API versions) at API 2026-07, server-side enforcement, and a handover ledger
Technical debt in a Shopify B2B build comes in two kinds, and they need different handling. The first is structural: decisions Shopify itself says are hard to reverse - a blended or a dedicated B2B store, how buyers are split into companies and locations, where standing prices live, which identity keys join Shopify to the ERP. The second has a due date: code built on a surface Shopify retires on a published schedule - checkout.liquid (13 August 2024 and 28 August 2025), Shopify Scripts (deactivated 30 June 2026), legacy customer accounts (deprecated 26 February 2026) and every API version, each supported for at least 12 months.
Structural debt is paid by deciding before any data exists. Dated debt is paid by writing the dates down and giving someone the job of watching them. Both are documented by Shopify before the build starts; what makes them expensive is finding them after it ends.
This page is for the agency scoping or inheriting a build: the decisions that get expensive later and why, the dated surfaces in one calendar, where rule enforcement has to live, the seven ways a build goes wrong, and the debt ledger to hand over with the project. Where it describes a quote layer, that is one implementation, labelled as such.
Everything about Shopify below was checked against Shopify's own pages on 24 September 2026 at API version 2026-07, unless another date is given; sources are at the end.
Two kinds of debt
| Aspect | Structural debt | Dated debt |
|---|---|---|
| What it is | A modelling decision that everything else attaches to | Code on a surface Shopify has announced it will retire |
| Examples | Store type, company tree, where prices live, identity keys, where deal state lives, Plus dependencies | checkout.liquid, Shopify Scripts, legacy customer accounts, API versions and deprecated fields |
| When it is cheap | Before the first company, catalog or order exists | Before the date - Shopify publishes it months ahead |
| What makes it expensive | Data attaches to it: orders, catalogs, terms, history | The date passes and the code stops running, sometimes silently |
| How to pay it down | Decide deliberately, write the reason down | A ledger of surfaces with their dates and an owner for each |
The two feed each other. A store that kept wholesale prices in Shopify Scripts carried dated debt (Scripts ended on 30 June 2026) on top of structural debt (the prices were never in a catalog), and paying the first meant paying the second.
The decisions that get expensive later
Six decisions, each with the Shopify constraint that makes it hard to change. Decide them in this order, before the first company exists.
1. Blended or dedicated store. Shopify's own guidance is blunt: "You need to make this decision carefully, as it's not easy to change at a later time. If you set up one type of store and then want to switch to the other type, then you'll need to redo most of your setup work, including companies, catalogs, and online store customizations." A dedicated B2B store is a separate store: "inventory is separate for B2B orders and customers by default", its apps must be "set up and paid for again", and between expansion stores "store settings, products, collections, and inventory aren't synced" - that is a job for an app or the ERP. On a Plus organisation, the expansion-store rules include one free B2B store per contract. None of that is wrong; it is a choice with a running cost, and it should be made because the business needs a separate storefront, not because that is how wholesale used to be run.
2. The company tree. What is one company, and what is a location? Catalogs, payment terms, tax exemptions and ship-to all attach to the location, and history is unforgiving: "B2B orders remain with the company they were created for and can't be migrated to another company", and one customer's migrated orders cannot be split across locations. A tree modelled one-to-one on an old system's customer list, without deciding which records are organisations and which are branches, cannot be corrected by re-migrating orders - it is fixed by rebuilding the tree. The migration playbook sets out the order of work that avoids it.
3. Where standing prices live. Since 2 April 2026 companies, catalogs and payment terms are on every Shopify plan, so the reasons to keep wholesale prices in customer tags, discount codes or duplicate "wholesale" products are gone. Each of those workarounds carries its own maintenance: duplicate products are separate inventory and reporting records by construction, and tag logic lives in the theme or an app rather than in what checkout reads. Catalogs are what B2B checkout, quantity rules and volume pricing read. Design the catalog count up front: off Plus, the cap is three active catalogs across all B2B markets combined, so a plan that assumes one catalog per customer is a Plus plan. Wholesale and retail pricing without duplicate SKUs covers the pricing side, and the catalog rows of the reference the precedence rules.
4. Identity keys. Company.externalId and CompanyLocation.externalId exist for one purpose: a "unique externally-supplied ID" that joins Shopify's records to the ERP's customer and ship-to numbers, and the companies query filters on it. Set on day one it costs nothing. Added a year later it means matching thousands of records by name, and every sync built in between keyed on something else. ERP, CRM and PIM integration patterns sets out who owns which field.
5. Where deal state lives. A draft order is an agreed order awaiting payment or approval: three statuses (OPEN, INVOICE_SENT, COMPLETED), no versions, and draftOrderUpdate replaces the input wholesale. Drafts created on or after 1 April 2025 are deleted after a year without an edit. A build that keeps negotiation in draft orders - the request, the counter, the history of who offered what - is storing it somewhere that can neither hold it nor keep it. A Shopify draft order is not a quote sets the draft's states against the ones a negotiation needs.
6. Plus dependencies. Some B2B features are on Shopify Plus: direct catalog assignment to a company or location, unlimited active catalogs, deposits, partial payments, a payment request per fulfilment, and - easily missed - custom apps that contain Shopify Function APIs, which Shopify documents as usable only on Plus stores (public App Store apps with functions run on any plan). None of these is a mistake. Each one ties the build to the plan, and the merchant should know that before they depend on it. Do you need Shopify Plus for B2B? has the plan line by line.
Debt with a due date
Shopify publishes retirement dates months ahead. The debt is not the old surface; it is not knowing the build uses it.
| Surface | Date | What happened | What replaces it |
|---|---|---|---|
| checkout.liquid, in-checkout steps | 13 Aug 2024 | Stopped working for Information, Shipping and Payment | Checkout extensibility |
| checkout.liquid and additional scripts, Thank you and Order status (Plus) | 28 Aug 2025 | Sunset; the additional scripts box became view-only | Extensions in checkout, blocks, web and app pixels |
| Thank you and Order status pages (non-Plus) | 26 Aug 2026 | Upgrade deadline | The same |
| REST Admin API | 1 Oct 2024 legacy; 1 Apr 2025 for new public apps | New public apps must be GraphQL-only | GraphQL Admin API |
| Legacy customer accounts | Deprecated 26 Feb 2026; sunset date to be announced | No new stores, no updates; never supported B2B | Customer accounts and their UI extensions |
| Shopify Scripts | Editing ended 15 Apr 2026; deactivated 30 Jun 2026 | Published scripts "no longer work" | Shopify Functions |
DraftOrderInput.customerId, marketRegionCountryCode |
Deprecated at 2026-07 | Still accepted, on the removal path | purchasingEntity (a customer or a purchasing company) |
DraftOrderLineItem.grams |
Removed at 2026-07 | Gone | weight |
| Every API version | Quarterly; each supported 12 months or more | An unsupported version "falls forward" to the oldest accessible stable version, silently | Upgrade every quarter or at least once a year |
Two rows deserve a second look for B2B specifically. checkout.liquid never worked for B2B checkout - Shopify lists checkout.liquid customisations among the things B2B does not support - so a B2B store never had that debt, but a blended store's D2C checkout might, and the fix touches both. And the API version row is the one that bites quietly. A custom app that names a version Shopify has stopped supporting does not fail: Shopify answers with the oldest version it still supports, and a field that was deprecated in between can simply stop being there. A public app on the App Store has a harder deadline: "If your app continues to use unsupported resources after the upgrade deadline, it's delisted from the Shopify App Store", with new installs blocked for at least seven days.
The Scripts row is the clearest recent example of both kinds of debt arriving at once. A store whose wholesale discounts or B2B shipping rules lived in Scripts had until 30 June 2026 to move them; the move to Functions is also the moment to ask whether those prices belong in a catalog instead, which is structural. Note the Functions plan rule when you scope it: a custom app with functions runs only on Plus, so a non-Plus merchant replacing Scripts is choosing between a public app and native features. The draft-order limits post covers the one documented draft-order caveat for discount functions.
Enforcement belongs on the server
The quietest form of debt is a rule that looks enforced and is not. Theme code runs in the buyer's browser; it can show or hide a button, but a determined buyer, a cached page or a second storefront does not run it. The rule that matters for money has to live where Shopify checks it.
Shopify gives you three server-side places. Catalogs decide the price a signed-in company location sees. Quantity rules - minimum, maximum and increment per variant - are enforced at cart and checkout, not only displayed. And the cart and checkout validation Function API runs server-side checks "to ensure that orders meet specific criteria before allowing customers to proceed", on B2B checkout and on draft orders in the admin and at checkout, with the buyer's purchasingCompany available to the function; a store can activate up to 25.
Know the two places it does not reach: the validation API does not run on the Create Order API or on order editing. An integration that writes orders directly through orderCreate - an ERP import, a migration script - bypasses the rules a buyer is held to, so those rules need to be enforced in the integration too. That is a design note for the statement of work, not a defect.
Seven ways a B2B build goes wrong
Each of these is a pattern traced to a constraint on this page, not a measured frequency - nobody publishes those, and we do not claim an order. Each is one of the decisions above, made late or by default.
- The store type was chosen for launch speed. A dedicated store because that is how wholesale used to be run, then inventory sync, double app billing and split reporting for as long as the store exists. Shopify says switching means redoing companies, catalogs and customisations.
- The company tree copied the old customer list. Branches became companies, or organisations became locations, and orders cannot move between companies to fix it.
- Prices stayed in tags after catalogs arrived. The workaround outlived its reason (B2B on every plan since 2 April 2026) and now needs its own maintenance, beside a catalog system that checkout already reads.
- Nobody set
externalId. The ERP sync keys on names or emails, and every duplicate becomes a support ticket. - Negotiation lived in draft orders. No versions, no counter, update replaces the whole input, and an idle draft is deleted after a year.
- Checkout logic sat on a retiring surface. Scripts, checkout.liquid or additional scripts, with the date known and not tracked.
- No one owned the API upgrade. A custom app pinned to a version that later fell forward, and a field it read was no longer there.
None of these is a Shopify bug. Each is a constraint that was documented before the build and discovered after it.
The debt ledger to hand over
The cheapest repayment is a page in the handover document that states each decision and its reason. It costs an hour at the end of a build and saves the next team from rediscovering it.
- Store type - blended or dedicated, and the business reason. If dedicated: what syncs between stores, and by what.
- Company tree rule - what one company is, what one location is, and who agreed it on the merchant's side.
- Where each price lives - catalogs (which ones, and the active count against the plan's cap), quantity rules, volume breaks, anything still in tags or discounts and why.
- Identity keys - the
externalIdscheme for companies and locations, and the source-system field each maps to. - Where deal state lives - which system owns requests, proposals, counters and approvals, and what it hands Shopify.
- Plus dependencies - every feature in the build that exists only on Plus, so a plan change is a known cost.
- Surfaces with a Shopify date - any extension, function, script, theme block or API the build relies on that has an announced deprecation, with the date.
- API version - the version each custom app pins, the date that version leaves support, and the named owner of the quarterly upgrade.
- Enforcement map - which rules are enforced by catalogs, quantity rules or validation functions, and which only in the theme.
- Integrations that bypass checkout - anything writing orders through
orderCreate, and where it re-applies the buyer rules.
The 50-test launch plan is the companion: the ledger records what was decided, the test plan proves it works.
Where a quote layer sits
Labelled as one implementation. A quote app adds a system of record for the deal, so it can add debt of its own. The question that decides how much is what it stores: references to Shopify's records, or copies of them. A copied customer list or price list is a second source of truth that someone has to keep in sync; a reference is read from Shopify when needed.
QuotWay stores references: a quote holds the Shopify IDs of the customer, company, location, contact, product and variant, a snapshot of the product titles on each version, and the prices that were negotiated - the catalog baseline, the buyer's ask, the offer and the final price. It keeps no standing price list; a company contact's proposal starts from their company-location catalog price, resolved when the request comes in. Negotiation state stays in the quote - versions, counters, approvals, and sent proposals whose lines are locked, so a change is a new version or a counter rather than an edit. Only the agreed result becomes a draft order, created with purchasingEntity rather than the deprecated customerId and, on the Enterprise plan, the location's payment terms. Company-aware quoting is on the Enterprise plan - the plan lines are on the pricing page - and the company-aware side is on the Shopify B2B quoting feature page; the architecture behind the split is in what lives in Shopify and what belongs in the quote layer.
Whichever app you evaluate, ask the storage question directly; how to evaluate a Shopify B2B app lists it with the other 52.
The Shopify facts in this post are kept current in the Shopify B2B reference.
FAQ
What is technical debt in a Shopify B2B build?
Two things. Structural decisions that Shopify says are hard to reverse - blended or dedicated store, the company tree, where standing prices live, identity keys - and code on a surface with a published retirement date, such as checkout.liquid, Shopify Scripts, legacy customer accounts and API versions. The first is paid by deciding early; the second by tracking the dates.
Can I switch from a separate wholesale store to one blended store later?
Yes, but it is a rebuild rather than a setting. Shopify's store-type guidance says the choice is "not easy to change at a later time" and that switching means redoing "most of your setup work, including companies, catalogs, and online store customizations". Expansion stores do not sync settings, products, collections or inventory between them.
Do Shopify Scripts still work for B2B pricing?
No. Shopify Scripts was deprecated on 30 June 2026 and published scripts were deactivated; editing had already ended on 15 April 2026. The replacement is Shopify Functions. A custom app containing functions runs only on Plus stores; public App Store apps with functions run on any plan.
Does the checkout.liquid deprecation affect B2B?
Not B2B checkout itself, which never supported checkout.liquid customisations. It affects a blended store's D2C checkout: checkout.liquid stopped working for the Information, Shipping and Payment steps on 13 August 2024, and for the Thank you and Order status pages on 28 August 2025 on Plus. Non-Plus stores had until 26 August 2026 to upgrade those two pages.
What happens if a custom app uses an old API version?
Nothing visible at first. Each stable version is supported for at least 12 months; after that, a request naming it "falls forward" to the oldest accessible stable version, so fields removed in between stop being returned. Public apps that keep using unsupported resources are delisted from the App Store, with installs blocked for at least seven days. Pin a version and give someone the quarterly upgrade.
Is tag-based wholesale pricing technical debt now?
It is a workaround whose original reason has gone. Since 2 April 2026 companies and catalogs are on every Shopify plan, and catalogs are what B2B checkout, quantity rules and volume pricing read. A tag-based setup still works, but it is extra logic to maintain beside a native system - move it when you next touch pricing, and plan the catalog count against the three-active-catalog cap below Plus.
Can a B2B order be moved to a different company?
No. Shopify states that B2B orders "remain with the company they were created for and can't be migrated to another company". That is why the company tree is the decision to get right before any orders exist.
Sources
Shopify pages, read on 24 September 2026 at API version 2026-07 unless dated otherwise:
- Choosing a store type for your B2B business - blended versus dedicated, and "not easy to change at a later time"
- Expansion stores - the B2B store type and what is not synced between stores
- checkout.liquid, the 13 August 2024 changelog and upgrading the Thank you and Order status pages
- Shopify Scripts requirements and limitations - deprecated 30 June 2026; the 15 April 2026 end of editing from the changelog, read 22 September 2026
- Shopify Functions - the plan rule for custom apps with functions
- Cart and checkout validation Function API - supported surfaces,
purchasingCompany, 25 per store - API versioning - cadence, 12-month support, fall-forward, delisting
- REST Admin API - legacy since 1 October 2024, read 20 September 2026
- Legacy customer accounts are now deprecated and sign-in and customer accounts in B2B, read 20-22 September 2026
- DraftOrderInput and the 2026-07 release notes, read 20-22 September 2026
- Migrating customers to B2B, read 23 September 2026
- CompanyInput and CompanyLocationInput, read 22 September 2026
- Plan, catalog, draft-order and limitation rules: the Shopify B2B reference, re-verified 20-23 September 2026
QuotWay's storage model is described from its own data schema.
Related articles
See how QuotWay handles this on your store.