Goal: fire a signed event into Zapier, n8n, or your own endpoint on every subscription lifecycle change — new subscriber, upgrade, payment, cancellation — so your CRM, Slack, and email flows react automatically and you never copy data between systems by hand.
Anyone running a subscription business who's tired of the gap between "someone subscribed" and "the rest of my stack knows about it." If you're manually adding new customers to a CRM, pinging a channel when someone cancels, or triggering onboarding emails by watching a dashboard, this closes that gap with a webhook and an automation tool you already use. It's for builders comfortable wiring a receiver — n8n, Zapier, or a small endpoint of your own.
In the admin backoffice, open the webhooks CRUD and create one. You'll set: the target URL (your n8n/Zapier/custom endpoint), the events it subscribes to, and a secret used to sign each delivery. Copy the secret now — you'll need it on the receiver to verify signatures, and it's the one thing that proves a request genuinely came from your platform and not from someone who guessed your webhook URL.
Success looks like: the webhook is listed as active with your URL and a secret set.
Subscribe only to the events you'll act on — every event you don't handle is noise on your receiver. The lifecycle events that matter for most flows: a new subscription created, an upgrade or downgrade, a successful payment, a failed payment, and a cancellation. The platform's event bus is the source; the webhook relays those events outward.
Gotcha: "payment failed" is the event most people forget to handle and the one that saves the most revenue — it's your cue to trigger a dunning email or a Slack alert before the customer churns silently. Subscribe to it.
This is the step that separates a toy from something you can trust. Each delivery is signed with an HMAC using your shared secret. Your receiver must recompute the HMAC over the raw request body and compare it to the signature header before trusting the payload. Skip this and your webhook URL becomes an open door — anyone who finds it can POST fake "new subscriber" events into your CRM.
The check is a few lines. In an n8n Function node or your own handler:
import hmac, hashlib
expected = hmac.new(SECRET.encode(), raw_body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, received_signature):
reject(401) # not from us — drop itUse a constant-time comparison (compare_digest), and hash the raw body — re-serialising the JSON first will change the bytes and break the check.
Success looks like: a real delivery verifies; a request with a tampered body or wrong signature is rejected with 401.
Wire a concrete automation so you can see it end to end. A useful first one: new subscriber → Slack + CRM + welcome email. In n8n or Zapier, after the signature check, fan the verified payload out to three actions — post to a Slack channel ("New Pro subscriber: {email}"), create/update the contact in your CRM with their plan, and trigger a welcome email sequence.
Keep each action idempotent where you can (see Step 5), and map the fields explicitly from the event payload rather than assuming a shape — log one real payload first and build against it.
Success looks like: a test subscription lights up all three actions with the correct customer and plan.
Networks fail, and a webhook that silently drops on your receiver's bad day loses data you can't get back. Two responsibilities, split between the two sides:
On delivery: the platform retries failed deliveries, so your receiver may see the same event more than once. Make your actions idempotent — key on the event id so processing the same "new subscriber" twice doesn't create two CRM contacts or send two welcome emails. Treat a delivery as "seen this id before? then acknowledge and stop."
On your side: return a 2xx quickly to acknowledge receipt, then do slow work asynchronously. If your handler takes ten seconds to call three APIs before responding, deliveries time out and retry, compounding the problem. Acknowledge fast, process after.
Success looks like: delivering the same event twice produces one outcome, not two; a momentary receiver outage recovers via retry without lost events.
The signature is the security boundary. An unverified webhook is an unauthenticated write into your business systems. Never skip Step 3, even in a prototype — prototypes become production.
Retries make at-least-once, not exactly-once. The platform guarantees it will try to deliver; idempotency on your side is what turns that into a clean result. That's your work, not the platform's.
Zapier/n8n add their own limits. Rate limits, task quotas and their own retry behaviour sit on top of this. For high volume, a small custom receiver you control is more predictable than a no-code tool's shared infrastructure.
Next: add a "payment failed → dunning" flow and a "cancellation → win-back sequence" flow. Those two turn passive event data into revenue you'd otherwise have lost silently.
The distance between "someone subscribed" and "my whole stack reacted" should be zero, and outbound webhooks close it: create a signed webhook, subscribe to the lifecycle events you act on, verify the HMAC on your receiver, fan verified events out to Slack/CRM/email, and make it reliable with idempotency and fast acknowledgement. The platform relays events off its own event bus and retries delivery; you verify signatures and stay idempotent. The result is a subscription business wired into your automation stack securely, without a human moving data between systems. Because it's self-hosted and source-available under BSL 1.1 (free for commercial use while annual VBWD-attributable sales stay under the value of 6.7 BTC per year), the events — and the customer data in them — flow through infrastructure you own.
Automating your stack? See the developer docs for the webhook payload shape and event list, and the features overview for what the event bus emits.