Webhooks
Receive real-time event notifications from the DramWell API — event types, payload structure, signature verification, and retry behavior.
Overview
Webhooks let you receive real-time notifications when things happen in DramWell. Instead of polling the API, register an HTTPS endpoint and we will send you a POST request every time a relevant event fires. Webhooks are the recommended way to keep external systems in sync with DramWell data.
Event Types
| Event | Description |
|---|---|
job.created |
A new job has been created |
job.updated |
A job's status, assignment, or details changed |
job.completed |
A job has been marked complete |
invoice.created |
A new invoice has been generated |
invoice.paid |
An invoice payment has been successfully collected |
invoice.voided |
An invoice has been voided |
customer.created |
A new customer record has been created |
customer.updated |
A customer's information has changed |
membership.enrolled |
A customer has enrolled in a membership plan |
membership.cancelled |
A membership plan has been cancelled |
membership.renewed |
A membership plan has been renewed |
Payload Structure
Every webhook delivery is a POST request with a JSON body:
{
"id": "evt_01HXYZ",
"type": "invoice.paid",
"created_at": "2026-03-10T14:32:00Z",
"org_id": "org_01HABC",
"data": {
"id": "inv_01HDEF",
"amount": 32500,
"currency": "usd",
"customer_id": "cus_01HGHI",
"paid_at": "2026-03-10T14:31:58Z"
}
}
| Field | Type | Description |
|---|---|---|
id |
string | Unique event ID — use this to deduplicate deliveries |
type |
string | The event type |
created_at |
ISO 8601 string | When the event was generated |
org_id |
string | The organization the event belongs to |
data |
object | The affected resource at the time of the event |
Signature Verification
Every delivery includes a X-DramWell-Signature header. You must verify this signature before processing the payload to ensure the request genuinely came from DramWell.
The signature is an HMAC-SHA256 hash of the raw request body, computed using your webhook secret as the key, then base64-encoded.
import crypto from 'crypto'
function verifyWebhook(
rawBody: string,
signature: string,
secret: string
): boolean {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('base64')
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
)
}
Always use timingSafeEqual to prevent timing attacks. Reject requests that fail verification with a 400 response.
Retry Policy
If your endpoint returns anything other than a 2xx response — or does not respond within 10 seconds — DramWell will retry the delivery:
| Attempt | Delay after previous failure |
|---|---|
| 1 | Immediate |
| 2 | 5 minutes |
| 3 | 30 minutes |
| 4 | 2 hours |
| 5 | 8 hours |
After 5 failed attempts the event is marked as undelivered and logged in the admin panel under Settings > Webhooks > Delivery Log. You can replay any event manually from that log.
Registering a Webhook
Via admin panel
- Go to Settings > Webhooks in the admin panel.
- Click Add Endpoint.
- Enter your HTTPS URL and select the event types you want to receive.
- Copy the generated webhook secret and store it securely.
Via API
POST /v1/webhooks
Authorization: Bearer sk_live_xxxx
Content-Type: application/json
{
"url": "https://your-app.example.com/webhooks/dramwell",
"events": ["job.completed", "invoice.paid", "membership.enrolled"]
}
Best Practices
- Respond with
200 OKimmediately and process the event asynchronously — never do heavy work inside the HTTP handler. - Use the event
idfield to deduplicate — your endpoint may receive the same event more than once during retries. - Store the raw request body before parsing JSON so signature verification can use the exact bytes we signed.
- Use a queue (SQS, BullMQ, etc.) to buffer events and handle processing failures independently of the webhook receiver.
Related Articles
Was this article helpful?