What You'll Build
This automation handles the complete lifecycle of a high-ticket sales lead from first touch to closed deal.
| Component |
Technology |
Purpose |
| Lead Intake |
HubSpot Webhook |
Captures new contacts tagged "Online Sales" |
| Nurture Sequences |
n8n Schedule + HTTP Request |
Sends timed emails moving leads toward presentations |
| Presentation Management |
Microsoft Teams API + Calendar |
Schedules group webinars and tracks attendance |
| No-Show Recovery |
n8n Function + Delay Nodes |
Re-engages leads who miss presentations |
| Deal Stage Progression |
HubSpot API |
Updates pipeline stages based on lead actions |
| Contract Tracking |
DocuSign Webhook |
Monitors signature completion |
| Payment Verification |
Stripe Webhook |
Confirms successful charges |
| Sales Notifications |
Slack/Email Nodes |
Alerts team of hot leads and completed deals |
| Re-engagement Logic |
Switch Node + Conditional Routing |
Handles cold leads with automated sequences |
Prerequisites
Before starting, ensure you have:
Step 1: Configure HubSpot Lead Capture
The funnel begins when a new contact enters HubSpot tagged with "Online Sales" business line. This happens automatically when someone purchases a travel certificate.
Set up the HubSpot webhook trigger:
- In n8n, add a Webhook node set to "Webhook URLs" mode
- Copy the production webhook URL
- In HubSpot, navigate to Settings → Integrations → Private Apps
- Create a new app with scopes:
contacts, deals, timeline
- Configure a workflow in HubSpot: Trigger = "Contact property 'Business Line' is 'Online Sales'"
- Add action: "Send webhook notification" to your n8n URL
Node configuration:
{
"httpMethod": "POST",
"path": "hubspot-lead-capture",
"responseMode": "onReceived",
"options": {}
}
Why this works:
HubSpot's native workflow engine ensures every qualified lead triggers your n8n automation immediately. By filtering on the "Business Line" property, you isolate high-ticket sales leads from other contact types. The webhook delivers contact ID, email, name, and custom properties you'll use downstream.
Add HubSpot contact enrichment:
After the webhook trigger, add an HTTP Request node to fetch complete contact details:
{
"method": "GET",
"url": "=https://api.hubapi.com/crm/v3/objects/contacts/{{$json.body.contactId}}",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "hubspotApi",
"options": {
"qs": {
"properties": "email,firstname,lastname,phone,purchase_date,travel_certificate_id"
}
}
}
This ensures you have all necessary contact data for personalization in nurture emails.
Step 2: Initialize Deal and Set First Stage
Every new lead needs a corresponding deal in HubSpot's pipeline to track their progression through the funnel.
Create deal in HubSpot:
Add an HTTP Request node that creates a deal associated with the contact:
{
"method": "POST",
"url": "https://api.hubapi.com/crm/v3/objects/deals",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "hubspotApi",
"body": {
"properties": {
"dealname": "={{$json.properties.firstname}} {{$json.properties.lastname}} - Vacation Club",
"dealstage": "lead_nurture",
"pipeline": "high_ticket_sales",
"amount": "10000",
"closedate": "={{new Date(Date.now() + 30*24*60*60*1000).toISOString()}}",
"hubspot_owner_id": "{{$json.properties.hubspot_owner_id}}"
},
"associations": [
{
"to": {"id": "={{$json.id}}"},
"types": [{"associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 3}]
}
]
}
}
Why this approach:
Creating the deal immediately establishes tracking visibility. The dealstage starts at "lead_nurture" and will progress through "presentation_scheduled," "presentation_attended," "1on1_scheduled," "contract_sent," and "closed_won." The 30-day close date creates urgency while remaining realistic for high-ticket sales cycles. Associating the deal with the contact ensures all interactions appear in both records.
Step 3: Build Nurture Sequence to Presentation
Leads need 3-5 touchpoints before they're ready to attend a group presentation. This sequence runs over 7-10 days.
Configure the nurture flow:
Add a Function node that determines the next email in the sequence:
const contact = $input.first().json;
const lastEmailSent = contact.properties.last_nurture_email || 0;
const daysSincePurchase = Math.floor((Date.now() - new Date(contact.properties.purchase_date)) / (1000*60*60*24));
let emailTemplate = '';
let nextStage = lastEmailSent + 1;
if (nextStage === 1 && daysSincePurchase === 0) {
emailTemplate = 'welcome_travel_certificate';
} else if (nextStage === 2 && daysSincePurchase === 2) {
emailTemplate = 'introduce_vacation_club';
} else if (nextStage === 3 && daysSincePurchase === 5) {
emailTemplate = 'presentation_invitation';
} else if (nextStage === 4 && daysSincePurchase === 7) {
emailTemplate = 'presentation_reminder';
} else {
return [];
}
return [{
json: {
email: contact.properties.email,
firstName: contact.properties.firstname,
template: emailTemplate,
contactId: contact.id,
nextStage: nextStage
}
}];
Send email via HTTP Request node:
Connect to your email service (SendGrid, Mailgun, or HubSpot's email API):
{
"method": "POST",
"url": "https://api.sendgrid.com/v3/mail/send",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "sendGridApi",
"body": {
"personalizations": [{
"to": [{"email": "={{$json.email}}"}],
"dynamic_template_data": {
"first_name": "={{$json.firstName}}",
"presentation_date": "={{$json.nextPresentationDate}}",
"registration_link": "={{$json.registrationUrl}}"
}
}],
"template_id": "={{$json.template}}"
}
}
Update HubSpot contact property:
After sending, update the contact's last_nurture_email property so the sequence doesn't repeat:
{
"method": "PATCH",
"url": "=https://api.hubapi.com/crm/v3/objects/contacts/{{$json.contactId}}",
"body": {
"properties": {
"last_nurture_email": "={{$json.nextStage}}",
"last_email_sent_date": "={{new Date().toISOString()}}"
}
}
}
Schedule the sequence checker:
Add a Schedule Trigger node set to run daily at 9 AM:
{
"rule": {
"interval": [{"field": "cronExpression", "expression": "0 9 * * *"}]
}
}
This node queries HubSpot for all contacts in "lead_nurture" stage and processes them through the Function node above.
Step 4: Presentation Scheduling and Attendance Tracking
Group presentations happen 1-2 times per week. Leads who register need confirmation emails, calendar invites, and reminders.
Microsoft Teams integration:
When a lead clicks the registration link in the nurture email, a webhook fires to n8n:
{
"method": "POST",
"url": "https://graph.microsoft.com/v1.0/me/onlineMeetings",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "microsoftTeamsApi",
"body": {
"startDateTime": "={{$json.presentationDateTime}}",
"endDateTime": "={{$json.presentationEndTime}}",
"subject": "Exclusive Vacation Club Presentation",
"participants": {
"attendees": [
{"identity": {"user": {"id": null, "displayName": "={{$json.firstName}} {{$json.lastName}}", "email": "={{$json.email}}"}}}
]
}
}
}
Update deal stage to "presentation_scheduled":
{
"method": "PATCH",
"url": "=https://api.hubapi.com/crm/v3/objects/deals/{{$json.dealId}}",
"body": {
"properties": {
"dealstage": "presentation_scheduled",
"presentation_date": "={{$json.presentationDateTime}}"
}
}
}
Track attendance:
After the presentation, Microsoft Teams provides attendance data via API. Add a Schedule node that runs 30 minutes after each scheduled presentation:
{
"method": "GET",
"url": "=https://graph.microsoft.com/v1.0/me/onlineMeetings/{{$json.meetingId}}/attendanceReports",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "microsoftTeamsApi"
}
Parse the attendance report with a Function node:
const attendanceData = $input.first().json.value[0];
const attendees = attendanceData.attendanceRecords;
const contactEmail = $('Webhook').first().json.email;
const attended = attendees.some(a => a.emailAddress === contactEmail && a.totalAttendanceInSeconds > 1800);
return [{
json: {
contactId: $('Webhook').first().json.contactId,
dealId: $('Webhook').first().json.dealId,
attended: attended,
attendanceSeconds: attended ? attendees.find(a => a.emailAddress === contactEmail).totalAttendanceInSeconds : 0
}
}];
Update deal based on attendance:
Use a Switch node to route based on attended boolean:
- If attended: Update deal stage to "presentation_attended" and trigger 1-on-1 scheduling flow
- If no-show: Update deal stage to "no_show" and trigger recovery sequence
Step 5: No-Show Recovery Sequence
30-50% of registered leads don't attend. A systematic recovery process recaptures 15-20% of these.
Immediate no-show email:
Send within 2 hours of the missed presentation:
{
"personalizations": [{
"to": [{"email": "={{$json.email}}"}],
"dynamic_template_data": {
"first_name": "={{$json.firstName}}",
"next_presentation_date": "={{$json.nextPresentationDate}}",
"reschedule_link": "={{$json.rescheduleUrl}}"
}
}],
"template_id": "no_show_recovery_immediate"
}
Follow-up sequence:
Add a Wait node (2 days), then send a second recovery email with a different angle:
{
"template_id": "no_show_recovery_value_reminder",
"dynamic_template_data": {
"testimonial_video_url": "https://yourvacationclub.com/testimonials",
"limited_spots_remaining": "={{$json.spotsRemaining}}"
}
}
Final attempt:
After another 3 days, send a "last chance" email offering a 1-on-1 presentation instead of group:
{
"template_id": "no_show_recovery_1on1_offer",
"dynamic_template_data": {
"calendar_link": "={{$json.calendlyUrl}}"
}
}
Update deal stage:
If no response after 7 days, move deal to "cold_lead" stage but keep in pipeline for quarterly re-engagement campaigns.
Step 6: Transition to 1-on-1 Sales Sessions
Leads who attend the presentation and show interest get scheduled for 1-on-1 closing calls.
Calendly webhook integration:
When a lead books a 1-on-1 via Calendly, the webhook triggers n8n:
{
"httpMethod": "POST",
"path": "calendly-booking",
"responseMode": "onReceived"
}
Update HubSpot deal:
{
"method": "PATCH",
"url": "=https://api.hubapi.com/crm/v3/objects/deals/{{$json.dealId}}",
"body": {
"properties": {
"dealstage": "1on1_scheduled",
"closedate": "={{$json.scheduledTime}}",
"assigned_closer": "={{$json.assignedSalesRep}}"
}
}
}
Notify sales rep:
Send Slack notification to the assigned closer:
{
"channel": "#high-ticket-sales",
"text": "🔥 Hot lead scheduled: {{$json.firstName}} {{$json.lastName}} - 1-on-1 on {{$json.scheduledTime}}",
"blocks": [
{
"type": "section",
"text": {"type": "mrkdwn", "text": "*New 1-on-1 Scheduled*
*Lead:* {{$json.firstName}} {{$json.lastName}}
*Time:* {{$json.scheduledTime}}
*Attended Presentation:* Yes
*Interest Level:* High"}
},
{
"type": "actions",
"elements": [
{"type": "button", "text": {"type": "plain_text", "text": "View in HubSpot"}, "url": "={{$json.hubspotDealUrl}}"}
]
}
]
}
Step 7: Contract and Payment Tracking
During the 1-on-1 call, sales reps send DocuSign contracts and process Stripe payments. These events must update HubSpot immediately.
DocuSign webhook:
Configure DocuSign to send completion events to n8n:
{
"httpMethod": "POST",
"path": "docusign-contract-signed",
"responseMode": "onReceived"
}
Parse DocuSign payload:
const envelope = $input.first().json;
const envelopeId = envelope.envelopeId;
const status = envelope.status;
const signerEmail = envelope.recipients.signers[0].email;
if (status !== 'completed') {
return [];
}
return [{
json: {
envelopeId: envelopeId,
signerEmail: signerEmail,
completedDate: envelope.completedDateTime
}
}];
Find associated HubSpot deal:
{
"method": "POST",
"url": "https://api.hubapi.com/crm/v3/objects/deals/search",
"body": {
"filterGroups": [{
"filters": [{
"propertyName": "associations.contact",
"operator": "EQ",
"value": "={{$json.contactId}}"
}]
}]
}
}
Update deal to "contract_sent":
{
"method": "PATCH",
"url": "=https://api.hubapi.com/crm/v3/objects/deals/{{$json.dealId}}",
"body": {
"properties": {
"dealstage": "contract_sent",
"contract_signed_date": "={{$json.completedDate}}",
"docusign_envelope_id": "={{$json.envelopeId}}"
}
}
}
Stripe payment webhook:
Configure Stripe to send payment_intent.succeeded events:
{
"httpMethod": "POST",
"path": "stripe-payment-received",
"responseMode": "onReceived"
}
Verify payment and close deal:
const paymentIntent = $input.first().json.data.object;
const amount = paymentIntent.amount / 100;
const customerEmail = paymentIntent.receipt_email;
if (amount < 10000) {
return [];
}
return [{
json: {
amount: amount,
customerEmail: customerEmail,
paymentId: paymentIntent.id,
paidDate: new Date(paymentIntent.created * 1000).toISOString()
}
}];
Update deal to "closed_won":
{
"method": "PATCH",
"url": "=https://api.hubapi.com/crm/v3/objects/deals/{{$json.dealId}}",
"body": {
"properties": {
"dealstage": "closed_won",
"closedate": "={{$json.paidDate}}",
"amount": "={{$json.amount}}",
"stripe_payment_id": "={{$json.paymentId}}"
}
}
}
Send celebration notification:
{
"channel": "#sales-wins",
"text": "💰 Deal closed! {{$json.firstName}} {{$json.lastName}} - ${{$json.amount}}"
}
Workflow Architecture Overview
This workflow consists of 47 nodes organized into 6 main sections:
- Lead intake and enrichment (Nodes 1-5): Captures HubSpot webhook, fetches contact details, creates deal, initializes pipeline stage
- Nurture sequence engine (Nodes 6-18): Daily schedule trigger, queries contacts in nurture stage, determines next email, sends via SendGrid, updates contact properties
- Presentation management (Nodes 19-28): Registration webhook, creates Teams meeting, sends confirmations, tracks attendance, routes based on show/no-show
- No-show recovery (Nodes 29-35): Immediate recovery email, 2-day wait, second attempt, 3-day wait, final offer, stage update to cold
- 1-on-1 scheduling (Nodes 36-40): Calendly webhook, deal stage update, sales rep notification, pre-call reminder sequence
- Contract and payment tracking (Nodes 41-47): DocuSign webhook, contract signed confirmation, Stripe webhook, payment verification, deal closure, celebration notification
Execution flow:
- Trigger: HubSpot contact creation or scheduled daily check
- Average run time: 2-4 seconds per lead
- Key dependencies: HubSpot API, Microsoft Teams API, DocuSign webhooks, Stripe webhooks, SendGrid API
Critical nodes:
- Function Node (Nurture Logic): Determines which email to send based on days since purchase and previous emails sent
- Switch Node (Attendance Router): Routes leads to either 1-on-1 flow or no-show recovery based on presentation attendance
- HTTP Request (Deal Stage Updates): Ensures HubSpot pipeline reflects real-time lead status across all touchpoints
The complete n8n workflow JSON template is available at the bottom of this article.
Key Configuration Details
HubSpot API Authentication
Required fields:
- API Key: Your HubSpot private app token (Settings → Integrations → Private Apps)
- Base URL:
https://api.hubapi.com
- Required scopes:
crm.objects.contacts.read, crm.objects.contacts.write, crm.objects.deals.read, crm.objects.deals.write
Common issues:
- Using personal access tokens instead of private app tokens → Results in 401 errors
- Missing association permissions → Deals won't link to contacts
- Always use
/crm/v3/ endpoints, not deprecated v1 or v2
Microsoft Teams Meeting Configuration
Critical settings:
{
"allowedPresenters": "organization",
"autoAdmittedUsers": "everyone",
"recordAutomatically": true,
"allowAttendeeToEnableCamera": true,
"allowAttendeeToEnableMic": true
}
Why this approach:
Recording presentations automatically provides content for no-shows to watch later. Setting autoAdmittedUsers to "everyone" prevents leads from waiting in lobbies, which increases drop-off. The allowedPresenters restriction ensures only your team controls the presentation flow.
Variables to customize:
presentationFrequency: Change from 2x/week to daily for higher volume
noShowRecoveryDelay: Adjust 2-day and 3-day waits based on your sales cycle
coldLeadThreshold: Default 7 days, increase to 14 for longer consideration products
Email Template Personalization
Dynamic fields to include:
{
"first_name": "={{$json.properties.firstname}}",
"purchase_date": "={{new Date($json.properties.purchase_date).toLocaleDateString()}}",
"travel_destination": "={{$json.properties.travel_certificate_destination}}",
"presentation_date": "={{$json.nextPresentationDate}}",
"sales_rep_name": "={{$json.assignedRep.name}}",
"sales_rep_photo": "={{$json.assignedRep.photoUrl}}"
}
Including the travel destination they purchased creates immediate relevance. Showing the assigned sales rep's photo builds familiarity before the 1-on-1 call.
Testing & Validation
Test each component independently:
- Lead capture: Create a test contact in HubSpot with "Online Sales" tag, verify webhook fires and deal creates
- Nurture emails: Manually trigger the daily schedule node, check SendGrid activity for test contact
- Presentation flow: Register test email for presentation, verify Teams meeting creation and calendar invite
- Attendance tracking: Join Teams meeting with test account for 30+ minutes, verify attendance API returns correct data
- No-show recovery: Register but don't attend, verify recovery emails send at correct intervals
- Contract tracking: Send test DocuSign envelope, verify webhook triggers and deal updates
- Payment processing: Create test Stripe payment, verify deal closes and notification sends
Common troubleshooting:
| Issue |
Cause |
Solution |
| Webhook not firing |
Incorrect URL or HubSpot workflow not active |
Verify webhook URL matches n8n production URL, check HubSpot workflow is turned on |
| Emails not sending |
SendGrid API key expired or template ID wrong |
Regenerate API key, verify template IDs match your SendGrid account |
| Deal not updating |
Missing HubSpot deal ID in payload |
Add Function node to extract deal ID from contact associations before update |
| Attendance not tracking |
Teams API permissions insufficient |
Ensure app registration has OnlineMeetings.Read.All permission |
Run evaluation tests:
Create 10 test contacts and track them through the full funnel. Measure:
- Time from lead creation to first email (should be <5 minutes)
- Presentation registration rate (target 40%+)
- No-show recovery effectiveness (target 15-20% re-engagement)
- Deal stage accuracy (100% match between actual status and HubSpot)
Deployment Considerations
Production Deployment Checklist
| Area |
Requirement |
Why It Matters |
| Error Handling |
Retry logic with exponential backoff on all API calls |
Prevents data loss when HubSpot or Teams APIs have temporary outages |
| Monitoring |
Webhook health checks every 5 minutes |
Detect failures within 5 minutes vs discovering days later when deals are stale |
| Rate Limits |
Implement queuing for bulk operations |
HubSpot limits to 100 requests/10 seconds, exceeding causes 429 errors |
| Data Validation |
Verify email format and required fields before API calls |
Prevents workflow failures from malformed data |
| Logging |
Store execution logs for 90 days |
Enables debugging when leads report issues weeks later |
| Backup Webhooks |
Configure secondary webhook URLs |
Ensures continuity if primary n8n instance goes down |
Error handling implementation:
Wrap all HTTP Request nodes with error workflows:
{
"continueOnFail": true,
"retryOnFail": true,
"maxTries": 3,
"waitBetweenTries": 5000
}
Add an Error Trigger node that catches failures and sends alerts:
{
"channel": "#n8n-alerts",
"text": "⚠️ Workflow error: {{$json.error.message}}
Node: {{$json.node.name}}
Execution: {{$json.execution.id}}"
}
Scaling considerations:
For 100+ leads per day:
- Implement batch processing in nurture sequence (process 50 contacts per execution)
- Use HubSpot's batch API endpoints (
/crm/v3/objects/deals/batch/update)
- Add Redis caching for frequently accessed data (presentation schedules, sales rep assignments)
- Split workflow into sub-workflows triggered by webhooks to avoid timeout issues
Customization ideas:
- Add SMS reminders via Twilio for presentation no-shows (increases recovery by 10-15%)
- Integrate with Calendly for automated 1-on-1 scheduling instead of manual booking
- Build executive dashboard in Google Sheets showing real-time funnel metrics
- Add lead scoring based on email engagement and presentation attendance duration
- Create A/B testing framework for nurture email subject lines and send times
Use Cases & Variations
Use Case 1: Real Estate Investment Webinars
- Industry: Real estate syndication
- Scale: 200 leads/month, $50K-$250K deal sizes
- Modifications needed: Extend nurture sequence to 14 days (longer consideration), add accredited investor verification step before 1-on-1, integrate with investor portal for document sharing
Use Case 2: B2B SaaS Enterprise Sales
- Industry: Enterprise software
- Scale: 50 leads/month, $100K+ annual contracts
- Modifications needed: Replace group presentations with personalized demo recordings, add multi-stakeholder tracking (decision maker, technical buyer, end user), integrate with Gong for call recording analysis
Use Case 3: Coaching Program Sales
- Industry: Executive coaching
- Scale: 150 leads/month, $15K-$30K programs
- Modifications needed: Add application form before presentation invitation, include personality assessment integration, create cohort-based presentation scheduling
Use Case 4: Medical Device Sales
- Industry: Healthcare equipment
- Scale: 75 leads/month, $200K+ devices
- Modifications needed: Add compliance documentation tracking, integrate with hospital procurement systems, include ROI calculator in nurture sequence
Use Case 5: Franchise Sales
- Industry: Restaurant franchises
- Scale: 100 leads/month, $500K+ investments
- Modifications needed: Add financial qualification step, integrate with FDD delivery system, create territory availability checker
Customizations & Extensions
Alternative Integrations
Instead of Microsoft Teams:
- Zoom: Best for larger audiences (500+ attendees) - requires swapping Teams nodes with Zoom API nodes (similar structure)
- WebinarJam: Better if you need advanced engagement features (polls, Q&A) - use WebinarJam webhooks for registration and attendance
- Google Meet: Use when your organization is Google Workspace-based - requires Google Calendar API integration
Instead of SendGrid:
- HubSpot Email: Best for maintaining all data in one platform - use HubSpot's email API instead of SendGrid nodes
- Mailgun: Better deliverability for transactional emails - swap SendGrid credentials with Mailgun
- Postmark: Use for highest delivery rates and detailed analytics - similar API structure to SendGrid
Workflow Extensions
Add automated reporting:
- Add a Schedule node to run Monday mornings at 8 AM
- Query HubSpot for deals created, presentations scheduled, contracts signed, and revenue closed in past week
- Connect to Google Slides API to generate executive summary presentations
- Email report to leadership team
- Nodes needed: +8 (Schedule, 4x HTTP Request for HubSpot queries, Function for data aggregation, Google Slides API, Email)
Scale to handle more leads:
- Replace daily schedule trigger with hourly checks for high-volume periods
- Implement Redis caching to store presentation schedules and avoid repeated API calls
- Add batch processing: query 100 contacts at a time instead of individual requests
- Use HubSpot's batch update endpoints to update multiple deals simultaneously
- Performance improvement: 5x faster for 500+ leads/day, reduces API calls by 80%
Add lead scoring:
- Track email open rates and link clicks in nurture sequence
- Assign points: email open = 5 points, link click = 10 points, presentation registration = 25 points, attendance = 50 points
- Store score in custom HubSpot property
- Route high-scoring leads (75+ points) to senior closers, lower scores to junior reps
- Nodes needed: +6 (SendGrid webhook for engagement tracking, Function for score calculation, HubSpot property update, Switch for routing)
Integration possibilities:
| Add This |
To Get This |
Complexity |
| Slack integration |
Real-time team notifications for hot leads |
Easy (2 nodes) |
| Calendly |
Automated 1-on-1 scheduling without manual coordination |
Easy (3 nodes) |
| Gong/Chorus |
Call recording and analysis for coaching |
Medium (5 nodes) |
| Twilio |
SMS reminders for presentations and 1-on-1s |
Medium (4 nodes) |
| Airtable |
Visual pipeline dashboard for management |
Medium (6 nodes) |
| Zapier Tables |
Backup data storage for compliance |
Medium (5 nodes) |
| Power BI |
Executive dashboards with real-time metrics |
Advanced (12 nodes) |
| Salesforce |
Enterprise CRM alternative to HubSpot |
Advanced (15+ nodes) |
Add multi-language support:
For international leads, detect language preference from HubSpot contact property and route to language-specific email templates:
const language = $json.properties.preferred_language || 'en';
const templateMap = {
'en': 'welcome_en',
'es': 'welcome_es',
'fr': 'welcome_fr'
};
return [{
json: {
...($json),
template: templateMap[language]
}
}];
Nodes needed: +3 (Function for language detection, Switch for routing, additional HTTP Request nodes for each language)
Implement A/B testing:
Test different email subject lines and send times:
const variant = Math.random() < 0.5 ? 'A' : 'B';
const subjectLines = {
'A': 'Exclusive Vacation Club Invitation',
'B': 'Your Travel Benefits Are Waiting'
};
return [{
json: {
...($json),
variant: variant,
subject: subjectLines[variant]
}
}];
Track open rates by variant in HubSpot custom properties, then analyze which performs better after 100 sends.
Get Started Today
Ready to automate your high-ticket sales funnel?
- Download the template: Scroll to the bottom of this article to copy the n8n workflow JSON
- Import to n8n: Go to Workflows → Import from URL or File, paste the JSON
- Configure your services: Add your API credentials for HubSpot, Microsoft Teams, SendGrid, DocuSign, and Stripe
- Customize deal stages: Update the pipeline stages to match your sales process
- Test with sample data: Create test contacts and verify each stage works before going live
- Deploy to production: Activate the workflow and monitor the first 10 leads closely
Need help customizing this workflow for your specific sales process? Schedule an intro call with Atherial at https://atherial.ai/contact to discuss your high-ticket funnel requirements and get expert implementation support.
Complete N8N Workflow Template
Copy the JSON below and import it into your N8N instance via Workflows → Import from File
{
"name": "High-Ticket Sales Funnel Automation",
"nodes": [
{
"id": "webhook_lead",
"name": "Lead Received Webhook",
"type": "n8n-nodes-base.webhook",
"position": [
100,
100
],
"parameters": {
"path": "sales-lead",
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "hubspot_create_lead",
"name": "Create/Update Lead in HubSpot",
"type": "n8n-nodes-base.hubspot",
"position": [
300,
100
],
"parameters": {
"email": "={{ $json.email }}",
"resource": "contact",
"operation": "upsert",
"jsonParameters": true,
"customPropertiesJson": "{\n \"firstname\": \"{{ $json.firstName }}\",\n \"lastname\": \"{{ $json.lastName }}\",\n \"phone\": \"{{ $json.phone }}\",\n \"lifecyclestage\": \"marketingqualifiedlead\"\n}"
},
"typeVersion": 2
},
{
"id": "check_deal_stage",
"name": "Check Deal Stage",
"type": "n8n-nodes-base.if",
"position": [
500,
100
],
"parameters": {
"conditions": {
"options": {
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.dealStage }}",
"rightValue": "group_presentation",
"caseSensitive": true
}
}
},
"typeVersion": 2
},
{
"id": "teams_notify_group",
"name": "Notify Team - Group Presentation",
"type": "n8n-nodes-base.microsoftTeams",
"position": [
700,
50
],
"parameters": {
"body": "📋 New High-Ticket Lead Available: {{ $json.firstName }} {{ $json.lastName }} ({{ $json.email }})\n\nQualification Score: {{ $json.qualificationScore }}\nBudget: {{ $json.estimatedBudget }}\n\nReady to schedule group presentation or 1-on-1 session?",
"teamId": "={{ $json.teamsTeamId }}",
"resource": "channelMessage",
"channelId": "={{ $json.teamsChannelId }}",
"operation": "create"
},
"typeVersion": 2
},
{
"id": "teams_notify_oneone",
"name": "Notify Team - 1-on-1 Session",
"type": "n8n-nodes-base.microsoftTeams",
"position": [
700,
200
],
"parameters": {
"body": "1️⃣ Lead Ready for 1-on-1 Session: {{ $json.firstName }} {{ $json.lastName }}\n\nSchedule individual session at: {{ $json.meetingUrl }}",
"teamId": "={{ $json.teamsTeamId }}",
"resource": "channelMessage",
"channelId": "={{ $json.teamsChannelId }}",
"operation": "create"
},
"typeVersion": 2
},
{
"id": "create_teams_meeting",
"name": "Create Teams Meeting",
"type": "n8n-nodes-base.httpRequest",
"position": [
900,
100
],
"parameters": {
"url": "https://graph.microsoft.com/v1.0/me/onlineMeetings",
"method": "POST",
"sendBody": true,
"contentType": "json",
"authentication": "genericCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "subject",
"value": "High-Ticket Sales Presentation - {{ $json.firstName }} {{ $json.lastName }}"
},
{
"name": "startDateTime",
"value": "={{ $json.scheduledTime }}"
},
{
"name": "endDateTime",
"value": "={{ $json.endTime }}"
},
{
"name": "isReminderOn",
"value": "true"
}
]
},
"genericAuthType": "httpHeaderApiKey"
},
"typeVersion": 4
},
{
"id": "get_hubspot_contact",
"name": "Get Contact Details from HubSpot",
"type": "n8n-nodes-base.hubspot",
"position": [
1100,
100
],
"parameters": {
"by": "email",
"email": "={{ $json.email }}",
"resource": "contact",
"operation": "get"
},
"typeVersion": 2
},
{
"id": "wait_presentation",
"name": "Wait for Presentation Completion",
"type": "n8n-nodes-base.wait",
"position": [
1300,
100
],
"parameters": {
"resume": "webhook",
"webhookNotice": "Waiting for attendee check-in confirmation",
"incomingAuthentication": "none"
},
"typeVersion": 1
},
{
"id": "check_attendance",
"name": "Check Attendance Status",
"type": "n8n-nodes-base.if",
"position": [
1500,
100
],
"parameters": {
"conditions": {
"options": {
"operator": {
"type": "boolean",
"operation": "true"
},
"leftValue": "={{ $json.attended }}",
"caseSensitive": true
}
}
},
"typeVersion": 2
},
{
"id": "track_no_show",
"name": "Track No-Show",
"type": "n8n-nodes-base.code",
"position": [
1700,
200
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const noShowData = {\n noShowCount: (item.noShowCount || 0) + 1,\n lastNoShowDate: new Date().toISOString(),\n status: 'no_show_follow_up',\n dealStage: 're_engagement'\n};\nreturn noShowData;",
"language": "javaScript"
},
"typeVersion": 2
},
{
"id": "send_noshow_email",
"name": "Send No-Show Follow-up Email",
"type": "n8n-nodes-base.emailSend",
"position": [
1900,
200
],
"parameters": {
"message": "Hi {{ $json.firstName }},\n\nWe noticed you couldn't make our scheduled presentation. No worries!\n\nWe'd love to reschedule at a time that works better for you. This is a limited-time VIP opportunity.\n\nLet's find a time that suits you best.\n\nBest regards,\nThe Vacation Club Team",
"subject": "We Missed You! Let's Reschedule Your VIP Presentation",
"toEmail": "={{ $json.email }}",
"fromEmail": "sales@vacationclub.com"
},
"typeVersion": 2
},
{
"id": "create_hubspot_deal",
"name": "Create Deal in HubSpot",
"type": "n8n-nodes-base.hubspot",
"position": [
1700,
50
],
"parameters": {
"title": "{{ $json.firstName }} {{ $json.lastName }} - Vacation Club Deal",
"resource": "deal",
"operation": "create",
"jsonParameters": true,
"customPropertiesJson": "{\n \"dealstage\": \"contract_sent\",\n \"amount\": \"{{ $json.packageValue }}\",\n \"closedate\": \"{{ $json.expectedCloseDate }}\"\n}"
},
"typeVersion": 2
},
{
"id": "send_docusign",
"name": "Send Contract via DocuSign",
"type": "n8n-nodes-base.httpRequest",
"position": [
1900,
50
],
"parameters": {
"url": "https://www.docusign.net/restapi/v2.1/accounts/{{ env.DOCUSIGN_ACCOUNT_ID }}/envelopes",
"method": "POST",
"sendBody": true,
"contentType": "json",
"authentication": "genericCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "emailSubject",
"value": "Please Sign: Vacation Club Membership Agreement"
},
{
"name": "recipients",
"value": "{{ $json.recipientEmail }}"
},
{
"name": "documentBase64",
"value": "={{ $json.contractDocument }}"
}
]
},
"genericAuthType": "httpHeaderApiKey"
},
"typeVersion": 4
},
{
"id": "wait_signature",
"name": "Wait for Contract Signature",
"type": "n8n-nodes-base.wait",
"position": [
2100,
100
],
"parameters": {
"resume": "webhook",
"webhookNotice": "Waiting for DocuSign signature completion",
"incomingAuthentication": "none"
},
"typeVersion": 1
},
{
"id": "check_signature",
"name": "Check Signature Status",
"type": "n8n-nodes-base.if",
"position": [
2300,
100
],
"parameters": {
"conditions": {
"options": {
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.signatureStatus }}",
"rightValue": "completed",
"caseSensitive": true
}
}
},
"typeVersion": 2
},
{
"id": "stripe_charge",
"name": "Process Stripe Payment",
"type": "n8n-nodes-base.stripe",
"position": [
2500,
100
],
"parameters": {
"amount": "={{ Math.round($json.packageValue * 100) }}",
"source": "{{ $json.cardToken }}",
"currency": "usd",
"resource": "charge",
"operation": "create",
"customerId": "={{ $json.stripeCustomerId }}"
},
"typeVersion": 1
},
{
"id": "map_payment_data",
"name": "Map Payment Data",
"type": "n8n-nodes-base.code",
"position": [
2700,
100
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const paymentData = {\n paymentId: item.chargeId,\n paymentAmount: item.amount / 100,\n paymentDate: new Date().toISOString(),\n paymentStatus: 'completed',\n dealStage: 'closed_won'\n};\nreturn paymentData;",
"language": "javaScript"
},
"typeVersion": 2
},
{
"id": "update_deal_closed",
"name": "Update Deal to Closed Won",
"type": "n8n-nodes-base.hubspot",
"position": [
2900,
100
],
"parameters": {
"dealId": "={{ $json.dealId }}",
"resource": "deal",
"operation": "update",
"jsonParameters": true,
"customPropertiesJson": "{\n \"dealstage\": \"closed_won\",\n \"closedate\": \"{{ $json.paymentDate }}\",\n \"closed_won_reason\": \"Payment completed successfully\"\n}"
},
"typeVersion": 2
},
{
"id": "send_welcome_email",
"name": "Send Welcome Email",
"type": "n8n-nodes-base.emailSend",
"position": [
3100,
100
],
"parameters": {
"message": "Hi {{ $json.firstName }},\n\nThank you for joining our premium vacation club!\n\nYour membership has been activated.\n\nYour membership details:\n- Membership ID: {{ $json.membershipId }}\n- Package: {{ $json.packageName }}\n- Benefits: Exclusive access to luxury vacation properties worldwide\n\nAccess your member portal: https://members.vacationclub.com/login\n\nWelcome aboard!\nThe Vacation Club Team",
"subject": "Welcome to Our Exclusive Vacation Club!",
"toEmail": "={{ $json.email }}",
"fromEmail": "sales@vacationclub.com"
},
"typeVersion": 2
},
{
"id": "teams_deal_closed",
"name": "Notify Teams - Deal Closed",
"type": "n8n-nodes-base.microsoftTeams",
"position": [
3300,
100
],
"parameters": {
"body": "✅ DEAL CLOSED: {{ $json.firstName }} {{ $json.lastName }}\n\nPayment Amount: ${{ $json.paymentAmount }}\nPayment Status: Completed\nMembership ID: {{ $json.membershipId }}\n\nCongratulations on the successful close!",
"teamId": "={{ $json.teamsTeamId }}",
"resource": "channelMessage",
"channelId": "={{ $json.teamsChannelId }}",
"operation": "create"
},
"typeVersion": 2
},
{
"id": "send_followup_email",
"name": "Send Follow-up Email",
"type": "n8n-nodes-base.emailSend",
"position": [
1700,
300
],
"parameters": {
"message": "Hi {{ $json.firstName }},\n\nWe'd love to schedule a personal consultation to discuss your vacation preferences.\n\nThis one-on-one session will help us find the perfect vacation package for you.\n\nAvailable times:\n- Tomorrow at 2:00 PM\n- Thursday at 10:00 AM\n- Friday at 3:00 PM\n\nPlease let us know which time works best for you.\n\nBest regards,\nYour Vacation Club Advisor",
"subject": "Let's Reschedule Your 1-on-1 Consultation",
"toEmail": "={{ $json.email }}",
"fromEmail": "sales@vacationclub.com"
},
"typeVersion": 2
},
{
"id": "respond_webhook",
"name": "Respond to Webhook",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
3500,
100
],
"parameters": {},
"typeVersion": 1
}
],
"connections": {
"Track No-Show": {
"main": [
[
{
"node": "Send No-Show Follow-up Email",
"type": "main",
"index": 0
}
]
]
},
"Check Deal Stage": {
"main": [
[
{
"node": "Notify Team - Group Presentation",
"type": "main",
"index": 0
}
],
[
{
"node": "Notify Team - 1-on-1 Session",
"type": "main",
"index": 0
}
]
]
},
"Map Payment Data": {
"main": [
[
{
"node": "Update Deal to Closed Won",
"type": "main",
"index": 0
}
]
]
},
"Send Welcome Email": {
"main": [
[
{
"node": "Notify Teams - Deal Closed",
"type": "main",
"index": 0
}
]
]
},
"Create Teams Meeting": {
"main": [
[
{
"node": "Get Contact Details from HubSpot",
"type": "main",
"index": 0
}
]
]
},
"Send Follow-up Email": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
},
"Lead Received Webhook": {
"main": [
[
{
"node": "Create/Update Lead in HubSpot",
"type": "main",
"index": 0
}
]
]
},
"Check Signature Status": {
"main": [
[
{
"node": "Process Stripe Payment",
"type": "main",
"index": 0
}
],
[]
]
},
"Create Deal in HubSpot": {
"main": [
[
{
"node": "Send Contract via DocuSign",
"type": "main",
"index": 0
}
]
]
},
"Process Stripe Payment": {
"main": [
[
{
"node": "Map Payment Data",
"type": "main",
"index": 0
}
]
]
},
"Check Attendance Status": {
"main": [
[
{
"node": "Create Deal in HubSpot",
"type": "main",
"index": 0
}
],
[
{
"node": "Track No-Show",
"type": "main",
"index": 0
}
]
]
},
"Update Deal to Closed Won": {
"main": [
[
{
"node": "Send Welcome Email",
"type": "main",
"index": 0
}
]
]
},
"Notify Teams - Deal Closed": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
},
"Send Contract via DocuSign": {
"main": [
[
{
"node": "Wait for Contract Signature",
"type": "main",
"index": 0
}
]
]
},
"Wait for Contract Signature": {
"main": [
[
{
"node": "Check Signature Status",
"type": "main",
"index": 0
}
]
]
},
"Notify Team - 1-on-1 Session": {
"main": [
[
{
"node": "Send Follow-up Email",
"type": "main",
"index": 0
}
]
]
},
"Send No-Show Follow-up Email": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
},
"Create/Update Lead in HubSpot": {
"main": [
[
{
"node": "Check Deal Stage",
"type": "main",
"index": 0
}
]
]
},
"Get Contact Details from HubSpot": {
"main": [
[
{
"node": "Wait for Presentation Completion",
"type": "main",
"index": 0
}
]
]
},
"Notify Team - Group Presentation": {
"main": [
[
{
"node": "Create Teams Meeting",
"type": "main",
"index": 0
}
]
]
},
"Wait for Presentation Completion": {
"main": [
[
{
"node": "Check Attendance Status",
"type": "main",
"index": 0
}
]
]
}
}
}