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:
| Event | Fires when |
|---|---|
subscribe | A new add-on checkout is started (Stripe redirect). |
cancel | An add-on or MRR plan is cancelled. |
plan_change | A monthly plan is subscribed to or switched. |
totp_enabled | Two-factor auth is confirmed and active. |
totp_disabled | Two-factor auth is removed. |
ip_lock_changed | IP lock mode is changed (off/relaxed/strict). |
session_revoked | A 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:
| Header | Value |
|---|---|
X-SLWP-Event | The event name, same as the event field in the body. |
X-SLWP-Timestamp | Unix epoch seconds when the payload was signed. |
X-SLWP-Signature | The 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.