driptab/Docs

Webhooks

Driptab fires HTTP webhooks when billing events occur. Your system listens on an endpoint, verifies the signature, and acts on the events that matter to you.


Registering an endpoint

dtab webhooks create \
  --url https://app.example.com/hooks/driptab \
  --events invoice.created,payment.succeeded,subscription.suspend_requested

Or via the API:

POST /v1/webhooks
X-Project-Id: <project_id>
Authorization: Bearer dtab_…

{
  "url": "https://app.example.com/hooks/driptab",
  "events": ["invoice.created", "payment.succeeded", "subscription.suspend_requested"]
}

Response includes the endpoint's secret — a per-endpoint HMAC-SHA256 signing key with the prefix whsec_. Store this secret; it is shown once. Subsequent list responses mask it.

You can register multiple endpoints, each subscribing to different event sets.


Webhook payload format

All webhook deliveries share the same envelope:

{
  "event": "invoice.created",
  "created_at": "2026-06-08T02:57:35.000Z",
  "data": {
    "invoice": { ... },
    "customer": { ... }
  }
}

The data object contains the relevant billing objects. See the webhook events reference for the full payload shape of each event type.


Verifying the signature

Every delivery includes an X-Driptab-Signature header containing an HMAC-SHA256 hex digest of the raw request body, signed with the endpoint's secret.

Verification in Node.js / TypeScript:

import { createHmac } from 'crypto';

function verifyDriptabWebhook(secret: string, body: string, sig: string): boolean {
  const expected = createHmac('sha256', secret).update(body).digest('hex');
  return expected === sig;
}

// In your request handler:
const sig    = req.headers['x-driptab-signature'];
const body   = req.rawBody;  // unparsed string
const valid  = verifyDriptabWebhook(process.env.DRIPTAB_WEBHOOK_SECRET, body, sig);
if (!valid) return res.status(401).send('Invalid signature');

Verification in Python:

import hmac, hashlib

def verify(secret: str, body: bytes, sig: str) -> bool:
    expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig)

Always verify the signature before processing the payload. Use the raw (pre-parsed) request body — not JSON.stringify(req.body), which may differ from the original.


Responding to deliveries

Your endpoint must return a 2xx response within 10 seconds. Driptab records the delivery result:

  • delivered — your endpoint returned a 2xx.
  • failed — your endpoint returned non-2xx, timed out, or was unreachable.

Failed deliveries are retried with exponential backoff. The next_retry_at field on the delivery record indicates the next attempt.

Acknowledge the webhook quickly — do expensive processing in the background. Return 200 OK immediately if you need more than a second to process.


Available events

Event Fired when
invoice.created An invoice is generated (period close or immediate charge)
invoice.payment_overdue_reminder_1 Invoice is 3 days past due (configurable via driptab.yaml)
invoice.payment_overdue_reminder_2 Invoice is 7 days past due
subscription.period_renewed A billing period closes and a new one starts
subscription.suspend_requested Dunning step requesting suspension (14 days past due)
subscription.terminate_requested Dunning step requesting termination (30 days past due)
addon.applied An add-on is applied to a customer
payment.succeeded A payment is recorded against an invoice

See the webhook events reference for full payload shapes.


The dunning pattern

Driptab does not suspend or terminate subscriptions automatically. It fires webhook events at configurable milestones; your system acts on them.

# driptab.yaml
dunning:
  enabled: true
  steps:
    - days_after_due: 3
      webhook_event: invoice.payment_overdue_reminder_1
    - days_after_due: 7
      webhook_event: invoice.payment_overdue_reminder_2
    - days_after_due: 14
      webhook_event: subscription.suspend_requested
    - days_after_due: 30
      webhook_event: subscription.terminate_requested

Your webhook handler decides what to do on each step:

  • Send a reminder email (days 3 and 7).
  • Call POST /v1/subscriptions/:id/suspend (day 14) to stop service.
  • Call POST /v1/subscriptions/:id/terminate (day 30) to permanently close.

This design gives you full control over customer communication and grace periods without requiring those decisions to live inside Driptab.


Delivery log

Every dispatch attempt is recorded in webhook_deliveries. View the log:

dtab webhooks deliveries --endpoint <endpoint_id>

Or via the API:

GET /v1/webhook-deliveries?endpoint_id=<id>