Instantly notify a rep in Slack when a high-intent lead books a demo using n8n

low complexityCost: $0-24/moRecommended

Prerequisites

Prerequisites
  • n8n instance (cloud or self-hosted)
  • HubSpot private app token with crm.objects.contacts.read and forms scopes
  • Slack app with Bot Token (chat:write scope)
  • n8n credentials configured for both HubSpot and Slack
  • A HubSpot form used for demo bookings (or a Calendly/Cal.com form synced to HubSpot)
  • A mapping of HubSpot owner IDs to Slack user IDs

Step 1: Add a HubSpot Trigger node

Create a new workflow and add a HubSpot Trigger node:

  • Authentication: Select your HubSpot credential
  • Event: Form Submission

This fires every time someone submits a form in HubSpot. The payload includes the form ID and the contact's email.

Filter to your demo form

HubSpot's form submission webhook fires for ALL forms. Add an IF node after the trigger to check $json.formId === 'YOUR_DEMO_FORM_ID' to only process demo bookings.

Step 2: Filter to the demo form

Add an IF node:

  • Condition: {{ $json.formId }} equals YOUR_DEMO_FORM_ID

Only the "true" branch continues. This prevents alerts for newsletter signups, content downloads, and other forms.

Step 3: Fetch the contact record

Add an HTTP Request node to get the full contact:

  • Method: GET
  • URL: https://api.hubapi.com/crm/v3/objects/contacts/{{ $json.objectId }}
  • Authentication: Predefined -> HubSpot API
  • Query params: properties=firstname,lastname,email,jobtitle,company,numberofemployees,industry,hubspot_owner_id,hs_analytics_source

Step 4: Look up the owner's Slack user ID

Add a Code node to map the HubSpot owner ID to a Slack user ID:

const contact = $('Fetch Contact').first().json;
const props = contact.properties;
 
// HubSpot owner ID → Slack user ID mapping
const ownerToSlack = {
  '12345678': 'U01AAAA',  // Alice
  '23456789': 'U02BBBB',  // Bob
  '34567890': 'U03CCCC',  // Carol
  '45678901': 'U04DDDD',  // Dave
};
 
const ownerId = props.hubspot_owner_id;
const slackUserId = ownerToSlack[ownerId];
 
if (!slackUserId) {
  // No owner or unmapped owner — send to a fallback channel
  return [{ json: { ...props, contactId: contact.id, slackTarget: '#demo-alerts', isFallback: true } }];
}
 
return [{ json: {
  contactId: contact.id,
  name: `${props.firstname || ''} ${props.lastname || ''}`.trim(),
  email: props.email,
  title: props.jobtitle,
  company: props.company,
  employees: props.numberofemployees,
  industry: props.industry,
  source: props.hs_analytics_source,
  slackTarget: slackUserId,
  isFallback: false,
}}];
Unassigned leads

If the lead doesn't have an owner yet (common if routing happens after the form submission), the Slack alert goes to a fallback channel. Chain this with a routing recipe to assign the owner first.

Step 5: Send a Slack DM with Block Kit

Add a Slack node:

  • Resource: Message
  • Operation: Send
  • Channel: {{ $json.slackTarget }}
  • Message Type: Block Kit
{
  "blocks": [
    {
      "type": "header",
      "text": { "type": "plain_text", "text": "🔥 Demo Booked!" }
    },
    {
      "type": "section",
      "fields": [
        { "type": "mrkdwn", "text": "*Name:*\n{{ $json.name }}" },
        { "type": "mrkdwn", "text": "*Title:*\n{{ $json.title || 'Not provided' }}" },
        { "type": "mrkdwn", "text": "*Company:*\n{{ $json.company || 'Unknown' }}" },
        { "type": "mrkdwn", "text": "*Size:*\n{{ $json.employees || 'Unknown' }} employees" },
        { "type": "mrkdwn", "text": "*Industry:*\n{{ $json.industry || 'Unknown' }}" },
        { "type": "mrkdwn", "text": "*Source:*\n{{ $json.source || 'Unknown' }}" }
      ]
    },
    {
      "type": "actions",
      "elements": [
        {
          "type": "button",
          "text": { "type": "plain_text", "text": "View Contact" },
          "url": "https://app.hubspot.com/contacts/YOUR_PORTAL_ID/contact/{{ $json.contactId }}",
          "style": "primary"
        }
      ]
    }
  ]
}

Step 6: Activate

  1. Submit your demo form with test data
  2. Verify the Slack DM arrives with the correct contact info
  3. Toggle the workflow to Active

Cost

  • n8n Cloud Starter: $24/mo for 2,500 executions. Each demo booking = 1 execution.
  • Self-hosted: Free. Unlimited executions.

Need help implementing this?

We build and optimize automation systems for mid-market businesses. Let's discuss the right approach for your team.