Webhook Reference

PrivaBase uses webhooks for two purposes: receiving Stripe billing events and sending monitoring alerts to your systems.

Stripe Billing Webhooks

PrivaBase's billing webhook at /api/v1/billing/webhook receives events from Stripe. These are configured in your Stripe dashboard — you don't need to do anything unless you're self-hosting.

Handled Stripe Events

EventWhat Happens
checkout.session.completedSubscription activated, user account updated to paid tier
customer.subscription.updatedPlan change processed (upgrade/downgrade)
customer.subscription.deletedSubscription cancelled, account reverts to free tier
invoice.payment_succeededPayment recorded, usage counters reset
invoice.payment_failedPayment failed notification sent to user
ℹ️ Stripe Signature Verification

All Stripe webhooks are verified using the Stripe-Signature header with HMAC-SHA256. Invalid signatures are rejected with 400.

Monitoring Webhooks

You can register your own webhook URLs to receive compliance monitoring alerts. See the Monitoring API for registration endpoints.

Monitoring Event Types

EventDescription
check.failedA scheduled compliance check had failures
check.degradedCompliance score dropped below threshold
check.passedA previously failing check now passes
schedule.errorA scheduled check failed to run (system error)

Webhook Payload Format

{
  "event": "check.failed",
  "timestamp": "2026-03-13T12:00:00Z",
  "data": {
    "scheduleId": "sched-uuid",
    "frameworkId": "gdpr",
    "frameworkName": "GDPR",
    "score": 45,
    "previousScore": 78,
    "failedChecks": [
      {
        "checkId": "gdpr-check-005",
        "rule": "Data Protection Officer",
        "severity": "required"
      }
    ]
  }
}

Webhook Security

When registering a webhook, you provide a secret. PrivaBase includes an HMAC-SHA256 signature in the X-PrivaBase-Signature header. Verify it server-side:

const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Retry Policy