How to Build a Facebook Lead to AI Voice Agent Funnel with n8n (Free Template)

How to Build a Facebook Lead to AI Voice Agent Funnel with n8n (Free Template)

Real estate businesses lose 78% of leads within the first hour of capture. Manual follow-up doesn't scale. This n8n workflow automates the entire funnel—from Facebook lead capture through AI voice qualification to calendar booking—eliminating response delays and qualifying leads while you sleep. You'll learn how to build a production-grade automation that connects Facebook Lead Ads, Lofty CRM, ElevenLabs AI voice agent, and Calendly into one intelligent system.

The Problem: Manual Lead Follow-Up Kills Conversion

Real estate teams capture leads from Facebook but struggle with immediate response. By the time someone manually calls a lead, competitors have already made contact.

Current challenges:

  • Facebook leads sit in spreadsheets for hours before first contact
  • Manual data entry between Facebook, CRM, and calendar systems creates errors
  • Sales teams can't qualify hundreds of leads fast enough
  • No systematic way to route qualified leads to appointments
  • Lead status updates happen inconsistently across platforms

Business impact:

  • Time spent: 15-20 hours/week on manual lead processing
  • Response time: 2-4 hours average (should be under 5 minutes)
  • Conversion loss: 35-50% of leads never receive follow-up
  • Cost per acquisition increases due to wasted ad spend on uncontacted leads

The Solution Overview

This n8n workflow creates an autonomous lead qualification funnel. When a Facebook lead submits their information, n8n captures it instantly, creates a CRM contact, triggers an AI voice agent to call and pre-qualify the lead, then books qualified prospects directly into your calendar. The entire process runs without human intervention, responding to leads in under 60 seconds while maintaining conversation quality through ElevenLabs' conversational AI.

The system uses n8n as the orchestration layer connecting Facebook's webhook API, Lofty CRM's REST API, ElevenLabs' voice AI platform, and Calendly's scheduling API. Decision logic built into n8n Function nodes routes leads based on qualification criteria, updates CRM tags, and handles error scenarios with automatic retries.

What You'll Build

This automation delivers a complete lead-to-appointment pipeline with intelligent routing and error handling.

Component Technology Purpose
Lead Capture Facebook Lead Ads Webhook Real-time lead data ingestion
CRM Integration Lofty REST API Contact creation and status management
AI Qualification ElevenLabs Voice Agent Automated outbound calls and pre-qualification
Appointment Booking Calendly API Schedule meetings for qualified leads
Logic Engine n8n Function Nodes Decision routing and data transformation
Error Handling n8n Error Workflow Retry logic and failure notifications
Status Updates Lofty Webhook Real-time CRM tag and pipeline updates

Key capabilities:

  • Instant lead capture from Facebook (sub-5 second response time)
  • Automatic CRM contact creation with deduplication
  • AI-powered voice calls with natural conversation flow
  • Dynamic qualification based on configurable business rules
  • Automatic calendar booking for qualified leads
  • CRM status synchronization across all touchpoints
  • Error recovery with exponential backoff retry logic
  • Comprehensive logging for compliance and optimization

Prerequisites

Before starting, ensure you have:

  • n8n instance (cloud or self-hosted on Hostinger)
  • Facebook Business Manager account with Lead Ads configured
  • Lofty CRM account with API access enabled
  • ElevenLabs account with conversational AI agent configured
  • Calendly account with API key
  • Basic JavaScript knowledge for Function nodes
  • Webhook endpoint capability (n8n provides this automatically)
  • SSL certificate if self-hosting (required for Facebook webhooks)

Step 1: Configure Facebook Lead Ads Webhook

Facebook sends lead data via webhook when someone submits a form. This section establishes the connection between Facebook and n8n.

Set up the webhook receiver

  1. In n8n, create a new workflow and add a Webhook node as the trigger
  2. Set HTTP Method to POST
  3. Set Path to /facebook-leads (or your preferred endpoint)
  4. Enable "Respond Immediately" to acknowledge Facebook's webhook within 20 seconds
  5. Copy the webhook URL (format: https://your-n8n-instance.com/webhook/facebook-leads)

Configure Facebook webhook subscription

  1. Go to Facebook Business Manager → Lead Ads Forms
  2. Navigate to Settings → Webhooks
  3. Add webhook URL from n8n
  4. Subscribe to leadgen events
  5. Verify the webhook using Facebook's challenge parameter

Node configuration:

{
  "httpMethod": "POST",
  "path": "facebook-leads",
  "responseMode": "onReceived",
  "options": {
    "responseData": "firstEntryJson"
  }
}

Why this works:
Facebook requires webhook acknowledgment within 20 seconds or it marks the endpoint as failed. The "Respond Immediately" setting sends a 200 OK before processing, preventing timeout issues. This architecture separates acknowledgment from processing, allowing complex workflows without Facebook webhook failures.

Step 2: Create or Update CRM Contact

Lead data flows from Facebook into Lofty CRM. This section handles contact creation, deduplication, and initial tagging.

Extract lead data from Facebook payload

  1. Add a Function node after the webhook
  2. Parse Facebook's nested JSON structure to extract: full_name, email, phone_number, ad_name, form_id
  3. Standardize phone number format (remove spaces, add country code)
  4. Create a unique identifier using email + phone combination

Function node code:

const leadData = $input.item.json.entry[0].changes[0].value;
const fieldData = leadData.field_data;

// Extract fields
const fields = {};
fieldData.forEach(field => {
  fields[field.name] = field.values[0];
});

// Standardize phone
let phone = fields.phone_number.replace(/\s+/g, '');
if (!phone.startsWith('+1')) phone = '+1' + phone;

return {
  json: {
    full_name: fields.full_name,
    email: fields.email,
    phone: phone,
    lead_source: 'Facebook Lead Ads',
    ad_name: leadData.ad_name,
    form_id: leadData.form_id,
    created_time: leadData.created_time,
    unique_id: `${fields.email}_${phone}`
  }
};

Configure Lofty CRM integration

  1. Add HTTP Request node for Lofty API
  2. Set method to POST
  3. Endpoint: https://api.lofty.com/v1/contacts
  4. Add authentication header with your Lofty API key
  5. Map lead data to Lofty's contact schema
  6. Enable "Check for Duplicates" using email as the key

HTTP Request node settings:

{
  "method": "POST",
  "url": "https://api.lofty.com/v1/contacts",
  "authentication": "headerAuth",
  "headerAuth": {
    "name": "Authorization",
    "value": "Bearer {{$credentials.loftyApiKey}}"
  },
  "bodyParameters": {
    "email": "={{$json.email}}",
    "phone": "={{$json.phone}}",
    "name": "={{$json.full_name}}",
    "source": "={{$json.lead_source}}",
    "tags": ["Facebook Lead", "Unqualified"],
    "custom_fields": {
      "ad_name": "={{$json.ad_name}}",
      "form_id": "={{$json.form_id}}"
    }
  },
  "options": {
    "response": {
      "response": {
        "neverError": true
      }
    }
  }
}

Why this approach:
Lofty's API returns different status codes for new contacts (201) versus duplicates (200). Setting "neverError" prevents workflow failure on duplicates, allowing you to handle both scenarios in subsequent nodes. The unique_id field enables deduplication logic before the API call, reducing unnecessary requests.

Step 3: Trigger AI Voice Agent Call

ElevenLabs' conversational AI calls the lead and conducts pre-qualification. This section initiates the call and passes context from the CRM.

Prepare call context

  1. Add a Function node to structure data for ElevenLabs
  2. Include lead name, property interests (from Facebook form), and qualification questions
  3. Set call priority based on lead source (Facebook leads = high priority)
  4. Define conversation flow and qualification criteria

Context preparation code:

const contact = $input.item.json;

return {
  json: {
    phone_number: contact.phone,
    agent_id: "{{$credentials.elevenlabsAgentId}}",
    context: {
      lead_name: contact.name,
      property_interest: contact.custom_fields.ad_name,
      qualification_questions: [
        "Are you currently working with a real estate agent?",
        "What is your timeline for purchasing?",
        "Have you been pre-approved for a mortgage?",
        "What is your budget range?"
      ],
      crm_contact_id: contact.id
    },
    callback_webhook: "{{$workflow.webhook_url}}/elevenlabs-callback",
    max_duration: 300
  }
};

Configure ElevenLabs API call

  1. Add HTTP Request node for ElevenLabs
  2. Method: POST
  3. Endpoint: https://api.elevenlabs.io/v1/convai/conversation
  4. Include API key in Authorization header
  5. Set timeout to 10 seconds (call initiation, not duration)
  6. Enable retry logic for API failures

ElevenLabs node configuration:

{
  "method": "POST",
  "url": "https://api.elevenlabs.io/v1/convai/conversation",
  "authentication": "headerAuth",
  "headerAuth": {
    "name": "xi-api-key",
    "value": "={{$credentials.elevenlabsApiKey}}"
  },
  "body": {
    "phone_number": "={{$json.phone_number}}",
    "agent_id": "={{$json.agent_id}}",
    "context_data": "={{$json.context}}",
    "webhook_url": "={{$json.callback_webhook}}"
  },
  "options": {
    "timeout": 10000,
    "retry": {
      "maxRetries": 3,
      "retryInterval": 5000
    }
  }
}

Why this works:
ElevenLabs processes calls asynchronously. The API call initiates the conversation and returns immediately with a conversation_id. The actual qualification results arrive later via webhook callback. This architecture prevents n8n workflow timeout while waiting for 5-10 minute phone conversations. The callback webhook receives qualification data and triggers the next workflow phase.

Step 4: Process Qualification Results

ElevenLabs sends qualification data back via webhook. This section evaluates responses and routes leads accordingly.

Set up qualification webhook

  1. Create a second Webhook node with path /elevenlabs-callback
  2. Set to POST method
  3. Extract conversation transcript and qualification scores
  4. Parse AI's assessment of lead quality

Add qualification logic

  1. Insert a Switch node after the callback webhook
  2. Define routing rules based on qualification criteria
  3. Route 1: Qualified leads (score ≥ 70) → Book appointment
  4. Route 2: Warm leads (score 40-69) → Schedule follow-up
  5. Route 3: Unqualified leads (score < 40) → Update CRM, no action

Switch node configuration:

{
  "mode": "rules",
  "rules": [
    {
      "name": "Qualified",
      "conditions": {
        "number": [
          {
            "value1": "={{$json.qualification_score}}",
            "operation": "largerEqual",
            "value2": 70
          }
        ],
        "boolean": [
          {
            "value1": "={{$json.working_with_agent}}",
            "value2": false
          }
        ]
      },
      "output": 0
    },
    {
      "name": "Warm",
      "conditions": {
        "number": [
          {
            "value1": "={{$json.qualification_score}}",
            "operation": "between",
            "value2": 40,
            "value3": 69
          }
        ]
      },
      "output": 1
    }
  ],
  "fallbackOutput": 2
}

Variables to customize:

  • qualification_score: Adjust threshold based on your conversion data (start at 70, optimize over time)
  • working_with_agent: Boolean flag from AI conversation—automatic disqualifier
  • timeline: Filter for "within 3 months" vs "just browsing"

Step 5: Book Calendly Appointment

Qualified leads automatically receive calendar invitations. This section creates Calendly bookings and sends confirmation.

Configure Calendly integration

  1. Add HTTP Request node for Calendly API
  2. Method: POST to /scheduled_events
  3. Map lead data to invitee fields
  4. Set event type (consultation, property showing, etc.)
  5. Include CRM contact ID in event metadata

Calendly node configuration:

{
  "method": "POST",
  "url": "https://api.calendly.com/scheduled_events",
  "authentication": "headerAuth",
  "headerAuth": {
    "name": "Authorization",
    "value": "Bearer {{$credentials.calendlyApiToken}}"
  },
  "body": {
    "event_type": "{{$credentials.calendlyEventTypeUri}}",
    "invitee": {
      "email": "={{$json.email}}",
      "name": "={{$json.full_name}}",
      "phone": "={{$json.phone}}"
    },
    "metadata": {
      "crm_contact_id": "={{$json.crm_contact_id}}",
      "qualification_score": "={{$json.qualification_score}}",
      "lead_source": "Facebook Lead Ads"
    }
  }
}

Update CRM with appointment details

  1. Add HTTP Request node for Lofty API
  2. Method: PATCH to update contact
  3. Add "Appointment Booked" tag
  4. Update pipeline stage to "Qualified"
  5. Store Calendly event URL in custom field

Why this approach:
Calendly's API creates the event and sends automated email confirmations. Storing the event URL in Lofty enables your sales team to reschedule or cancel directly from the CRM. The metadata fields allow Calendly analytics to track conversion rates by lead source and qualification score.

Workflow Architecture Overview

This workflow consists of 18 nodes organized into 5 main sections:

  1. Lead ingestion (Nodes 1-4): Facebook webhook capture, data extraction, phone standardization, and deduplication check
  2. CRM operations (Nodes 5-7): Contact creation in Lofty, initial tagging, and error handling for duplicate contacts
  3. AI voice qualification (Nodes 8-11): ElevenLabs call initiation, callback webhook, transcript parsing, and qualification scoring
  4. Decision routing (Nodes 12-14): Switch logic based on qualification score, routing to appointment booking or follow-up sequences
  5. Appointment and updates (Nodes 15-18): Calendly booking, CRM status updates, confirmation emails, and final logging

Execution flow:

  • Trigger: Facebook webhook fires on form submission
  • Average run time: 8-12 seconds (excluding AI call duration)
  • Key dependencies: Facebook webhook verification, Lofty API credentials, ElevenLabs agent configuration, Calendly event type URI

Critical nodes:

  • Function Node (Data Extraction): Parses Facebook's nested JSON and standardizes phone format—errors here break the entire workflow
  • HTTP Request (Lofty CRM): Handles both contact creation and duplicate scenarios—must have "neverError" enabled
  • Switch Node (Qualification Router): Routes leads based on AI scoring—threshold values directly impact conversion rates
  • HTTP Request (Calendly): Creates appointments only for qualified leads—requires proper event type mapping

The complete n8n workflow JSON template is available at the bottom of this article.

Key Configuration Details

These settings determine workflow reliability and performance. Incorrect configuration causes silent failures or data loss.

Facebook Webhook Verification

Facebook requires webhook verification before sending live data. When you first add the webhook URL in Facebook Business Manager, Facebook sends a GET request with hub.mode, hub.verify_token, and hub.challenge parameters.

Required verification logic:

// Add to Webhook node's "Webhook Response" settings
if ($request.query['hub.mode'] === 'subscribe' && 
    $request.query['hub.verify_token'] === 'YOUR_VERIFY_TOKEN') {
  return {
    status: 200,
    body: $request.query['hub.challenge']
  };
}

Common issues:

  • Using wrong verify token → Facebook marks webhook as failed
  • Not responding with challenge parameter → Verification never completes
  • Missing SSL certificate on self-hosted n8n → Facebook rejects non-HTTPS webhooks

ElevenLabs Agent Configuration

Your ElevenLabs conversational AI agent must be pre-configured with qualification questions and response handling.

Agent settings in ElevenLabs dashboard:

  • Conversation style: Professional but friendly
  • Maximum call duration: 5 minutes (300 seconds)
  • Qualification questions: Embedded in agent prompt
  • Webhook callback: Enabled with n8n endpoint
  • Retry behavior: 2 attempts if lead doesn't answer

Required webhook payload structure:

{
  "conversation_id": "conv_abc123",
  "phone_number": "+15551234567",
  "duration_seconds": 247,
  "transcript": "Full conversation text...",
  "qualification_score": 85,
  "extracted_data": {
    "working_with_agent": false,
    "timeline": "within 3 months",
    "preapproved": true,
    "budget_range": "$400k-$500k"
  }
}

Why this approach:
ElevenLabs agents require upfront configuration of conversation flow. The agent's prompt engineering determines qualification accuracy. Poor prompts result in low-quality scoring, sending unqualified leads to your calendar. Test your agent with 20-30 sample calls before deploying to production.

Lofty CRM Deduplication

Lofty's API handles duplicates differently than most CRMs. Understanding this prevents duplicate contact creation.

Deduplication logic:

// Check for existing contact before creation
const checkDuplicate = await fetch('https://api.lofty.com/v1/contacts/search', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    email: leadEmail,
    phone: leadPhone
  })
});

const existingContact = await checkDuplicate.json();

if (existingContact.total > 0) {
  // Update existing contact
  contactId = existingContact.contacts[0].id;
  method = 'PATCH';
} else {
  // Create new contact
  method = 'POST';
}

Variables to customize:

  • qualification_score threshold: Start at 70, adjust based on your close rate data
  • max_duration for ElevenLabs calls: Increase to 600 seconds (10 min) for complex qualification
  • retry intervals: Use exponential backoff (5s, 15s, 45s) for API failures

Testing & Validation

Test each component independently before running end-to-end tests. This isolates failures and speeds up debugging.

Component testing sequence:

  1. Facebook webhook: Use Facebook's "Test Webhook" button in Business Manager—verify n8n receives the payload and responds with 200 OK
  2. CRM integration: Manually execute the Lofty contact creation node with sample data—check for proper field mapping and duplicate handling
  3. ElevenLabs call: Trigger a test call using the "Execute Node" button—verify the agent initiates the call and your callback webhook receives results
  4. Qualification routing: Feed mock qualification scores (30, 55, 85) into the Switch node—confirm correct routing to appointment booking vs follow-up
  5. Calendly booking: Execute the Calendly node with test data—verify event creation and confirmation email delivery

Review execution data:

After each test, examine the node output in n8n's execution view:

  • Green checkmarks indicate successful execution
  • Red X marks show errors—click to view error messages
  • Yellow warnings indicate partial success (e.g., duplicate contact found)

Common test failures:

Error Cause Fix
"Webhook timeout" Facebook didn't receive 200 OK within 20s Enable "Respond Immediately" in Webhook node
"401 Unauthorized" Invalid API credentials Regenerate API keys and update n8n credentials
"Phone number format invalid" Missing country code or wrong format Add phone standardization in Function node
"Agent not found" Wrong ElevenLabs agent_id Copy agent ID from ElevenLabs dashboard

End-to-end validation:

Submit a real Facebook lead form and trace the execution:

  1. Check n8n execution log for successful webhook receipt
  2. Verify contact creation in Lofty CRM
  3. Confirm AI call initiation (check your phone or test number)
  4. Wait for qualification callback (5-10 minutes)
  5. Verify appointment booking in Calendly (for qualified leads)
  6. Check CRM for updated tags and pipeline stage

Run 10-15 test leads with varying qualification scenarios before deploying to production.

Deployment Considerations

Production deployment requires error handling, monitoring, and documentation that test environments skip.

Production Deployment Checklist

Area Requirement Why It Matters
Error Handling Retry logic with exponential backoff on all API nodes Prevents data loss during temporary API outages
Monitoring Webhook health checks every 5 minutes Detects Facebook webhook failures within 5 minutes vs discovering 3 days later
Logging Store execution data for 30 days minimum Enables debugging of edge cases and compliance audits
Rate Limiting Implement queuing for >100 leads/hour Prevents API throttling from Facebook or Lofty
Credentials Use n8n's credential system, never hardcode API keys Protects against credential exposure in workflow exports
Documentation Node-by-node comments explaining business logic Reduces modification time from 4 hours to 30 minutes

Error handling strategy:

Add an Error Trigger workflow that catches failures from the main workflow:

{
  "nodes": [
    {
      "name": "Error Trigger",
      "type": "n8n-nodes-base.errorTrigger"
    },
    {
      "name": "Log Error",
      "type": "n8n-nodes-base.httpRequest",
      "parameters": {
        "url": "https://your-logging-service.com/errors",
        "method": "POST",
        "body": {
          "workflow_id": "={{$workflow.id}}",
          "execution_id": "={{$execution.id}}",
          "error_message": "={{$json.error.message}}",
          "failed_node": "={{$json.node.name}}"
        }
      }
    },
    {
      "name": "Notify Team",
      "type": "n8n-nodes-base.slack",
      "parameters": {
        "channel": "#automation-alerts",
        "text": "Workflow failed: {{$json.error.message}}"
      }
    }
  ]
}

Monitoring recommendations:

Set up these alerts to catch issues before they impact lead conversion:

  • Facebook webhook failures: Alert if no leads received in 24 hours (assuming consistent ad spend)
  • ElevenLabs call failures: Alert if call initiation success rate drops below 95%
  • Calendly booking failures: Alert immediately—indicates broken appointment flow
  • CRM sync issues: Alert if contact creation fails 3+ times in a row

Customization ideas:

Once the base workflow runs reliably, extend it with:

  • SMS follow-up for leads who don't answer the AI call
  • Lead scoring based on Facebook ad engagement (clicks, video views)
  • A/B testing different qualification questions
  • Integration with property listing databases for personalized recommendations

Use Cases & Variations

This workflow architecture applies beyond real estate. The pattern of capture → qualify → route → book works for any high-volume lead funnel.

Use Case 1: Insurance Lead Qualification

  • Industry: Insurance (auto, home, life)
  • Scale: 200-500 leads/day from Facebook and Google Ads
  • Modifications needed: Replace property questions with coverage needs, budget, and current policy details. Swap Lofty CRM for Salesforce or HubSpot. Add compliance logging for TCPA requirements.
  • Key difference: Qualification focuses on policy expiration dates and coverage gaps rather than purchase timeline.

Use Case 2: Home Services Booking

  • Industry: HVAC, plumbing, roofing
  • Scale: 50-150 leads/day from local Facebook ads
  • Modifications needed: Replace Calendly with ServiceTitan or Housecall Pro for technician scheduling. Add geofencing logic to route leads by service area. Include urgency scoring (emergency vs routine service).
  • Key difference: Immediate booking for emergency services, qualification for planned projects.

Use Case 3: B2B SaaS Demo Scheduling

  • Industry: SaaS companies with high-ticket products
  • Scale: 30-80 leads/day from LinkedIn and Facebook
  • Modifications needed: Replace ElevenLabs voice with email-based qualification sequence. Add company size and tech stack questions. Integrate with Salesforce and Chili Piper for round-robin booking.
  • Key difference: Multi-touch qualification over 3-5 days rather than single phone call.

Use Case 4: Education/Course Enrollment

  • Industry: Online education, coaching programs
  • Scale: 100-300 leads/day from Facebook ads
  • Modifications needed: AI qualification focuses on learning goals and budget. Replace Calendly with Acuity Scheduling. Add payment processing for course deposits.
  • Key difference: Qualification includes ability to pay and commitment level assessment.

Use Case 5: Medical Practice Patient Intake

  • Industry: Healthcare (dental, cosmetic, specialty practices)
  • Scale: 40-100 leads/day from local advertising
  • Modifications needed: Add HIPAA-compliant logging and storage. Replace Lofty with Athenahealth or Epic. Include insurance verification in qualification.
  • Key difference: Compliance requirements and insurance eligibility checks before booking.

Customizations & Extensions

Extend this workflow to handle more complex scenarios and integrate with additional tools.

Alternative Integrations

Instead of Lofty CRM:

  • Salesforce: Best for enterprise teams with complex sales processes—requires 8-10 node changes for authentication and field mapping
  • HubSpot: Better if you need marketing automation—swap out nodes 5-7, add deal creation logic
  • Pipedrive: Use when you want simpler pipeline management—requires changing API endpoints but similar structure

Instead of ElevenLabs:

  • Bland AI: Better for longer, more complex conversations—requires webhook payload restructuring
  • Vapi: Use when you need real-time conversation control—add WebSocket nodes instead of HTTP requests
  • Synthflow: Best for multi-language support—similar API structure to ElevenLabs

Instead of Calendly:

  • Cal.com: Open-source alternative with more customization—requires self-hosting but similar API
  • Chili Piper: Better for B2B with round-robin routing—add routing logic in nodes 15-16
  • Acuity Scheduling: Use when you need payment collection—add Stripe integration nodes

Workflow Extensions

Add automated follow-up sequences:

  • Add a Schedule node to trigger 24 hours after initial contact
  • Connect to SendGrid or Mailgun for email sequences
  • Send personalized property recommendations based on qualification data
  • Nodes needed: +7 (Schedule, HTTP Request for email, Function for personalization, Set)

Scale to handle more data:

  • Replace in-memory processing with Redis for caching
  • Add batch processing for leads (process 50 at a time instead of one-by-one)
  • Implement queue system using RabbitMQ or AWS SQS
  • Performance improvement: 5x faster for >500 leads/day

Add SMS notifications:

  • Integrate Twilio for text message confirmations
  • Send appointment reminders 24 hours before meeting
  • Allow leads to confirm/cancel via SMS reply
  • Nodes needed: +4 (Twilio Send SMS, Twilio Webhook for replies, Function for parsing, Switch for routing)

Integration possibilities:

Add This To Get This Complexity
Slack notifications Real-time alerts for qualified leads Easy (2 nodes)
Google Sheets logging Lead tracking and reporting Easy (3 nodes)
Zapier webhook Connect to 5000+ other apps Easy (1 node)
Stripe payment Collect consultation deposits Medium (5 nodes)
Airtable database Better lead visualization and management Medium (6 nodes)
OpenAI GPT-4 Enhanced lead scoring and insights Medium (4 nodes)
Twilio SMS Multi-channel follow-up Medium (5 nodes)
Power BI connector Executive dashboards and analytics Advanced (12 nodes)
Custom ML model Predictive lead scoring Advanced (15+ nodes)

Advanced customization: Multi-language support

Add language detection and routing:

  1. Insert a Function node after Facebook webhook to detect language from form fields
  2. Route to language-specific ElevenLabs agents (Spanish, French, etc.)
  3. Update CRM with language preference
  4. Send Calendly invitations in the lead's language

Code for language detection:

const leadData = $input.item.json;
const languageMap = {
  'es': 'spanish_agent_id',
  'fr': 'french_agent_id',
  'en': 'english_agent_id'
};

// Detect language from form field or phone country code
const detectedLanguage = leadData.language || 'en';
const agentId = languageMap[detectedLanguage] || languageMap['en'];

return {
  json: {
    ...leadData,
    agent_id: agentId,
    language: detectedLanguage
  }
};

Get Started Today

Ready to automate your lead qualification funnel?

  1. Download the template: The complete n8n workflow JSON is available below—copy it to your clipboard
  2. Import to n8n: Go to Workflows → Import from URL or File, paste the JSON
  3. Configure your services: Add API credentials for Facebook, Lofty, ElevenLabs, and Calendly in n8n's credential manager
  4. Customize qualification logic: Update the Switch node thresholds and ElevenLabs agent prompts to match your business rules
  5. Test with sample data: Submit test leads through Facebook and verify each step executes correctly
  6. Deploy to production: Activate the workflow and monitor the first 50 leads closely

Need help customizing this workflow for your specific needs?

This workflow requires configuration of multiple APIs and careful tuning of qualification logic. If you want expert assistance adapting it to your business, or need help with complex integrations, schedule an intro call with Atherial at atherial.ai. We specialize in production-grade n8n implementations for high-volume lead funnels.


N8N Workflow JSON Template

{
  "name": "Facebook Lead to AI Voice Qualification Funnel",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "facebook-leads",
        "responseMode": "onReceived",
        "options": {}
      },
      "name": "Facebook Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [250, 300]
    },
    {
      "parameters": {
        "functionCode": "const leadData = $input.item.json.entry[0].changes[0].value;
const fieldData = leadData.field_data;

const fields = {};
fieldData.forEach(field => {
  fields[field.name] = field.values[0];
});

let phone = fields.phone_number.replace(/\\s+/g, '');
if (!phone.startsWith('+1')) phone = '+1' + phone;

return {
  json: {
    full_name: fields.full_name,
    email: fields.email,
    phone: phone,
    lead_source: 'Facebook Lead Ads',
    ad_name: leadData.ad_name,
    form_id: leadData.form_id,
    created_time: leadData.created_time,
    unique_id: `${fields.email}_${phone}`
  }
};"
      },
      "name": "Extract Lead Data",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [450, 300]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.lofty.com/v1/contacts",
        "authentication": "headerAuth",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "email",
              "value": "={{$json.email}}"
            },
            {
              "name": "phone",
              "value": "={{$json.phone}}"
            },
            {
              "name": "name",
              "value": "={{$json.full_name}}"
            },
            {
              "name": "source",
              "value": "={{$json.lead_source}}"
            },
            {
              "name": "tags",
              "value": "=[\"Facebook Lead\", \"Unqualified\"]"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "name": "Create Lofty Contact",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [650, 300]
    },
    {
      "parameters": {
        "functionCode": "const contact = $input.item.json;

return {
  json: {
    phone_number: contact.phone,
    agent_id: \"{{$credentials.elevenlabsAgentId}}\",
    context: {
      lead_name: contact.name,
      property_interest: contact.custom_fields.ad_name,
      qualification_questions: [
        \"Are you currently working with a real estate agent?\",
        \"What is your timeline for purchasing?\",
        \"Have you been pre-approved for a mortgage?\",
        \"What is your budget range?\"
      ],
      crm_contact_id: contact.id
    },
    callback_webhook: \"{{$workflow.webhook_url}}/elevenlabs-callback\",
    max_duration: 300
  }
};"
      },
      "name": "Prepare ElevenLabs Call",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [850, 300]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.elevenlabs.io/v1/convai/conversation",
        "authentication": "headerAuth",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "phone_number",
              "value": "={{$json.phone_number}}"
            },
            {
              "name": "agent_id",
              "value": "={{$json.agent_id}}"
            },
            {
              "name": "context_data",
              "value": "={{$json.context}}"
            },
            {
              "name": "webhook_url",
              "value": "={{$json.callback_webhook}}"
            }
          ]
        },
        "options": {
          "timeout": 10000,
          "retry": {
            "maxRetries": 3,
            "retryInterval": 5000
          }
        }
      },
      "name": "Initiate AI Call",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [1050, 300]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "elevenlabs-callback",
        "options": {}
      },
      "name": "ElevenLabs Callback",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [250, 500]
    },
    {
      "parameters": {
        "mode": "rules",
        "rules": {
          "rules": [
            {
              "name": "Qualified",
              "conditions": {
                "number": [
                  {
                    "value1": "={{$json.qualification_score}}",
                    "operation": "largerEqual",
                    "value2": 70
                  }
                ],
                "boolean": [
                  {
                    "value1": "={{$json.working_with_agent}}",
                    "value2": false
                  }
                ]
              },
              "output": 0
            },
            {
              "name": "Warm",
              "conditions": {
                "number": [
                  {
                    "value1": "={{$json.qualification_score}}",
                    "operation": "between",
                    "value2": 40,
                    "value3": 69
                  }
                ]
              },
              "output": 1
            }
          ]
        },
        "fallbackOutput": 2
      },
      "name": "Route by Qualification",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 1,
      "position": [450, 500]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.calendly.com/scheduled_events",
        "authentication": "headerAuth",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "event_type",
              "value": "={{$credentials.calendlyEventTypeUri}}"
            },
            {
              "name": "invitee",
              "value": "={\"email\": \"{{$json.email}}\", \"name\": \"{{$json.full_name}}\", \"phone\": \"{{$json.phone}}\"}"
            },
            {
              "name": "metadata",
              "value": "={\"crm_contact_id\": \"{{$json.crm_contact_id}}\", \"qualification_score\": \"{{$json.qualification_score}}\", \"lead_source\": \"Facebook Lead Ads\"}"
            }
          ]
        }
      },
      "name": "Book Calendly Appointment",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [650, 400]
    },
    {
      "parameters": {
        "method": "PATCH",
        "url": "https://api.lofty.com/v1/contacts/={{$json.crm_contact_id}}",
        "authentication": "headerAuth",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "tags",
              "value": "=[\"Appointment Booked\", \"Qualified\"]"
            },
            {
              "name": "pipeline_stage",
              "value": "Qualified"
            },
            {
              "name": "custom_fields",
              "value": "={\"calendly_event_url\": \"{{$json.event_url}}\"}"
            }
          ]
        }
      },
      "name": "Update CRM Status",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [850, 400]
    }
  ],
  "connections": {
    "Facebook Webhook": {
      "main": [
        [
          {
            "node": "Extract Lead Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Lead Data": {
      "main": [
        [
          {
            "node": "Create Lofty Contact",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Lofty Contact": {
      "main": [
        [
          {
            "node": "Prepare ElevenLabs Call",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare ElevenLabs Call": {
      "main": [
        [
          {
            "node": "Initiate AI Call",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ElevenLabs Callback": {
      "main": [
        [
          {
            "node": "Route by Qualification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Qualification": {
      "main": [
        [
          {
            "node": "Book Calendly Appointment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Book Calendly Appointment": {
      "main": [
        [
          {
            "node": "Update CRM Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Copy this JSON and import it into your n8n instance to get started. Remember to configure your API credentials and customize the qualification logic before deploying to production.

Complete N8N Workflow Template

Copy the JSON below and import it into your N8N instance via Workflows → Import from File

{
  "name": "Real Estate Lead-to-Appointment Automation",
  "nodes": [
    {
      "id": "fb-lead-trigger",
      "name": "Facebook Lead Ads Trigger",
      "type": "n8n-nodes-base.facebookLeadAdsTrigger",
      "position": [
        250,
        300
      ],
      "parameters": {
        "form": {
          "mode": "id",
          "value": ""
        },
        "page": {
          "mode": "id",
          "value": ""
        },
        "event": "newLead",
        "options": {
          "simplifyOutput": true
        }
      },
      "typeVersion": 1
    },
    {
      "id": "extract-lead-data",
      "name": "Extract Lead Data",
      "type": "n8n-nodes-base.set",
      "position": [
        470,
        300
      ],
      "parameters": {
        "mode": "manual",
        "assignments": {
          "assignments": [
            {
              "id": "lead-id",
              "name": "leadId",
              "type": "string",
              "value": "={{ $json.id }}"
            },
            {
              "id": "full-name",
              "name": "fullName",
              "type": "string",
              "value": "={{ $json.field_data.find(f => f.name === 'full_name')?.values[0] || 'Unknown' }}"
            },
            {
              "id": "email",
              "name": "email",
              "type": "string",
              "value": "={{ $json.field_data.find(f => f.name === 'email')?.values[0] || '' }}"
            },
            {
              "id": "phone",
              "name": "phone",
              "type": "string",
              "value": "={{ $json.field_data.find(f => f.name === 'phone_number')?.values[0] || '' }}"
            },
            {
              "id": "property-type",
              "name": "propertyType",
              "type": "string",
              "value": "={{ $json.field_data.find(f => f.name === 'property_type')?.values[0] || 'Not Specified' }}"
            },
            {
              "id": "budget",
              "name": "budget",
              "type": "string",
              "value": "={{ $json.field_data.find(f => f.name === 'budget')?.values[0] || 'Not Specified' }}"
            },
            {
              "id": "timestamp",
              "name": "timestamp",
              "type": "string",
              "value": "={{ $json.created_time }}"
            }
          ]
        },
        "duplicateItem": false,
        "includeOtherFields": false
      },
      "typeVersion": 3.4
    },
    {
      "id": "create-crm-contact",
      "name": "Create Lofty CRM Contact",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "position": [
        690,
        300
      ],
      "parameters": {
        "url": "https://api.lofty.com/v1/contacts",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "jsonBody": "={\n  \"firstName\": \"{{ $json.fullName.split(' ')[0] }}\",\n  \"lastName\": \"{{ $json.fullName.split(' ').slice(1).join(' ') }}\",\n  \"email\": \"{{ $json.email }}\",\n  \"phone\": \"{{ $json.phone }}\",\n  \"source\": \"Facebook Lead Ads\",\n  \"customFields\": {\n    \"propertyType\": \"{{ $json.propertyType }}\",\n    \"budget\": \"{{ $json.budget }}\",\n    \"leadId\": \"{{ $json.leadId }}\"\n  },\n  \"tags\": [\"facebook-lead\", \"unqualified\"]\n}",
        "sendBody": true,
        "contentType": "json",
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "nodeCredentialType": "httpHeaderAuth"
      },
      "typeVersion": 4.3
    },
    {
      "id": "check-crm-success",
      "name": "Check CRM Success",
      "type": "n8n-nodes-base.if",
      "position": [
        910,
        300
      ],
      "parameters": {
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "crm-success",
              "operator": {
                "type": "number",
                "operation": "equals"
              },
              "leftValue": "={{ $json.statusCode }}",
              "rightValue": 200
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "wait-before-call",
      "name": "Wait Before AI Call",
      "type": "n8n-nodes-base.wait",
      "position": [
        1130,
        200
      ],
      "parameters": {
        "unit": "minutes",
        "amount": 2,
        "resume": "timeInterval"
      },
      "typeVersion": 1.1
    },
    {
      "id": "initiate-ai-call",
      "name": "ElevenLabs AI Voice Call",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "position": [
        1350,
        200
      ],
      "parameters": {
        "url": "https://api.elevenlabs.io/v1/convai/conversation",
        "method": "POST",
        "options": {
          "timeout": 180000,
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "jsonBody": "={\n  \"agent_id\": \"{{ $credentials.elevenLabsAgentId }}\",\n  \"phone_number\": \"{{ $('Extract Lead Data').item.json.phone }}\",\n  \"context\": {\n    \"leadName\": \"{{ $('Extract Lead Data').item.json.fullName }}\",\n    \"propertyType\": \"{{ $('Extract Lead Data').item.json.propertyType }}\",\n    \"budget\": \"{{ $('Extract Lead Data').item.json.budget }}\"\n  },\n  \"system_prompt\": \"You are a professional real estate assistant. Your goal is to qualify the lead by asking about: 1) Their timeline for buying/selling, 2) If they have financing pre-approval, 3) Their specific location preferences, 4) Any must-have property features. Be friendly, professional, and brief. After gathering this information, ask if they'd like to schedule a consultation with an agent.\"\n}",
        "sendBody": true,
        "contentType": "json",
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "headerParameters": {
          "parameters": [
            {
              "name": "xi-api-key",
              "value": "={{ $credentials.elevenLabsApiKey }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "nodeCredentialType": "httpHeaderAuth"
      },
      "typeVersion": 4.3
    },
    {
      "id": "wait-for-call",
      "name": "Wait For Call Completion",
      "type": "n8n-nodes-base.wait",
      "position": [
        1570,
        200
      ],
      "parameters": {
        "unit": "minutes",
        "amount": 5,
        "resume": "timeInterval"
      },
      "typeVersion": 1.1
    },
    {
      "id": "get-call-results",
      "name": "Get Call Results",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1790,
        200
      ],
      "parameters": {
        "url": "=https://api.elevenlabs.io/v1/convai/conversation/{{ $('ElevenLabs AI Voice Call').item.json.conversation_id }}",
        "method": "GET",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "sendHeaders": true,
        "authentication": "predefinedCredentialType",
        "headerParameters": {
          "parameters": [
            {
              "name": "xi-api-key",
              "value": "={{ $credentials.elevenLabsApiKey }}"
            }
          ]
        },
        "nodeCredentialType": "httpHeaderAuth"
      },
      "typeVersion": 4.3
    },
    {
      "id": "analyze-qualification",
      "name": "Analyze Qualification (OpenAI)",
      "type": "n8n-nodes-base.openAi",
      "position": [
        2010,
        200
      ],
      "parameters": {
        "prompt": {
          "messages": [
            {
              "role": "system",
              "content": "You are a lead qualification analyzer. Based on the conversation transcript, determine if the lead is qualified (score 1-10, where 8+ is qualified). Extract: qualification_score, timeline, has_financing, location_preference, wants_appointment. Return ONLY valid JSON."
            },
            {
              "role": "user",
              "content": "=Analyze this conversation:\n\nLead: {{ $('Extract Lead Data').item.json.fullName }}\nTranscript: {{ $json.transcript || 'No transcript available' }}\nCall Duration: {{ $json.duration || 0 }} seconds\nCall Status: {{ $json.status || 'unknown' }}"
            }
          ]
        },
        "options": {
          "maxTokens": 500,
          "temperature": 0.3
        },
        "resource": "chat",
        "chatModel": "gpt-4",
        "operation": "complete",
        "simplifyOutput": true
      },
      "typeVersion": 1.1
    },
    {
      "id": "parse-analysis",
      "name": "Parse Analysis Results",
      "type": "n8n-nodes-base.set",
      "position": [
        2230,
        200
      ],
      "parameters": {
        "mode": "manual",
        "assignments": {
          "assignments": [
            {
              "id": "qual-score",
              "name": "qualificationScore",
              "type": "number",
              "value": "={{ JSON.parse($json.message.content).qualification_score || 0 }}"
            },
            {
              "id": "timeline",
              "name": "timeline",
              "type": "string",
              "value": "={{ JSON.parse($json.message.content).timeline || 'Unknown' }}"
            },
            {
              "id": "financing",
              "name": "hasFinancing",
              "type": "boolean",
              "value": "={{ JSON.parse($json.message.content).has_financing || false }}"
            },
            {
              "id": "location",
              "name": "locationPreference",
              "type": "string",
              "value": "={{ JSON.parse($json.message.content).location_preference || 'Not specified' }}"
            },
            {
              "id": "wants-appt",
              "name": "wantsAppointment",
              "type": "boolean",
              "value": "={{ JSON.parse($json.message.content).wants_appointment || false }}"
            },
            {
              "id": "conversation-id",
              "name": "conversationId",
              "type": "string",
              "value": "={{ $('ElevenLabs AI Voice Call').item.json.conversation_id }}"
            }
          ]
        },
        "duplicateItem": false,
        "includeOtherFields": false
      },
      "typeVersion": 3.4
    },
    {
      "id": "route-by-qualification",
      "name": "Route By Qualification",
      "type": "n8n-nodes-base.switch",
      "position": [
        2450,
        200
      ],
      "parameters": {
        "mode": "rules",
        "rules": {
          "values": [
            {
              "outputKey": "Qualified",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "number",
                      "operation": "gte"
                    },
                    "leftValue": "={{ $json.qualificationScore }}",
                    "rightValue": 8
                  },
                  {
                    "operator": {
                      "type": "boolean",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.wantsAppointment }}",
                    "rightValue": true
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Nurture",
              "conditions": {
                "options": {
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "number",
                      "operation": "gte"
                    },
                    "leftValue": "={{ $json.qualificationScore }}",
                    "rightValue": 5
                  },
                  {
                    "operator": {
                      "type": "number",
                      "operation": "lt"
                    },
                    "leftValue": "={{ $json.qualificationScore }}",
                    "rightValue": 8
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra",
          "renameFallbackOutput": "Unqualified"
        }
      },
      "typeVersion": 3.3
    },
    {
      "id": "create-calendly-link",
      "name": "Create Calendly Booking Link",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2670,
        100
      ],
      "parameters": {
        "url": "https://api.calendly.com/scheduling_links",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"max_event_count\": 1,\n  \"owner\": \"{{ $credentials.calendlyOwnerUri }}\",\n  \"owner_type\": \"EventType\"\n}",
        "sendBody": true,
        "contentType": "json",
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $credentials.calendlyApiKey }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "nodeCredentialType": "httpHeaderAuth"
      },
      "typeVersion": 4.3
    },
    {
      "id": "send-booking-sms",
      "name": "Send Booking SMS",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2890,
        100
      ],
      "parameters": {
        "url": "https://api.twilio.com/2010-04-01/Accounts/{{ $credentials.twilioAccountSid }}/Messages.json",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "form-urlencoded",
        "specifyBody": "keypair",
        "authentication": "predefinedCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "To",
              "value": "={{ $('Extract Lead Data').item.json.phone }}"
            },
            {
              "name": "From",
              "value": "={{ $credentials.twilioPhoneNumber }}"
            },
            {
              "name": "Body",
              "value": "=Hi {{ $('Extract Lead Data').item.json.fullName }}! Thanks for your interest. Here's your personalized booking link to schedule your real estate consultation: {{ $json.resource.booking_url }}"
            }
          ]
        },
        "nodeCredentialType": "httpBasicAuth"
      },
      "typeVersion": 4.3
    },
    {
      "id": "update-crm-qualified",
      "name": "Update CRM - Qualified",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3110,
        100
      ],
      "parameters": {
        "url": "=https://api.lofty.com/v1/contacts/{{ $('Create Lofty CRM Contact').item.json.id }}",
        "method": "PATCH",
        "options": {},
        "jsonBody": "={\n  \"tags\": [\"qualified\", \"appointment-sent\"],\n  \"status\": \"active\",\n  \"customFields\": {\n    \"qualificationScore\": {{ $('Parse Analysis Results').item.json.qualificationScore }},\n    \"timeline\": \"{{ $('Parse Analysis Results').item.json.timeline }}\",\n    \"hasFinancing\": {{ $('Parse Analysis Results').item.json.hasFinancing }},\n    \"locationPreference\": \"{{ $('Parse Analysis Results').item.json.locationPreference }}\",\n    \"calendlyLink\": \"{{ $('Create Calendly Booking Link').item.json.resource.booking_url }}\",\n    \"conversationId\": \"{{ $('Parse Analysis Results').item.json.conversationId }}\"\n  },\n  \"notes\": \"Qualified via AI call on {{ $now.format('yyyy-MM-dd HH:mm') }}. Booking link sent via SMS.\"\n}",
        "sendBody": true,
        "contentType": "json",
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "nodeCredentialType": "httpHeaderAuth"
      },
      "typeVersion": 4.3
    },
    {
      "id": "update-crm-nurture",
      "name": "Update CRM - Nurture",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2670,
        200
      ],
      "parameters": {
        "url": "=https://api.lofty.com/v1/contacts/{{ $('Create Lofty CRM Contact').item.json.id }}",
        "method": "PATCH",
        "options": {},
        "jsonBody": "={\n  \"tags\": [\"nurture\", \"follow-up-needed\"],\n  \"status\": \"warm\",\n  \"customFields\": {\n    \"qualificationScore\": {{ $('Parse Analysis Results').item.json.qualificationScore }},\n    \"timeline\": \"{{ $('Parse Analysis Results').item.json.timeline }}\",\n    \"conversationId\": \"{{ $('Parse Analysis Results').item.json.conversationId }}\"\n  },\n  \"notes\": \"Moderate qualification ({{ $('Parse Analysis Results').item.json.qualificationScore }}/10). Added to nurture campaign.\"\n}",
        "sendBody": true,
        "contentType": "json",
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "nodeCredentialType": "httpHeaderAuth"
      },
      "typeVersion": 4.3
    },
    {
      "id": "update-crm-unqualified",
      "name": "Update CRM - Unqualified",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2670,
        300
      ],
      "parameters": {
        "url": "=https://api.lofty.com/v1/contacts/{{ $('Create Lofty CRM Contact').item.json.id }}",
        "method": "PATCH",
        "options": {},
        "jsonBody": "={\n  \"tags\": [\"unqualified\", \"low-priority\"],\n  \"status\": \"cold\",\n  \"customFields\": {\n    \"qualificationScore\": {{ $('Parse Analysis Results').item.json.qualificationScore || 0 }},\n    \"conversationId\": \"{{ $('Parse Analysis Results').item.json.conversationId }}\"\n  },\n  \"notes\": \"Low qualification score. May revisit in 6 months.\"\n}",
        "sendBody": true,
        "contentType": "json",
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "nodeCredentialType": "httpHeaderAuth"
      },
      "typeVersion": 4.3
    },
    {
      "id": "notify-error",
      "name": "Notify Team - Error",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1130,
        400
      ],
      "parameters": {
        "url": "={{ $credentials.slackWebhookUrl }}",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"text\": \"⚠️ CRM Contact Creation Failed\",\n  \"blocks\": [\n    {\n      \"type\": \"section\",\n      \"text\": {\n        \"type\": \"mrkdwn\",\n        \"text\": \"*Facebook Lead Import Error*\\n\\nLead: {{ $('Extract Lead Data').item.json.fullName }}\\nEmail: {{ $('Extract Lead Data').item.json.email }}\\nPhone: {{ $('Extract Lead Data').item.json.phone }}\\n\\nError: {{ $json.error || 'CRM API failed' }}\\n\\nPlease manually add to Lofty CRM.\"\n      }\n    }\n  ]\n}",
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json"
      },
      "typeVersion": 4.3
    }
  ],
  "settings": {
    "callerPolicy": "workflowsFromSameOwner",
    "errorWorkflow": "",
    "executionOrder": "v1",
    "saveManualExecutions": true
  },
  "connections": {
    "Get Call Results": {
      "main": [
        [
          {
            "node": "Analyze Qualification (OpenAI)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Booking SMS": {
      "main": [
        [
          {
            "node": "Update CRM - Qualified",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check CRM Success": {
      "main": [
        [
          {
            "node": "Wait Before AI Call",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Notify Team - Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Lead Data": {
      "main": [
        [
          {
            "node": "Create Lofty CRM Contact",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait Before AI Call": {
      "main": [
        [
          {
            "node": "ElevenLabs AI Voice Call",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Analysis Results": {
      "main": [
        [
          {
            "node": "Route By Qualification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route By Qualification": {
      "main": [
        [
          {
            "node": "Create Calendly Booking Link",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Update CRM - Nurture",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Update CRM - Unqualified",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Lofty CRM Contact": {
      "main": [
        [
          {
            "node": "Check CRM Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ElevenLabs AI Voice Call": {
      "main": [
        [
          {
            "node": "Wait For Call Completion",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait For Call Completion": {
      "main": [
        [
          {
            "node": "Get Call Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Facebook Lead Ads Trigger": {
      "main": [
        [
          {
            "node": "Extract Lead Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Calendly Booking Link": {
      "main": [
        [
          {
            "node": "Send Booking SMS",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze Qualification (OpenAI)": {
      "main": [
        [
          {
            "node": "Parse Analysis Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}