HubSpot webhooks are deceptively simple. POST a JSON event to your URL. In production, the failure modes are subtle: silent retries, ordered delivery assumptions, signature validation gaps. Here’s the production-grade pattern.
Always validate the signature
HubSpot signs every webhook with HMAC-SHA256 of the request body using your app secret. Validate before processing:
const crypto = require("crypto");
function validateSignature(body, signature, secret) {
const computed = crypto.createHmac("sha256", secret).update(body).digest("hex");
return crypto.timingSafeEqual(Buffer.from(computed), Buffer.from(signature));
}
Without this, anyone can POST fake events to your endpoint.
Acknowledge fast, process async
HubSpot expects a 2xx response within a few seconds. Spin up a queue, push the event, return 200 immediately. Process the event asynchronously. If you process inline and your handler takes 8 seconds, HubSpot retries; you process the same event twice.
POST /webhook -> validate sig -> SQS.send(event) -> 200
Worker -> SQS.receive -> process -> handle errors
Idempotency with eventId
Every event has an eventId. Store the last 24 hours of processed eventIds. On retry, skip ones you’ve already handled. Without this, your CRM ends up with duplicate writes from HubSpot’s retry logic.
Don’t trust event ordering
Webhooks deliver near-real-time but not strictly ordered. A contact.propertyChange for lifecyclestage may arrive after a later contact.deletion event for the same contact. Your handlers must be order-tolerant.
If order matters, fetch the current state from the HubSpot API after receiving the event rather than acting on the event payload.
Subscribe granularly
Subscribing to contact.propertyChange (any property) gives you a firehose. Subscribe to specific properties:
contact.propertyChange where property = "lifecyclestage"
contact.propertyChange where property = "hs_lead_status"
Cuts your incoming volume by 90%+ in most portals.
Handle the 5-minute batching
HubSpot batches change events. A bulk import of 5,000 contacts will deliver events over many minutes, not all at once. Your worker pool needs to handle the burst without falling behind.
Monitor delivery success in HubSpot
The integration settings page shows webhook delivery success rate. Below 95% sustained means HubSpot is throttling your endpoint and you’re missing events. Investigate before you discover the gap from a downstream report.
Replay endpoint for debugging
Build a /webhook/replay endpoint that takes a date range and re-fires events from your event store. When something looks wrong, you can re-process without waiting for HubSpot to retry.
Don’t subscribe from a workflow
Workflow webhooks are for outbound calls, not inbound. Use subscription webhooks (the dev docs version) for incoming events. Workflow webhooks lack signature signing and replay.
What to do this week
Audit your webhook handler. Confirm signature validation is on, response is async, and idempotency is enforced. If any of three is missing, fix it before your next bulk operation.