Webhooks
Receive real-time notifications when events happen in your account. Configure outgoing webhook endpoints from your dashboard.
Setting Up Webhooks
Navigate to Webhooks
Create Endpoint
Select Events
Save and Get Secret
whsec_.Keep your secret safe
Event Types
| Event | Description |
|---|---|
subscription.created | A new subscription has been created |
subscription.activated | Subscription became active (payment confirmed) |
subscription.renewed | Recurring payment succeeded |
subscription.cancelled | Subscription was cancelled (still active until period end) |
subscription.expired | Subscription period ended, roles revoked |
subscription.paused | Subscription was paused |
subscription.resumed | Paused subscription was resumed |
payment.succeeded | A payment was successfully processed |
payment.failed | A payment attempt failed |
refund.created | A refund was issued |
customer.created | A new customer signed up |
customer.updated | Customer information was updated |
Payload Format
Webhook payloads are JSON with the following structure:
{
"id": "whd_abc123",
"eventType": "subscription.created",
"timestamp": 1708000000000,
"data": {
"subscriptionId": "sub_xyz",
"userId": "user_123",
"packageId": "pkg_456",
"status": "active"
}
}Signature Verification
Every webhook request includes an X-Webhook-Signature header containing an HMAC-SHA256 signature. You should verify this signature to ensure the request came from our platform.
Verification Example (Node.js)
import crypto from "crypto";
function verifyWebhookSignature(
payload: string,
signature: string,
secret: string
): boolean {
const expected = crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your webhook handler:
app.post("/webhook", (req, res) => {
const signature = req.headers["x-webhook-signature"];
const isValid = verifyWebhookSignature(
JSON.stringify(req.body),
signature,
process.env.WEBHOOK_SECRET // whsec_...
);
if (!isValid) {
return res.status(401).send("Invalid signature");
}
// Process the event
const event = req.body;
switch (event.eventType) {
case "subscription.created":
// Handle new subscription
break;
// ... other events
}
res.status(200).send("OK");
});Retry Policy
If your endpoint returns a non-2xx status code or fails to respond within 10 seconds, we'll retry up to 3 times with exponential backoff:
| Retry | Delay |
|---|---|
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
After 3 failed retries, the delivery is marked as failed. You can inspect failed deliveries in the Webhook Delivery Logs section of your dashboard.
Testing Webhooks
You can send a test ping to your endpoint from the dashboard. The test event has the type ping with a test payload.
Local Development
Best Practices
- Always verify signatures — never trust a webhook without verifying the HMAC signature
- Respond quickly — return a 200 status code within 10 seconds and process asynchronously
- Handle duplicates — use the event ID for idempotency; the same event may be delivered more than once
- Use HTTPS — only HTTPS endpoints are supported