Skip to main content

Magento Integration

Connect your Magento 2 store using the Joryio Connect extension. Sync products, orders, customers, and carts, track storefront behavior, and recover abandoned carts.

Features​

  • Composer-installed extension - Native Magento 2 module, no OAuth needed
  • Automatic sync - Products, orders, customers, and carts stream to Joryio every minute
  • Historical import - Existing products, customers, and orders (last 90 days by default) are imported on connect
  • Abandoned cart recovery - Detect abandoned carts and send recovery links that restore the cart
  • Storefront tracking - Page views, product and category views, add-to-cart, and checkout events
  • Identity capture - Automatically identify customers from login, newsletter signup, and checkout
  • Revenue attribution - Orders are attributed to the campaign or journey that drove them
  • Catalog triggers - Stock and price updates power back-in-stock and price-drop triggers

Prerequisites​

  • Magento Open Source or Adobe Commerce 2.4.x
  • Command-line access to your Magento server with Composer
  • Magento cron configured and running - the extension delivers events via cron, so this is required
  • Outbound HTTPS access from your Magento server to Joryio

Setup Steps​

Step 1: Get Your Server URL and API Key​

  1. In Joryio, go to Settings → Integrations → Magento
  2. Copy the Server URL. It is the address of the region your Joryio account is in, for example https://api-eu1.joryio.com. From extension v1.4.1 there is no default, so paste exactly this value
  3. Click Generate API Key and copy the key (starts with jry_mg_). When you open this page again later it shows the same key, masked - click the eye icon to see it. The key stays the same until the store is disconnected

Step 2: Install the Extension​

Download the extension zip (joryio-connect-magento2.zip) with the Download Extension button on the Magento integration page in your dashboard, extract it, then run these commands from your Magento root:

composer config repositories.joryio path /path/to/joryio-connect
composer require joryio/module-connect
bin/magento module:enable Joryio_Connect
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento cache:flush

Alternatively (no composer): extract the zip into app/code/Joryio/Connect and run the same bin/magento commands.

Step 3: Connect​

  1. In Magento admin, go to Stores → Configuration → Joryio
  2. Paste the Server URL and API key from Step 1
  3. Click Connect

The extension automatically creates a Magento integration access token and shares it securely with Joryio - you don't need to create any tokens by hand. Joryio validates the connection and immediately starts a full historical sync (products, then customers, then orders - the last 90 days by default).

When it finishes, the status shows Connected in both the Magento admin page and the Joryio dashboard.

Allow OAuth access tokens as bearer tokens​

Joryio reads your store's REST API with the access token of the "Joryio" integration, sent as a bearer token. From Magento 2.4.4, Magento accepts that only when Stores → Configuration → Services → OAuth → Consumer Settings → Allow OAuth Access Tokens to be used as standalone Bearer tokens is Yes (the default is No). When it is No, Connect (extension v1.4.1 or later) explains this and asks before turning it on - nothing is created until you confirm. It is a store-wide setting: it applies to the access tokens of every integration on your store. You can also set it to Yes yourself and then click Connect.

If a connection fails, nothing is left behind: the "Joryio" integration that Connect created is deleted (one left by an earlier connection gets its token revoked), and a bearer setting that Connect turned on is turned off again. The message shows Joryio's reason - see Connect fails.

What Gets Synced​

DataHowWhen
ProductsHistorical import + real-time updates (including stock and price changes)On connect, then continuously
CustomersHistorical import + real-time updatesOn connect, then continuously
OrdersHistorical import (last 90 days by default) + real-time updatesOn connect, then continuously
CartsReal-time tracking + abandonment detectionContinuously
IdentityLogin, newsletter signup, checkout emailContinuously

Real-time updates are queued locally in your store and delivered to Joryio by a once-a-minute cron job - so a healthy Magento cron is essential. Stock and price updates also feed catalog triggers (back in stock, price drop).

Deleting a customer in your store does not delete the contact in Joryio. To erase someone's data, use Erase Data on the contact.

Products, stock and categories​

  • Configurable products are one product in Joryio. Their child products (each size or colour, for example) become its variants when they are set to Not Visible Individually; a child that is also visible on its own stays a product too. The product shows the lowest child price, is in stock when any child is, and its quantity is the children's total. Each variant carries its options (for example Color: Red), and deleting a child removes it from the product.
  • Stock comes from your sources when the store uses Multi-Source Inventory (MSI), otherwise from the product's stock settings; with MSI, quantities reserved by orders not yet shipped are taken off (for up to 2,000 products per sync - the rest show their source quantity until their next update). A product whose stock is not managed shows as in stock, with no quantity.
  • Categories are synced by name (the store's root category is not).
  • Order lines for a configurable product name the product and carry the SKU of the variant the customer bought.
  • Coupon amounts: Magento reports one discount for all the cart price rules on an order. When the coupon was the only rule, the coupon's amount is the order's discount. With several rules, Joryio shows the coupon's amount only when each discounted line was discounted by one rule; otherwise the order says the store did not report it.

Categories, MSI stock and variant updates from the extension need extension v1.4.4 - see Upgrading to 1.4.4.

Scheduled reconciliation​

Live updates (the extension) 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, credit memos (refunds), customers and products. Carts come only from the extension (Magento's API has no list of abandoned carts Joryio can read).

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 Refunded and Order Cancelled are 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 → Magento: 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 Magento 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​

A refund on a Magento order becomes one Order Refunded event with the refunded amount, and a cancelled order one Order Cancelled, so a journey can react to them. The extension reports cancellations and full refunds (status closed) as they happen; a partial credit memo is picked up the next time orders are synced. See Refund and cancellation events.

How store data updates a contact​

Orders, checkouts and customer updates from your store update the matching contact in Joryio:

  • Phone - guest checkout: a checkout by a shopper who is not logged in to the store never replaces a contact's existing phone. It only fills the phone when the contact has none - anyone can type anyone's email at a guest checkout.
  • Phone - signed-in customers: an order placed by a signed-in customer, and changes to the customer's account in the store, update the contact's phone.
  • Names update from orders and customer updates.
  • Store customer number: the contact's Magento customer number comes from customer account data (customer updates, the customer import and orders by signed-in customers), never from a guest checkout. When the account 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: a logged-in checkout or 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.

Storefront Tracking​

The extension adds a lightweight tracking snippet to your storefront (enabled by default - you can turn it off in Stores → Configuration → Joryio). Each event is stored under its standard name (the Stored as column) - the same name the WooCommerce and Shopify integrations and the SDKs use:

EventStored asWhen it fires
Page ViewPage ViewedEvery page visit
Product ViewedProduct ViewedA product page. Carries the stock status: in_stock (true or false) and stock_status (in_stock or out_of_stock)
Category ViewedProduct List ViewedA category page, with category_id, category_name and the page address (extension v1.1.1 or later)
Add to CartProduct AddedEvery add: the AJAX button, a product form that reloads the page, and wishlist to cart. Magento records the add and the shopper's browser sends it once (extension v1.1.1 or later). A reorder from My Account is not counted
Checkout StartedCheckout StartedThe checkout page, once per cart. A reload of the checkout is not a second start
Identify$identifyEmail capture

Signed-in customers (extension 1.4.5 or later). When a customer is logged in to the store, the extension signs the customer's Magento customer number on the store's server, and the identify carries that proof. Joryio checks it and joins the visitor's browsing to the contact that already holds that customer number - never to a contact found by email, because several contacts can share an email. A guest's typed email follows the store's Recognise visitors by the email they type setting. Older versions identified a logged-in customer by email only, which could leave a second contact with just the email next to the customer's contact; after the update, that second contact is joined to the customer's contact the next time the customer logs in on the same browser.

Shared browsers (extension 1.4.25 or later). Signing out of the store gives the browser a new anonymous visitor, so the next person on the same device is not shown as the one before. A different customer signing in on a browser another customer used gets a new visitor first, and when Joryio finds that the browser's visitor already belongs to someone else (for example the store admin who used it before), the extension starts a new visitor and sends the sign-in proof again. The earlier person's profile and history stay as they are, and two people are never merged. While this is being worked out, a page's events wait a few seconds at most.

Product events carry product_id, variant_id (the chosen option of a configurable product), quantity, price, currency and the stock status.

The snippet also captures UTM parameters and Joryio click IDs from your campaign links, so orders are attributed back to the campaign or journey that drove the sale. See Revenue Attribution.

Content Security Policy​

From extension 1.4.6 the tracking snippet contains no inline script: it is a file served from your store (Joryio_Connect/js/tracking.js), and its settings are a data block in the page. Magento 2.4.7 and later enforce a strict Content Security Policy on the checkout and payment pages, which blocks inline scripts - with 1.4.5 or older, the checkout sent no Checkout Started, no identify from the email field and no Page View. Nothing needs 'unsafe-inline'. The extension adds *.joryio.com and sdk.joryio.com to the policy for scripts and API calls; if your Server URL is not on joryio.com, add its host to your policy yourself. After upgrading a store in production mode, run bin/magento setup:static-content:deploy so the new file is published.

After changing settings​

The snippet's settings - the Server URL, Inject Tracking Snippet and the in-app message and web push settings - are part of each page, so Magento's full-page cache keeps the values a page was cached with. From extension 1.4.6, Save Config, Connect and Disconnect clear exactly the cached pages that carry the snippet, in Magento's page cache and in Varnish, and leave the rest of the cache alone. With 1.4.5 or older, run bin/magento cache:flush after changing these settings. A CDN or proxy cache in front of Magento is not cleared by Magento: purge it after changing the Server URL. Until then, an outdated cached page sends nothing in a browser that has already loaded a fresh page - it never sends to the old address. Magento may still show One or more of the Cache Types are invalidated: Page Cache after Save Config: Magento marks the page cache that way after every configuration save, whatever the section. The pages with the snippet are already cleared; refresh Page Cache in System > Cache Management when it suits you.

Page views and the web SDK. The Page View event always comes from the tracking snippet. trackPageViews: false in the snippet's web SDK settings turns off only the SDK's own automatic page views, so each page is counted once.

Recognise visitors by the email they type​

From extension 1.4.21, Stores → Configuration → Joryio has Recognise visitors by the email they type. It is on by default, and an older extension behaves as on. The Default Config value is the one reported.

  • 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. The cart, the order and the form submission still go to the contact with that email, so abandoned cart emails still reach the address typed. Only a customer signed in to the store is recognised in the browser.

Joryio shows the setting read-only under Integrations → Magento → Settings, with the extension version and when it was last reported - it is set only in the extension. Saving it reports it to Joryio at once, and a change applies within a few minutes.

The cart follows the email typed, and a corrected email. A guest's cart moves to the contact their checkout email names as soon as they type it, not only when the abandoned cart is reported. When a visitor corrects a mistyped email, the mistyped address is taken off again and the corrected one is matched like any other, so the visitor and the cart move to the right contact and no contact is left behind with the typo. 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.

In-app messages and web push​

From extension v1.3.0 the tracking snippet also loads the Joryio web SDK on your store, from https://sdk.joryio.com/web/1/joryio.min.js (the latest 1.x version). Its data calls go to the Server URL you configured, like the snippet. The settings are in Stores → Configuration → Joryio → In-app messages and web push:

SettingDefaultWhat it does
In-app messagesYesShows your Joryio in-app messages on the store
Allow HTML in-app messagesYesAlso 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
Web pushNoLets shoppers subscribe to browser notifications

Stores that upgrade from an older extension get the same defaults. The SDK is loaded by the tracking snippet, so keep Inject Tracking Snippet on. The extension allows *.joryio.com in Magento's Content Security Policy for scripts and API calls.

Nothing is counted twice. The SDK uses the snippet's anonymous visitor id, so both belong to one profile. Emails are still identified by the snippet only, and the SDK sends no page views or session starts of its own: page and cart events come from the snippet, as before.

Web push. A browser accepts the notification service worker only from your store's own address. With Web push on, the extension answers https://your-store/joryio-sw.js (a Magento route, covering the whole store), which loads Joryio's hosted worker. Magento's standard nginx and Apache configurations pass that address to Magento; if your server answers .js files from disk instead, add a rewrite from /joryio-sw.js to /joryio/sw/index. Shoppers are asked through a push opt-in in-app message, so keep in-app messages on, and turn on web push for the app in Joryio too. Turning web push off makes the same address serve a worker that removes itself from visitors' browsers.

Turn on web push in Joryio too. Connecting the store created an app for it in Joryio (Settings → Apps, named after the store). Open it, click the bell (Push) and Enable Web Push: that creates the store's own web push keys, which the SDK on your store reads by itself - there is nothing to copy. Then create an in-app message with a button set to Ask to enable notifications and show it in that app; shoppers who accept can receive your push campaigns and journey steps.

Magento's newsletter subscription is sent to Joryio (extension v1.1.0 or later) and applied to the contact's email subscription:

In MagentoIn Joryio
SubscribedSubscribed to email. Someone who unsubscribed is re-subscribed only in the cases in What a store opt-in or opt-out does; otherwise they can resubscribe themselves through the preference center.
UnsubscribedUnsubscribed from email - unless Joryio recorded a later change for that contact.
Not Activated, UnconfirmedNo change.

Every way the status changes counts: the "Sign Up for Newsletter" box on the registration form and the account page, the store admin, the API and the unsubscribe link in your newsletters. The historical import subscribes customers Magento lists as subscribed; it does not unsubscribe anyone, because Magento's API does not tell "never subscribed" apart from "unsubscribed". Each change is recorded in the contact's subscription history (source magento_newsletter), with the opt-in level (confirmed when Magento's Need to Confirm is on) and the time Magento recorded the change.

Proof recorded (extension 1.4.28): the checkout box on an order also records the order's IP address and the checkout page, and a newsletter change made on the storefront (the registration form, the account page, the newsletter block, the unsubscribe link) the visitor's IP address. A change made in the admin, through the API or by cron records none. The IP address is shown only to team members who can see personal data, is included when the person asks for their data, and is removed when their data is erased.

Who counts at once: a newsletter subscription counts at once for a known buyer (Joryio has an order from the contact) or someone already subscribed to email or SMS, and when Magento confirmed it - Need to Confirm is on and the shopper clicked Magento's confirmation email. Magento verifies no address at registration, on the account page, in the admin or through the API, so anyone else - the historical import included - is held as pending until their own order with the box ticked or a click on Joryio's confirmation email, unless you choose to count them immediately: see Opt-ins from new shoppers. An unsubscribe always applies at once.

Magento has no newsletter box on the checkout. To ask there, turn on Stores → Configuration → Joryio → Marketing Consent Checkbox → Show the Checkbox at Checkout. The box appears below the payment methods.

SettingDefaultNotes
Show the Checkbox at CheckoutNo
Checkbox Text"Yes, I'd like to receive news and offers by email"The exact text the shopper saw is recorded with their choice.
Ticked by DefaultNoNot recommended - under the GDPR (EU and UK) a pre-ticked box is not valid consent.
SMS Consent CheckboxNoA second box for text messages, with its own text. The billing phone is recorded.

A tick subscribes the contact (re-subscribing someone who unsubscribed only in the cases below, and never an address on your suppression list); an unticked box changes nothing. A signed-in customer's tick is the exception - see What a store opt-in or opt-out does. The choice, the text shown and the time are sent with the order and recorded in the contact's subscription history (source magento_checkout). With the optional An unticked box unsubscribes on, an unticked box on a placed order records an unsubscribe instead.

Extension 1.4.14 or later: Stores → Configuration → Joryio → Marketing Consent Checkbox → When a Shopper Ticks the Checkout Opt-In.

OptionWhat happens
Only when the order is placed (default)The choice reaches Joryio with the order. A shopper who ticks and abandons is not subscribed.
Record consent immediatelyThe tick reaches Joryio as soon as the shopper makes it, before they order. Known buyers and subscribers count at once; a new guest counts after their order or after they confirm by email (below). If the shopper unticks the box again in the same checkout, that tick is withdrawn - only that one: a subscription they gave anywhere else stays. The abandoned-cart event also carries the box as the shopper left it.

Who counts at once with Record consent immediately (1.4.16 or later):

ShopperWhat a tick before the order does
A known buyer (Joryio has an order from the contact) or someone already subscribed to email or SMSCounts at once: subscribed, so your abandoned-cart reminder can reach them.
A new guestHeld as pending: the tick is recorded (text, time, store, checkout), but the contact is not subscribed and gets no marketing - an abandoned-cart reminder is skipped as Awaiting confirmation. Their own order with the box ticked subscribes them, and so does a click on the confirmation email if you turn it on.
Someone who unsubscribed and has not orderedStays unsubscribed. Their order then follows the rules below.

Why: at a guest checkout anyone can type anyone's email. Without this rule a bot could put an item in the cart, type a stranger's email, tick the box and leave - subscribing someone who never ordered.

Two safeguards in Integrations → Magento → Settings (admins only; each change is recorded in the audit log):

  • Email new guests a confirmation link - off by default, because the address was typed at checkout and nobody has verified it yet. When on, a new guest held as pending gets one email; its link subscribes them (source double_opt_in). It uses your double opt-in confirmation template when you chose one.

  • Opt-in ticks per shopper per hour - default 3, counted per IP address, per checkout and per email address. Ticks over the limit are ignored and that checkout's abandoned-cart event carries no consent; the order still records the choice. The extension has the same limit (Opt-In Ticks per Shopper per Hour, default 3) and stops such a tick before it is sent.

  • Only a tick the shopper makes counts, in either mode. A box shown pre-ticked (Ticked by Default) counts only after the shopper interacts with it: left untouched, it is not consent - not at once and not with the order. Unticking and ticking it again counts.

  • Each change is sent once; opening or reloading the checkout sends nothing.

  • Someone who unsubscribed is re-subscribed only as described in What a store opt-in or opt-out does. An untick never unsubscribes anyone - it only withdraws the tick made in that checkout.

  • The store must know the shopper's email when they tick (Magento asks for it before the payment step). If it does not yet, the choice travels with the abandoned cart or the order.

Consent wording and the GDPR

With Record consent immediately, a tick is marketing consent on its own - it is not tied to a purchase. Make the checkbox text say what the shopper agrees to receive and on which channel (for example "Yes, I'd like to receive news and offers by email"), keep the box unticked by default and separate from accepting your terms, and mention it in your privacy notice. Joryio records the text shown, the time, the store, the checkout and the shopper's IP address with each tick. A new guest's tick stays pending until their own order or their confirmation, so nobody gets marketing because someone else typed their address; the confirmation email is sent only if you turn it on. The abandoned-cart reminder then reaches a subscribed shopper as a marketing message.

Shown in Joryio, set in the extension​

Integrations → Magento → Settings shows the extension's checkout opt-in settings in Store checkout opt-in (set in your store), read-only: Show the Checkbox at Checkout, Ticked by Default and When a Shopper Ticks the Checkout Opt-In, with the extension version and when it last reported them. You change them only in Magento, under Stores → Configuration → Joryio → Marketing Consent Checkbox; Joryio never changes them. The Default Config values are shown - a store view that overrides one is not. With Record consent immediately, the card also lists the Joryio settings that then decide what a tick does: Opt-ins from new shoppers, the confirmation email and the hourly limit.

Extension 1.4.17 or later reports them with every request it makes to Joryio - carts, abandoned carts, orders, customers, products and the rest - and once more when you save one of them. A change, such as a new extension version after an update, shows from the next request; an unchanged report is re-recorded at most once an hour, so last reported can be up to an hour old. With an older extension the card says Not reported yet - update the plugin.

Opt-ins from new shoppers​

Opt-ins from new shoppers (not yet buyers) in Integrations → Magento → Settings (admins only; each change is recorded in the audit log) decides what an opt-in from someone who has not bought yet does:

OptionWhat happens
Hold until they order or confirm by email (default, recommended)A new shopper's opt-in is held as pending until their own order with the box ticked or a click on the confirmation email (if you turned Email new guests a confirmation link on).
Count immediately (less strict - any visitor can enter any email)It counts at once. Any visitor can type any email address, so someone can subscribe another person; they can unsubscribe with one click.

It covers every opt-in that arrives without an order: the newsletter subscription from the registration form, the account page, the admin and the API, the historical import, and - with Record consent immediately - the checkout tick. Either way:

  • a known buyer or someone already subscribed counts at once, and so does a subscription Magento confirmed (Need to Confirm);
  • a tick that comes with the shopper's own order always counts;
  • an unsubscribe always applies at once;
  • the Opt-in ticks per shopper per hour limit stays in force.

This is not the same setting as When a Shopper Ticks the Checkout Opt-In in the extension: that one decides when a checkout tick is sent - with the order, or as soon as it is made; this one decides what an early opt-in from a new shopper does. With Only when the order is placed, a checkout tick comes with the order and always counts, so this setting covers registration and account opt-ins only.

What a store opt-in or opt-out does​

What the store sendsWhat Joryio does
An opt-in tick at a guest checkoutSubscribes 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.
An opt-in by a logged-in customer (an order placed by a signed-in customer, not a guest), dated after the contact's latest unsubscribeAlso re-subscribes a contact who unsubscribed through an unsubscribe link or the one-click button, the preference page, the API, a team member, or this store's own opt-out.
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: at a guest checkout Magento cannot tell Joryio who the shopper is, 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 boxNo change - it is never an unsubscribe. Unless you turn on An unticked box unsubscribes in the store (off by default).
An opt-out in the storeUnsubscribes, unless Joryio recorded a later change for that contact.
Never undone by any store opt-in, setting on or offA 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 → Magento → Settings, one per store connection, off by default. Only admins can change it, and each change is recorded in the audit log.
  • A re-subscribe applies only to that contact; other contacts with the same email or phone stay unsubscribed. The contact's history says why, for example re-subscribed by the store checkout (logged-in customer) after unsubscribing via the preference page.
  • SMS: with SMS double opt-in on, a re-subscribe sends one "Reply YES" text and the contact stays pending until they reply. A contact who texted STOP comes back only this way - with SMS double opt-in off, a store opt-in never re-subscribes them.
  • 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.

An unticked box unsubscribes (optional)​

For stores with a strict consent policy, extension 1.4.26 or later has an optional setting in Stores → Configuration → Joryio → Marketing Consent: An unticked opt-in box at checkout or registration unsubscribes the person. It is off by default.

SettingWhat an unticked box does
Off (default)Nothing. An unticked box never changes anyone's status.
OnA box that was shown and submitted unticked - with a placed order, or on the registration form - records an unsubscribe for that box's channel (email or SMS). Only for signed-in customers, whom the store verified.
On, with Also for guest checkoutsThe same for guest checkouts and new account registrations. Anyone can type someone else's email at a guest checkout - a guest could unsubscribe another person. A new account registration counts as a guest: the store does not verify the email address.
  • The unsubscribe is recorded like any other: in the contact's subscription history (source magento_checkout, reason checkout_box_unticked, with the text shown next to the box) and as the message.unsubscribed event.
  • Also someone who never opted in: with the setting on, an unticked box unsubscribes the person even if they never opted in (Not Set becomes Unsubscribed). Like every unsubscribe, it applies to every contact with the same email or phone in the workspace, and undoing it needs a new opt-in.
  • Never for a box that was not shown (the SMS box only where it is shown), never from a sync of past orders, and never for a box unticked before the order is placed - unticking during the checkout only withdraws that checkout's own tick. It never subscribes or re-subscribes anyone and never changes a bounce or complaint suppression.
  • Integrations → Magento → Settings shows the setting read-only. Older versions do not report it, and Joryio treats it as off.

Delivery and retries​

From extension v1.4.0, an event is not lost when Joryio cannot be reached for minutes or hours. Each event (order, product, customer, cart, newsletter change) waits in your store's queue until Joryio accepts it:

  • Joryio unreachable or busy - the queue pauses and tries again after growing pauses: 1, 2, 4, 8, 16 and 32 minutes, then every hour, for up to 7 days. When Joryio is back, the waiting events arrive, in the order they happened for each order, cart and customer.
  • An event Joryio refuses (for example, invalid data) is set aside at once, so it never holds up the others.
  • Joryio refuses the API key (the store was disconnected in Joryio, or a new key was generated) - delivery stops and the Magento admin shows a notice. The events are kept: paste the current key and click Connect, and delivery resumes.
  • Events that could not be delivered are kept for 30 days.

Stores → Configuration → Joryio → Event Queue shows how many events are waiting, retrying and not delivered, the last error and the last successful delivery. Retry now sends every waiting and undelivered event again. From v1.4.4, saving a new Server URL or API key, a successful Connect or a passing Test Connection ends the pause at once: waiting events go with the next run (within a minute) and the old error is cleared.

Disconnecting and uninstalling​

  • Disconnect in the Magento admin ends the connection: Joryio stops syncing, the access token of the "Joryio" Magento integration is revoked, and events still waiting are set aside (after you reconnect, Retry now sends them). The API key stays valid - click Connect again with the same key (extension v1.4.3 or later; earlier versions revoked it).
  • Disconnect in Joryio (Settings → Integrations → Magento) revokes the API key at once: the next time the extension sends an event it stops and shows a notice. Connecting with that key then says "This key was revoked when the store was disconnected in Joryio", and the Magento page in Joryio says why there is no key. Generate a new key to connect again. To also revoke the Magento access token, click Disconnect in the Magento admin.
  • One store, one workspace. A store connected to one Joryio workspace cannot be connected to another: Connect shows "This store is already connected to another Joryio workspace." Disconnect it in the other workspace first.
  • Uninstall with bin/magento module:uninstall Joryio_Connect (installed with composer) or bin/magento module:uninstall --non-composer Joryio_Connect (installed in app/code). It disconnects the store, revokes the API key (v1.4.3 or later), deletes the "Joryio" integration and removes the extension's tables and settings. Deleting the files without module:uninstall does none of this - click Disconnect first.

Abandoned Cart Recovery​

  1. The extension tracks carts as customers shop
  2. After 60 minutes of inactivity (configurable), the cart is marked abandoned
  3. Joryio receives the cart with a recovery URL
  4. Use a journey to send recovery messages with the restore link

What the recovery link (https://your-store.com/joryio/cart/restore?token=...) does depends on who opens it (extension v1.4.8 or later). Magento keeps a cart that belongs to a customer account only while that customer is signed in, so:

  • The customer, signed in - their saved cart is put back.
  • Nobody signed in (the usual case: the link opened from an email on another device) - the products, with their options, quantities and the discount code if it still applies, are copied into a cart in this browser, with the note "Sign in to use your saved details at checkout." Signing in afterwards keeps one cart; quantities are never doubled.
  • A guest's cart - the products are copied too. The guest's name, email and addresses are not, so a forwarded link shows nobody's details.
  • A different customer signed in - refused with "This cart belongs to a different account. Sign out to open it as a guest." Nothing is added to their cart and nobody is named.
  • A signed-in customer opens a guest's cart, or this browser's cart already has products - a page asks "Add these products from your saved cart to your current cart?"; nothing is added until they click Add to my cart.
  • The cart was already ordered - "You already completed this order."
  • Opened again - the same browser goes to the cart it already has; another browser or device gets a new cart with the saved products (below). A customer's cart already taken over at sign-in shows "This link was already used - sign in to see your cart."

A link can never change somebody else's existing cart: loaded by another site as an image or frame it does nothing, and adding to an existing cart always needs the confirmation. If that confirmation is sent after the page's session expired, the shopper sees "Your session has expired. Please open the cart link again." and nothing changes.

A link opens only in the store view the cart was saved in (extension v1.4.10 or later): opened in another store view of the same shop it says "This cart was saved in another of our stores. Please open the link from your email again." and changes nothing. Links sent by v1.4.9 and earlier keep working, with the same check, until 31 January 2027.

"Your cart has been restored." appears only when the cart page shows the products. Products that can no longer be bought are left out and named. The order placed from a copied cart counts as that cart's recovery in Joryio, even when the shopper checks out as a guest with another email.

Extension v1.4.7 and earlier said "Your cart has been restored." over an empty cart when the customer was not signed in - update to v1.4.8.

Opening the link: a restore page, one cart per link​

From extension 1.4.18, opening the link changes nothing by itself. Link previews in chat apps and email security scanners (Outlook Safe Links, Gmail and others) open links before the shopper does, and each of those openings used to copy the cart once more - one link could leave several copies.

  • Opening the link shows a short page, "Restoring your saved cart...", that is never cached or indexed. Its script restores the cart and goes on to the cart page; without script the shopper clicks Restore my cart. A preview or scanner that runs no script restores nothing. The confirmation page above still waits for the shopper's click.
  • One cart per link, per browser - the cart the link restored is remembered together with the browser it was restored in (a cookie). Opening the same link again in that browser (for example after its session ended) brings back that same cart instead of a new copy, for 7 days: Stores > Configuration > Joryio > Tracking > Recovery Link Reuses Its Cart For (days), 1-30. Another browser or device - or a scanner that did run the page - never gets that cart: it gets a new cart with the saved cart's products and discount code only, so nothing anyone typed at checkout in one browser ever shows in another. The link then remembers the newest browser's cart. Both carts still count as the same saved cart: an order from either is one recovery, and only one "left again" reminder is sent per original cart.
  • Already ordered - when the cart the link restored was ordered, the link says "You already completed this order." and restores nothing.
  • At most 10 restores per link per hour - after that, "Too many attempts with this cart link. Please try again later."

Copies that earlier versions already made still get at most one reminder per original cart (A restored cart abandoned again). bin/magento setup:upgrade to 1.4.18 also removes the leftover marks on restored carts that were already ordered and fills in their original cart; the counts go to var/log/joryio.log.

Attribution of recovery orders​

From extension 1.4.12, the recovery link keeps the attribution parameters Joryio's click tracking adds (jr_cid, jr_jid, utm_*) when it sends the shopper on to the cart, so the order is credited to the message that sent the link. Only these parameters are carried, each checked for its format - never the link's token or anything else. When the shopper opens the link and checks out under a different email, Joryio credits the order to the click of the contact the message was sent to. See Carts.

A restored cart abandoned again​

From extension 1.4.13, restoring a cart marks nothing as abandoned. A restored copy that the shopper leaves again is reported once more after the normal abandonment time, with reabandonedAfterRecovery: true and originalSessionId on the Cart Abandoned event; any further idle copy of the same original cart within 30 days is not reported. A cart that already had products and received the saved ones is an ordinary cart. An order from any copy counts as one recovery of the original. See Carts.

A guest's restored copy has no email (the link never copies it); from 1.4.18 it is still reported, without an email, and Joryio sends the one reminder to the contact of the original cart - see Carts.

From extension 1.4.22, every cart event of a restored copy names the cart it was restored from (restoredFromSessionId), so a guest's copy shows on E-Commerce → Carts as a Restored copy of the original contact's cart instead of a "No contact" cart, and is not counted as another abandoned cart - see Carts.

From extension 1.4.23, every cart event carries the cart's recovery link and whether it is a guest's cart, and the extension reports Track Guest Carts and the abandonment time with every request, so a cart Joryio marks abandoned itself follows them - see Carts. A guest's email typed on the checkout's email step reaches the cart only when the shopper continues to the shipping step (Magento saves it then); before that it identifies the browser only, and the cart is not reported.

Extension 1.4.8 to 1.4.12 marked a restored cart as already reported the moment it was restored, so a shopper who left it again got no reminder. bin/magento setup:upgrade to 1.4.13 removes those marks (only the ones set at the moment of the restore, with no abandoned-cart event behind them) and writes the counts to var/log/joryio.log.

Troubleshooting​

Events stuck / nothing arriving in Joryio​

This is almost always Magento cron not running. The extension queues events locally and a per-minute cron delivers them - with no cron, nothing is sent.

  • Verify cron is set up: bin/magento cron:install (or check your crontab)
  • Run it manually to test: bin/magento cron:run
  • Check var/log/joryio.log in your Magento root for delivery errors
  • Check Stores → Configuration → Joryio → Event Queue (extension v1.4.0 or later): it shows the events waiting and not delivered, with the last error. See Delivery and retries

Connect fails​

From extension v1.4.2, Connect first tests the new access token on your store, the way Magento's REST API will check it: the OAuth bearer setting for the store view Joryio calls, Magento's own token check, the "Joryio" integration's permissions, and a request from your server to its own store URL. When one of them fails, Connect stops before Joryio is involved and names the cause; nothing is left behind. The result is also written to var/log/joryio.log (the line starting Joryio connect self-test). Next to the buttons, the configuration page shows whether the bearer setting is already on.

The message saysWhat to do
"Allow OAuth Access Tokens to be used as standalone Bearer tokens" is No for the store view Joryio callsSet it to Yes - check the website and store view scopes too, a value there overrides Default Config. See Allow OAuth access tokens as bearer tokens
The "Joryio" integration lacks these permissionsRun bin/magento setup:upgrade and bin/magento cache:flush, then click Connect again. Check System → Extensions → Integrations → Joryio → API: the listed resources must be ticked
Your store URL is answered by a different Magento installationThe base URL in Stores → Configuration → General → Web points to another copy of the store (for example staging). Fix it, then connect
Your web server removes the Authorization headerSee "The Authorization header is removed" below
A request with the new token to rest/V1/store/storeConfigs was refusedA security module, firewall or web server rule in front of Magento's REST API refuses it. Allow /rest/ requests with an Authorization header
Your store accepts the token from its own server, but refused it when Joryio calledSomething in front of the store treats outside requests differently (firewall, CDN, WAF, bot protection). Let requests to /rest/ with an Authorization header through
Update the Joryio Connect extension to 1.4.2 or laterYour extension is older. Upgrade (below) and click Connect again
Joryio could not reach your storeThe store URL must be public and online, with a valid HTTPS certificate. Private and internal addresses are refused
This store is already connected to another Joryio workspaceDisconnect it in that workspace first - see Disconnecting and uninstalling
Invalid API keyCopy the API key and the Server URL again from Settings → Integrations → Magento in Joryio. A key is only known to the server of its own region

The Authorization header is removed. Connect reports this only when its own request to your store arrived without the header. Most often Apache running PHP as CGI or FastCGI drops it. Magento's own .htaccess passes it on; if yours was replaced, or your host ignores it, add this to the .htaccess in your Magento root (and in pub/ when that is your web root):

RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1

Upgrading to 1.4.2: replace the extension files with the v1.4.2 zip from your Joryio dashboard, then run bin/magento setup:upgrade, bin/magento setup:di:compile (production mode) and bin/magento cache:flush. Versions 1.4.0 and 1.4.1 lost the "Joryio" integration's permissions every time they activated or deactivated it; setup:upgrade puts them back, and Connect reuses the existing integration, issues a new token and activates it - you do not need to delete it. In Stores → Configuration → Joryio, check Server URL, click Save Config, then Connect.

Upgrading to 1.4.4​

Replace the extension files with the v1.4.4 zip from your Joryio dashboard, then run bin/magento setup:upgrade, bin/magento setup:di:compile (production mode) and bin/magento cache:flush. setup:upgrade lets the "Joryio" integration read categories and stock (including your MSI sources, when the store uses them) - no need to connect again. Then, in Joryio, open Settings → Integrations → Magento and click Sync All: the sync merges child products into their configurable products and fills in stock, categories and coupon amounts for what was already synced.

"Invalid API key" / key mismatch​

  • The key must start with jry_mg_ and match the one currently shown in Settings → Integrations → Magento in Joryio
  • Opening the setup page in Joryio again does not change the key - only a disconnect revokes it. If the key in Magento differs from the one shown in Joryio, paste the one shown in Joryio and click Connect again
  • Double-check the Server URL was copied exactly
  • A Disconnect in the Magento admin keeps the key (v1.4.3 or later): click Connect again with it. A Disconnect in Joryio, or uninstalling the extension, revokes it - Connect then says so; generate a new key in Settings → Integrations → Magento and paste it before you click Connect again. If Connect says "This store is already connected to another Joryio workspace", disconnect it in that workspace first - see Disconnecting and uninstalling

Historical data missing​

  • Orders older than the sync window (90 days by default) are not imported
  • Historical sync runs in order: products, then customers, then orders - give a large store some time to finish

Tracking not working​

  • Check the tracking snippet toggle is enabled in Stores → Configuration → Joryio
  • Extension 1.4.6 or later clears the cached pages itself when you save the toggle; with an older version run bin/magento cache:flush (see After changing settings)
  • Verify the snippet loads in your browser's developer tools

Add to cart missing, full-page cache and Varnish​

  • Full-page cache and Varnish are fine: the snippet carries only page data, and add to cart reaches the browser through Magento's own customer data (customer/section/load), which is never cached. Do not add a rule that caches that address
  • Luma-based themes and Hyva are supported. A headless or PWA storefront does not load the snippet - send events with the Web SDK instead
  • After upgrading the extension, run bin/magento setup:upgrade and bin/magento cache:flush so the new customer-data section is registered

Checkout stuck on a spinner, "Mismatched anonymous define()"​

  • With extension 1.4.6, the checkout of a Luma-based store could stay on its loading spinner until the page was reloaded. The browser console showed Mismatched anonymous define() module (from the Joryio web SDK file) and templateEngine must inherit from ko.templateEngine. The snippet loaded the web SDK with a plain script tag, and on a page that uses RequireJS the SDK file registered itself in a way RequireJS attributed to one of Magento's own checkout modules
  • Extension 1.4.7 loads the web SDK through RequireJS whenever the page has it (Luma and themes built on it), and with a plain script tag only where it does not (Hyva). Upgrade, run bin/magento setup:upgrade, bin/magento setup:static-content:deploy in production mode and bin/magento cache:flush
  • To check: open the checkout with the console open - neither message appears, and window.joryio.sdk exists once the page has loaded (when in-app messages or web push are on)

Guest carts not marked abandoned​

  • Magento keeps a guest's email on the cart's billing (or shipping) address while they are in the checkout, and copies it onto the cart only when the order is placed. Extension 1.4.6 and older looked only at the cart itself, so a guest who entered an email at checkout and left was never marked abandoned. From 1.4.7 the email on the billing or shipping address counts too (keep Track Guest Carts on)
  • Guest carts count from the moment extension 1.4.9 is installed (bin/magento setup:upgrade records it); guest carts left idle before that are not sent all at once. With 1.4.7 and 1.4.8 no guest cart was reported through its address email (the start time was never kept) - upgrade to 1.4.9. To test, start a new guest checkout, enter an email, leave, and wait for the abandonment time plus up to 15 minutes (the cron interval)
  • A cart is marked abandoned once. A shopper who never typed an email (and is not logged in) cannot be reached and is not sent
  • From 1.4.11 var/log/joryio.log shows it: Joryio: guest-cart start time set to <date> UTC (...) (or already set (<date> UTC), kept.) at setup:upgrade, and Joryio: cart abandon queued: quote <id>, store <id>. for every recovery link created (ids only - no email, link or token)

Add to cart or tracking blocked by bot protection​

Some hosting security tools - for example Imunify360 bot protection - can block the storefront's own requests (add to cart, the tracking calls) with "Access denied by Imunify360 bot-protection". Joryio then receives no add-to-cart or page events, and shoppers may see the add to cart fail. In the hosting panel, allow the store's own AJAX routes and Joryio's routes (/joryio/..., /customer/section/load, /checkout/cart/add), or lower the bot-protection sensitivity for the store's domain. Your host can tell you which rule matched.