Webhooks

Send recovery events to up to ten endpoints, such as your CRM, email marketing, Zapier, Make or n8n. Each endpoint has its own events and signing secret. Webhooks are signed, sent in the background and retried automatically.

ProSiteryn → Abandoned Cart → Integrations

Adding an endpoint

  1. Get an endpoint URL

    From your tool, for example a Zapier "Catch Hook", a Make "Custom webhook", an n8n "Webhook" node or your own server. It must start with https://.

  2. Add it

    Click Add endpoint, paste the Endpoint URL, keep Send events to this endpoint on and tick the Events you want. Then click Save Changes.

  3. Generate its secret

    In the endpoint's Signing secret row, click Generate secret and copy it now: "New secret generated. Copy it now: it will not be shown again."

  4. Send a test event

    Click the endpoint's Send test event. You should see "Test event delivered." Your tool now knows the payload fields.

  • Webhook Endpoints card before any endpoint is added ("No endpoints yet.", Add endpoint)
  • Recent Deliveries in the sidebar
  • The "Zapier, Make or n8n" setup card
Only administrators can add, change, remove, rotate or test endpoints. Shop managers see the tab read-only.

Endpoint settings

You can add up to 10 endpoints. Each is numbered ("Endpoint 1", "Endpoint 2"…) and shows a state: New (not saved yet), Off, Needs a secret or Active.

Endpoint URL
HTTPS only

Must be a public address starting with https://. Addresses on private, loopback or local networks are refused. If an endpoint's URL isn't valid, nothing is saved and you see, for example, "Endpoint 2: enter a public HTTPS URL (starting with https://). Nothing was saved."

Send events to this endpoint
Default On

Pause one endpoint without removing it.

Events
Default 3 events

Cart abandoned, Cart restored from a recovery link and Checkout completed are ticked for a new endpoint.

Signing secret

Each endpoint has its own. See Rotating the secret.

Remove takes an endpoint off the list; it is deleted, with its secret, when you click Save Changes. Sites that used the single webhook of version 1.0.0 see it as Endpoint 1, with the same secret and queued deliveries.

Zapier, Make or n8n

  1. In your tool, start a workflow with a "catch webhook" trigger: Zapier Webhooks by Zapier → Catch Hook, Make Webhooks → Custom webhook, or the n8n Webhook node (POST).
  2. Copy the HTTPS URL it gives you, add it here as an endpoint, choose events and save.
  3. Generate the endpoint's secret, then click Send test event so the tool learns the payload fields.
  4. Optional but recommended: verify the signature in a code step, and ignore requests that don't match.

Events

EventSent whenExtra fields
cart.abandonedA cart is marked abandoned—
campaign.enrolledA cart joins a campaigncampaign {id, name}
email.sentA campaign email or the Free email is sentstep (1-based); campaign for campaign emails
cart.recoveredA cart is restored from a recovery link—
cart.completedCheckout finishedcart.order_id, cart.order_total filled in

Payload

Every event is a JSON POST:

{
  "id": "3f2b8c1e-9a4d-4f6b-8e2a-1c7d5b9e0f42",
  "event": "cart.abandoned",
  "occurred_at": "2026-10-02T10:00:00+00:00",
  "site": "https://your-store.com/",
  "cart": {
    "id": 7,
    "status": "abandoned",
    "email": "sarah@example.com",
    "first_name": "Sarah",
    "customer_id": 0,
    "total": 35.0,
    "currency": "USD",
    "items": [
      { "product_id": 419, "variation_id": 0, "name": "Canvas Tote", "quantity": 1, "line_total": 35.0 }
    ],
    "created_at": "2026-10-02T08:52:11+00:00",
    "abandoned_at": "2026-10-02T10:00:00+00:00",
    "recovered_at": null,
    "completed_at": null,
    "order_id": null,
    "order_total": null
  }
}

The payload includes the customer's email and first name, which CRM and email tools need. It never includes a recovery link or token. A test event adds "test": true and uses sample data.

Headers

HeaderValue
X-Siteryn-EventThe event name, e.g. cart.abandoned
X-Siteryn-DeliveryA unique ID, the same as the payload id. Use it to ignore duplicates.
X-Siteryn-TimestampUnix time in seconds
X-Siteryn-Signaturesha256= + HMAC-SHA256 of "{timestamp}.{raw body}" with your secret
X-Siteryn-Signature-PreviousThe same, signed with the old secret, for 24 hours after a rotation

Verifying the signature

Always verify before trusting a request. Compare in constant time and reject old timestamps.

$secret    = getenv( 'SITERYN_WEBHOOK_SECRET' ); // whsec_…
$body      = file_get_contents( 'php://input' );
$timestamp = $_SERVER['HTTP_X_SITERYN_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_SITERYN_SIGNATURE'] ?? '';
$previous  = $_SERVER['HTTP_X_SITERYN_SIGNATURE_PREVIOUS'] ?? '';

$expected = 'sha256=' . hash_hmac( 'sha256', $timestamp . '.' . $body, $secret );
$valid    = hash_equals( $expected, $signature ) || ( $previous && hash_equals( $expected, $previous ) );

// Reject requests older than 5 minutes.
if ( ! $valid || abs( time() - (int) $timestamp ) > 300 ) {
	http_response_code( 401 );
	exit;
}

$event = json_decode( $body, true );
http_response_code( 200 ); // Any 2xx counts as delivered.
import crypto from 'node:crypto';
import express from 'express';

const app = express();
const secret = process.env.SITERYN_WEBHOOK_SECRET;

app.post('/siteryn', express.raw({ type: 'application/json' }), (req, res) => {
  const ts = req.get('X-Siteryn-Timestamp') || '';
  const sig = req.get('X-Siteryn-Signature') || '';
  const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(ts + '.' + req.body).digest('hex');

  const ok = sig.length === expected.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
  if (!ok || Math.abs(Date.now() / 1000 - Number(ts)) > 300) {
    return res.sendStatus(401);
  }

  const event = JSON.parse(req.body);
  res.sendStatus(200);
});

Rotating the secret

Each endpoint's secret starts with whsec_ and is shown masked (first 6 and last 4 characters) once saved. Click the endpoint's Rotate secret and confirm "Generate a new secret for this endpoint? The current one keeps working for 24 hours." For the next 24 hours each request is signed with both secrets (the old one in X-Siteryn-Signature-Previous), so you can update your receiver without missing events.

Delivery and retries

Recent Deliveries

The sidebar card shows the last 10 deliveries across all endpoints, with a status pill (Delivered, Failed, Retrying or Queued), the event, when, the endpoint ("Webhook: hooks.zapier.com") and the last error, such as "The endpoint answered 500." Test events are sent directly and are not listed.

Custom integrations

Developers can add their own connector, for example straight to a CRM API, with the siteryn_abandoned_cart_pro_integrations filter. Pro handles queueing, retries and logging. See the Developer guide.