Connect Shopify: full catalog sync

Connecting Shopify is the recommended way to get a complete product catalog. You install the A vs B app and link it to your project. From there, A vs B does the rest: it imports your full catalog and keeps it live automatically, with no manual steps.

See Shopify Integration Setup for the install and connect steps. This page covers what happens once you're connected:

  • how the catalog import works
  • how it stays live
  • how A vs B merges data when more than one source feeds the same product

When you connect a Shopify store, A vs B does two things for your Live Catalog. This is an always-current copy of your products. Recommendations and audiences (a named, reusable group of visitors who share something in common, like what they viewed or bought) both read from it:

  1. Initial import: the moment you connect, A vs B pulls your full catalog from Shopify. It runs as a durable background job and writes straight into the Live Catalog.
  2. Live updates: four Shopify webhooks (automatic messages Shopify sends the instant something changes) keep the catalog current after that. You never re-import by hand.

What you need on the Shopify side

When you install the A vs B app and link it to a project, it asks for a set of scopes. A scope (a named permission on an API key that controls exactly what it can read or change) covers one thing the app needs to do. Two scopes power the catalog sync:

ScopeWhy it is needed
read_productsRead product titles, handles, images, variants (a product's different sizes or colours), and status, for the catalog import and the products/create, products/update, products/delete webhooks
read_inventoryRead inventory quantities per variant and receive inventory_levels/update webhooks so stock levels stay current

The app also requests three more scopes for order tracking and the browser pixel: read_orders, write_pixels, and read_customer_events.

How the initial import works

Connecting the store starts a background import job and returns right away: you don't wait for it to finish. A vs B's ingestion service runs the job in three tracked stages:

  1. Start: Shopify begins exporting your entire product and variant catalog as a single bulk stream.
  2. Wait: the job checks the export's status every few seconds, for up to 60 checks. If Shopify reports the export failed, or it never finishes, the job is marked failed with the reason recorded. It is never silently abandoned.
  3. Ingest: A vs B writes the exported products into the Live Catalog in chunks of 1,000, tracking progress per product. If the service restarts mid-import, the job resumes from where it left off instead of starting over.

The import:

  • Pulls every product, with its variants, pricing, inventory quantities, availability, images, vendor, product type, and tags.
  • Writes each product into the Live Catalog under source shopify.
  • Records each variant's merchant SKU and platform variant ID, so order lines and product-view events always resolve back to the right product.
  • Stores an internal inventoryItemId → SKU mapping. Later stock-change webhooks use this mapping to update the right product without a database lookup.

Progress shows on the Product catalog page, under Catalog source health. The Shopify row shows a syncing status during the import, then switches to a last-synced timestamp when it completes. If the import fails, the row shows the error instead: a failed import is always visible, never a silently half-filled catalog. A large catalog (tens of thousands of products) typically takes seconds to a few minutes.

The import is a durable job, not a request

Connecting does not wait for the import to finish. If you open the Product catalog page right after connecting, the Shopify row will show "syncing". Refresh after a minute or two to see the completed count. The import runs as a durable job, so a deploy or restart on our side pauses it at most briefly, and it picks back up on its own.

Re-syncing the catalog

You can run a full re-import any time, for example after a large bulk edit in Shopify. Click Re-sync:

  • on the Product catalog page, under Catalog source health, or
  • on the Store connections page, where the same action is labelled Re-sync catalog.

Both buttons call the same endpoint:

Plain text
POST /api/orgs/{orgId}/projects/{projectId}/catalog/sources/shopify/resync
Plain text1 line

Re-sync is single-flight. If an import is already queued or running for the project, A vs B returns that in-flight job instead of starting a second one. Clicking Re-sync twice never runs two imports at once, and the button shows live progress while the job runs. Under the hood, both buttons poll the same jobId a developer could also watch through the commerce jobs API.

  1. The Shopify row's sync status.
  2. Click Re-sync to run a full re-import on demand.

How live updates work

After the initial import, four Shopify webhooks keep the catalog current:

Webhook topicWhat triggers itWhat A vs B does
products/createA new product is published in ShopifyAdds it to the Live Catalog
products/updateA product's title, price, image, status, or variants changeUpdates all changed fields in the Live Catalog
products/deleteA product is permanently deletedMarks the product as removed in the Live Catalog
inventory_levels/updateStock quantity changes at a locationUpdates the product's availability and stock count

Shopify registers all four webhooks automatically when the app installs. Each one delivers to A vs B's ingestion service. The service merges the update using the priority order described below, so a Shopify update never overwrites a more recent write from a higher-priority source, such as a live price a shopper just saw in their browser.

How inventory updates work

Shopify's inventory_levels/update webhook identifies the stock change by inventory_item_id, not by SKU. A vs B resolves this using the mapping it wrote during the initial import: inventory_item_id → product SKU. If that mapping is not ready yet (for example, the initial import is still running), A vs B skips the update. The next products/update webhook for that product re-syncs its stock instead.

Archived and draft products

Products with a Shopify status of archived or draft are imported as removed availability. They stay in the catalog, so recommendations can filter them out as out of stock, but they never show as available inventory.

Variants

Each product variant is stored as part of its parent product's catalog row. A variant carries the platform variant ID (variantSku), the merchant's human SKU (sku), its own price, stock count, and availability. When a variant sells out, the inventory_levels/update webhook fires and A vs B updates that variant's availability immediately.

Multi-currency

When your Shopify store uses multi-currency pricing (Shopify Markets), A vs B stores the prices in your store's default presentment currency. The catalog's currency field reflects the ISO 4217 code. If you need different prices per market, use the Catalog Push API to write per-currency price maps alongside your Shopify data.

Priority when sources disagree

Shopify ranks just below live browser events (source live_event) in A vs B's source priority:

Plain text
live_event (live browser event)  >  shopify  >  push_api ≈ feed  >  upload
Plain text1 line
  • Live details (price, stock, availability): whichever source wrote most recently wins. A real-time productView event from the browser snippet can update stock or price ahead of Shopify, as long as it's recent.
  • Descriptive details (title, image, category): the higher-priority source always wins, regardless of timing. Shopify overrides the Push API, a product feed, or an uploaded file even if one of those wrote more recently.

Disconnecting Shopify

To disconnect, go to Commerce → Sources → Shopify → Disconnect. This stops future syncs and unregisters the webhooks. Products already in the Live Catalog are not removed: they keep their last-known values from Shopify. A higher-priority source, like a browser event, can still update them afterward.

You can also manage the connection from the Store connections page, where the same actions are available.

  1. Re-sync catalog runs a full re-import as a durable job.
  2. Disconnect stops future syncs and unregisters the webhooks. Your catalog data stays.
Was this helpful?