Webhooks
Webhooks let your application receive real-time notifications when data changes in Driftwood — a contact is created, a deal is updated, a partnership is deleted, and so on. Instead of polling the API, you register an HTTPS endpoint and Driftwood POSTs a signed JSON payload to it whenever a subscribed event occurs.
Manage endpoints with the Webhooks API (webhooks-create, webhooks-list, webhooks-update, webhooks-delete). This guide covers the payload format, signature verification, and delivery behavior.
Subscribing
Section titled “Subscribing”Create an endpoint with webhooks-create, providing the destination url and the list of events you want to receive. The response includes a signing secret — it is shown once, at creation. Store it securely; afterward only a non-sensitive secret_prefix is returned for identification. If you lose it, rotate the endpoint to get a new one.
Event types
Section titled “Event types”| Resource | Events |
|---|---|
| Contacts | contact.created, contact.updated, contact.deleted |
| Companies | company.created, company.updated, company.deleted |
| Projects | project.created, project.updated, project.deleted |
| Deals | deal.created, deal.updated, deal.deleted |
| Partnerships | partnership.created, partnership.updated, partnership.deleted |
| Activities | activity.created, activity.updated, activity.deleted |
| Sources | source.created, source.updated, source.archived |
Payload
Section titled “Payload”Each delivery is a POST with a JSON body:
{ "id": "8f3b2c1a-...", "event": "contact.created", "created_at": "2026-06-04T12:00:00Z", "account_id": "550e8400-...", "data": { "...": "the affected resource" }}| Field | Description |
|---|---|
id | Unique delivery ID (also sent as the X-Driftwood-Delivery-ID header). Use it to deduplicate. |
event | The event type that triggered the delivery. |
created_at | When the event occurred (ISO 8601 / RFC 3339). |
account_id | The account the event belongs to. |
data | The affected resource object. |
Headers
Section titled “Headers”| Header | Description |
|---|---|
X-Driftwood-Signature | HMAC-SHA256 signature of the payload (see below). |
X-Driftwood-Event | The event type. |
X-Driftwood-Delivery-ID | Unique delivery ID, matching the body id. |
X-Driftwood-Timestamp | Unix timestamp (seconds) used in the signature. |
Verifying signatures
Section titled “Verifying signatures”Always verify the signature before trusting a payload. The signature is computed as:
HMAC-SHA256( signing_secret, "{X-Driftwood-Timestamp}.{raw_request_body}" )…hex-encoded, and sent in X-Driftwood-Signature. Recompute it over the raw request body (do not re-serialize the parsed JSON) and compare using a constant-time comparison.
import hashlib, hmac
def verify(request, signing_secret: str) -> bool: timestamp = request.headers["X-Driftwood-Timestamp"] raw_body = request.get_data(as_text=True) # the exact bytes received signed = f"{timestamp}.{raw_body}".encode() expected = hmac.new(signing_secret.encode(), signed, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, request.headers.get("X-Driftwood-Signature", ""))import crypto from "node:crypto";
function verify(req, signingSecret) { const timestamp = req.headers["x-driftwood-timestamp"]; const rawBody = req.rawBody; // capture the raw body, not the parsed object const expected = crypto .createHmac("sha256", signingSecret) .update(`${timestamp}.${rawBody}`) .digest("hex"); const sig = req.headers["x-driftwood-signature"] || ""; return expected.length === sig.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig));}To defend against replay, you may also reject deliveries whose X-Driftwood-Timestamp is too old.
Delivery behavior
Section titled “Delivery behavior”- Driftwood
POSTs to yoururland expects a2xxresponse within 10 seconds. - Non-
2xxresponses and timeouts are treated as failures and retried. Respond2xxas soon as you’ve durably accepted the event, then process it asynchronously. - Make your handler idempotent — the same event may be delivered more than once. Deduplicate on the delivery
id. - Delivery attempts (status and response) are recorded and visible via the Webhooks API so you can inspect failures.
Security
Section titled “Security”- Serve your endpoint over HTTPS and always verify the signature.
- Keep the signing secret server-side; never expose it to a browser or commit it.
- Subscribe only to the events you need.