Ready to maximize your revenue?Book a Demo
TOPCODE
B2b

How Shopify Plus B2B catalogs work under the hood

Last updated on June 2, 2026

How Shopify Plus B2B catalogs work under the hood

TOPCODE

Shopify Plus B2B is not simply a wholesale price tag stapled to your retail store. It is a parallel commerce model built on a distinct data graph: company accounts, location hierarchies, catalogs, price lists, payment terms, and a buyer role permission system. If you have ever wondered why a B2B buyer sees different prices from a retail customer browsing the same domain — or why a custom catalog stops showing up after a store migration — this article walks through exactly what is happening at the data layer, how each object relates to the next, and what that means for how you build integrations and configure your storefront.

The B2B data model from the top down

Everything in B2B starts with the Company object. A Company is the top-level entity representing a business customer — think "Acme Hardware Distributors." Beneath it sit one or more Company Locations (e.g., Acme Chicago Warehouse, Acme Dallas Branch). Each location carries its own shipping address, payment terms, and — critically — its own catalog assignment. This location-level granularity is what enables large enterprise accounts to have a single login with differentiated pricing across business units. A regional manager logging in can be scoped to their own location's pricing without any access to another branch's rates or purchase history.

Company Locations also carry Buyer Roles. A customer account can be assigned the role of Order Placer (can create orders and submit draft orders), Buyer (limited draft order access, cannot submit independently), or Location Admin (can manage the location's settings, add and remove staff, and invite new buyers). Roles are enforced at both the storefront and Admin API level, so a buyer with the Order Placer role cannot accidentally elevate their own permissions. This role model is enforced server-side by Shopify, not by theme logic — it cannot be bypassed by a savvy buyer who inspects your storefront code.

The Company object also stores important metadata: an external ID (for mapping to your ERP's account record), notes, and custom metafields. The metafield support on Company and Company Location objects is particularly useful for segmentation — you can tag companies by tier (Gold, Silver, Bronze), sales rep assignment, or custom pricing group, then reference those metafield values in Shopify Flow automations or Function logic.

Catalogs: the object that ties everything together

A Catalog is the linking layer between a Company Location and its visible product range and pricing. Internally, a catalog holds two references: a Publication (which products are visible) and a Price List (what prices apply). When a buyer authenticates and lands on the storefront, Shopify resolves their company location, fetches the assigned catalog, and merges the price list overrides into the product data returned by the Storefront API. This happens transparently — from the buyer's perspective, the store simply shows different prices and different products. They never see a "catalog" object explicitly.

The Publication within a catalog behaves like any other Shopify publication (a sales channel). Products and product variants must be explicitly published to it. If you add a new product to your store and forget to publish it to the B2B catalog, it will remain invisible to all buyers using that catalog — even if it is live on the online store. This is one of the most common operational oversights in B2B setups: a new SKU is created, passes QA, goes live for retail customers, but B2B buyers report they cannot find it. The fix is to include B2B catalog publication in your product launch checklist as a mandatory step.

A single catalog can be assigned to multiple company locations. If your enterprise accounts all receive the same product range and pricing, you create one catalog and assign it to all of them — no duplication. If certain accounts need unique pricing or a restricted product set, you create a separate catalog for each tier. Shopify does not limit the number of catalogs per store, so even complex segmentation scenarios (distributor tier A, distributor tier B, direct buyer, OEM reseller) are manageable within the data model.

Price lists: fixed prices and percentage adjustments

A Price List defines how prices deviate from the retail price for a given catalog. Shopify supports two pricing strategies: percentage adjustments (e.g., 20% off all products) and fixed per-variant prices. The two can be combined: set a global 15% adjustment on the price list, then override specific SKUs with exact dollar amounts. The Admin API evaluates fixed prices first, falling back to the percentage rule if no fixed price exists for a given variant. This "fix-and-adjust" hybrid is the most common pattern for distributors that have negotiated specific contract prices on their top 50 SKUs but want automatic wholesale pricing on everything else.

Price lists also carry a currency code. This means a Japanese wholesale account can see JPY pricing while a UK distributor sees GBP — all from the same store, using separate catalogs each with their own price list and currency setting. The price list currency must align with a currency enabled in Shopify Payments or Markets for it to be surfaced at checkout. If you set a price list currency to SEK but SEK is not enabled as a presentment currency in your Shopify Payments settings, buyers will not see SEK at checkout regardless of the price list configuration — a common setup error.

An important pricing behaviour to understand: B2B price lists take precedence over Shopify automatic discounts. If a buyer's catalog price list sets a variant at $50 and you have an active automatic discount applying 10% off sitewide, the buyer still pays $50 — the price list price is treated as the base, and automatic discounts layer on top. This means a merchant running a sitewide sale should verify whether that sale applies to B2B buyers or should be scoped to the retail channel only.

Querying catalogs via the Admin GraphQL API

The Admin GraphQL API exposes catalogs, price lists, and company relationships through a consistent graph. The query below fetches a company's locations alongside their assigned catalog and price list details — useful for building a merchant dashboard or an integration that syncs B2B pricing with an ERP. Notice the paymentTermsTemplate field on the location — this is where you read the Net 30 or Net 60 configuration, which determines how the buyer's checkout experience is presented.

get-company-catalogs.graphqlgraphql
query GetCompanyCatalogs($companyId: ID!) {
  company(id: $companyId) {
    id
    name
    locations(first: 10) {
      nodes {
        id
        name
        shippingAddress {
          city
          countryCode
        }
        catalog {
          id
          title
          status
          priceList {
            id
            name
            currency
            adjustment {
              type   # PERCENTAGE
              value  # e.g. -20 for 20% off
            }
          }
          publication {
            id
            name
          }
        }
        paymentTermsTemplate {
          name
          dueInDays
        }
      }
    }
  }
}

How the storefront reflects a catalog

When a B2B buyer visits your storefront and is authenticated via a customer account token, the Storefront API automatically scopes all product and pricing queries to their catalog. You do not need to pass a catalog ID in every query; Shopify resolves it via the buyer's session context. On a Hydrogen (headless) storefront, you pass the buyerIdentity field (including companyLocationId) in cart create and cart queries to force the correct catalog context even before the buyer is fully authenticated. This is important for pre-auth browsing flows: a B2B buyer who is browsing product pages before logging in will see retail prices, then see prices update to their B2B rates once the cart is associated with their company location.

Product visibility is enforced at the GraphQL resolver level. If a variant is not published to the catalog's publication, a product query for that product returns null — not an empty price, a full null. Collections also respect catalog publication; a product can appear in a collection on the retail store but be absent from the same collection for a B2B buyer whose catalog does not include it. This means your collection page templates need to handle null product responses gracefully — a common oversight in headless storefronts that assume non-null returns from collection queries.

Search also respects catalog scoping. When a B2B buyer searches your storefront, results are filtered to products in their catalog's publication. If you are using Shopify Search & Discovery or a third-party search provider that integrates via the Storefront API, the catalog context is automatically applied. Third-party search providers that maintain their own search index (not via the Storefront API) will need to be explicitly configured to filter results by catalog publication status — check your provider's B2B documentation carefully.

B2B checkout differences

B2B checkout behaves differently from retail checkout in several meaningful ways. First, payment terms replace immediate payment for eligible locations. A company location with Net 30 terms will see a "Pay by invoice" option at checkout rather than a card entry form. The order is created with a paymentTerms node and a due date calculated automatically. Shopify also supports Net 7, Net 15, Net 30, Net 60, and Net 90 terms, as well as fixed date terms for one-time contract billing. The payment terms template is set on the Company Location and can be updated via the Admin API — useful for integrations that manage credit terms through an ERP or accounts receivable system.

Second, B2B buyers can create draft orders directly from the storefront — a purchase order workflow that routes orders to merchant approval before fulfilment. This is commonly used when a buyer's authorization threshold has not been met (e.g., only orders under $5,000 auto-confirm; larger orders require procurement sign-off). Third, shipping address selection at checkout defaults to the company location's registered addresses, not the buyer's personal address book. This matters for merchants who need all orders for an account to route to a single warehouse address regardless of which individual employee places the order.

Finally, checkout extensibility (UI extensions, post-purchase pages, branding API) works in B2B checkout just as it does in retail. You can use the same extensions to render B2B-specific upsells, inject custom validation logic, or display order reference fields that map to the buyer's internal PO number. The PO number field is a particularly common request — buyers need to enter their internal purchase order reference at checkout for reconciliation with their accounts payable systems. This is cleanly implemented as a checkout UI extension that writes to a cart attribute.

Common integration patterns and gotchas

ERP integrations are the most common B2B API use case. Typically you sync company accounts and locations from the ERP on customer onboarding, then push price list updates nightly. The priceListFixedPricesAdd mutation accepts up to 250 variant-price pairs per call and is idempotent — safe to re-run. For stores with hundreds of thousands of SKUs and dozens of price lists, batched mutations with exponential back-off are essential because the API enforces cost-based rate limits. A common pattern is to hash the current price list state and only push updates for variants whose prices have actually changed, rather than re-syncing the entire catalog nightly.

A less obvious gotcha: when you delete a product variant that has a fixed price entry in a price list, Shopify automatically removes the fixed price record. However, if you archive and re-create the variant (a common operation when SKUs are restructured or size ranges change), the new variant ID will not have the fixed price — even if the SKU string is identical. Shopify uses the variant GID (global ID), not the SKU string, to key price list entries. Always re-push price list entries after variant recreation, and ensure your ERP sync logic handles variant ID changes rather than assuming SKU stability.

Another integration consideration: catalog-scoped metafields (available since API version 2025-01) allow you to attach custom metadata directly to a catalog object. This unlocks use cases like storing a catalog's effective date range, its associated contract ID, or a display name override for the buyer portal. Previously this information had to live in the Company or Company Location metafields and be cross-referenced manually. With catalog metafields, the data model is cleaner and reporting is simpler.

Shopify's B2B APIs continue to evolve rapidly. The B2B reference documentation at help.shopify.com/manual/b2b is the canonical source for feature availability and version changes. Track the API changelog actively — the 2024-07 version introduced bulk catalog operations that dramatically improved ERP sync throughput, and the 2025-01 version added catalog-scoped metafields, both of which meaningfully change integration architecture for complex B2B deployments.

Buyer portal design considerations

The default Shopify B2B storefront experience — a standard Online Store 2.0 theme with B2B features activated — works for many merchants, but sophisticated B2B buyers often have higher expectations around account management. Consider what additional capabilities your buyer portal should expose: order history filtered by company location (not just the individual buyer's orders), a quick-reorder flow for standing orders, CSV order upload for bulk purchasing, and saved draft orders that buyers can return to before submitting.

Hydrogen (Shopify's React-based headless framework) is the recommended foundation for custom B2B buyer portals that go beyond the default theme capabilities. Hydrogen has built-in B2B context handling — the useCustomer hook and cart utilities automatically propagate companyLocationId through buyer identity so catalog scoping works throughout the shopping experience without manual plumbing.

Key takeaways

The Shopify Plus B2B data model is principled once you see it clearly: Company → Location → Catalog → (Publication + Price List). Every B2B behaviour — pricing, product visibility, checkout flow, payment terms — is a downstream consequence of this hierarchy. Build your integrations and merchant tooling around the catalog assignment as the central state, keep your ERP sync idempotent and variant-ID-aware, and you will find the model handles even complex multi-tier distributor setups cleanly. The API surface area is large but the underlying graph is coherent — invest time understanding it and every integration decision becomes straightforward.

Founder & Engineer

Rico Tan

Keep reading