Subscription Webhooks

HMAC-signed webhook notifications for subscribe, cancel, plan change, and security events on your SheetLinkWP license.

Overview

If you configure a webhook URL on a license, we POST a JSON payload to it whenever the license's subscription state changes. Use this to:

  • Mirror subscription state into your own CRM or accounting system.
  • Alert in Slack when a cancellation happens.
  • Trigger an internal automation when a new add-on goes live.
  • Audit-log security-relevant events (2FA enrolled/disabled, sessions revoked).

Configure the webhook URL in the portal's Security tab. Only HTTPS URLs are accepted. Adding or changing the URL requires a fresh email re-auth code.

Event types

The event field in the payload is one of:

EventFires when
subscribeA new add-on checkout is started (Stripe redirect).
cancelAn add-on or MRR plan is cancelled.
plan_changeA monthly plan is subscribed to or switched.
totp_enabledTwo-factor auth is confirmed and active.
totp_disabledTwo-factor auth is removed.
ip_lock_changedIP lock mode is changed (off/relaxed/strict).
session_revokedA session is signed out or a client site is deactivated.

Payload format

Every webhook is a POST with Content-Type: application/json. The body is a single object:

{
  "event": "cancel",
  "licenseId": "cmo6e08tt0000vjxu2s2bffbm",
  "email": "you@example.com",
  "detail": { "addonType": "crm_fanout", "kind": "addon" },
  "ip": "203.0.113.42",
  "userAgent": "Mozilla/5.0 ...",
  "at": "2026-04-19T23:30:00.000Z"
}

detail varies by event: subscribe/cancel/plan_change include the addonType or planType; ip_lock_changed includes ipLockMode; session_revoked with detail.action = 'site_deactivated' includes the client domain.

Signature verification

Every request carries three headers you must verify:

HeaderValue
X-SLWP-EventThe event name, same as the event field in the body.
X-SLWP-TimestampUnix epoch seconds when the payload was signed.
X-SLWP-SignatureThe string v1= followed by the hex HMAC-SHA256 of <timestamp>.<body> using your license's webhook secret.

Reject the request if the signature doesn't match, or if the timestamp is more than 5 minutes old (replay protection).

Node example:

import { createHmac, timingSafeEqual } from 'crypto';

function verifySlwpWebhook(rawBody, headers, secret) {
  const sig = (headers['x-slwp-signature'] || '').replace(/^v1=/, '');
  const ts  = headers['x-slwp-timestamp'] || '';
  if (Math.abs(Date.now()/1000 - Number(ts)) > 300) return false;
  const expected = createHmac('sha256', secret).update(ts + '.' + rawBody).digest('hex');
  try {
    return timingSafeEqual(Buffer.from(sig, 'hex'), Buffer.from(expected, 'hex'));
  } catch { return false; }
}

PHP example:

function slwp_verify_webhook($raw_body, $headers, $secret) {
  $sig = isset($headers['x-slwp-signature']) ? preg_replace('/^v1=/', '', $headers['x-slwp-signature']) : '';
  $ts  = isset($headers['x-slwp-timestamp']) ? (int) $headers['x-slwp-timestamp'] : 0;
  if (abs(time() - $ts) > 300) return false;
  $expected = hash_hmac('sha256', $ts . '.' . $raw_body, $secret);
  return hash_equals($expected, $sig);
}

Retries and delivery

Webhooks are best-effort. We POST once with a 10-second timeout. If your endpoint times out, returns non-2xx, or is unreachable, we log the failure in the notification log and move on - we don't retry.

If you need guaranteed delivery, combine webhooks with periodic polling of GET /api/sheetlinkwp/portal/agency for the authoritative state.

Monitoring: delivery outcomes for every webhook (success, status code, error message) are stored in the platform's notification log and available to support if something looks wrong.

Rotating the signing secret

To rotate the signing secret (for example if you suspect it's been leaked), click Rotate signing secret on the webhook row in the portal's Security tab. A new secret is shown once - save it in your server's env immediately. Requests using the old secret will start failing verification as soon as you rotate.

Rotating requires a fresh email re-auth code, matching other security-settings changes.

Email notifications as a fallback

Separate from the webhook channel, the portal can also email you a human-readable summary for every one of these events. Enable with Email me on subscription changes in the Security tab. You can have both channels on - they fire independently and failures on one don't affect the other.

Ready to Get Started?

Install SheetLink Forms and connect your first form in under 10 minutes.