Skip to main content

Connecting WordPress and WooCommerce

With the Joinotify plugin connected to your account, a WordPress site can do more than send messages: it syncs with the platform. The store's customers become contacts, along with what the store knows about them — how many orders they placed, how much they spent, when they last bought. And what happens on the site — an order paid, a sign-up, an abandoned cart, a form submitted — reaches your flows as a site event, through the Site event trigger.

The difference from the webhook trigger: there is no URL to paste and no field to map. The site already says who the customer is and sends each event with a name — wc.order.paid — that any flow in the account can listen to. What each event carries is in the site event catalog.

Turning the sync on​

The site must be connected to Joinotify. The connection alone sends nothing beyond what sending messages needs: syncing is off by default, and nothing leaves the site before you switch it on.

  1. In WordPress, go to Joinotify → Settings → Applications and click Configure on the Joinotify Cloud sync card.
  2. Read the text at the top of the window: it says exactly what the site will send.
  3. Choose what to sync (table below), switch the card on and save.
OptionDefaultWhat it does
WooCommerce orders and customersonOrder and subscription events, and each customer with their order count, total spent and last purchase
WordPress usersonSign-ups and profile updates
Form submissionsoffWPForms and Elementor Pro submissions — with every field of the form
Abandoned cartsonCaptured contacts and abandoned, recovered or lost carts from Flexify Checkout
Send the items of each orderonProducts, quantities and categories of each order
Send billing and shipping addressesoffThe full address. City, state and country are always sent
Tag for contacts from this siteemptyThe tag every contact from the site gets. Empty means the site's name
Ask customers for marketing consentoffThe consent checkbox at checkout, in the sign-up forms and in My account (see below)
Consent checkbox textemptyThe checkbox text. Say what the person will receive, and on which channel

Besides the customers, the site reports its own address, its name, the Joinotify version, which store, form and checkout plugins are active, and a made-up sample of each event — that is how the flow editor shows the fields before the first order arrives.

Nothing is sent during checkout. Each event is written to a queue on the site itself and sent in batches, in the background, right after. If Joinotify does not answer, the event waits in the queue and is sent again later. Switching the card off stops sending at once; what already arrived stays in your account.

What the platform does with each contact​

The sync adds and never deletes:

  • A contact is found first by who they are on the site (the WordPress user, the store customer) and then by phone. A customer who changes numbers stays one contact; if the new number already belongs to another contact, nothing is merged and the clash is recorded on the timeline.
  • Without a phone, no contact is created — the platform is WhatsApp. The event is recorded as No contact.
  • Name and e-mail only fill what is empty: what someone typed in the panel wins.
  • Tags only come in. Every person gets the site's source tag, which lets you build audiences and campaigns with that store's customers only.
  • The store fields — Orders, Total spent, Average order value, First purchase, Last purchase, Last order status, City, State, Registered on the site, Role on the site and, with subscriptions, Subscription status and Next payment — are created by the platform on the first sync and take the snapshot the site computes: they replace the previous value.
  • A new contact counts toward your plan's contact limit. Above it, the contact is not created and the event is recorded as Contact limit. Existing contacts carry on as usual.

On the contact's page, the source shows as Connected site, and the On the site section shows who the person is on each connected site — WordPress user, WooCommerce customer, Buyer without an account or Form or cart lead — and when the data is from.

With Ask customers for marketing consent on, an unticked checkbox shows:

  • at checkout — both the classic and the block checkout (WooCommerce 8.9 or newer);
  • in the WordPress and WooCommerce sign-up forms;
  • in My account → Account details.

Whoever already agreed does not see the checkbox again at checkout. Whoever ticks it has the checkbox text, where and when stored on the order or on the user, as evidence, and that person's next event carries the opt-in to Joinotify. Whoever unticks the preference in My account opts out on the platform — and that opt-out always applies.

Two rules protect your base:

  • Consent only moves up from unknown: a checkbox ticked on an old order never undoes an opt-out, and never takes anyone off the suppression list.
  • The way back works too: an opt-out made on Joinotify — by keyword, in the panel, by a flow — is reported to the site, and the person's preference shows unticked there. An opt-in made on Joinotify comes back as well.

Without the checkbox, contacts arrive with unknown consent: transactional messages remain possible, and marketing campaigns follow the rules in Contacts and consent.

Sending the customers you already have​

Events only reach whoever buys or signs up from now on. For your first campaign to reach the customers the store already has, send them once:

  1. In the sync window, under Existing customers, click Send existing customers.
  2. Check the estimate: how many users have a phone number, and how many orders were placed without an account.
  3. Click Send them. It runs in the background — you can close the page. Stop interrupts it, and whoever is already queued is still sent.

Each person goes once: users first, then buyers without an account, by e-mail (someone with an account goes as the account). No flow starts for anyone who arrives this way. People without a phone number are left out and counted, and your plan's contact limit is respected. At the end, the window shows how many were sent, how many are waiting, how many were given up, and what the platform did with some of them — no phone number, invalid phone number, over the plan's limit, kept opted out.

Monitoring from WordPress​

The top of the window shows whether the sync is on (Sync on), off (Sync off) or paused (Sync paused), the last delivery and the last error, and three counters: what is waiting (Waiting), what was sent in the last 7 days (Sent (7 days)) and what was given up (Given up).

A send that fails is retried at growing intervals — from 1 minute up to 1 hour. After 12 attempts, or when the platform refuses the event for what it is, it is given up and listed with its reason. Once you fix the problem, Send again sends it again; Discard deletes it. Given-up items stay on the site for 30 days.

The sync pauses when retrying would not help:

ReasonWhat to do
The site's key was revoked or cannot send this dataConnect the site again; the sync resumes on its own
The site was removed from your accountConnect the site again
The site is at another address than its key'sIf it is a staging copy, leave it paused. If the site moved, connect it again

While paused, the sync keeps queueing events on the site, and nothing is lost. Try again retries right away. A site paused in the panel (below) does not pause the plugin: events wait and are offered again every 15 minutes, until you resume it.

Staging copies​

A copy of the site at another address — staging, a clone, a test environment — that took the production key along is refused by Joinotify and stays paused: test orders never reach real customers. In the panel, the site shows that a copy tried to use its key. To test syncing on a copy, connect the copy on its own: it becomes a separate site under Integrations.

In the panel: Integrations​

The Integrations page lists the connected sites, with each one's active integrations, its last event, and its events and problems over the last 24 hours. From it you can:

  • Pause a site: what it sends is refused until you Resume it — the plugin keeps it and sends it later;
  • Disconnect: the site's key is revoked right away. Contacts and history stay, and connecting the same site again picks up where it left off;
  • See events: every event the site sent, its contact and its outcome.
StatusWhat it means
QueuedIt arrived and is about to be processed
ProcessedIt was handed to the flows that listen to this event
Handed to a waitNo flow started, but a run in Wait for event moved on
No flow listensNo active flow uses this event
No contactThe event carried no contact that could be found or created
Contact limitThe contact would be new, and the account is at its plan's limit
Account blockedThe account has no access to sending
ErrorSomething failed while processing it

Problems only narrows the list to what needs attention. Each event's detail shows the data, the contact sent by the site and what each flow did with it — Started, Filtered out, Repeated, In cooldown, Other site, No next step. Process again hands the event to the flows again; Process again even if it was already handled skips the trigger's Act once per. An event's data is kept for 30 days, or for your plan's message retention period if that is shorter.

Building a flow with the Site event trigger​

  1. In the editor, add a trigger and choose Site event.
  2. Under Event, pick the event from the list, grouped by source. It shows the events your connected sites reported; for an event from your own code, use Custom event.
  3. Under Sites, tick which sites the event counts from. None ticked: any site in the account.
  4. The Event sample shows the fields — click a field to use it in messages.
  5. Connect the trigger to the first step, publish and activate the flow.

There is no contact to map: it comes in the event itself. The trigger has the same options as the webhook — Filter, Keep on the contact, Contact timeline, With the contact's other automations (the default is Run alongside) and Minimum interval per contact — plus one more: Act once per. With order.id, the same order does not start the flow twice within 24 hours, even on wc.order.status_changed, which goes out on every status change. The same event delivered twice by the site never starts the flow twice.

The event's data is in {{trigger.…}} — {{trigger.order.number}}, {{trigger.order.total:money}}, {{trigger.links.payment_url}} — and the flow also has {{trigger.$event.name}} and {{trigger.$site.name}}. The paths of every event are in the catalog.

Waiting for a site event​

The Wait for event step holds the run until a connected site sends an event that matches it. Under Wait for, choose A site event and fill in:

  • Site event — what is awaited, such as wc.order.paid;
  • This run's value — a variable of this run, such as {{trigger.order.id}};
  • Where the same value is in the awaited event — the path in the event to come, such as order.id;
  • the maximum wait, which is required (up to 30 days).

Values are compared as text, ignoring case. If the event arrives, the run moves on through the Arrived output; if time runs out, through Did not arrive. With Keep as a variable, the event that arrived is kept in {{flow.<name>.…}}.

Recipes​

Pending Pix​

Whoever generated a Pix and did not pay gets a reminder 30 minutes later — and whoever paid does not.

  1. Site event trigger: wc.order.created. Filter: order.status equals pending and order.payment_method equals the id of your Pix payment method (check it in the Event sample).
  2. Wait for event: wc.order.paid, value {{trigger.order.id}}, path order.id, up to 30 minutes.
  3. Through Did not arrive, send the reminder with {{trigger.order.total:money}} and the link {{trigger.links.payment_url}}. Through Arrived, end — or say thanks.

The customer has most likely not written to the store in the last 24 hours: the reminder has to be a template.

Abandoned cart​

  1. Site event trigger: fcrc.cart.abandoned, with Act once per cart.id.
  2. A template with the link back to the cart, {{trigger.cart.recovery_url}}.
  3. Wait for event: fcrc.cart.recovered, value {{trigger.cart.id}}, path cart.id, up to 1 day.
  4. Through Did not arrive, the second reminder. Through Arrived, end.

Review after the purchase​

  1. Site event trigger: wc.order.completed, with Act once per order.id.
  2. Wait 7 days.
  3. A template asking for a review of {{trigger.order.line_items[0].name}}, with the link {{trigger.links.review_urls[0]}}.

A condition on customer.is_first_order tells the first purchase from the next ones — for a different thank-you to someone who just met the store.

WordPress flows and platform flows​

The flows of the plugin's builder, in WordPress, and the platform's flows run independently: the same order can trigger both, and nothing warns you or stops it. Avoiding repeated messages is the responsibility of whoever runs the store — when you move an automation to the platform, deactivate the WordPress one (and vice versa).

Custom events​

With the sync on, your code can send events of its own through joinotify_track():

functions.php or a plugin of your own
joinotify_track(
'custom.quote.requested',
array( 'quote' => array( 'id' => 991, 'total' => '1290.00' ) ),
array(
'ref' => array( 'kind' => 'wp_user', 'id' => (string) $user_id ),
'phone' => '+5511987654321',
'email' => '[email protected]',
'first_name' => 'Ana',
)
);
  • The name starts with custom. and has one to three parts of lowercase letters, digits and _.
  • The second argument is the event's data: in the flow, {{trigger.quote.total}}.
  • The third says who the event is about and needs a ref; without one, the event is recorded but reaches no contact. For wc_guest and lead, the id is a hash, never the e-mail in the clear.
  • The function returns the event id, or false with the sync off or a name outside custom..

For the editor to offer the event with its fields, describe it with a made-up sample in the Joinotify/Cloud_Sync/Catalog filter:

add_filter( 'Joinotify/Cloud_Sync/Catalog', function( $entries ) {
$entries[] = array(
'name' => 'custom.quote.requested',
'schemaVersion' => 1,
'sample' => array( 'quote' => array( 'id' => 1, 'total' => '100.00' ) ),
);

return $entries;
} );

The Send webhook action in the WordPress builder​

The plugin's flow builder has a Send webhook action that sends data to any URL — n8n, Zapier, Make, your ERP or the webhook trigger of a platform flow:

  • the body is the trigger's data, in the same shape as site events (order, customer, links, user, cart), or a JSON of your own, with the plugin's placeholders;
  • method POST, PUT or PATCH, and headers as Name: value, one per line;
  • with a secret, each call carries X-Joinotify-Timestamp and X-Joinotify-Signature-256 (sha256= and the HMAC-SHA256 of timestamp.body), so the receiver can check where it came from.

Only public addresses are accepted. The call goes out when the flow reaches the action, waits up to 10 seconds and does not follow redirects; a failure goes to the plugin's log. The action does not depend on the sync.

Privacy​

WordPress's privacy tools (Tools → Export Personal Data and Tools → Erase Personal Data) cover the sync:

  • export lists the person's marketing consent and what the site's queue holds about their e-mail;
  • erase removes the consent and the queue rows, and asks Joinotify to erase the contacts this site linked to the person — found by who they are on the site, never by phone. The contact is erased whole, as in the panel's erasure. If Joinotify cannot erase it right away, WordPress says so: erase it from the panel or run the request again.

Switching the sync off does not erase what already arrived: the contacts stay in the account, where you can erase them.

Through the API​

Events arrive through POST /site-events, and the initial load's customers through POST /contacts/sync, always with the site's key. The envelope is described in the event catalog, and the routes in the API reference.