Integration Guide

How to Send Instagram Leads to HubSpot CRM with InstantDM

Learn how to automatically send Instagram DM leads to HubSpot CRM using InstantDM. Three methods: direct API integration, Make.com (no code), and Zapier (no code). Complete guide with field mapping, deal creation, and workflow automation.

Meta Business Partner
30,000+ creators
$9.99/mo flat

This guide shows you how to automatically create HubSpot contacts and deals from Instagram DM conversations. When someone completes a lead capture flow in InstantDM - providing their name, email, phone, or other details - that data flows directly into HubSpot as a new contact with all properties mapped.

You have three options, from no-code to full custom:

  • Option A: Direct API integration (your own server)
  • Option B: Via Make.com (no code)
  • Option C: Via Zapier (no code)

Why Connect InstantDM to HubSpot?

Instagram is one of the highest-converting lead sources for many businesses, but manually copying lead data from DMs into your CRM is slow and error-prone. By connecting InstantDM to HubSpot, you:

  • Capture leads automatically - every completed DM flow creates a HubSpot contact instantly
  • Eliminate manual data entry - name, email, phone, and custom fields sync automatically
  • Speed up follow-up - your sales team sees new leads in HubSpot within seconds
  • Track lead source - know exactly which Instagram post and flow generated each lead
  • Trigger HubSpot workflows - automatically enroll new Instagram leads in email sequences, assign to reps, or create deals

What Happens End-to-End

  1. Someone comments a keyword on your Instagram post (e.g., "pricing")
  2. InstantDM sends them a DM flow that collects their name, email, and interest
  3. When the flow completes, InstantDM fires a flow_completed webhook
  4. The webhook data is routed to HubSpot, creating a new contact
  5. Optionally, a deal is created in your sales pipeline and your team is notified

What You'll Need

Requirement Details
InstantDM account Trendsetter, Trendsetter Pro, or any Multi plan
InstantDM API key Found at Settings → API (or /api-integration page)
HubSpot account Free CRM works for contacts; Sales Hub for pipeline automation
Make.com account (Option B) Free plan works for testing
Zapier account (Option C) Free plan works for simple Zaps
Server (Option A) Node.js or Python server for direct API integration

For full API documentation, visit instantdm.com/instagram-api-docs.


Option A: Direct Integration (Webhook → Your Server → HubSpot API)

This option gives you the most control. Your server receives the InstantDM webhook, processes the data, and calls the HubSpot API directly.

Step 1: Set Up the Webhook Server

const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());

const INSTANTDM_API_KEY = process.env.INSTANTDM_API_KEY;
const HUBSPOT_ACCESS_TOKEN = process.env.HUBSPOT_ACCESS_TOKEN;

function verifySignature(req) {
  const signature = req.headers['x-webhook-signature'];
  if (!signature) return false;
  const expected = crypto
    .createHmac('sha256', INSTANTDM_API_KEY)
    .update(JSON.stringify(req.body))
    .digest('hex');
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

app.post('/webhook/instantdm', async (req, res) => {
  res.status(200).json({ received: true });

  if (!verifySignature(req)) {
    console.error('Invalid webhook signature');
    return;
  }

  const { event, data } = req.body;

  if (event === 'flow_completed') {
    await createHubSpotContact(data);
  }
});

app.listen(3000, () => console.log('Server running on port 3000'));

Step 2: Create HubSpot Contacts via API

async function createHubSpotContact(data) {
  const { username, response_variables, flow_name } = data;

  const contactData = {
    properties: {
      email: response_variables.email,
      firstname: response_variables.full_name?.split(' ')[0] || '',
      lastname: response_variables.full_name?.split(' ').slice(1).join(' ') || '',
      phone: response_variables.phone || '',
      hs_lead_status: 'NEW',
      leadsource: 'Instagram DM',
      notes_last_contacted: `Completed "${flow_name}" flow on Instagram. Username: @${username}`
    }
  };

  try {
    const response = await fetch('https://api.hubapi.com/crm/v3/objects/contacts', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${HUBSPOT_ACCESS_TOKEN}`
      },
      body: JSON.stringify(contactData)
    });

    if (response.status === 409) {
      // Contact already exists  -  update instead
      const existing = await response.json();
      const contactId = existing.message.match(/ID: (\d+)/)?.[1];

      if (contactId) {
        await fetch(`https://api.hubapi.com/crm/v3/objects/contacts/${contactId}`, {
          method: 'PATCH',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${HUBSPOT_ACCESS_TOKEN}`
          },
          body: JSON.stringify(contactData)
        });
        console.log(`Updated existing HubSpot contact: ${contactId}`);
        return contactId;
      }
    } else if (response.ok) {
      const result = await response.json();
      console.log(`Created HubSpot contact: ${result.id}`);
      return result.id;
    }
  } catch (error) {
    console.error('HubSpot API error:', error);
  }
}

Step 3: Create Deals Automatically

async function createHubSpotDeal(contactId, data) {
  const dealData = {
    properties: {
      dealname: `Instagram Lead - @${data.username}`,
      pipeline: 'default',
      dealstage: 'appointmentscheduled',
      amount: data.response_variables.budget || '',
      description: `Lead from Instagram DM flow: "${data.flow_name}". Interest: ${data.response_variables.interest || 'Not specified'}`
    },
    associations: [
      {
        to: { id: contactId },
        types: [{ associationCategory: 'HUBSPOT_DEFINED', associationTypeId: 3 }]
      }
    ]
  };

  const response = await fetch('https://api.hubapi.com/crm/v3/objects/deals', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${HUBSPOT_ACCESS_TOKEN}`
    },
    body: JSON.stringify(dealData)
  });

  if (response.ok) {
    const result = await response.json();
    console.log(`Created HubSpot deal: ${result.id}`);
  }
}

Python Version (Direct API)

import os
import requests
import hmac
import hashlib
from flask import Flask, request, jsonify
import threading

app = Flask(__name__)

INSTANTDM_API_KEY = os.environ.get('INSTANTDM_API_KEY')
HUBSPOT_ACCESS_TOKEN = os.environ.get('HUBSPOT_ACCESS_TOKEN')

def verify_signature(req):
    signature = req.headers.get('X-Webhook-Signature')
    if not signature:
        return False
    expected = hmac.new(
        INSTANTDM_API_KEY.encode(),
        req.get_data(),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

@app.route('/webhook/instantdm', methods=['POST'])
def webhook():
    if not verify_signature(request):
        return jsonify({'error': 'Invalid signature'}), 401

    payload = request.get_json()
    event = payload.get('event')
    data = payload.get('data')

    if event == 'flow_completed':
        thread = threading.Thread(target=create_hubspot_contact, args=(data,))
        thread.start()

    return jsonify({'received': True}), 200

def create_hubspot_contact(data):
    response_vars = data.get('response_variables', {})
    full_name = response_vars.get('full_name', '')
    name_parts = full_name.split(' ', 1)

    contact_data = {
        'properties': {
            'email': response_vars.get('email', ''),
            'firstname': name_parts[0] if name_parts else '',
            'lastname': name_parts[1] if len(name_parts) > 1 else '',
            'phone': response_vars.get('phone', ''),
            'hs_lead_status': 'NEW',
            'leadsource': 'Instagram DM',
            'notes_last_contacted': f'Completed "{data.get("flow_name")}" flow. Username: @{data.get("username")}'
        }
    }

    response = requests.post(
        'https://api.hubapi.com/crm/v3/objects/contacts',
        headers={
            'Content-Type': 'application/json',
            'Authorization': f'Bearer {HUBSPOT_ACCESS_TOKEN}'
        },
        json=contact_data
    )

    if response.status_code == 409:
        # Contact exists  -  update
        import re
        match = re.search(r'ID: (\d+)', response.json().get('message', ''))
        if match:
            contact_id = match.group(1)
            requests.patch(
                f'https://api.hubapi.com/crm/v3/objects/contacts/{contact_id}',
                headers={
                    'Content-Type': 'application/json',
                    'Authorization': f'Bearer {HUBSPOT_ACCESS_TOKEN}'
                },
                json=contact_data
            )
    elif response.ok:
        result = response.json()
        print(f'Created HubSpot contact: {result["id"]}')

if __name__ == '__main__':
    app.run(port=3000)

Option B: Via Make.com (No Code)

This is the recommended approach if you don't want to write code. Make.com handles the webhook reception and HubSpot API calls with a visual interface.

Step 1: Create a Make.com Webhook

  1. Log in to make.com and click Create a new scenario.
  2. Add a Webhooks → Custom webhook module.
  3. Click Add, name it InstantDM Leads, and click Save.
  4. Copy the generated webhook URL.

Step 2: Configure InstantDM Webhook

  1. In InstantDM, go to Settings → API.
  2. Paste the Make.com webhook URL into the Webhook URL field.
  3. Enable the flow_completed event.
  4. Click Save.

Step 3: Test the Connection

  1. In Make.com, click Run once on the webhook module.
  2. Trigger a test by completing a lead capture flow on your Instagram account (or use the Send Test Webhook button).
  3. Make.com receives the payload and maps the data structure.

Step 4: Add a Filter

  1. Click the connection line after the webhook module.
  2. Add a Filter: event equals flow_completed.

Step 5: Add HubSpot - Create/Update Contact

  1. Add a new module: HubSpot → Create/Update a Contact.
  2. Connect your HubSpot account via OAuth.
  3. Map the fields:
HubSpot Property Make.com Field
Email data.response_variables.email
First Name data.response_variables.full_name
Phone data.response_variables.phone
Lead Source Instagram DM (static text)
Notes Completed "{{data.flow_name}}" flow. Interest: {{data.response_variables.interest}}

Step 6: (Optional) Add HubSpot Deal

  1. Add another module: HubSpot → Create a Deal.
  2. Map:
Deal Property Value
Deal Name Instagram Lead - @{{data.username}}
Pipeline Select your sales pipeline
Deal Stage Your first stage (e.g., New Lead)
Associated Contact Contact ID from the previous step

Step 7: Activate

  1. Test the full scenario end-to-end.
  2. Toggle the scenario to ON and set scheduling to Immediately.

Option C: Via Zapier (No Code)

Step 1: Create a Zapier Webhook Trigger

  1. Log in to zapier.com and click Create Zap.
  2. Trigger: Webhooks by Zapier → Catch Hook.
  3. Copy the generated webhook URL.

Step 2: Configure InstantDM

  1. In InstantDM, go to Settings → API.
  2. Paste the Zapier webhook URL and enable flow_completed.
  3. Click Save.

Step 3: Test the Trigger

  1. Click Test trigger in Zapier.
  2. Trigger a test event from InstantDM.
  3. Zapier detects the payload.

Step 4: Add Filter

  1. Add Filter by Zapier: Only continue if event exactly matches flow_completed.

Step 5: Add HubSpot Action

  1. Add action: HubSpot → Create Contact.
  2. Connect your HubSpot account.
  3. Map the fields:
HubSpot Field Zapier Mapping
Email data__response_variables__email
First Name data__response_variables__full_name
Phone data__response_variables__phone
Lead Source Instagram DM (static text)

Step 6: Publish

  1. Test the Zap end-to-end.
  2. Click Publish to activate.

Tip: Zapier uses double-underscore notation for nested fields (e.g., data__response_variables__email).


Mapping InstantDM response_variables to HubSpot Contact Properties

The response_variables object in the flow_completed webhook contains all data collected during the DM flow. The field names depend on how you configured your question nodes in InstantDM's Flow Editor.

Common Field Mappings

InstantDM Variable HubSpot Property HubSpot Internal Name
full_name First Name / Last Name firstname / lastname
email Email email
phone Phone Number phone
company Company Name company
budget Deal Amount amount (on Deal object)
interest Notes notes_last_contacted
city City city
website Website URL website

Example Webhook Payload

{
  "event": "flow_completed",
  "timestamp": "2025-01-15T10:30:00Z",
  "data": {
    "instagram_user_id": "17841400123456789",
    "username": "johndoe",
    "flow_name": "Lead Capture Flow",
    "response_variables": {
      "full_name": "John Doe",
      "email": "john@example.com",
      "phone": "+1234567890",
      "interest": "Premium Plan",
      "company": "Acme Corp",
      "budget": "5000"
    }
  }
}

Creating Custom HubSpot Properties

If you collect data that doesn't map to a default HubSpot property (e.g., instagram_username, preferred_service):

  1. Go to HubSpot → Settings → Properties.
  2. Click Create property.
  3. Name it (e.g., instagram_username).
  4. Use the internal name in your API mapping or Make.com/Zapier field mapping.

Creating HubSpot Deals from Flow Completions

Deals let you track Instagram leads through your sales pipeline. Here's the recommended setup:

Deal Pipeline Structure

Stage Description
New Lead Contact just created from Instagram flow
Contacted Sales rep has reached out
Qualified Lead confirmed as a good fit
Proposal Sent Pricing or proposal delivered
Closed Won / Lost Final outcome

Automatic Deal Creation (API)

{
  "properties": {
    "dealname": "Instagram Lead - @johndoe",
    "pipeline": "default",
    "dealstage": "appointmentscheduled",
    "amount": "5000",
    "description": "Lead from Instagram DM flow: 'Lead Capture Flow'. Interest: Premium Plan"
  },
  "associations": [
    {
      "to": { "id": "CONTACT_ID" },
      "types": [{ "associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 3 }]
    }
  ]
}

Adding Contacts to HubSpot Workflows

Once contacts are in HubSpot, you can enroll them in automated workflows:

Example Workflow: Instagram Lead Nurture

  1. Enrollment trigger: Contact property leadsource equals Instagram DM
  2. Step 1: Send welcome email immediately
  3. Step 2: Wait 2 days
  4. Step 3: Send case study email
  5. Step 4: Wait 3 days
  6. Step 5: Send pricing email with CTA to book a call
  7. Step 6: If no reply after 7 days, assign to sales rep for manual follow-up

Setting Up the Workflow

  1. Go to HubSpot → Automation → Workflows.
  2. Click Create workflow → Contact-based.
  3. Set enrollment trigger: Contact property leadsource is Instagram DM.
  4. Add your email steps with delays.
  5. Activate the workflow.

Now every Instagram lead that enters HubSpot via InstantDM is automatically enrolled in your nurture sequence.


Example: Full Lead Capture Pipeline

Here's the complete end-to-end flow:

Instagram comment → DM flow → HubSpot contact → Email sequence

  1. Instagram Post: You publish a post about your service with CTA "Comment PRICING for details"
  2. InstantDM Trigger: Detects the keyword "PRICING" and starts the lead capture flow
  3. DM Flow: Asks for name, email, phone, and interest via conversational DM messages
  4. Webhook: InstantDM fires flow_completed with all response_variables
  5. HubSpot Contact: Created automatically with all fields mapped
  6. HubSpot Deal: Created in your sales pipeline, associated with the contact
  7. HubSpot Workflow: Enrolls the contact in your Instagram Lead Nurture email sequence
  8. Slack Notification: Your sales team gets an alert in #new-leads
  9. Confirmation DM: InstantDM sends a thank-you message back to the user

Troubleshooting

Issue Solution
Contact not appearing in HubSpot Check that the email field is mapped correctly - HubSpot requires a valid email to create a contact.
Duplicate contacts being created Use HubSpot's "Create or Update" operation (Make.com/Zapier) or handle the 409 conflict response (direct API).
Missing fields in HubSpot Verify the field names in response_variables match your flow's question node variable names exactly.
Make.com scenario not triggering Ensure the scenario is set to ON and scheduling is Immediately. Check that the webhook URL is saved in InstantDM.
HubSpot API returning 401 Regenerate your HubSpot access token or reconnect the OAuth in Make.com/Zapier.
Deal not associated with contact Ensure you're passing the correct contactId from the Create Contact step to the Create Deal step.

Frequently Asked Questions

Do I need HubSpot Sales Hub to connect InstantDM?

No. The free HubSpot CRM supports contact creation via API, Make.com, and Zapier. You only need Sales Hub if you want pipeline automation, sequences, or advanced deal management features.

Which method should I choose - direct API, Make.com, or Zapier?

Choose Make.com or Zapier if you want a no-code solution that's quick to set up. Choose direct API if you need custom logic (like conditional deal creation, data transformation, or integration with other internal systems). Make.com is generally more cost-effective than Zapier for high-volume use cases.

Can I update existing HubSpot contacts instead of creating duplicates?

Yes. All three methods support upsert behavior. HubSpot uses the email address as the unique identifier. Make.com's "Create/Update Contact" module and Zapier's "Create or Update Contact" action handle this automatically. For direct API, handle the 409 conflict response to update existing contacts.

How do I track which Instagram post generated the lead?

Include the post URL or post ID in your flow's response variables. In InstantDM's Flow Editor, you can pass metadata through the flow. Map this to a custom HubSpot property like instagram_source_post.

What if the user doesn't complete the DM flow?

The flow_completed webhook only fires when the user reaches the end of the flow. Partial completions don't trigger it. If you want to capture partial data, use the question_answered event instead and update the HubSpot contact incrementally as each question is answered.

Can I assign Instagram leads to specific sales reps automatically?

Yes. In HubSpot, set up a workflow that assigns contacts based on properties (e.g., by interest, location, or round-robin). Alternatively, set the hubspot_owner_id property in the API call or Make.com/Zapier mapping to assign directly during contact creation.

Can I send a confirmation DM after the HubSpot contact is created?

Yes. In Make.com, add an HTTP module after the HubSpot step that calls the InstantDM API (POST https://api.instantdm.com/api-webhook) to send a confirmation DM. In Zapier, add a "Webhooks by Zapier → POST" action. In the direct API approach, call your sendDM() function after the HubSpot contact is created.


What's Next

  • Build a lead capture flow in InstantDM's Flow Editor with question nodes for name, email, and phone.
  • Set up the integration using whichever method (A, B, or C) fits your needs.
  • Create a HubSpot workflow to automatically nurture Instagram leads with email sequences.
  • Read the Make.com integration guide for more Make.com scenario examples.
  • Read the Zapier integration guide for more Zap examples.
  • Get Slack notifications when new leads arrive.
  • Explore the full API docs at instantdm.com/instagram-api-docs.

Ready to Automate Your Instagram DMs?

Join 30,000+ creators and brands using InstantDM today.

Start Your Free Trial

No credit card required. Setup in under 15 minutes.