Skip to main content

WooCommerce Integration

Connect your WooCommerce store using our lightweight WordPress plugin, Joryio (called "Joryio for WooCommerce" before version 1.6.2 and "Joryio for WordPress" before 1.6.25). The Download Plugin button in your dashboard always gives you the latest version. Sync products, orders, customers, and enable abandoned cart tracking.

No store? The same plugin connects any WordPress site for page tracking, website forms, in-app messages and web push - see WordPress site (no store).

Features​

  • WordPress plugin - Easy installation via plugin upload; the dashboard always serves the latest version
  • Website forms - Contact Form 7 and Elementor Pro submissions create or update the contact and record Form Submitted; each form decides whether it subscribes to a list. Works without WooCommerce (see Forms: Elementor and Contact Form 7)
  • Real-time sync - Products, orders, customers, and carts sync in real time through the plugin
  • Abandoned cart tracking & recovery - Track abandoned carts, recover them with restore links, and record recovered carts
  • Storefront tracking - Track page views, product and category views, add-to-cart (every add, WooCommerce Blocks included), and checkout-started events (classic and block checkout)
  • Email capture - Automatically identify customers from checkout and forms
  • Marketing consent checkbox - Collect email (and optionally SMS) opt-ins on checkout and registration, with the evidence kept on each order (see Marketing consent checkbox)
  • Cart recovery URLs - Generate recovery links to restore abandoned carts
  • Multi-store support - Connect multiple WooCommerce stores with per-store segmentation
  • Checkout never blocked - Order events are sent in the background, so Joryio can never slow down or stall a shopper's checkout

Setup Steps​

Step 1: Get Your API Key​

  1. Navigate to Settings → Integrations in Joryio
  2. In the WooCommerce section, click Get API Key
  3. Copy the generated API key (starts with jry_wc_)

Step 2: Install the Plugin​

  1. Download the Joryio WordPress plugin from Settings → Integrations in your Joryio dashboard (the Download Plugin button)
  2. In WordPress admin, go to Plugins → Add New → Upload Plugin
  3. Upload the plugin ZIP file and click Install Now
  4. Click Activate

Step 3: Connect the plugin​

  1. In WordPress admin, go to WooCommerce → Joryio
  2. Paste your API key from Step 1
  3. Check Server URL: it must be your region's API address, https://api-eu1.joryio.com
  4. Click Connect

The page then shows Connected to Joryio with your store name, store URL and Workspace ID.

From plugin 1.6.32 the settings page has tabs. Until the store is connected only Connection is shown; then:

TabWhat is on it
ConnectionThe store, its URL and Workspace ID, Test connection, Disconnect and the sync status
Tracking & identityInject Tracking Script and Recognise visitors by the email they type
StoreCart Abandonment Time, Recovery Link Reuses Its Cart For and Track Guest Carts
ConsentThe marketing consent checkbox
In-app & web pushIn-app messages and web push
FormsContact Form 7 and Elementor

Each tab saves only its own settings, and the page says Settings saved. after a save. A site without WooCommerce has no Store or Consent tab. Earlier versions show the same settings on one page.

Step 4: Nothing more to set up​

You do not create WooCommerce REST API keys yourself: on Connect the plugin creates a read/write key named Joryio Integration (under WooCommerce → Settings → Advanced → REST API) and hands it to Joryio, which then imports your products, customers and orders. Do not delete that key - Joryio stops syncing without it; if you do, click Connect again to create a new one.

Sync Settings​

Configure sync options in Settings → Integrations → WooCommerce → Settings tab:

SettingDescriptionDefault
Sync ProductsImport products from WooCommerceEnabled
Sync OrdersImport orders and update statusesEnabled
Sync CustomersImport customer dataEnabled
Sync CartsTrack abandoned cartsEnabled
Historical DaysDays of order history to import90

Storefront Tracking​

The plugin injects a tracking script on all storefront pages. Each event is stored under its standard name (the Stored as column) - the same name the Shopify and Magento integrations and the SDKs use, so one segment or journey trigger works for every store.

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, out_of_stock or backorder)
Category ViewedProduct List ViewedA product category page, with category_id, category_name and the page address (plugin v1.3.1 or later)
Add to CartProduct AddedEvery add, however it is made: the AJAX button on shop and category pages, the form on the product page, and the WooCommerce Blocks buttons and mini-cart (plugin v1.3.1 or later; earlier versions saw the AJAX button only)
Checkout StartedCheckout StartedThe checkout page - classic or Checkout block - once per cart. A reload of the checkout is not a second start
Identify$identifyEmail capture from forms

Signed-in customers (plugin 1.6.6 or later). When a customer is logged in to the store, the plugin signs the customer's WooCommerce 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. From plugin 1.6.21, when the proof cannot be signed (no API key saved, or Joryio cannot be reached) the browser sends nothing for a logged-in customer and tries again on the next page - it never falls back to the email alone. A customer created in wp-admin (Users → Add New), through the WooCommerce REST API or by an import also reaches Joryio with its customer number, and a customer Joryio has not received yet is sent before their first proof.

Shared browsers (plugin 1.6.26 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 plugin 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 (for a variation), quantity, price, currency and the stock status.

How an add to cart is counted. The store records each add when it happens, and the shopper's browser sends it straight after - or on the next page, when the product page form reloads the page. One add is one event, whichever way it was made. Re-adding a past order ("Order again") is not counted.

Email Capture​

Emails are automatically captured from:

  • Checkout billing email field
  • Newsletter signup forms
  • Any email input field

Recognise visitors by the email they type​

From plugin 1.6.22, the plugin's settings page (WooCommerce → Joryio → Tracking & identity) has Recognise visitors by the email they type. It is on by default, and an older plugin behaves as on.

  • 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 → WooCommerce → Settings, with the plugin version and when it was last reported - it is set only in the plugin. 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. From plugin 1.6.30 the cart is also kept with the guest's own browser from the first product added, so one guest browser is one contact. 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. Only an address typed in the checkout's email field is corrected this way: an address typed into a website form (Contact Form 7, Elementor, a newsletter box) is never taken off. From plugin 1.6.33 the plugin tells Joryio which kind of field each typed email came from.

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 plugin v1.5.0 the tracking script 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 your store's Joryio server, like the tracking script. The settings are in WordPress under WooCommerce → Joryio → In-app & web push:

SettingDefaultWhat it does
In-app messagesOnShows your Joryio in-app messages on the store
Allow HTML in-app messagesOnAlso 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 pushOffLets shoppers subscribe to browser notifications

Stores that upgrade from an older plugin get the same defaults. The SDK is loaded by the tracking script, so keep Inject Tracking Script on.

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

Web push. A browser accepts the notification service worker only from your store's own address. With Web push on, the plugin writes a joryio-sw.js file to your site folder and also answers https://your-store/joryio-sw.js from WordPress; the worker covers the whole site and loads Joryio's hosted worker. 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. Web push needs the store at the root of its domain (not in a sub-folder such as /shop/). If the plugin cannot write the file, the settings page shows the one line to upload yourself. 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.

From plugin v1.3.0 you can ask shoppers whether they want your marketing, with a checkbox on the pages you choose. Turn it on in WordPress under WooCommerce → Joryio → Consent.

SettingWhat it doesDefault
Show the checkbox onPick any of: Checkout (classic shortcode checkout), Checkout (block checkout), My Account registration formAll off
Checkbox textThe text next to the box. Leave it empty to use "Yes, I'd like to receive news and offers by email"Default text
Ticked by defaultShows the box already ticked on the classic checkout and registration formOff
SMS consentAdds a second box on the checkout for text messages, for the billing phone number, with its own textOff

Where the box shows​

  • Classic checkout - below the order notes.
  • Block checkout - in the order section, added through WooCommerce's Additional Checkout Fields API. This needs WooCommerce 8.9 or later; on older versions the settings page tells you the box cannot show there. The block checkout always shows the box unticked.
  • Registration form - on the My Account registration form (email box only).

What a tick does in Joryio​

  • Ticked - the contact is subscribed to email (or SMS) in Joryio. Their subscription history shows the source woocommerce_checkout or woocommerce_registration and the exact text they saw.
  • A new shopper - a tick on the My Account registration form, and a checkout tick sent before the order, counts at once only for a known buyer or someone already subscribed. Anyone else is held as pending until their own order with the box ticked or a click on the confirmation email, unless you choose to count them immediately - see Opt-ins from new shoppers. A tick that comes with the order always counts. A held registration tick is recorded with the reason registration_tick_pending_confirmation (a held checkout tick: checkout_tick_without_order).
  • Not ticked - nothing changes. An unticked box is never treated as an unsubscribe, so someone who subscribed elsewhere stays subscribed. The one exception is the optional setting An unticked box unsubscribes, off by default.
  • Previously unsubscribed - a tick does not re-subscribe someone who unsubscribed, or whose address or number is suppressed. They can subscribe again from the preference center. A logged-in customer's tick is the exception - see What a store opt-in or opt-out does.
  • Already subscribed - nothing is rewritten, so the original opt-in date and source are kept.
  • Proof recorded - every change a box makes (a tick, a held tick, an unticked-box unsubscribe) records the text the shopper saw, the time, the page (the checkout or the My Account page) and, from plugin 1.6.31, the shopper's IP address: the order's customer IP for a checkout, the registration request's address for the registration form. 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.
  • SMS - the opt-in is for the billing phone. If the contact has no phone yet, that number is added; a number already on the contact is never replaced. A number that replied STOP stays unsubscribed.

Plugin 1.6.13 or later, classic and block checkout: WooCommerce → Joryio → Consent → 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.6.15 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 → WooCommerce → 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 plugin 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 (typed in the checkout's email field, or the shopper is signed in). 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 plugin​

Integrations → WooCommerce → Settings shows the plugin's checkout opt-in settings in Store checkout opt-in (set in your store), read-only: whether the box is shown (classic or block checkout), Ticked by default and When a shopper ticks the checkout opt-in, with the plugin version and when it last reported them. You change them only in the plugin, under WooCommerce → Joryio → Consent; Joryio never changes them. 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.

Plugin 1.6.16 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 plugin 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 plugin the card says Not reported yet - update the plugin.

Opt-ins from new shoppers​

Opt-ins from new shoppers (not yet buyers) in Integrations → WooCommerce → 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 My Account registration form's box and - with Record consent immediately - the checkout tick. Either way:

  • a known buyer or someone already subscribed counts at once;
  • 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 plugin: 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 while logged in to a store account - plugin 1.6.1 or later), 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 WooCommerce 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).
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 → WooCommerce → 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, plugin 1.6.27 or later has an optional setting in WooCommerce → Joryio → Consent, next to When a shopper ticks the checkout opt-in: 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 woocommerce_checkout / woocommerce_registration, 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 → WooCommerce → Settings shows the setting read-only. Older versions do not report it, and Joryio treats it as off.

What is recorded​

Each choice is stored on the WooCommerce order (or on the customer, for the registration form) and sent to Joryio with the order or customer:

  • whether each box was ticked (yes or no)
  • the date and time
  • where it was shown (classic checkout, block checkout or registration form)
  • the text shown next to the box
  • your store URL

The order screen in WooCommerce shows the recorded choice under the billing address.

Do not pre-tick the box if you sell in Europe

Under the GDPR (EU and UK), a pre-ticked box is not valid consent - the shopper has to tick it themselves. Leave Ticked by default off unless you are sure the rules where you sell allow it. The settings page shows this warning next to the option.

Forms: Elementor and Contact Form 7​

From plugin v1.6.0, the plugin sends the submissions of your Contact Form 7 and Elementor Pro forms to Joryio. Each submission creates the contact, or updates the one with the same email (the same phone when the form has no email), and records the Form Submitted event. Only the fields you map leave your site.

Contact Form 7​

  1. In WordPress, go to WooCommerce → Joryio (on a site without WooCommerce: Settings → Joryio). The Forms tab lists every Contact Form 7 form
  2. Tick Send this form to Joryio
  3. Choose the List (optional - without one the form only saves the contact and the event)
  4. Choose the List subscription mode (see below) and, for the consent mode, the form's acceptance field
  5. Map the fields: each one to Email, Phone, First name, Last name, Custom attribute (type its attribute key), or Do not send. Map at least an email or a phone field
  6. Click Save form settings (administrators only)

A submission is sent after Contact Form 7 has accepted it and sent its own email.

Elementor Pro​

  1. Edit the form in Elementor and, under Actions After Submit, add Joryio
  2. In the Joryio section, choose the List and the List subscription mode. For the consent mode, enter the Consent field ID - the ID of the form's Acceptance field, from the field's Advanced tab
  3. Enter the field IDs for Email, Phone, First name and Last name, and any custom attributes one per line as attribute_key = field_id
  4. Click Update

Each form has its own mode:

ModeContact and Form Submitted eventList subscription
This is a signup form - always subscribeEvery submissionEvery submission
Only when a consent field is tickedEvery submissionOnly when the form's consent field (a Contact Form 7 acceptance field or an Elementor Acceptance field) is ticked

The event's consentGiven property says which happened. An unticked box never unsubscribes anyone.

Double opt-in​

When the form's list requires double opt-in, the contact is not subscribed straight away: Joryio sends the confirmation email (the same one as landing page forms - choose its template under Settings → Subscriptions → Double opt-in) and subscribes them when they click the link. Until then the event names the list in listsPending. In the list dropdowns these lists are marked (double opt-in).

What Joryio records​

  • The contact - a visitor's form only fills in the email and phone when the contact has none: it never changes the email or phone of an existing contact. A signed-in customer can update their phone (plugin 1.6.34): when a customer logged in to your site submits a form and the form's email belongs to their own contact, the phone typed into the form replaces the contact's phone. The plugin proves who is logged in with a signed proof made on your server, only for a form submitted on your own site; the email is never changed this way. The names and custom attributes you mapped are written to the contact. A contact created by a form shows Created via: Website form
  • The event - Form Submitted, with source (cf7 or elementor), formId, formName, pageUrl, consentGiven, consentText (the wording of the ticked consent box, from plugin 1.6.31), listsSubscribed, listsPending, the form's own email and phone (formatted the way the contact stores them, so a different phone a visitor typed is not lost) and phoneUpdated when the form's phone differed from the contact's - true when the contact now has it, false when the contact kept its own. Team members who cannot see personal data do not see the email and phone values, and they are erased with the person. Use it as a journey trigger, a campaign trigger or a segment condition. The other submitted values are not on the event
  • The consent record - when the form subscribes the contact to a list, the subscription history keeps the proof of consent: the exact wording of the consent box the person ticked, the page the form was submitted on (pageUrl), the time and the visitor's IP address (shown only to team members who can see personal data). See Consent proof

From plugin 1.6.31, when the person ticks the form's consent field the plugin also sends what they agreed to:

  • The wording - the visible text of the consent field: a Contact Form 7 acceptance tag's text ([acceptance your-consent] I agree to receive news by email [/acceptance]), or an Elementor Acceptance field's Acceptance Text (else its label). Links and formatting are removed; at most 500 characters. Text you write outside the acceptance tag in the Contact Form 7 template is not sent - put the wording inside the tag. For Elementor, the field is the one named in Consent field ID (any custom ID) and must be an Acceptance field
  • The page, the time and the IP address of the visitor's submission

A box left unticked never sends its wording, and a signup form (no box) sends none. The proof is written to a consent entry only when the form subscribes the contact to a list: with No list nothing is subscribed, so the wording stays on the Form Submitted event only (consentText; the IP address is never put on the event). For a double opt-in list the proof is kept with the confirmation and recorded when the person confirms.

Submissions never slow the visitor down: they are queued and sent in the background by WP-Cron, and retried if Joryio cannot be reached (three attempts, five minutes apart). A retry is never counted twice. WP-Cron runs on site visits, so on a site with very little traffic, or with WP-Cron turned off, run it from a server cron job.

Recognising the visitor. An email typed into a form on the site also identifies the visitor's browser when Recognise visitors by the email they type is on (the default). The submission itself always goes to the contact with that email.

Sites without WooCommerce​

The plugin works on any WordPress site. Without WooCommerce you find it under Settings → Joryio and connect it the same way, with the same jry_wc_ key: you get website forms, the tracking script with email capture, in-app messages and web push. The store features - cart tracking, order, product and customer sync, coupon links and the checkout consent checkbox - turn on when WooCommerce is active. If you add WooCommerce later, click Disconnect and then Connect again, so Joryio can create its REST key and sync your store. The full guide for a site without a store is WordPress site (no store).

Abandoned Cart Recovery​

The plugin tracks shopping carts in real-time:

  1. Cart items and value are tracked as customers shop
  2. After 30 minutes of inactivity, cart is marked abandoned
  3. A recovery URL is generated to restore the cart
  4. Use Canvas to send recovery emails with the restore link

Cart Recovery URLs​

When a customer clicks the recovery link:

  1. Their cart session is restored
  2. All cart items are added back
  3. They're redirected to checkout

Products that can no longer be added (out of stock, deleted or unavailable) are named: "No longer available, so not added: ...". When none can be added, the shopper sees "The products in this cart are no longer available." on the cart page instead of an empty checkout, and the cart is not counted as recovered (plugin v1.6.9 or later).

From plugin v1.6.10 the link works like the Magento extension's:

  • It never empties the shopper's current cart - the saved products are added to it. When the cart already has products, or a signed-in shopper opens a guest's saved cart, a page asks first: "Add these products from your saved cart to your current cart?"
  • A registered customer's cart opened by a different signed-in customer is refused: "This cart belongs to a different account. Sign out to open it as a guest." Nobody is named.
  • Opened signed out, a registered customer's products are restored with "Sign in to use your saved details at checkout."
  • Opened again - the same browser goes to the cart it already restored (nothing doubled), another browser or device gets a new cart with the saved products (below), and after the order the link says "You already completed this order."
  • Loaded by another site (as an image, frame or fetch) the link changes nothing.
  • The order, not the click, counts as the recovery: the order names the saved cart it came from, so the abandoned cart counts as recovered by that order even when the shopper checks out with another email.

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

From plugin 1.6.17, 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 restore the cart once more.

  • 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 checkout; without script the shopper clicks Restore my cart. A preview or scanner that runs no script restores nothing. The confirmation page ("Add these products from your saved cart to your current cart?") 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 (WooCommerce keeps a cart in the browser's session). Opening the same link again in that browser brings the saved products back into that same cart instead of a new copy, for 7 days (WooCommerce → Joryio → Store → Recovery Link Reuses Its Cart For, 1-30 days). 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 - "You already completed this order.", and nothing is restored.
  • At most 10 restores per link per hour - after that, "Too many attempts with this cart link. Please try again later."

Attribution of recovery orders​

From plugin 1.6.11, 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 plugin 1.6.12, 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.6.17 it is still reported, without an email, and Joryio sends the one reminder to the contact of the original cart - see Carts.

From plugin 1.6.23, 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.

Plugin 1.6.11 and earlier reported every restored cart left again as an ordinary abandonment, with no limit. The update adds three columns to the plugin's carts table by itself; nothing needs to be reactivated or cleaned up.

Abandonment time, time zone and Track Guest Carts​

Cart Abandonment Time and Track Guest Carts are under WooCommerce → Joryio → Store.

From plugin 1.6.24:

  • The abandoned-cart check uses the site's own time zone. Earlier versions compared a cart's last change, saved in the site's time zone, with a time in UTC: a store east of UTC (for example Israel or Europe) reported its carts hours late, so Joryio marked them abandoned itself first, with the plain checkout page instead of the recovery link; a store west of UTC could report a cart while the shopper was still on it.
  • The abandoned cart is sent in the same WP-Cron run that finds it. WordPress runs WP-Cron only when someone visits the site, so on a quiet store the check itself can still run late; Joryio then marks the cart itself, with the recovery link, after your abandonment time plus 20 minutes - see Carts. For exact timing, run WP-Cron from a server cron job.
  • Track Guest Carts off: guest carts are not sent to Joryio and are never marked abandoned, by the plugin or by Joryio. Page views, product views, Product Added and Checkout Started are still recorded for guests.

From plugin 1.6.28:

  • A cart change is sent to Joryio from the shopper's own visit, at the end of the request (after the page is sent on PHP-FPM and LiteSpeed servers), not queued for a later WP-Cron run. A product added to a cart Joryio already marked abandoned reaches Joryio at once: the cart is no longer abandoned, shows its new contents, and is reported again once it is left for your abandonment time.
  • The abandoned-cart check also runs at the end of a visit when it has not run for a minute (PHP-FPM and LiteSpeed servers, after the page is sent), so carts are reported about when the abandonment time has passed even where WP-Cron runs late.
  • A run of the check on its own - the end-of-visit run, or Run now in WP Crontrol, which runs only that one event - first sends the plugin's events still waiting to be sent (an ordinary WP-Cron run sends them itself), so Joryio has a cart's last change before its abandonment.
  • To run WP-Cron by hand, use wp cron event run --due-now (WP-CLI), or request https://<your store>/wp-cron.php without a doing_wp_cron value. A request that carries a doing_wp_cron value (for example ?doing_wp_cron=$(date +%s)) runs nothing unless the value is exactly WordPress's own current lock, and still answers 200. A plain request also runs nothing when WP-Cron was started in the last 60 seconds.
  • The check's reports (cart row ids only) and any failed send are written to WooCommerce → Status → Logs, source joryio.

Multi-Store Support​

When connecting multiple WooCommerce stores:

  1. Each store gets its own App entity with unique SDK key
  2. All events are tagged with the store's $app_id
  3. Filter events by $platform = 'woocommerce' or specific $app_id
  4. Users can be linked across stores via email

Scheduled reconciliation​

Live updates (the plugin and the 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), products and newly registered customers. Edits to an existing customer's profile are not checked (WooCommerce's API cannot list customers by change date), and carts come only from the plugin (WooCommerce's API has no list of carts). A WooCommerce version that ignores the "modified after" filter is not checked for orders and products.

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 → WooCommerce: 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 WooCommerce 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 WooCommerce refund becomes one Order Refunded event (with the refunded amount and reason) and a cancelled order one Order Cancelled, so a journey can react to them. A refund reported by both the plugin and a webhook is still one event. 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 - orders: an order never replaces a contact's existing phone, whether or not the shopper is logged in - the billing email can be typed freely at checkout. It only fills the phone when the contact has none.
  • Phone - customer accounts: changes to the customer's account in the store (customer updates and the customer import) update the contact's phone.
  • Names update from orders and customer updates.
  • Store customer number: the contact's WooCommerce customer number comes from the customer's account (customer updates, the customer import and the plugin's customer events), never from an order - a logged-in shopper can type someone else's email. 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: 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 plugin sends events directly to Joryio (faster than webhooks). Additionally, REST API webhooks are registered as backup:

  • Order created, updated, deleted
  • Product created, updated, deleted
  • Customer created, updated

Data Sync​

Manual Sync​

Trigger manual sync from Settings → Integrations → WooCommerce → Sync Status tab:

  • Sync All - Full sync of products, orders, and customers
  • Sync Products - Sync only products
  • Sync Orders - Sync only orders
  • Sync Customers - Sync only customers

Automatic Sync​

The plugin sends real-time events for:

  • New orders
  • Order status changes
  • Product updates
  • Customer updates
  • Cart changes
  • Recovered carts

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

Product data​

Each product is stored in your Joryio catalog with:

  • Name, price, sale price, currency, SKU, images, product link, categories and tags
  • Stock status and quantity, for the product and for each variation
  • Variations, each with its own price, stock, SKU and options (for example Color: Blue)
  • Brand - from WooCommerce Brands (WooCommerce 9.6 or later), or from Perfect Brands for WooCommerce or YITH WooCommerce Brands
  • Barcode - the GTIN, UPC, EAN or ISBN field (WooCommerce 9.2 or later), for the product and for each variation
  • Attributes - every attribute with its values (for example Material: Linen)
  • Description - as plain text, without HTML or page-builder shortcodes, up to 5,000 characters

Before WooCommerce 9.2 there is no barcode field, so products sync without one; everything else syncs on any supported version.

Product changes reach Joryio two ways: from the plugin and from the WooCommerce webhooks. Either one is enough, so the catalog - and back-in-stock and price-drop alerts - stays current even if WooCommerce disables its webhooks after repeated delivery failures. When both deliver the same change it is stored once and alerts fire once, and a delivery that arrives late never overwrites newer product data.

Disconnecting and reconnecting​

  • Test connection on the plugin's settings page (shown while connected) checks that Joryio accepts the key and has this store connected, and says why not if it does not.
  • Disconnect on the plugin's settings page ends the connection. The plugin deletes the Joryio webhooks in WooCommerce and its Joryio Integration REST key, then tells Joryio, which marks the store disconnected and stops syncing. The API key stays valid - click Connect again with the same key. If Joryio cannot be reached at that moment, the site still disconnects and the page says Joryio may still show the store as connected: disconnect it in Joryio too.
  • Disconnect in Joryio (Integrations → WooCommerce & WordPress) revokes the API key at once; the page confirms it and shows the store as disconnected right away. Connecting with that key then says "This key was revoked when the store was disconnected in Joryio", and the WooCommerce page in Joryio says why there is no key. Generate a new key and paste it in the plugin to connect again.
  • After reconnecting, Connected At shows the new connection time and a full sync starts: Last Sync reads Syncing now... until it finishes. Your consent settings on the store's page in Joryio are kept across a disconnect and a reconnect.

Plugin settings survive Save Changes and plugin updates. Before 1.6.19, Save Changes also erased the API key: if the settings page says no API key is saved, click Disconnect, then connect again with your key. From plugin 1.6.21 every admin page shows a notice while no API key is saved.

Page caches​

The tracking script is the same on every page and holds nothing about the visitor - no email, session or user id - so a page cache may store it. Who is signed in is asked by the script itself, through a request that is never cached. A page cached before you connected has no Joryio script: the plugin empties the cache of the common cache plugins (Breeze, WP Rocket, LiteSpeed Cache, W3 Total Cache, WP Super Cache, WP Fastest Cache, SiteGround Optimizer) when you connect, disconnect, change the tracking settings or update the plugin. With any other cache - your host's server cache, Varnish or a CDN - purge it yourself after connecting.

Troubleshooting​

"Cannot redeclare joryio()" when activating​

An older copy of the plugin is still installed (often in a folder named woocommerce-plugin). In Plugins, deactivate and delete every Joryio entry, then upload the plugin from your Joryio dashboard again. From version 1.2.1 a second copy shows a notice instead of stopping WordPress.

Connect fails, or shows "Malformed identifier"​

Check that Server URL is https://api-eu1.joryio.com and that the API key starts with jry_wc_. If it still fails, contact Joryio support with the time you clicked Connect.

Connect says the Authorization header is removed​

Joryio reads your store with the REST API key the plugin creates, sent in an Authorization header. Some servers - most often Apache running PHP as CGI or FastCGI - or a security plugin drop that header before WordPress sees it, and WooCommerce answers as if no key was sent. Add this to the .htaccess in your WordPress root, above the WordPress block:

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

Then click Connect again. From plugin v1.6.5 a refused connection deletes the REST API key it created, so no unused key is left under WooCommerce → Settings → Advanced → REST API, and the plugin shows the reason Joryio gives.

Plugin won't connect​

  • Verify the API key is correct (starts with jry_wc_)
  • Check WordPress has outbound HTTPS access
  • Ensure the API endpoint is reachable

WooCommerce API errors​

  • Check that the Joryio Integration key still exists under WooCommerce → Settings → Advanced → REST API; if it was deleted, click Connect again to create a new one
  • Check the API user has Read/Write permissions
  • Ensure your site has SSL (HTTPS)

Cart tracking not working​

  • Verify plugin is activated
  • Check tracking script loads in browser dev tools
  • Clear any caching plugins

Products/Orders not syncing​

  • Check sync is enabled in Settings tab
  • Try manual sync with "Sync All" button
  • Check WooCommerce REST API is enabled
  • A webhook WooCommerce disabled after delivery failures shows as Disabled under WooCommerce > Settings > Advanced > Webhooks. Products still arrive through the plugin; orders and customers are also sent by the plugin. Set it back to Active so both paths work

Caching and optimization plugins​

  • Page caching does not affect tracking: nothing about the visitor is stored in the cached page. After an add, the plugin sets a small joryio_pending cookie and the script collects the add through admin-ajax.php, so do not cache admin-ajax.php (caching plugins leave it alone by default) and do not strip that cookie
  • A cached page keeps the Server URL and SDK key it was cached with. After you change the Server URL or connect again, purge your page cache so visitors' browsers send to the new address. A Content Security Policy plugin can add its nonce to the tracking script (plugin 1.6.7 or later, WordPress 5.7 or later)
  • The checkout page must not be cached (WooCommerce and caching plugins exclude it by default), or Checkout Started cannot fire
  • A "Delay JavaScript" or "load JavaScript on interaction" option holds the tracking script back until the visitor scrolls or clicks, so a visitor who leaves at once is not counted. Exclude the Joryio script (it contains joryio) from that option