> ## Documentation Index
> Fetch the complete documentation index at: https://docs.merchantops.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Shopify

> Connect a Shopify store to MerchantOps: import your catalog, enrich it, and publish approved changes back.

The Shopify app connects a Shopify store to MerchantOps in both directions. It
**imports** your store's catalog into MerchantOps, lets you
[enrich](/enrichment/how-it-works) it, and **publishes** approved changes back to
the store as two [publishing targets](/publishing/overview).

It is the one integration that covers the whole round trip, so this page covers
all four stages — connect, import, enrich, publish. Everything downstream of the
import is ordinary MerchantOps: the same products, jobs, batches, and approval
model as any other source.

<Note>
  **Nothing reaches your store until you approve a batch.** Connecting and
  importing only read from Shopify. Writes happen when an approved
  [batch](/publishing/overview) publishes to a Shopify target, and not before.
</Note>

## Getting the app

MerchantOps is installed on your store as a **custom app built for your
organization**, not a listing you find in the Shopify App Store. Searching the
App Store for MerchantOps will not find it.

Instead, your MerchantOps contact creates an app registration for your
organization and sends you an **install link**. Opening that link installs the
app on your store and starts the connect flow.

Because it is a custom app, it also cannot bill through Shopify. There are no
Shopify charges, plans, or usage records for this app — everything stays on your
existing MerchantOps contract. See [Billing](/settings/billing) for how
enrichment allowance works.

## Connecting your store

<Steps>
  <Step title="Open the install link">
    Install the app on your store from the link your MerchantOps contact sent
    you. You are never asked to type your `myshopify.com` domain — the app
    already knows which store it was installed on.
  </Step>

  <Step title="Choose connect or create">
    The first screen offers two paths:

    * **Connect your MerchantOps account** — if your organization already uses
      MerchantOps. You are sent to the MerchantOps app to sign in and confirm,
      then returned to the embedded app.
    * **Create a MerchantOps account for this store** — if you are new. This
      creates an organization and makes you its Owner.

    If your email already belongs to a MerchantOps organization, you are routed
    to connect rather than given a second organization.
  </Step>

  <Step title="Import your catalog">
    The app offers to import your Shopify catalog. This creates a
    [Job](/jobs/overview) you can watch; a large catalog takes minutes. See
    [what the import brings in](#importing-your-catalog).
  </Step>

  <Step title="Review, then enrich">
    Once the import finishes, the app offers an explicit **enrich** step. Review
    what the import derived first — see [enriching an imported catalog](#enriching-an-imported-catalog).
  </Step>
</Steps>

Connecting registers **two publishing targets** for the store, `shopify_catalog`
and `shopify_pricing`, sharing one connection. They appear under
**Settings › Publish Targets** like any other target — see
[publishing settings](/settings/publishing-settings).

### Who can connect

Connecting and disconnecting a store use the **`pricing:approve`** permission
(Owner and Admin). Viewing the connection uses **`pricing:read`**. There is no
separate "Shopify" permission — see [team and roles](/settings/team-and-roles).

### One store per organization

An organization connects **one** Shopify store. If a store is uninstalled and
then reinstalled under a *different* MerchantOps organization, the connection is
refused rather than moved. Moving a store between organizations is not a
self-serve operation — talk to your MerchantOps contact.

### Managing the connection

**Settings › Shopify** shows the connected shop domain, the granted scopes, the
API version, and the last sync. It also has **Test** and **Disconnect** actions.

If the store's authorization goes stale, the connection is marked
**needs re-auth** on that page and in the test result. Opening the app in
Shopify again clears it.

## Importing your catalog

The import pulls from Shopify; nothing is pushed to MerchantOps. You trigger it
from the embedded app or from MerchantOps, and it runs as a
[Job](/jobs/overview) of type **Connector Sync**.

### What comes in

| In Shopify                   | Becomes in MerchantOps                                                                       |
| ---------------------------- | -------------------------------------------------------------------------------------------- |
| Product                      | A [product](/catalog/products-variants), keyed from its handle                               |
| Variant                      | A [variant](/catalog/products-variants), keyed from its SKU                                  |
| Option (Size, Color)         | A variant-level [property](/catalog/product-types-properties), shared across product types\* |
| Metafield definition         | A property definition, product- or variant-level                                             |
| Taxonomy category            | A [product type](/catalog/product-types-properties)                                          |
| Vendor                       | A [brand](/catalog/brands)                                                                   |
| Tags                         | A string-list property of their own                                                          |
| `price` and `compareAtPrice` | Active [price records](/pricing/price-records)                                               |

**Only active products are imported.** Draft and archived products are skipped.

The schema is worked out **before** any product is written, so your imported
products are immediately visible and filterable in the type-driven views rather
than carrying properties nothing knows about. This derivation is deterministic —
no AI is involved in reading your Shopify catalog.

<Note>
  **\*An option reuses an existing property only when it's a safe fit** — the
  existing property has to already be variant-level, an enumerated type, and set
  to allow one value per variant. If your organization already has a property
  with the same name that doesn't meet that bar (a product-level `Size`, say), the
  import never changes that property's shape to make the option fit. Instead it
  creates a separate property for the option (named `shopify_size`, or
  `shopify_size_2` if that's also taken) and reports why on the job. Prices are
  written only for the variants that landed; any skipped by this are counted and
  reported on the job too, so you never end up with an active price for a variant
  that doesn't exist.
</Note>

<Note>
  Importing is **not** counted against your enrichment allowance. Enrichment is
  what the meter counts, and enrichment does not run during an import. You can
  import a ten-thousand-variant catalog and spend nothing on
  [allowance](/settings/billing).
</Note>

### Re-syncing

Running the import again updates what changed:

* Products match on the stored Shopify identifier first, then on the derived
  key — so **renaming a handle updates the product instead of duplicating it**.
* A row that changes an existing product adds a new
  [version](/catalog/versioning) rather than overwriting history. A row that
  changed nothing is skipped.
* **Your manual edits win.** A value you edited by hand in MerchantOps is never
  overwritten by a Shopify value — see [manual edits](/enrichment/manual-edits).

<Warning>
  **A re-sync never deletes and never archives, on either side.** Products in
  MerchantOps with no counterpart in Shopify — and the reverse — are *reported* as
  warnings on the job, for you to act on. Neither system removes anything on the
  other's behalf.
</Warning>

If a sync finishes with any warnings — the property-collision case above
included — the embedded app's status line says so explicitly ("Sync completed
with warnings: N of M items — K warning(s)") instead of a bare "completed."
Open the job in MerchantOps for the details behind the count.

## Enriching an imported catalog

Enrichment is a **separate step you trigger**, never part of the import. After
the import completes, the app and MerchantOps both offer to enrich the imported
products. That runs as its own [Job](/jobs/overview) and counts against your
[enrichment allowance](/settings/billing#when-limits-bite).

The reason for the split is that you should see the derived
[product types and properties](/catalog/product-types-properties) before
spending allowance against them.

### What enrichment will and will not overwrite

Imported values are treated as **merchant input** — data you supplied — and the
merge rules protect them. In practice:

* Enrichment **fills gaps**. A property Shopify had no value for gets one.
* Enrichment **does not overwrite** a value that came from Shopify, with three
  exceptions.
* The exceptions are `description`, `features`, and `metaTitle`, which are
  configured to allow enrichment to rewrite them.

If you want enrichment to overwrite some other Shopify-supplied property, that
is a configuration change on that property — see
[how enrichment works](/enrichment/how-it-works).

## Publishing back to Shopify

Shopify is a [publishing target](/publishing/overview) like any other: you
approve a batch, choose a Shopify target, and MerchantOps writes the changes to
the store. Two targets are configured independently:

* **Shopify Catalog** (`shopify_catalog`) — publishes product content.
* **Shopify Pricing** (`shopify_pricing`) — publishes prices.

### What a catalog publish writes

By default the catalog target writes the fields you came to MerchantOps to
improve, and leaves your merchandising alone:

| Written by default                     | Not written by default |
| -------------------------------------- | ---------------------- |
| Description                            | Product title          |
| SEO title and description              | Tags                   |
| Features and benefits                  | Taxonomy category      |
| Standardized properties, as metafields | Images and alt text    |

Standardized properties are written as metafields under a MerchantOps
namespace, whose definitions are created on connect. Only **standardized**
properties become metafields, and each is written as either a list of text
values (for a multi-value property) or a single text value.

<Warning>
  **A catalog publish never adds a variant to a product that already exists in
  Shopify.** Updates write product-level content and metafields only. If you add a
  size or a colorway in MerchantOps, it does not reach an existing Shopify product
  through the catalog target — add it in Shopify.

  Creating a product that does not yet exist in Shopify *does* write its full
  variant list, but a create carrying **more than 250 variants is refused** with a
  named error rather than silently truncated.
</Warning>

### What a pricing publish writes

The pricing target sets each variant's price and compare-at price in the store.

<Note>
  **Variant prices reach Shopify through the pricing target only** — never through
  the catalog target. If prices are not updating, check that the batch published
  to `shopify_pricing`.
</Note>

This is also what makes MerchantOps' scheduled, effective-dated pricing reach
Shopify. Shopify has no native scheduled price change, so the MerchantOps
[effective date](/publishing/overview) stays the mechanism: the batch publishes
when its date arrives, and the store is written then.

### Outcomes

Every record gets an outcome stamped per target and per environment, exactly as
for any other connector. See
[reading publish status](/publishing/publish-status).

## Disconnecting, uninstalling, and your data

<AccordionGroup>
  <Accordion title="What happens when I disconnect or uninstall?">
    The stored authorization for the store is wiped and both Shopify publishing
    targets are disabled, so no further reads or writes happen.

    **Your MerchantOps catalog stays.** Uninstalling the Shopify app does not
    delete your organization, your products, or your price records — that data
    is yours, in your own account.
  </Accordion>

  <Accordion title="What is deleted 48 hours after uninstall?">
    Shopify asks apps to erase shop data 48 hours after an uninstall. For
    MerchantOps that means the **Shopify-side sync state only**: the record of
    which store was connected, and the bookkeeping used to avoid processing the
    same Shopify notification twice.

    It does **not** mean your catalog. Products, variants, properties, and
    prices imported into your MerchantOps organization are unaffected.
  </Accordion>

  <Accordion title="Does MerchantOps hold my customers' personal data?">
    No. The app reads product catalog data only — products, variants, options,
    metafields, and prices. It does not read, store, or process customer
    personal data, so there is nothing customer-related to return or erase.
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Publishing overview" icon="rocket" href="/publishing/overview">
    Batches, targets, approval, scheduling, and promotion.
  </Card>

  <Card title="Publishing settings" icon="gear" href="/settings/publishing-settings">
    Where Shopify targets appear alongside your other targets.
  </Card>

  <Card title="How enrichment works" icon="wand-magic-sparkles" href="/enrichment/how-it-works">
    What the explicit enrich step actually does to imported products.
  </Card>

  <Card title="Jobs" icon="list-check" href="/jobs/overview">
    Watch a Connector Sync run and read its warnings.
  </Card>
</CardGroup>
