Webhooks

Receive real-time notifications when events happen in your account. Configure outgoing webhook endpoints from your dashboard.

Setting Up Webhooks

Navigate to Webhooks

Go to API & Webhooks → Webhook Endpoints in your dashboard.

Create Endpoint

Click Create Endpoint and enter your endpoint URL (must be HTTPS).

Select Events

Select which events you want to subscribe to.

Save and Get Secret

Save the endpoint. You'll receive a signing secret starting with whsec_.
Keep your secret safe
The webhook secret is used to verify that events were sent by us. Do not expose it in client-side code or public repositories.

Event Types

EventDescription
subscription.createdA new subscription has been created
subscription.activatedSubscription became active (payment confirmed)
subscription.renewedRecurring payment succeeded
subscription.cancelledSubscription was cancelled (still active until period end)
subscription.expiredSubscription period ended, roles revoked
subscription.pausedSubscription was paused
subscription.resumedPaused subscription was resumed
payment.succeededA payment was successfully processed
payment.failedA payment attempt failed
refund.createdA refund was issued
customer.createdA new customer signed up
customer.updatedCustomer 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:

RetryDelay
1st retry1 minute
2nd retry5 minutes
3rd retry30 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
For local development, we recommend using a tool like ngrok to expose your local server to the internet.

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