Shopify Integration
Connect your Shopify store to automatically sync products, orders, customers, and abandoned carts. Enable powerful customer segmentation and abandoned cart recovery campaigns.
Features
- Automatic sync - Products, orders, and customers sync in real-time via webhooks
- Abandoned cart recovery - Capture checkout URLs for recovery email campaigns
- Storefront and checkout tracking - The Joryio pixel tracks page, product, collection, search and cart activity and every checkout step, with no code in your theme
- On-site messages - The Joryio app embed shows Joryio on-site messages on your store
- Email capture - Recognise shoppers from checkout, their store account and store forms
- UTM attribution - Capture and attribute marketing UTM parameters
- Multi-store support - Connect multiple Shopify stores with per-store event segmentation
Connecting Your Store
You install Joryio from Shopify, then link the store to your Joryio workspace from inside your Shopify admin. You need a Joryio account - accounts are set up by the Joryio team (Book a demo).
- Install Joryio from Shopify: use the install link Joryio gave you, or click Install Joryio from Shopify on the Shopify page in Joryio (Settings → Integrations → Shopify). Shopify lists the permissions Joryio asks for - products, orders, customers, checkouts, discounts (for coupons), and customer events and pixels (
read_customer_events,write_pixels), which add the Joryio pixel to your store - and you approve them in Shopify - Shopify opens Joryio inside your Shopify admin. A store that is not linked yet shows This store isn't linked to a Joryio account yet
- Click I have an account - sign in. Joryio opens in a new tab
- Sign in to Joryio. If Joryio asks for Your Joryio account, enter the account name from your sign-in address (
.../a/your-account) - On Link a Shopify store, choose the workspace for this store and click Link store to this workspace. A workspace you cannot manage integrations in, or that already holds another Shopify store, is listed with the reason but cannot be chosen
- Go back to the Shopify admin tab. Within a few seconds it asks Link this store to Joryio? and shows the workspace and account names. Check them and click Link this store
- The page shows the connection. Joryio imports your products, orders and customers, subscribes to Shopify's updates and adds the pixel automatically
- Turn on popups: click Turn on Joryio in your theme, then Save in Shopify - see Turn on popups
Only the Shopify staff member who started the link can finish it, and nothing is connected until they click Link this store. A link that is not used within 15 minutes expires: start over from the Joryio page in your Shopify admin.
Joryio never asks you to type your store's address. If you open the Shopify page in Joryio before Joryio is installed in your store, it shows Install Joryio from Shopify - install from there, then continue with step 2. Already installed? Open Joryio from Apps in your Shopify admin and click I have an account - sign in.
Where your store's data is stored
Your store's data is stored in the same Joryio region as your account - the region your account was set up in. You never choose a region when you install: signing in decides it, so a store cannot end up in a different region from its account. Nothing about the store is stored anywhere until you link it.
Opening Joryio from Shopify
Open Joryio from Apps in your Shopify admin at any time. Inside the admin it shows:
- Connection and Last sync - the same status as the Shopify page in Joryio
- Joryio pixel and Popups on your store, with Turn on Joryio in your theme while popups are not on
- Open Joryio - the Shopify page in Joryio, in a new tab
A store that was disconnected shows Reconnect - sign in, which runs the same linking steps as above.
Stores connected before Joryio opened inside Shopify
A store connected earlier from Joryio (by entering its store domain) stays connected: products, orders, live updates and the pixel keep working. The first time someone opens Joryio from Apps in the store's Shopify admin, Joryio renews its access to the store in the background - Shopify requires this kind of access for every app from January 1, 2027. Nothing else changes and there is nothing to approve in Joryio. Open Joryio from Shopify once before that date.
Sync Settings
After connecting, configure what data to sync:
| Setting | Description | Default |
|---|---|---|
| Sync Products | Import products from Shopify | Enabled |
| Sync Orders | Import orders and update statuses | Enabled |
| Sync Customers | Import customer data | Enabled |
| Sync Carts | Track abandoned checkouts | Enabled |
| Historical Days | Days of order history to import | 60 (90 when the store has granted access to all orders) |
Shopify returns only the last 60 days of orders unless the store has granted Joryio access to all orders. Until then a longer period is shortened to 60 days, and the Settings tab says so.
Access these in Settings → Integrations → Shopify → Settings tab.
Storefront Tracking
When you connect, Joryio adds the Joryio pixel to your store - there is nothing to install. Shopify runs the pixel on every store page and inside checkout, so checkout steps are tracked too. You can see it in your Shopify admin under Settings → Customer events. Each event is stored under its standard name (the Stored as column) - the same name the WooCommerce and Magento integrations and the SDKs use:
| Event | Stored as | When it fires |
|---|---|---|
| Page View | Page Viewed | Every page visit, including checkout pages |
| Product Viewed | Product Viewed | A product page, with the variant shown, its price and its stock status: in_stock (true or false) and stock_status (in_stock or out_of_stock) |
| Collection Viewed | Product List Viewed | A collection page, with category_id (the collection id) and category_name |
| Search | Products Searched | A store search, with the search words in query |
| Add to Cart | Product Added | Every add to cart, with the quantity |
| Remove from Cart | Product Removed | A product removed from the cart |
| Cart Viewed | Cart Viewed | The cart page, with the cart total and items |
| Checkout Started | Checkout Started | The shopper enters checkout, with the total and items |
| Checkout step | Checkout Step Completed | The shopper completes a checkout step: step is contact, address or shipping |
| Payment info | Payment Info Entered | The shopper submits payment details |
| Identify | $identify | The shopper's email, from checkout, a signed-in store account or a store form |
Product events carry product_id, variant_id, quantity, price and currency. Orders are not a pixel event: each order arrives once through Shopify's order webhooks and is stored as Order Completed, so revenue is never counted twice.
On-site messages and sign-ups: the app embed
Some features need to run on the store page itself, which the pixel cannot do. The Joryio app embed loads the Joryio web SDK on your store so that:
- Joryio on-site (in-app) messages appear on your store
- a shopper signed in to their store account is recognised, and their browsing, cart and on-site messages from before they signed in join their contact. Joryio relies only on Shopify's own confirmation that the shopper is signed in
- an email typed into any store form (newsletter, contact, account) identifies the shopper. A typed email never joins the visitor to an existing contact - anyone can type anyone's address
Shopify lets only the store owner switch an app embed on - see Turn on popups. The embed's own settings let you turn off on-site messages or form email capture.
Turn on popups
Joryio popups (in-app messages) show on your store only once the Joryio app embed is on in your theme. Shopify lets only the store owner switch it on:
- In Integrations → Shopify → Connection, find Popups on your store
- Click Turn on Joryio in your theme. Shopify opens your theme editor with Joryio already switched on under App embeds
- Click Save
To do it by hand: in Shopify, go to Online Store → Themes → Customize → App embeds, switch Joryio on and click Save. Do this once for each theme you publish.
The badge next to Popups on your store reads On once your store has loaded Joryio for a visitor in the last 7 days, and Not on yet until then - so after you save it can take one visit to your store. While it reads On, the button is replaced by a small Open theme editor link.
In-app message settings
The embed loads the SDK from https://sdk.joryio.com/web/1/joryio.min.js (the latest 1.x version, the same file the WooCommerce plugin and the Magento extension load); its data calls go to your store's Joryio server. In the theme editor, the Joryio embed has these settings:
| Setting | Default | What it does |
|---|---|---|
| In-app messages | On | Shows your Joryio in-app messages on the store |
| Allow HTML in-app messages | On | Also shows messages built with the HTML editor. They are shown in a protected frame on your store: scripts written into them do not run, and the message can't reach your store's pages or make network requests of its own |
Nothing is counted twice: the SDK shares the pixel's anonymous visitor id and sends no page views or session starts of its own - page and cart events come from the pixel.
Web push is not available on Shopify
Browser notifications need a service worker served from the store's own address at its root (/joryio-sw.js). Shopify does not let an app put a file there: app embed files are served from Shopify's CDN, which is another address, and app proxies answer only under /apps/... (or /a, /community, /tools) and remove the header that would let a worker cover the whole store. So the embed has no web push switch. Web push works on WooCommerce and Magento stores.
Privacy and consent
The pixel and the app embed follow your store's cookie banner (Shopify's Customer Privacy settings). They run only for visitors who allowed both analytics and marketing, and they stop sending when a visitor withdraws consent. In regions where your store does not ask for consent, they run for every visitor.
Shopify shares a shopper's email with app pixels only when the app is approved for protected customer data. Until then, checkout emails reach Joryio through the checkout and order webhooks rather than the pixel.
Stores connected before October 2026
These stores were set up with a tracking script that Shopify stops loading on March 1, 2027, and that never ran inside checkout. It keeps working until you switch:
- In Integrations → Shopify → Connection, click Approve new permissions
- Approve the new customer events and pixels permissions in Shopify
- Back in Joryio, the pixel is added and the old script stops sending events automatically, so nothing is counted twice
- Click Turn on Joryio in your theme to turn on the app embed
If Shopify asks you to approve new permissions when you open Joryio from Apps in your Shopify admin, approve them there instead - Joryio then adds the pixel and switches the old script off.
The old script itself stays listed on your store until Shopify removes it: Shopify deletes it when you uninstall the app, and stops loading it on March 1, 2027. Joryio does not delete it - it only stops sending events.
UTM Attribution
Marketing parameters are automatically captured:
utm_source,utm_medium,utm_campaignutm_term,utm_content
UTM parameters are stored for the visit and attached to its events.
Multi-Store Support
When connecting multiple Shopify stores to the same workspace:
- Each store gets its own App entity with unique SDK key
- All events are tagged with the store's
$app_id - Filter events by
$platform = 'shopify'or specific$app_id - Users can be linked across stores via email
Recognise visitors by the email they type
Recognise visitors by the email they type is set in Joryio, under Integrations → Shopify → Settings. It is on by default.
- On (default) - an email the visitor types at checkout or in a form identifies their browser as the contact with that email: the existing contact (the most recently active one, when several contacts share the email - see below) or a new one. The visitor's earlier browsing joins that contact, and no second contact with the same email is made. This is what most stores expect. The trade-off: anyone who types someone else's email is recognised as that person in that browser - for example, they could see that person's in-app messages and personalized content.
- Off (strict identity) - a typed email never identifies the browser. A guest's email reaches Joryio with the order, and the order goes to the contact with that email. Only a customer signed in to the store is recognised in the browser.
A change applies within a few minutes.
A corrected email. When a visitor corrects a mistyped email, the mistyped address is taken off their browser again and the corrected one is matched like any other, so the visitor moves to the right contact. Shopify sends the cart with every checkout update, so the cart already follows the email. A typed address is only taken off while nothing else names a person on that contact: an order, a phone number, a consent choice, a store account, or an address typed more than a day ago keeps it.
When several contacts share an email
An email can belong to more than one contact in Joryio (for example a couple, or one person imported twice). When a guest's checkout email matches several contacts, the cart, the checkout and the order go to the contact that was most recently active - the same rule imports, the API and forms use, so one person's history is not split between two contacts. Once a checkout has picked a contact, it stays with it: later cart updates, consent, the abandoned cart, the order and its status updates all go to that same contact, even if the other contact changes in the meantime. A customer signed in to the store always goes to the contact that holds their store customer account. Contacts are never merged, and an unsubscribe still applies to every contact with the email.
How store data updates a contact
Orders, checkouts and customer updates from your store update the matching contact in Joryio:
- Phone - orders and checkouts: the phone a shopper types at checkout never replaces a contact's existing phone, even when Shopify links the order to a customer - Shopify attaches a guest checkout to the existing customer with the email typed, so the link does not prove who the shopper is. It only fills the phone when the contact has none.
- Phone - Shopify customer record: customer updates from Shopify (and the customer import) update the contact's phone.
- Names come from the Shopify customer record.
- Store customer number: the contact's Shopify customer number comes from the Shopify customer record (customer updates and the customer import), never from an order or checkout. When the customer record carries a different email, the number moves to the contact with that email.
- The order keeps the phone typed at checkout on its billing and shipping address, whatever happens to the contact.
- SMS consent given at checkout with a number other than the contact's does not subscribe the contact's number.
- To change a contact's phone otherwise: the customer's account in the store, the SDK or the API from your own site or app (for example after the person signs in), an import, or editing the contact in Joryio.
Webhooks
The integration automatically registers webhooks for real-time updates:
- Order created, updated, cancelled, fulfilled
- Checkout created, updated (abandoned carts)
- Product created, updated, deleted
- Customer created, updated
View registered webhooks in Settings → Integrations → Shopify → Webhooks tab.
Scheduled reconciliation
Live updates (webhooks) can be lost - for example while the store is offline, or when a delivery fails too many times. Every 6 hours Joryio checks the store for what changed since its last check and fills in what live updates missed: orders (with their refunds and cancellations), customers, products, and abandoned checkouts of the last 24 hours.
Nothing is counted twice: an item Joryio already holds at the same or a newer version is left alone, and an older copy never overwrites a newer one.
- A missing order placed in the last 24 hours is recorded as if it had arrived live, so post-purchase journeys still start. An older missing order, and a refund or cancellation of an order Joryio already had, are recorded without starting journeys;
Order RefundedandOrder Cancelledare still recorded once each. - Each store is checked at its own time, so stores are not all read at once, and one check reads a limited number of changes; a store with more changes continues within half an hour.
- Stores that are disconnected, in error or still syncing are skipped until they are connected again.
The result is shown in Settings → Integrations → Shopify: Last reconciliation: 22/10/2026 12:00 - items filled in: 3. If the checks keep finding changes that never arrived live, Joryio raises a Live updates from Shopify may be failing alert (bell, Logs & Monitoring, and an email to workspace admins). The missing data is filled in either way, but check the store connection: journeys that react to those changes start late.
The same page shows Last event received - when a live update from the store last reached Joryio (at most a minute behind) - and Last sync, the later of the last full sync and the last reconciliation. A store that sends live updates moves Last event received even when no sync has run.
Refunds and cancellations
Each Shopify refund becomes one Order Refunded event (with the refunded amount, items and note) and a cancellation one Order Cancelled, so a journey can react to them. See Refund and cancellation events.
Privacy requests from Shopify
Shopify forwards its customers' privacy requests to every app the store uses. Joryio handles each one in every workspace connected to the store and tells the workspace's owners, admins and members who can manage settings (email and the bell):
| Request from Shopify | What Joryio does | What you do |
|---|---|---|
| A customer asked for their data | Finds the contact by their Shopify customer ID or email and starts a data export on the Data Subject Requests page | When the export is ready, download it and send it to the customer through Shopify, within 30 days. If Joryio holds nothing on the customer, the notice says so |
| A customer asked to be erased | Starts an erasure for the contact (profile, events, messages; orders keep their amounts but lose the customer's name, addresses, Shopify customer ID, order note, order status link, tracking number and link to the contact) and removes personal details from the orders Shopify lists | Nothing |
| Store data erased (48 hours after you uninstall the app) | Removes the store connection - see Uninstalling Joryio from Shopify | Nothing. If you reinstalled the app within the 48 hours, Joryio leaves the store connected |
Uninstalling Joryio from Shopify
When you uninstall, Joryio stops receiving Shopify data and removes the connection. Your contacts and order history stay in your Joryio workspace until you delete them or a customer asks to be erased; products are archived.
In detail, 48 hours after the uninstall (when Shopify asks Joryio to remove the store's data):
- Removed: the store connection and its access, abandoned carts and checkouts from the store.
- Kept: contacts, and orders - still linked to their contacts and marked as coming from a store that was disconnected - so lifetime value, RFM, segments and attribution keep working, for example if you move to WooCommerce or Magento.
- Archived: the store's products. Past orders still show what was bought; archived products are never recommended, offered in product selections or used by back-in-stock and price-drop messages.
Installing again: install Joryio from the install link and open it from Apps. The page offers the linking steps again - see Connecting your store. Link the store to the workspace that held it before, so its contacts and order history stay together.
Connection Health
Once Shopify is connected, a health panel (in Integrations → Shopify → Connection) is the first thing to check if store data stops syncing:
- Store details - live name, plan, currency, and domain pulled from Shopify. Seeing them confirms the access token still works.
- Webhook status - on the Webhooks tab, each required topic is shown as registered or missing. A Re-register button re-creates any missing webhooks so real-time updates resume.
- Storefront tracking - whether the Joryio pixel is active, with Approve new permissions or Re-register tracking when it is not, and the Turn on Joryio in your theme button for the app embed. See Storefront Tracking.
If data has drifted, re-register missing webhooks on the Webhooks tab, then run a manual Sync All to backfill.
Marketing consent
Joryio reads the marketing consent Shopify keeps on each customer - the email and SMS consent your store collects at checkout, on forms and in the Shopify admin - and applies it to the contact's subscription in Joryio:
| In Shopify | In Joryio |
|---|---|
| Subscribed (email or SMS) | The contact is subscribed to that channel. Someone who unsubscribed is not re-subscribed - see the table below; they can resubscribe themselves through the preference center. |
| Unsubscribed | The contact is unsubscribed from that channel - unless Joryio recorded a later change for that contact (for example, they opted in on a Joryio form after unsubscribing in Shopify). |
| Not subscribed, pending (a double opt-in still waiting for confirmation) or no consent | No change. |
- It applies to the historical import, to every customer and order update, and to Shopify's dedicated consent-change notifications.
- For an order whose customer carries no consent, the checkout's marketing box counts: a tick subscribes, an unticked box changes nothing.
- Each change is recorded in the contact's subscription history with its source (
shopify_customerorshopify_checkout), Shopify's opt-in level (single or confirmed) and the time Shopify recorded the change. - A consent change that comes with a new or updated order also records the shopper's IP address Shopify kept on the order (its browser IP), shown only to team members who can see personal data. The historical import and Shopify's customer consent notifications carry no IP address - Shopify does not provide one.
- An address on your suppression list (a bounce, or an unsubscribe recorded on another contact) is not subscribed.
- Joryio does not write consent back to Shopify: an unsubscribe in Joryio does not change your Shopify customer list.
What a store opt-in or opt-out does
| What the store sends | What Joryio does |
|---|---|
| An opt-in tick at a guest checkout | Subscribes a contact who has no subscription on that channel yet. Unless the store setting below is on, never re-subscribes someone who unsubscribed - anyone can type anyone's email at a guest checkout. |
| Any other opt-in from Shopify, including one from a customer who has a store account | The same as a guest tick: unless the setting below is on, never re-subscribes someone who unsubscribed. Shopify attaches a guest checkout to the existing customer with that email and does not tell Joryio whether the shopper was logged in, so Joryio cannot tell the account holder from someone who typed their email. (WooCommerce and Magento do say so - see their pages.) |
| An opt-in at checkout or in the customer record, with Let checkout opt-ins re-subscribe people who unsubscribed turned on (off by default) | Re-subscribes a contact who unsubscribed - including after a guest checkout - when the store's time for the opt-in is after their latest unsubscribe. The risk: Shopify does not tell Joryio whether the shopper was logged in, so someone typing another person's email at checkout could re-subscribe them; they can always unsubscribe again with one click. The never-undone cases below still hold. |
| An unticked box | No change - it is never an unsubscribe. |
| An opt-out in the store | Unsubscribes, unless Joryio recorded a later change for that contact. |
| Never undone by any store opt-in, setting on or off | A spam complaint, a hard bounce, an organization-wide block, another workspace, an unsubscribe recorded on another contact with the same email or phone, and an SMS STOP. |
- The setting is in Integrations → Shopify → Settings, one per store connection, off by default. Only admins can change it, and each change is recorded in the audit log.
- Store consent subscribes the channel, not a list: a list that requires double opt-in still sends its confirmation email when the person joins that list.
The consent-change notifications are registered on the next Sync All or Re-register. Until then, consent still arrives with every customer and order update.
Abandoned Cart Recovery
When a customer abandons their cart:
- The checkout URL is captured
- The cart value and items are stored
- The customer's email (if captured) is linked
Use this data to create abandoned cart recovery campaigns in Canvas with the actual checkout URL.
Troubleshooting
Products/Orders not syncing
- Check sync is enabled in Settings tab
- Verify webhooks are registered in Webhooks tab
- Try manual sync with "Sync All" button
"Shopify has not approved this app's access to protected customer data"
- Shopify gives customer data (customers, and the customer details on orders) only to apps approved for protected customer data. Until then Shopify refuses those reads, and the Last Error on the integration page names the object it refused
- The approval is requested by the app's developer in the Shopify Dev Dashboard (or Partner Dashboard): the app -> API access -> Protected customer data. Once it is approved, click Sync All
- One refused entity does not stop the rest: products, orders and abandoned checkouts still sync, and the webhooks (live orders and carts) are registered before the sync starts. The status stays Error until a sync finishes without errors, and Last Sync shows the last sync that did
- The Joryio page inside your Shopify admin shows the same status with a short reason
Pixel not active
- Approve new permissions is shown: the store has not approved the pixel permissions yet - see Stores connected before October 2026
- An error is shown: click Re-register tracking. If it persists, the message from Shopify says why
- Check Shopify admin → Settings → Customer events: the Joryio pixel should show as connected
Email not being captured
- Checkout emails arrive through the checkout and order webhooks
- Store forms and signed-in accounts need the app embed switched on - see the app embed
- A visitor who has not allowed analytics and marketing is not tracked
Connection issues
- Verify your Shopify app has the required permissions
- Open Joryio from Apps in your Shopify admin. A store that was disconnected shows Reconnect - sign in, which links it again - see Opening Joryio from Shopify
Product views or add to cart missing
- The pixel runs on Online Store themes and Shopify checkout. A headless storefront (Hydrogen or another custom front end) may not run app pixels - send events with the Web SDK instead
- Visitors who declined analytics or marketing cookies are not tracked
- Product views read the stock status from your store's
/products/<handle>.jsaddress. If a theme or app blocks that address, product views arrive without the stock status