Skip to content

EP Connect

EP Connect sends webhook notifications from your PageMotor site to external services when things happen. Someone fills out a contact form, a booking is confirmed, a newsletter subscription lands — EP Connect fires a JSON POST to whatever URL you configured.

Published by ElmsPark Studio.

Use EP Connect to plug PageMotor into the wider automation ecosystem:

  • Zapier or Make to trigger multi-step automations.
  • Slack or Discord to post alerts to a channel.
  • Your own API for custom workflows.
  • Google Sheets via Zapier or Apps Script to log every event.

Seven built-in events, with hooks for other plugins to register their own.

EventSource pluginFired when
Email Form SubmittedEP EmailSomeone submits a contact form.
Booking ConfirmedEP BookingA booking moves to Confirmed status.
Newsletter SubscriptionEP NewsletterSomeone opts in to a newsletter list.
Comment PostedEP CommentsA comment is submitted (before moderation).
Order CompletedEP EcommerceAn order settles payment.
Content PublishedPageMotor coreA page’s status changes to Published.
User RegisteredPageMotor coreA new user account is created.

Five events (Email, Booking, Newsletter, Comments, Ecommerce) fire in real time via the emit() API. The remaining two (Content Published and User Registered) use database observation with a 30-second throttle, because PageMotor core doesn’t have an emit hook for them. Events still arrive, just with up to 30 seconds of latency.

  • PageMotor 0.8.2b or later
  • EP Suite base class (bundled)
  1. ep-connect.zip comes with an EP Suite licence — ElmsPark supplies it directly (see EP Suite plugins); after install it updates through your site’s Updates screen.
  2. Upload via Plugins → Manage Plugins. Activate.
  1. Open Plugin Settings → EP Connect.
  2. Click Add Webhook.
  3. Fill in:
    • Name — internal label, e.g. “Slack alerts”.
    • Event — pick from the dropdown (the seven built-in events plus any custom ones).
    • URL — the endpoint to POST to.
    • Secret — a string used to sign the payload via HMAC-SHA256. Your receiving endpoint uses this to verify the payload came from you and wasn’t tampered with. Generate a random 32-character string and paste it here and on the receiver.
    • Enabled — toggle.
  4. Click Save.
  5. Click Send Test on the saved webhook row. A sample payload fires to your URL. Check the receiver got it.

Every webhook POST body is JSON with this shape:

{
"event": "booking.confirmed",
"site": "https://yoursite.com",
"timestamp": "2026-04-20T14:32:11Z",
"data": {
"booking_id": 123,
"service": "60-minute consultation",
"customer_name": "Alice Smith",
"customer_email": "[email protected]",
"...": "..."
}
}

Headers:

  • Content-Type: application/json
  • X-EP-Event: <event name>: for example user.registered.
  • X-EP-Timestamp: <unix seconds>: when the webhook was signed.
  • X-EP-Signature-V2: <hex>: HMAC-SHA256 of the timestamp, a full stop, then the body (timestamp + "." + body), using your webhook secret. Use this one.
  • X-EP-Signature: <hex>: HMAC-SHA256 of the body alone, kept for older receivers. It does not protect against a captured request being sent again.

Both signatures are plain lowercase hex, with no sha256= prefix.

On your receiver, verify the signature before trusting the payload, and refuse old timestamps so a captured request cannot be replayed. Example in Node.js:

import crypto from 'node:crypto';
const body = rawRequestBody; // the raw bytes, not re-serialised JSON
const timestamp = request.headers['x-ep-timestamp'];
const signature = request.headers['x-ep-signature-v2'];
const expected = crypto
.createHmac('sha256', process.env.EP_CONNECT_SECRET)
.update(timestamp + '.' + body)
.digest('hex');
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
if (!fresh || !signature || signature.length !== expected.length
|| !crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return response.status(401).send('Invalid signature');
}

Without verification, anyone who guesses your webhook URL can fake payloads. Always verify. If the receiver is another PageMotor site, EP Flows does all of this for you, including refusing a repeated request.

Every payload has these left out, whichever plugin the event came from. A field is dropped when its name looks like:

  • a password, password hash or salt;
  • a token, key, secret, session or nonce (API keys, private keys, reset and activation codes);
  • an IP address or browser details (user agent).

Very long text values are shortened to 2,000 characters.

The settings page shows the last 200 deliveries across all webhooks:

  • Event, webhook name, destination URL (truncated), HTTP status code, duration.
  • Green for 2xx, yellow for 3xx, red for 4xx and 5xx.
  • Click a row to see the full request and response.

Logs auto-rotate after 200 entries.

  • Webhook delivery happens via register_shutdown_function(), after the response has been sent to the user. Zero latency impact on your page loads.
  • 30-second observation throttle for DB-observed events so this plugin never becomes the performance bottleneck on a busy site.

Registering custom events from another plugin

Section titled “Registering custom events from another plugin”

Any plugin can add an event to the dropdown by calling:

EP_Connect::register_event('my_plugin.something_happened', 'My custom event');

Then emit it when the thing happens:

EP_Connect::emit('my_plugin.something_happened', [
'field' => 'value',
'another' => 42,
]);

Your event appears in the webhook dropdown and fires through the same signing and delivery pipeline as built-in events.

“Send Test succeeds but real events don’t fire”

Section titled ““Send Test succeeds but real events don’t fire””

Check the webhook is Enabled (toggle in the management UI). Check the event you are expecting actually happened — look at the source plugin’s own logs.

“Receiver gets the payload but signature verification fails”

Section titled ““Receiver gets the payload but signature verification fails””

The secret on your receiver must match the secret saved on the webhook, byte for byte, including any trailing whitespace. EP Connect stores secrets encrypted and never shows them again, so if you are not sure, type a new secret into the webhook and paste the same value into your receiver. Also check the receiver signs the raw request body exactly as it arrived, and compares plain hex with no sha256= prefix.

“Delivery log shows HTTP 403 from my receiver”

Section titled ““Delivery log shows HTTP 403 from my receiver””

Your receiver is rejecting the request. Common cause: the receiver expects a specific authentication header. EP Connect sends the headers listed under Headers above and nothing else. If the receiver needs anything else, it needs to be a proxy or adapter, not a direct EP Connect target.

The receiver answered with a redirect. EP Connect does not follow redirects, so nothing reached the final address. Put the final URL (the one the redirect points to) into the webhook instead.

“Delivery log shows ‘timeout’ for every event”

Section titled ““Delivery log shows ‘timeout’ for every event””

The receiver is slow or unreachable. EP Connect waits up to 10 seconds. If your receiver is legitimately slow, use a faster endpoint (a Zapier webhook that queues, rather than a slow direct integration).

“Events I expect to fire aren’t in the delivery log”

Section titled ““Events I expect to fire aren’t in the delivery log””

Some events come from plugins that must be active and configured. For example, “Booking Confirmed” only fires if EP Booking is installed and a booking was actually confirmed. Check the source plugin is doing its bit.

For a quick question about this plugin, EP Support inside your admin is the fastest option. The chat widget sits on every EP plugin settings page and knows which one you’re on, with starter questions and links preloaded for that exact screen.

For anything bigger — a bug report, a feature request, or a “how do I…” that needs a real reply — open a ticket at help.elmspark.com. A real person, helped by AI, writes the reply. Usually within a few hours. Tickets don’t disappear into the void.

  • EP Connect no longer stops working when a plugin it watches is missing. On a site where EP Connect watches for something whose plugin is not installed, or was removed (EP Comments, for example), EP Connect could fail to load on some page views, about once every 30 seconds. Events on those page views were lost, and an error was written to your log each time. It now skips what it cannot read and carries on.
  • This release also includes 1.1.5 and 1.1.6 (below), not previously released.
  • New webhooks start from now. When you add the first webhook for an event, EP Connect no longer sends a burst of old records (existing users, past bookings, old orders); it starts with the next new one.
  • Webhook settings are admin-only on every PageMotor version, and the security check on them is stricter.
  • Nothing to do after updating.
  • Security fix: webhook secret keys are stored encrypted and never shown again. When you edit a webhook, leave the secret blank to keep it, type a new one to replace it, or tick “Remove the saved secret”. Existing secrets are encrypted the first time the site loads after updating.
  • Security fix: webhooks carry less personal data. Passwords, tokens, keys, IP addresses and browser details are left out of every webhook, whichever plugin the event came from.
  • Signed webhooks now include a timestamp, so receivers (including EP Flows) can refuse a replayed request. The original signature header is still sent.
  • No more duplicate deliveries when two visitors load pages at the same moment.
  • Webhooks no longer follow redirects. A receiver that answers with a redirect is logged with that code, and nothing is sent on.
  • Settings language menu. The language menu in this plugin’s settings now lists only the languages it is actually translated into, plus English, so you can no longer pick a language that changes nothing.
  • Danish. Adds a Danish translation.
  • “Order Completed” now fires only for orders that are really paid. Before, every new EP Ecommerce order was announced as completed the moment it was created, while it was still waiting for payment. Webhooks and EP Flows automations for “Order Completed” could run for an order that was never paid, or that failed or expired.
  • An order is now announced once it completes, even when EP Connect was not running on the request that completed it. It is announced once. An order that was completed and then refunded is still announced once, because it did complete.
  • An order left waiting for more than 14 days stops holding back the check.
  • The order’s payment-page token is no longer included in what EP Connect sends.
  • “Booking Confirmed” now fires only for confirmed bookings. Before, every new EP Booking booking was announced as confirmed the moment it was made, including bookings still waiting for payment or for your approval. Webhooks and EP Flows automations for “Booking Confirmed” could then run for a booking that was never paid.
  • A booking is now announced when it becomes confirmed, even when that happens later (the payment arrives, or you approve it). It is announced once.
  • A booking left waiting for more than 14 days stops holding back the check, so an abandoned booking cannot delay the others.
  • A missing table for one of the watched plugins now skips just that check instead of risking an error on the page.
  • Fixes “Your session has expired. Please reload to ensure your security.” on PageMotor 0.11. The message appeared on this plugin’s admin screens even though you were signed in perfectly normally, and whatever you were doing failed to save.
  • Nothing was wrong with your session. PageMotor 0.11 started handling part of the security check that this plugin was already handling itself, and the two together made every save look invalid. The plugin now checks whether PageMotor has already done it.
  • Visitors who were not signed in were never affected, on any version.
  • There is nothing to reconfigure, and nothing else changed.