# Shopify — the base integration

> How Ordinary connects to your Shopify store, what data flows in, and how to verify the connection is healthy.

Source: https://help.tryordinary.com/integrations/shopify

---

Shopify is the one integration that's connected at install and stays
connected. Everything else in Ordinary depends on it.


## What's connected

At install, Ordinary requests these scopes from your Shopify admin:

- **Read products** — product catalog, variants, and per-variant
  "Cost per item" values.
- **Read orders, read_all_orders** — orders and historical data.
- **Read customers** — customer list.
- **Read reports** — Shopify's analytics API analytics (for your 12-month GMV).
- **Read shipping** — your store's delivery profiles so we can surface
  a representative shipping rate in tools like the Offer Calculator.
- **Read customer events, write pixels** — the first-party pixel.

Once approved, Ordinary stores an encrypted access token for your
store and starts syncing.

## What data flows in

Constant, low-latency, webhook-based:

- **Orders** — `orders/create`, `orders/update`, `orders/delete`
  webhooks. Arrives within seconds of checkout completion.
- **Checkouts** — `checkouts/create`, `checkouts/update`. Used for
  abandoned-cart tracking and the funnel's "Checkout started" step.
- **Customers** — `customers/create`, `customers/update`,
  `customers/delete`.
- **Products** — `products/create`, `products/update`. Includes
  per-variant "Cost per item" (product cost / COGS) when you've set
  it in Shopify — used to auto-fill Manufacturing Cost in the Offer
  Calculator.
- **App subscriptions** — `app_subscriptions/update`. Our billing
  state machine is driven by this.
- **GDPR topics** — `customers/data_request`, `customers/redact`,
  `shop/redact`. Forwarded to our compliance queue.

Daily or scheduled:

- **GMV snapshot** — via Shopify's analytics API, refreshed once per day.
- **Inventory** — hourly pull of inventory levels.
- **Shipping rates** — pulled on demand from your default delivery
  profile (the first active flat-rate in your closest shipping zone).
  Used as a rough pointer for the Offer Calculator's "Last Mile
  Shipping Cost" field; falls back to the median of recent orders'
  shipping amounts when the profile has no flat rate (carrier-
  calculated stores).
- **Historical backfill** — on initial install, we pull your **full
  order history** from Shopify automatically (not just recent data).
  For stores with years of history or tens of thousands of orders,
  this completes within minutes to an hour depending on volume. The
  backfill runs in the background; your dashboard may show partial
  data for a short time, then fills in as the sync progresses.
  Historical customers and products are pulled alongside.
- **Ongoing revenue reconciliation** — once data is loaded, an hourly
  reconciliation check compares our revenue totals to Shopify's Sales
  report to guarantee match-to-the-cent accuracy. See
  [Revenue matches Shopify exactly](https://help.tryordinary.com/features/reconciliation).

## Sync status

Per-integration sync progress lives at **Settings → Sync status** —
last-sync timestamp, last-data timestamp, and any active backfill
jobs with progress bars. While a backfill is running, an in-app
banner at the top of every page shows the active job(s) and links
to the sync-status board for details. The banner flashes green
briefly when the job completes; you don't need to refresh the page
to know your data is ready.

## Connection status on Settings → Integrations

The Shopify card shows:

- **Connected** (green) — access token valid, webhooks firing,
  recent sync within the last hour.
- **Needs reauth** (amber) — we received a 401 from Shopify on the
  last API call. You'll be prompted to reconnect.
- **Disconnected** (red) — access token rejected or uninstall
  detected. App must be reinstalled from the Shopify admin.

## What "store domain" and "custom domain" mean

Each Shopify store has two URLs we track:

- **Store domain** — `yourbrand.myshopify.com`. Always present,
  fixed, used as the key for webhook routing and API calls.
- **Custom domain** — `yourbrand.com`. Optional. Used to display
  your brand name nicely across the UI and to validate pixel events
  that come in with custom-domain referrers.

Both are visible on Settings → Integrations → Shopify. Custom domain
is read-only in Ordinary; change it in Shopify admin → Settings →
Domains.

## Troubleshooting

- **Pixel Disconnected** — see
  [Pixel says "Disconnected"](https://help.tryordinary.com/troubleshooting/pixel-disconnected).
- **Orders not showing** — see
  [Orders not showing up](https://help.tryordinary.com/troubleshooting/orders-missing).
- **Needs reauth** — click the Reconnect button on the Shopify
  integration card and reapprove the scopes. Usually means Shopify
  has invalidated the token (shop password changed, app reinstalled,
  etc.).

## Related articles

- [Customer Events pixel extension](https://help.tryordinary.com/integrations/customer-events-pixel) — how
  the pixel piece works.
- [Understanding your GMV tier](https://help.tryordinary.com/concepts/gmv-tiers) — uses the
  Shopify's analytics API-fetched GMV number.
