Skip to content

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.

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.

ResourceEvents
Contactscontact.created, contact.updated, contact.deleted
Companiescompany.created, company.updated, company.deleted
Projectsproject.created, project.updated, project.deleted
Dealsdeal.created, deal.updated, deal.deleted
Partnershipspartnership.created, partnership.updated, partnership.deleted
Activitiesactivity.created, activity.updated, activity.deleted
Sourcessource.created, source.updated, source.archived

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" }
}
FieldDescription
idUnique delivery ID (also sent as the X-Driftwood-Delivery-ID header). Use it to deduplicate.
eventThe event type that triggered the delivery.
created_atWhen the event occurred (ISO 8601 / RFC 3339).
account_idThe account the event belongs to.
dataThe affected resource object.
HeaderDescription
X-Driftwood-SignatureHMAC-SHA256 signature of the payload (see below).
X-Driftwood-EventThe event type.
X-Driftwood-Delivery-IDUnique delivery ID, matching the body id.
X-Driftwood-TimestampUnix timestamp (seconds) used in the signature.

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.

  • Driftwood POSTs to your url and expects a 2xx response within 10 seconds.
  • Non-2xx responses and timeouts are treated as failures and retried. Respond 2xx as 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.
  • 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.