Headless and custom storefronts do not use a Shopify theme, so Cart Sync uses a lightweight JavaScript SDK instead of the theme app embed. One script tag and one init call wire Tapcart to your existing cart and customer auth.
Sync is logged-in user only and best-effort: it never blocks or breaks your cart UI, and checkout stays 100% native Shopify.
Before you start
Your app must be on Tapcart React Native release 21.8.0 or newer.
Cart Sync must be enabled for your app. In the Tapcart dashboard, open Settings → Cart Sync, turn Enable Cart Sync on, configure abandoned-cart settings if needed, and save. If you do not see that page, contact Tapcart to enable it.
Have your Tapcart App ID ready.
Open the Tapcart dashboard
Click the shop name (top right) → Settings
In App Settings, scroll to the Tapcart CLI / Application ID section
Copy the App ID
1. Allowlist your web origins
Go to Settings → Cart Sync → Allowed Web Origins.
Add every storefront origin you will serve from, including staging if needed.
Use a bare origin only: scheme + host + optional port, with no path. Example: https://shop.example.com
Origin allowlist updates typically take effect within about a minute. If the SDK returns a forbidden-origin error, double-check the exact origin string.
2. Load the SDK
<script src="https://api.tapcart.com/cart-sync/sdk/v1/cart-sync.js"></script>
The script defines window.TapcartCartSync. Nothing runs until you call init.
3. Initialize with your cart and auth
const sync = window.TapcartCartSync.init({
appId: '<your Tapcart App ID>',
// Logged-in shopper's Shopify customer token, or null for guests.
// Customer Account API token (shcat_...) or classic customerAccessToken.
getCustomerToken: async () => myAuth.getShopifyCustomerToken(),
// Current cart as plain lines.
readCart: async () => myCart.lines.map(l => ({
variantId: l.merchandiseId, // Storefront variant id (gid or numeric)
quantity: l.quantity,
sellingPlanId: l.sellingPlanId || null,
properties: l.attributes || {}, // line-item properties/attributes
})),
// Apply Tapcart's reconciliation through YOUR cart layer.
applyCartOps: async ({ upserts, removals }) => {
for (const u of upserts) await myCart.setLine(u.variantId, u.quantity, u);
for (const r of removals) await myCart.removeLine(r.variantId, r);
},
// Optional:
// debounceMs: 2500,
// onSynced: ({ phase, revision }) => {},
// onError: ({ phase, error }) => {},
});
4. Call hydrate and capture
sync.hydrate(); // on page load AND right after login myCart.onChange(sync.capture); // after every local cart mutation (debounced)// Also available: // sync.captureNow() // e.g. beforeunload // sync.destroy()
hydrate()pulls the server mirror and usesapplyCartOpsso your cart converges to the shopper's cross-surface state.capture()reports your current cart lines so other surfaces (including the app) can converge to them.
That is the full integration; no webhooks or metafields are required on your side.
How it Works
Identity
It works with Logged-in customers only. If getCustomerToken returns null, sync pauses cleanly. After login, call hydrate() so the account mirror flows in.
Your cart stays yours
Tapcart never creates or adopts Shopify carts and never writes cart metafields. Every read and write goes through the callbacks you provide. Checkout continues to use your live Shopify cart.
Merging
Per-line last-write-wins with deletion tombstones, merged server-side. A line missing from a stale snapshot is never treated as a deletion. Only explicit removals delete, so an empty or stale mirror cannot wipe a real cart.
Corruption safety and failures
Only plain line items sync. If a cart goes bad, discard it, build a fresh one, and call hydrate(). Network or auth failures surface through onError and retry on the next capture or hydrate. The SDK does not throw into your cart code paths.
Data lifecycle
The mirror is keyed by app and customer, cleared on order completion, expires after 10 days idle, and is removed on customer redaction.
Enablement checklist
Add production and staging origins to Allowed Web Origins.
Enable Cart Sync in Settings → Cart Sync.
Add the script and init on your site.
Verify by logging in, mutating the cart on web, and confirming the same lines in the app (and the reverse).
If you also want unified wishlist on the same headless storefront, allowlist the origin under Mobile Wishlist as well and load the Wishlist SDK separately.
Troubleshooting
Forbidden origin errors
Add the exact bare origin to Allowed Web Origins and retry after about a minute.
Feature disabled errors
Turn Enable Cart Sync on in Settings → Cart Sync and save.
Sync appears to do nothing
Confirm the shopper is logged in and that getCustomerToken returns a valid Shopify customer token. Returning null is an intentional no-op for guests.
More Questions
Need help with a headless Cart Sync integration? Reach out through LiveChat on your Tapcart Dashboard or email us at [email protected].
