Back to Blog
·Hook Mesh Team

HubSpot Webhooks: CRM Integration Patterns

Learn how to build reliable HubSpot webhook integrations for your CRM workflows. Complete guide covering event subscriptions, signature verification, batch processing, and real-world use cases for lead sync and sales automation.

HubSpot Webhooks: CRM Integration Patterns

HubSpot Webhooks: CRM Integration Patterns

HubSpot webhooks enable real-time data sync between CRM and external systems for lead routing, contact warehousing, and marketing automation. This guide covers setup through advanced batch processing.

HubSpot webhook architecture showing event flow from CRM to your application endpoint

Understanding HubSpot Webhook Subscriptions

HubSpot uses a subscription-based model tied to your app. Two approaches exist: the Webhooks API (available on all tiers, including free) for developer apps, and Workflow webhooks (Operations Hub Professional+) for no-code automation.

Event Types

HubSpot webhook event types organized by CRM object: contacts, companies, and deals

Contact Events

  • contact.creation - New contact added to CRM
  • contact.deletion - Contact removed
  • contact.propertyChange - Any contact property updated
  • contact.privacyDeletion - GDPR deletion request

Company Events

  • company.creation - New company record
  • company.deletion - Company removed
  • company.propertyChange - Company property updated

Deal Events

  • deal.creation - New deal created
  • deal.deletion - Deal removed
  • deal.propertyChange - Deal property changed (stages, amounts, etc.)

Conversation Events

  • conversation.creation - New conversation started
  • conversation.newMessage - Message added to conversation

Additional Events

  • ticket.creation / ticket.deletion / ticket.propertyChange - Support tickets
  • *.merge - When records are merged
  • *.associationChange - Association updates between objects

Setting Up Webhook Subscriptions

HubSpot offers two paths: the Webhooks API for developer apps (public or private) and workflow-based webhooks for Operations Hub users.

Via the HubSpot Developer Portal

  1. Navigate to your app in the HubSpot Developer Portal
  2. Select "Webhooks" from the left sidebar
  3. Configure your target URL endpoint
  4. Select the subscription types you need
  5. Save and activate the subscriptions

Programmatic Setup via API

For more control, create subscriptions programmatically:

const axios = require('axios');

async function createWebhookSubscription(appId, accessToken) {
  const subscriptions = [
    {
      subscriptionType: 'contact.creation',
      propertyName: null,
      active: true
    },
    {
      subscriptionType: 'contact.propertyChange',
      propertyName: 'email',
      active: true
    },
    {
      subscriptionType: 'deal.propertyChange',
      propertyName: 'dealstage',
      active: true
    }
  ];

  for (const subscription of subscriptions) {
    await axios.post(
      `https://api.hubapi.com/webhooks/v3/${appId}/subscriptions`,
      subscription,
      {
        headers: {
          'Authorization': `Bearer ${accessToken}`,
          'Content-Type': 'application/json'
        }
      }
    );
  }
}

propertyChange events require specifying which property to monitor—this keeps volume manageable.

Via Workflow Actions (Operations Hub)

For no-code setups, use workflow webhooks:

  1. Navigate to Automations > Workflows
  2. Create or edit a workflow with your desired trigger
  3. Add action: Data ops > Send a webhook
  4. Configure method (GET/POST), URL, and authentication
  5. Map properties to send in the request body

Workflow webhooks support rate limiting configuration directly in the UI—useful for protecting downstream systems.

Authentication Options

HubSpot supports multiple authentication methods for webhook endpoints:

  • Request signature - HMAC-SHA256 signature in X-HubSpot-Signature-v3 header
  • API key - Passed as query parameter or header
  • Bearer token - OAuth 2.0 access tokens

For private apps, configure webhooks under Settings > Integrations > Private Apps > [App Name] > Webhooks tab.

Handling HubSpot Webhook Events

HubSpot sends batched arrays, even for single events. Your endpoint must handle batch format. See receiving and verifying webhooks in Node.js.

Basic Event Handler

const express = require('express');
const crypto = require('crypto');

const app = express();
app.use(express.json());

const CLIENT_SECRET = process.env.HUBSPOT_CLIENT_SECRET;

app.post('/webhooks/hubspot', async (req, res) => {
  // HubSpot expects a response within 5 seconds
  res.status(200).send('OK');

  // Process events asynchronously
  processHubSpotEvents(req.body).catch(err => {
    console.error('Event processing failed:', err);
  });
});

async function processHubSpotEvents(events) {
  for (const event of events) {
    switch (event.subscriptionType) {
      case 'contact.creation':
        await handleNewContact(event);
        break;
      case 'contact.propertyChange':
        await handleContactUpdate(event);
        break;
      case 'deal.propertyChange':
        await handleDealChange(event);
        break;
      default:
        console.log('Unhandled event type:', event.subscriptionType);
    }
  }
}

Signature Verification

Always verify HubSpot signatures in production. This is critical webhook security best practice.

function verifyHubSpotSignature(req) {
  const signature = req.headers['x-hubspot-signature-v3'];
  const timestamp = req.headers['x-hubspot-request-timestamp'];

  // Reject requests older than 5 minutes
  const currentTime = Date.now();
  if (currentTime - parseInt(timestamp) > 300000) {
    throw new Error('Request timestamp too old');
  }

  // Build the signature base string
  const requestBody = JSON.stringify(req.body);
  const uri = `https://${req.headers.host}${req.originalUrl}`;
  const signatureBaseString = `${req.method}${uri}${requestBody}${timestamp}`;

  // Calculate expected signature
  const expectedSignature = crypto
    .createHmac('sha256', CLIENT_SECRET)
    .update(signatureBaseString)
    .digest('base64');

  if (signature !== expectedSignature) {
    throw new Error('Invalid signature');
  }

  return true;
}

// Middleware for signature verification
function hubspotSignatureMiddleware(req, res, next) {
  try {
    verifyHubSpotSignature(req);
    next();
  } catch (error) {
    console.error('Signature verification failed:', error.message);
    res.status(401).send('Unauthorized');
  }
}

app.post('/webhooks/hubspot', hubspotSignatureMiddleware, async (req, res) => {
  // Handler code here
});

Real-World Use Cases

Lead Sync to External Systems

Auto-sync new contacts to billing systems, support desks, or custom apps:

async function handleNewContact(event) {
  const contactId = event.objectId;

  // Fetch full contact details from HubSpot API
  const contact = await hubspotClient.crm.contacts.basicApi.getById(
    contactId,
    ['email', 'firstname', 'lastname', 'company', 'phone']
  );

  // Sync to your external system
  await externalCRM.createLead({
    email: contact.properties.email,
    name: `${contact.properties.firstname} ${contact.properties.lastname}`,
    company: contact.properties.company,
    source: 'hubspot',
    hubspotId: contactId
  });
}

Deal Stage Change Notifications

Trigger actions when deals move through pipeline:

async function handleDealChange(event) {
  if (event.propertyName !== 'dealstage') return;

  const dealId = event.objectId;
  const newStage = event.propertyValue;

  // Fetch deal details
  const deal = await hubspotClient.crm.deals.basicApi.getById(
    dealId,
    ['dealname', 'amount', 'hubspot_owner_id'],
    undefined,
    ['contacts']
  );

  // Stage-specific actions
  switch (newStage) {
    case 'qualifiedtobuy':
      await notifySalesTeam(deal);
      break;
    case 'closedwon':
      await triggerOnboarding(deal);
      await updateRevenueMetrics(deal);
      break;
    case 'closedlost':
      await logLostDealAnalytics(deal);
      break;
  }
}

Marketing Automation Triggers

Trigger marketing workflows on contact property changes:

async function handleContactUpdate(event) {
  const contactId = event.objectId;
  const propertyName = event.propertyName;
  const newValue = event.propertyValue;

  // Trigger campaigns based on lifecycle stage changes
  if (propertyName === 'lifecyclestage' && newValue === 'customer') {
    await marketingPlatform.enrollInCampaign(contactId, 'customer-onboarding');
  }

  // Update email preferences in external ESP
  if (propertyName === 'email') {
    await emailServiceProvider.updateSubscriber({
      oldEmail: event.propertyValueBefore,
      newEmail: newValue,
      hubspotId: contactId
    });
  }
}

Batch Processing Best Practices

HubSpot webhook batch processing flow showing parallel event handling

HubSpot batches events for efficiency. A single request may contain up to 100 events, especially during bulk imports or workflows. Bulk imports of 1,000 contacts can trigger 1,000+ webhook notifications simultaneously.

async function processHubSpotEvents(events) {
  // Group events by type for batch processing
  const eventsByType = events.reduce((acc, event) => {
    const type = event.subscriptionType;
    if (!acc[type]) acc[type] = [];
    acc[type].push(event);
    return acc;
  }, {});

  // Process each type in parallel
  const processors = Object.entries(eventsByType).map(([type, typeEvents]) => {
    return processBatchByType(type, typeEvents);
  });

  await Promise.all(processors);
}

async function processBatchByType(type, events) {
  // Fetch all records in one batch API call
  const records = await hubspotClient.crm.contacts.batchApi.read({
    inputs: events.map(e => ({ id: e.objectId })),
    properties: ['email', 'firstname', 'lastname']
  });

  for (const record of records.results) {
    await processRecord(type, record);
  }
}

Deduplication

HubSpot may send duplicates. Use idempotent handlers with event IDs:

const processedEvents = new Set(); // Use Redis in production

async function processEventWithDeduplication(event) {
  const eventKey = `${event.eventId}-${event.subscriptionType}`;

  if (processedEvents.has(eventKey)) {
    console.log('Skipping duplicate event:', eventKey);
    return;
  }

  processedEvents.add(eventKey);
  await processEvent(event);
}

Rate Limits and Retry Behavior

HubSpot's Limits

PlanRequests per 10 secondsDaily requests
Professional100650,000
Enterprise1001,000,000

HubSpot sets a concurrency limit of 10 simultaneous requests per account. Each batch can contain up to 100 events. Maximum subscriptions: 1,000 per application.

Retry Behavior

HubSpot retries failed webhooks up to 10 times over 24 hours:

  • First retry: 1 minute after failure
  • Subsequent retries: Increasing intervals, max 8 hours between attempts
  • After exhausting retries, events are dropped

Monitor your endpoint health—HubSpot disables subscriptions that consistently fail.

Testing Webhooks

Before production deployment, test your endpoint:

  1. webhook.site - Capture and inspect payloads without code
  2. HubSpot Developer Portal - Use the "Test" button on subscription settings
  3. ngrok - Expose local development servers for end-to-end testing

For private apps, navigate to the Webhooks tab and click "Test" to send a sample payload to your configured URL.

Production Best Practices

Respond immediately: HubSpot expects 200 within 5 seconds.

Process asynchronously: Queue events (Redis, SQS, RabbitMQ) for background processing.

Handle bulk import floods: Implement queuing to absorb spikes from contact imports.

Monitor health: Track success rates, processing times, error counts. Set alerts for failure thresholds.

Implement retries: Build your own retry logic for downstream API failures with exponential backoff.

Simplifying CRM Webhooks with Hook Mesh

Hook Mesh provides managed CRM webhook delivery with automatic signature verification, intelligent retry policies, and event queuing for batched delivery. Let your team focus on business logic instead of infrastructure.

Conclusion

HubSpot webhooks enable real-time CRM workflows. Success requires understanding subscriptions, handling batched events, and verifying signatures.

Start with core event types, implement async processing from day one, and consider managed infrastructure as complexity grows.

See SendGrid email webhooks for delivery tracking or Slack webhooks for team notifications. Browse our Platform Integration Hub.

Related Posts