Agent skills › manifold-shopify-and-cin7

Download the skill · SKILL.md · over MCP, the prompt manifold-shopify-and-cin7

Setting Manifold up when Cin7 and Shopify are both in use

Who this is for: a merchant whose stock, products and orders are run in Cin7, which feeds their Shopify store through Cin7's own Shopify integration. Manifold will replace Cin7, and then drive the store itself.

The danger is two systems driving one store. If Cin7 and Manifold both push stock levels, they overwrite each other. If both take orders, each order is fulfilled twice. So the order you connect things in is the whole of this skill.

What the merchant needs ready

  • Everything the manifold-from-cin7 skill lists: a Cin7 login that can create an API application, and the connector.
  • Shopify admin access with permission to install apps, for the cut-over day.
  • The name of Cin7's Shopify integration in their Cin7 account, which they will pause on the day.

Never

  • Never ask for, read out or type the Cin7 key, a Shopify token, a password or a card number.
  • Never connect Shopify to Manifold, or suggest the merchant does, while Cin7's Shopify integration is still running.
  • Never pause or change Cin7's integration yourself, and never decide the cut-over day, go-live or billing. They are the merchant's.

Who is the source of truth, when

| | Before the cut-over (evaluating) | From the cut-over (live) |
|---|---|---|
| Products, SKUs, prices | Cin7. Manifold mirrors it, read-only | Manifold |
| Stock on hand | Cin7. Each sync corrects Manifold to it | Manifold, pushed to Shopify |
| Shopify orders | Taken by Cin7 (its integration) and mirrored into Manifold as history | Taken by Manifold (the bridge) |
| Suppliers, customers, purchase orders | Cin7 | Manifold |
| The Shopify store's stock levels | Pushed by Cin7 | Pushed by Manifold |
| Accounting | Whatever Cin7 posts to today | Manifold's ledger, from go-live |

Stage 1: Cin7 first, as a mirror

Follow the manifold-from-cin7 skill to the end of its §4:

  • the migration, with Keep following ticked and Order history ticked, so Cin7's Shopify orders arrive as history;
  • the follow link;
  • the rest of setup.

Leave Shopify unconnected. shopify_list_connections should be empty throughout this stage. If it is not, stop and tell the merchant: Manifold is read-only, so it pushes nothing, but orders the store sends Manifold would be counted again beside Cin7's.

While they evaluate, two things to check now save trouble later:

  • The SKUs match. Every Shopify variant's SKU should be a Manifold SKU (Cin7's). Ask the merchant to compare, or to export the store's products. A variant with no SKU, or a different one, is fixed in Cin7 or Shopify before the cut-over.
  • The locations. Note which Cin7 location ships Shopify orders. It becomes the Manifold location each Shopify location is bound to.

Stage 2: the cut-over day (the merchant's steps, in this order)

Agree the day and a quiet hour with the merchant. Then, in order:

  1. The merchant pauses Cin7's Shopify integration, in Cin7. From here, nothing pushes to the store and no new orders reach Cin7.
  2. One last sync: onboarding_sync_link, then manifold_wait on migration_get for that cycle's migration (named in onboarding_get_link) until applied. Manifold now holds Cin7's final stock and the day's orders.
  3. The boundary, to the second, inclusive. Ask the merchant for the newest Shopify order Cin7 holds: its Shopify order number (#1234) and its id from Cin7's sales list, and the moment it was placed, to the second: the Shopify admin's order page shows only the minute, so the merchant exports that order (Orders › Export, the one order) and reads its Created at. The boundary is that moment exactly, as ISO 8601 UTC (2026-11-06T02:00:20Z): never a second later, and never rounded down to the minute. Shopify stamps orders to the second, so another order can share Cin7's newest order's second (#1235 beside #1234, both at 02:00:20); a later boundary would leave it in neither system. An inclusive boundary brings Cin7's newest order (and any other Cin7 took in that second) across again, on purpose: the reconcile in stage 3 cancels those. Keep the number and id: the reconcile matches every gap order against them.
  4. The merchant goes live, at Settings › General: the handover read, the open Cin7 orders made live, the link ended, the mirror lock lifted, the opening stocktake completed. onboarding_get_link shows ended.
  5. The merchant installs Manifold on the store from Settings › Sales channels: they type the store's address there first, then approve the app in Shopify. Starting from Manifold, not from the Shopify App Store, keeps the organization on the billing it already has.

Billing: the merchant starts the 60-day trial on a card at Settings › Billing during stage 1, as the manifold-from-cin7 skill says. A tenant on a card subscription stays on it when the store is connected later.

Every Shopify order in exactly one system

The cut-over keeps one rule: every Shopify order is a live order in exactly one system: Cin7's (handed over at go-live) or the bridge's. The gap import's boundary is inclusive, so the orders in the boundary's second can be in both for a moment; that overlap is expected, and the reconcile (stage 3, step 6) cancels Manifold's copy before the connection pushes anything. A boundary that leaves an order in neither system is the failure this order of steps exists to prevent.

Stage 3: Shopify, the Manifold way

The tenant is live, so the store's connection would push stock the moment it can. The gap's orders hold stock that Manifold does not know about until they are in, so the connection stays receive-only until they are. In this order:

  1. Receive-only first: shopify_update_connection with readOnly: true, before anything else. Orders still come in; nothing (stock, prices, fulfilments) is pushed to the store, and a push is refused 409 ConnectionReadOnly with heldBy: "connection".
  2. Locations: bind each Shopify location to the Manifold location Cin7 shipped its orders from (shopify_list_store_locations, then shopify_update_connection with the whole set of bindings).
  3. Catalogs and bridging: map the catalogs (catalogMappings) and turn on bridgeOrders. New orders bridge from the moment the store was connected.
  4. The gap: orders placed from the boundary until the connection reached Cin7 only up to its newest order, and never the bridge, which never takes an order older than the connection. shopify_import_from_store with areas: ["orders"] and ordersPlacedSince set to the boundary (stage 2, step 3) brings those, from the boundary's second inclusive: the open ones as live orders, the ended ones as history. Bring nothing else from the store (no catalogs or customers area, no catalog pull): products, stock, customers and earlier orders came from Cin7, and importing them again duplicates companies, history and the open orders Cin7 handed over, which would then ship twice. Never run the orders area here without ordersPlacedSince.
  5. Wait until every gap order is in and holding stock, not just accepted:
    • the import's orders area is completed in shopify_get_connection's imports (its live is how many came in live);
    • shopify_list_jobs with kind: "bridge-order" and status: "open" is empty: every bridge has run;
    • shopify_list_unbridged_orders is empty, or each one is fixed and retried (shopify_retry_order_bridge) and the jobs are empty again;
    • the gap orders are in fulfillment_list_orders, each naming its Shopify number in source.name, and their SKUs show the units reserved (inventory_get_stock).
  6. Reconcile with the merchant. List the gap orders that came in live, by Shopify number and id. Every one Cin7 also holds is cancelled here, on the merchant's word, with fulfillment_cancel_order, before anything ships; the Cin7 one is the record. That is always Cin7's newest order (it sits on the inclusive boundary), and any other at or below its number or found in Cin7's sales list. Match by Shopify order number or id, never by customer or total. Do it while the connection is still receive-only: the cancel then stays in Manifold. Once the connection pushes, cancelling a store order in Manifold cancels it in Shopify too, and that is the order Cin7 is fulfilling.
  7. Pushing on: on the merchant's word, shopify_update_connection with readOnly: false, then shopify_sync_stock pushes Manifold's stock, the gap's reservations included, to the store.

Done looks like

  • onboarding_get_link: ended, went-live.
  • shopify_list_connections: one store, connected, every active location bound, bridgeOrders on, readOnly off only after the reconcile.
  • shopify_list_unbridged_orders: empty. The store's stock matches inventory_list_items.
  • Cin7's Shopify integration is paused; the merchant knows they can retire Cin7.

When it goes wrong

| What you see | What it means | Next |
|---|---|---|
| A Shopify store connected before the cut-over | Store orders would be counted twice | Ask the merchant to uninstall it from Shopify until the day; nothing was pushed (read-only) |
| 409 ConnectionReadOnly on shopify_sync_stock | heldBy: "organization": the organization is not live yet. heldBy: "connection": the connection is still receive-only (stage 3) | Stage 2, step 4 first; or finish stage 3's steps 5 and 6, then readOnly: false |
| Unbridged orders after the cut-over | A Shopify SKU is not a Manifold SKU | Fix the SKU in Shopify or create it in Manifold, then retry the bridge |
| Stock pushed looks wrong | A location bound to the wrong Manifold site | Bindings are fixed once made: tell the merchant, and contact support before pushing again |
| An order shipped by both | Cin7's integration was still running at the cut-over | The merchant cancels one; pause the integration before going further |
| An order from the cut-over hour is missing | It was placed from the boundary to the connection, and the gap import has not run, or ran from too late a moment | Keep the connection receive-only; shopify_import_from_store, areas: ["orders"], ordersPlacedSince the boundary, under a new jobId (orders it already brought are acknowledged, not doubled); then stage 3, steps 5 to 7 |
| A bridge-order job stays open | It is stalled on a step (shopify_list_jobs says which, and why), or queued behind one that is | Keep the connection receive-only: its order is not holding stock yet. Tell the merchant what the job says; when the cause is fixed it runs on, and shopify_cancel_job lets go of one that never will |
| The gap import brought orders Cin7 already had | Expected for the boundary's second (Cin7's newest order is always among them); more means the boundary was too early, or Cin7 took an order out of sequence | Before readOnly: false: cancel the duplicates in Manifold (fulfillment_cancel_order); the Cin7 ones are the record |