Pre-meeting email nurturing sequence with Cal.com and Gmail
Pull contacts, verify each address with BillionVerify, and continue to Cal.com — only deliverable addresses get through.
Why verify before the send
Sending to invalid, risky, catch-all, or disposable addresses spikes your bounce rate and erodes sender reputation. A verification gate before the Cal.com step removes that risk automatically — only deliverable addresses continue, the rest are flagged.
The workflow
BillionVerify — verification sits right before the send.
Node by node
- 1Cal.com TriggerTrigger· n8n
Starts the workflow — on a schedule, a webhook, or manually while you test.
- 2Extract prospectSource· n8n
Provides or transforms the contact data flowing through the workflow.
- 3IfLogic· n8n
Branches on the verification result: only deliverable addresses continue to the send; the rest are skipped and flagged.
- 4Calculate timeSource· n8n
Provides or transforms the contact data flowing through the workflow.
- 5Switch: Days Until MeetingLogic· n8n
Routes items based on the workflow logic.
- 6Quick prep emailSource· n8n
Provides or transforms the contact data flowing through the workflow.
- 7Verify Email (BillionVerify)Verify· billionverify
The BillionVerify node verifies the address — status (valid / invalid / risky / catch-all / role / disposable), is_deliverable, and a confidence score — before anything is sent.
- 8Verify Email (BillionVerify) 5Verify· billionverify
The BillionVerify node verifies the address — status (valid / invalid / risky / catch-all / role / disposable), is_deliverable, and a confidence score — before anything is sent.
- 9Verify Email (BillionVerify) 4Verify· billionverify
The BillionVerify node verifies the address — status (valid / invalid / risky / catch-all / role / disposable), is_deliverable, and a confidence score — before anything is sent.
- 10IF deliverableLogic· n8n
Branches on the verification result: only deliverable addresses continue to the send; the rest are skipped and flagged.
- 11IF deliverable 5Logic· n8n
Branches on the verification result: only deliverable addresses continue to the send; the rest are skipped and flagged.
- 12IF deliverable 4Logic· n8n
Branches on the verification result: only deliverable addresses continue to the send; the rest are skipped and flagged.
- 13Casual flexSend· n8n
Sends only to verified, deliverable addresses. Swap in your own provider node if you send elsewhere.
- 14Casual flex 8+ daysSend· n8n
Sends only to verified, deliverable addresses. Swap in your own provider node if you send elsewhere.
- 15Send itSend· n8n
Sends only to verified, deliverable addresses. Swap in your own provider node if you send elsewhere.
- 16Wait 1 DaySource· n8n
Provides or transforms the contact data flowing through the workflow.
- 17Wait 1 Day1Source· n8n
Provides or transforms the contact data flowing through the workflow.
- 18Verify Email (BillionVerify) 2Verify· billionverify
The BillionVerify node verifies the address — status (valid / invalid / risky / catch-all / role / disposable), is_deliverable, and a confidence score — before anything is sent.
- 19Verify Email (BillionVerify) 6Verify· billionverify
The BillionVerify node verifies the address — status (valid / invalid / risky / catch-all / role / disposable), is_deliverable, and a confidence score — before anything is sent.
- 20IF deliverable 2Logic· n8n
Branches on the verification result: only deliverable addresses continue to the send; the rest are skipped and flagged.
- 21IF deliverable 6Logic· n8n
Branches on the verification result: only deliverable addresses continue to the send; the rest are skipped and flagged.
- 22Casual pressSend· n8n
Sends only to verified, deliverable addresses. Swap in your own provider node if you send elsewhere.
- 23Casual press 8+daysSend· n8n
Sends only to verified, deliverable addresses. Swap in your own provider node if you send elsewhere.
- 24Wait another daySource· n8n
Provides or transforms the contact data flowing through the workflow.
- 25Wait another day 8+daysSource· n8n
Provides or transforms the contact data flowing through the workflow.
- 26Verify Email (BillionVerify) 3Verify· billionverify
The BillionVerify node verifies the address — status (valid / invalid / risky / catch-all / role / disposable), is_deliverable, and a confidence score — before anything is sent.
- 27Verify Email (BillionVerify) 7Verify· billionverify
The BillionVerify node verifies the address — status (valid / invalid / risky / catch-all / role / disposable), is_deliverable, and a confidence score — before anything is sent.
- 28IF deliverable 3Logic· n8n
Branches on the verification result: only deliverable addresses continue to the send; the rest are skipped and flagged.
- 29IF deliverable 7Logic· n8n
Branches on the verification result: only deliverable addresses continue to the send; the rest are skipped and flagged.
- 30Casual knowledge flexSend· n8n
Sends only to verified, deliverable addresses. Swap in your own provider node if you send elsewhere.
- 31Casual knowledge flex 8+daysSend· n8n
Sends only to verified, deliverable addresses. Swap in your own provider node if you send elsewhere.
- 32WaitSource· n8n
Provides or transforms the contact data flowing through the workflow.
- 33Wait1Source· n8n
Provides or transforms the contact data flowing through the workflow.
- 34Quick prep email (2)Source· n8n
Provides or transforms the contact data flowing through the workflow.
- 35Quick prep email2Source· n8n
Provides or transforms the contact data flowing through the workflow.
- 36Verify Email (BillionVerify) 8Verify· billionverify
The BillionVerify node verifies the address — status (valid / invalid / risky / catch-all / role / disposable), is_deliverable, and a confidence score — before anything is sent.
- 37Verify Email (BillionVerify) 9Verify· billionverify
The BillionVerify node verifies the address — status (valid / invalid / risky / catch-all / role / disposable), is_deliverable, and a confidence score — before anything is sent.
- 38IF deliverable 8Logic· n8n
Branches on the verification result: only deliverable addresses continue to the send; the rest are skipped and flagged.
- 39IF deliverable 9Logic· n8n
Branches on the verification result: only deliverable addresses continue to the send; the rest are skipped and flagged.
- 40Quick prepSend· n8n
Sends only to verified, deliverable addresses. Swap in your own provider node if you send elsewhere.
- 41Quick prep +8daysSend· n8n
Sends only to verified, deliverable addresses. Swap in your own provider node if you send elsewhere.
Workflow JSON
Copy or download this workflow, then import it in n8n (Workflows → Import from File / Paste). Install the BillionVerify community node first, then add your API key credential.
{
"name": "Pre-meeting email nurturing sequence with Cal.com and Gmail + BillionVerify",
"nodes": [
{
"id": "f63b66f8-d376-46a0-8966-f66fe5739a38",
"name": "Cal.com Trigger",
"type": "n8n-nodes-base.calTrigger",
"position": [
240,
464
],
"webhookId": "419db506-3586-4c55-908b-d15efb6677a0",
"parameters": {
"events": [
"BOOKING_CREATED"
],
"options": {}
},
"credentials": {
"calApi": {
"id": "credential-id",
"name": "calApi Credential"
}
},
"typeVersion": 2
},
{
"id": "b1d64da4-5a5e-47ad-bd1d-e8741a675157",
"name": "If",
"type": "n8n-nodes-base.if",
"position": [
688,
464
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "425c0f1f-dd29-4a73-9f75-b4a26e4435f9",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.is_company_email }}",
"rightValue": "true"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "035248af-3e48-4a48-b552-4ec9ff71dde6",
"name": "Calculate time",
"type": "n8n-nodes-base.code",
"position": [
912,
464
],
"parameters": {
"jsCode": "// Get meeting start time from original Cal.com data\nconst meetingStartTime = $('Extract prospect').item.json.meeting_start_time;\n\n// Calculate days until meeting\nconst meetingDate = new Date(meetingStartTime);\nconst today = new Date();\nconst diffTime = meetingDate - today;\nconst diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));\n\n// Get all previous data\nconst prospectData = $('Extract prospect').item.json;\nconst aiData = $input.item.json;\n\nreturn {\n json: {\n // Original prospect data\n attendee_email: prospectData.attendee_email,\n attendee_name: prospectData.attendee_name,\n meeting_start_time: prospectData.meeting_start_time,\n meeting_title: prospectData.meeting_title,\n email_domain: prospectData.email_domain,\n company_name: prospectData.company_name, // ← ADDED\n website_url: prospectData.website_url,\n \n // AI analysis data\n has_website_data: aiData.has_website_data,\n business_type: aiData.business_type,\n tech_stack: aiData.tech_stack,\n likely_pain_points: aiData.likely_pain_points,\n personalization_hook: aiData.personalization_hook,\n \n // Days calculation\n days_until_meeting: diffDays\n }\n};"
},
"typeVersion": 2
},
{
"id": "70c2a2c4-02db-42c1-bdad-fcbb5469bb49",
"name": "Extract prospect",
"type": "n8n-nodes-base.code",
"position": [
464,
464
],
"parameters": {
"jsCode": "// Get attendee email from Cal.com webhook\nconst attendeeEmail = $input.item.json.attendees[0].email;\nconst attendeeName = $input.item.json.attendees[0].name;\nconst meetingStartTime = $input.item.json.startTime;\nconst meetingTitle = $input.item.json.eventTitle;\n\n// Get company name from custom field (if provided)\nconst companyName = $input.item.json.responses['Company-Name']?.value || null;\n\n// Extract domain from email\nconst domain = attendeeEmail.split('@')[1];\n\n// List of consumer email providers\nconst consumerDomains = [\n 'gmail.com', 'yahoo.com', 'outlook.com', 'hotmail.com',\n 'aol.com', 'icloud.com', 'protonmail.com', 'mail.com',\n 'live.com', 'msn.com', 'ymail.com', 'googlemail.com'\n];\n\n// Check if it's a company email\nconst isCompanyEmail = !consumerDomains.includes(domain.toLowerCase());\n\n// Build website URL - use company name if provided and consumer email, otherwise use domain\nlet websiteUrl = null;\nif (companyName && !isCompanyEmail) {\n // They gave us company name but have Gmail - try to build URL from company name\n websiteUrl = `https://${companyName.toLowerCase().replace(/\\s+/g, '')}.com`;\n} else if (isCompanyEmail) {\n // Company email - use domain\n websiteUrl = `https://${domain}`;\n}\n\nreturn {\n json: {\n attendee_email: attendeeEmail,\n attendee_name: attendeeName,\n meeting_start_time: meetingStartTime,\n meeting_title: meetingTitle,\n email_domain: domain,\n company_name: companyName,\n is_company_email: isCompanyEmail,\n website_url: websiteUrl\n }\n};"
},
"typeVersion": 2
},
{
"id": "14f95913-ea00-4041-aacd-329ac4770e65",
"name": "Wait 1 Day",
"type": "n8n-nodes-base.wait",
"position": [
1584,
464
],
"webhookId": "3c81bf74-3632-4011-b07f-e2486e12ebe6",
"parameters": {},
"typeVersion": 1.1
},
{
"id": "65db5d98-cca3-4ba0-89b9-d583e8b4fb75",
"name": "Switch: Days Until Meeting",
"type": "n8n-nodes-base.switch",
"position": [
1136,
448
],
"parameters": {
"rules": {
"values": [
{
"outputKey": "0–1 days",
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "number",
"operation": "lte"
},
"leftValue": "={{ $json.days_until_meeting }}",
"rightValue": 1
}
]
},
"renameOutput": true
},
{
"outputKey": "2–7 days",
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "number",
"operation": "lte"
},
"leftValue": "={{ $json.days_until_meeting }}",
"rightValue": 7
},
{
"operator": {
"type": "number",
"operation": "gt"
},
"leftValue": "={{ $json.days_until_meeting }}",
"rightValue": 1
}
]
},
"renameOutput": true
},
{
"outputKey": "8+ days",
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "dd9c015f-4dcc-4577-9c12-a610e167dccb",
"operator": {
"type": "number",
"operation": "gt"
},
"leftValue": "={{ $json.days_until_meeting }}",
"rightValue": 7
}
]
},
"renameOutput": true
}
]
},
"options": {
"fallbackOutput": "none",
"looseTypeValidation": true
}
},
"typeVersion": 3
},
{
"id": "074aa12d-bc36-46a6-b10f-6dc144d76ce6",
"name": "Quick prep email",
"type": "n8n-nodes-base.set",
"position": [
1360,
272
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "a008a6aa-e391-4684-87d7-42c0a19540ae",
"name": "email",
"type": "string",
"value": "={{(() => {\n const firstName = $json.attendee_name.split(' ')[0];\n const company = $json.company_name;\n const rawDate = new Date($json.meeting_start_time);\n const now = new Date();\n\n const isTomorrow = rawDate.getDate() === (now.getDate() + 1) &&\n rawDate.getMonth() === now.getMonth() &&\n rawDate.getFullYear() === now.getFullYear();\n\n const options = { hour: 'numeric', minute: '2-digit', hour12: true };\n const timeString = rawDate.toLocaleTimeString('en-US', options).toLowerCase().replace(':00', '');\n\n const daysOfWeek = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];\n const dayName = daysOfWeek[rawDate.getDay()];\n\n const friendlyTime = isTomorrow ? `${timeString} tomorrow` : `${timeString} on ${dayName}`;\n\n return `Hey ${firstName},\n\nAppreciate you booking — looking forward to chatting at ${friendlyTime}.\n\nTo make this actually useful for both of us, quick q’s:\n\n– What’s the biggest thing you’re stuck on right now?\n– Are you the main decider on this, or is someone else looped in?\n\nDoesn’t need to be a long reply — just gives me a bit of context so I can prep around what matters for ${company}.\n\nCatch you soon 🙏`;\n})()}}\n"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "a88320c9-184a-40b3-9cac-14b5d2cdddf5",
"name": "Casual flex",
"type": "n8n-nodes-base.gmail",
"position": [
1360,
464
],
"webhookId": "5bdbdeb7-29c5-464c-ab86-9a31ebcded9b",
"parameters": {
"sendTo": "={{ $('Calculate time').item.json.attendee_email }}",
"message": "=Looking forward to chat about how we can connect {{ $('Calculate time').item.json.company_name }} with ideal clients before your competitors\n\nTo prep: Think about your ideal client profile and how many intros per month would move the needle for you.\n\nPS: not to flex here but — we helped Vention add $85K last quarter using the same system\n\nCatch you then 🙏\n\n— Maks\n",
"options": {},
"subject": "=Meeting confirmed 👋🏻",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"id": "credential-id",
"name": "gmailOAuth2 Credential"
}
},
"typeVersion": 2.1
},
{
"id": "d72e7083-48db-4ca6-821c-84ce0f64270b",
"name": "Casual press",
"type": "n8n-nodes-base.gmail",
"position": [
1808,
464
],
"webhookId": "5bdbdeb7-29c5-464c-ab86-9a31ebcded9b",
"parameters": {
"sendTo": "={{ $('Calculate time').item.json.attendee_email }}",
"message": "=Hey {{ $json.attendee_name.split(' ')[0] }}—\n\nQuick context about who I am:\n\nI’ve been deep in the industry for a while now. At this point, I’ve seen what works (and what doesn’t) when referals dry up\n\nOver time, I started connecting the right firms to the right buyers — not with ads or cold pitches, but through early, quiet convos before things go live.\n\nI’m not an agency — I’m more like a researcher/network hub depending on the setup.\n\nRight now I’m helping companies in your space connect with folks who’ve already proven they can deliver.\n\nThought I’d share this to give you a quick idea before we chat 🙏\n\nCheers\n\n— Maks",
"options": {},
"subject": "=Meeting confirmed 👋🏻",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"id": "credential-id",
"name": "gmailOAuth2 Credential"
}
},
"typeVersion": 2.1
},
{
"id": "1d8e95e9-aa8b-4e61-bd4f-643dc22ba075",
"name": "Casual knowledge flex",
"type": "n8n-nodes-base.gmail",
"position": [
2256,
464
],
"webhookId": "2166b2e3-0908-479f-8293-cc4232136c4b",
"parameters": {
"sendTo": "={{ $('Calculate time').item.json.attendee_email }}",
"message": "=Hey again {{ $json.attendee_name.split(' ')[0] }}, \n\nMaks here\n\nA lot of teams we’ve worked with hit the same wall — outbound either burns cash or burns out their team.\n\nReferrals slow down, ads get expensive, and internal SDRs can’t scale.\n\nOne thing that tends to move the needle early:\n\nRunning early-intro campaigns that quietly connect you to decision-makers before roles go public, since:\n\n- you skip the queue — connect before roles/postings go live\n- you stay invisible to competitors — no noisy job boards or bidding wars\n- you own the timing — you’re not waiting for inbound or referral luck\n\nLet me know if this resonates — happy to adapt our call.\n\n— Maks",
"options": {},
"subject": "quick thought"
},
"credentials": {
"gmailOAuth2": {
"id": "credential-id",
"name": "gmailOAuth2 Credential"
}
},
"typeVersion": 2.1
},
{
"id": "162b2e87-a79c-48f5-998c-de9bf5f5f27e",
"name": "Send it",
"type": "n8n-nodes-base.gmail",
"position": [
1584,
272
],
"webhookId": "3c58d302-25c8-4bb8-9332-601402d43958",
"parameters": {
"sendTo": "={{ $('Calculate time').item.json.attendee_email }}",
"message": "={{ $json.email }}",
"options": {
"appendAttribution": false
},
"subject": "Quick prep for tomorrow's call 🙏🏻",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"id": "credential-id",
"name": "gmailOAuth2 Credential"
}
},
"typeVersion": 2.1
},
{
"id": "e494daf8-064a-445e-924f-97623573764b",
"name": "Wait another day",
"type": "n8n-nodes-base.wait",
"position": [
2032,
464
],
"webhookId": "b5bee3b1-ee44-4c0c-8daa-f98e5704a27d",
"parameters": {},
"typeVersion": 1.1
},
{
"id": "5fa9f448-f8e8-4a57-bbfd-150e9c3e9cb6",
"name": "Wait 1 Day1",
"type": "n8n-nodes-base.wait",
"position": [
1584,
656
],
"webhookId": "3c81bf74-3632-4011-b07f-e2486e12ebe6",
"parameters": {},
"typeVersion": 1.1
},
{
"id": "292b1910-34c4-4b3c-9da7-5624f605c301",
"name": "Casual flex 8+ days",
"type": "n8n-nodes-base.gmail",
"position": [
1360,
656
],
"webhookId": "5bdbdeb7-29c5-464c-ab86-9a31ebcded9b",
"parameters": {
"sendTo": "={{ $('Calculate time').item.json.attendee_email }}",
"message": "=Excited to connect with {{ $('Calculate time').item.json.company_name }} soon — plenty of time to prep, so I’ll keep it light for now.\n\nIf you get a spare moment, it’s worth reflecting on your ideal client profile — and how many intros per month would make a real difference for your team.\n\nPS: We helped Vention add $85K last quarter using this same system. Looking forward to exploring how it could map to your setup too.\n\nCatch you soon 🙏\n\n— Maks\n",
"options": {},
"subject": "=Looking ahead to our call 👋🏻",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"id": "credential-id",
"name": "gmailOAuth2 Credential"
}
},
"typeVersion": 2.1
},
{
"id": "48e0d4d3-64bb-49df-8b2f-d950b3f39ca0",
"name": "Casual press 8+days",
"type": "n8n-nodes-base.gmail",
"position": [
1808,
656
],
"webhookId": "5bdbdeb7-29c5-464c-ab86-9a31ebcded9b",
"parameters": {
"sendTo": "={{ $('Calculate time').item.json.attendee_email }}",
"message": "=Hey {{ $json.attendee_name.split(' ')[0] }}—\n\nQuick context about who I am:\n\nI’ve been deep in the indusry for a while now. At this point, I’ve seen what works (and what doesn’t) when referals dry up\n\nOver time, I started connecting the right firms to the right buyers — not with ads or cold pitches, but through early, quiet convos before things go live.\n\nI’m not an agency — I’m more like a researcher/network hub depending on the setup.\n\nRight now I’m helping companines in your space connect with folks who’ve already proven they can deliver.\n\nThought I’d share this to give you a quick idea before we chat 🙏\n\nCheers\n\n— Maks",
"options": {},
"subject": "=Meeting confirmed 👋🏻",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"id": "credential-id",
"name": "gmailOAuth2 Credential"
}
},
"typeVersion": 2.1
},
{
"id": "17645622-c90a-4df3-b682-fee7aa2bd402",
"name": "Wait another day 8+days",
"type": "n8n-nodes-base.wait",
"position": [
2032,
656
],
"webhookId": "b5bee3b1-ee44-4c0c-8daa-f98e5704a27d",
"parameters": {},
"typeVersion": 1.1
},
{
"id": "50b296d1-235c-45fe-8859-25633979854d",
"name": "Casual knowledge flex 8+days",
"type": "n8n-nodes-base.gmail",
"position": [
2256,
656
],
"webhookId": "2166b2e3-0908-479f-8293-cc4232136c4b",
"parameters": {
"sendTo": "={{ $('Calculate time').item.json.attendee_email }}",
"message": "=Hey again {{ $json.attendee_name.split(' ')[0] }}, \n\nMaks here\n\nA lot of {{ $json.business_type }} teams we worked with struggle with {{ $json.likely_pain_points }}. \n\nHere's one thing that tends to move the needle early: [Insert tactic, framework, or article] \n\nLet me know if this resonates — happy to adapt our call.\n\n— Maks",
"options": {},
"subject": "insight"
},
"credentials": {
"gmailOAuth2": {
"id": "credential-id",
"name": "gmailOAuth2 Credential"
}
},
"typeVersion": 2.1
},
{
"id": "cdebfebe-b561-4560-92ea-a5574babeeb6",
"name": "Wait",
"type": "n8n-nodes-base.wait",
"position": [
2480,
464
],
"webhookId": "f3d26cc4-72be-4b47-89f4-a0c5bd3a19ea",
"parameters": {
"unit": "days",
"amount": 1
},
"typeVersion": 1.1
},
{
"id": "ea936593-456d-487d-9568-140428c19c36",
"name": "Quick prep email2",
"type": "n8n-nodes-base.set",
"position": [
2704,
656
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "a008a6aa-e391-4684-87d7-42c0a19540ae",
"name": "email",
"type": "string",
"value": "={{(() => {\n const firstName = $json.attendee_name.split(' ')[0];\n const company = $json.company_name;\n const rawDate = new Date($json.meeting_start_time);\n const now = new Date();\n\n const isTomorrow = rawDate.getDate() === (now.getDate() + 1) &&\n rawDate.getMonth() === now.getMonth() &&\n rawDate.getFullYear() === now.getFullYear();\n\n const options = { hour: 'numeric', minute: '2-digit', hour12: true };\n const timeString = rawDate.toLocaleTimeString('en-US', options).toLowerCase().replace(':00', '');\n\n const daysOfWeek = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];\n const dayName = daysOfWeek[rawDate.getDay()];\n\n const friendlyTime = isTomorrow ? `${timeString} tomorrow` : `${timeString} on ${dayName}`;\n\n return `Hey ${firstName},\n\nAppreciate you booking — looking forward to chatting at ${friendlyTime}.\n\nTo make this actually useful for both of us, quick q’s:\n\n– What’s the biggest thing you’re stuck on right now?\n– Are you the main decider on this, or is someone else looped in?\n\nDoesn’t need to be a long reply — just gives me a bit of context so I can prep around what matters for ${company}.\n\nCatch you soon 🙏`;\n})()}}\n"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "7ee51230-b333-4f40-a0fd-aa39b23c8013",
"name": "Quick prep",
"type": "n8n-nodes-base.gmail",
"position": [
2928,
464
],
"webhookId": "3c58d302-25c8-4bb8-9332-601402d43958",
"parameters": {
"sendTo": "={{ $('Calculate time').item.json.attendee_email }}",
"message": "={{ $json.email }}",
"options": {
"appendAttribution": true
},
"subject": "Quick prep for tomorrow's call 🙏🏻",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"id": "credential-id",
"name": "gmailOAuth2 Credential"
}
},
"typeVersion": 2.1
},
{
"id": "630c45e5-5fb5-417e-83f6-fee3f56998d3",
"name": "Quick prep +8days",
"type": "n8n-nodes-base.gmail",
"position": [
2928,
656
],
"webhookId": "3c58d302-25c8-4bb8-9332-601402d43958",
"parameters": {
"sendTo": "={{ $('Calculate time').item.json.attendee_email }}",
"message": "={{ $json.email }}",
"options": {
"appendAttribution": true
},
"subject": "Quick prep for tomorrow's call 🙏🏻",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"id": "credential-id",
"name": "gmailOAuth2 Credential"
}
},
"typeVersion": 2.1
},
{
"id": "e4b7cae4-af14-40b2-bc33-04e14f450cea",
"name": "Quick prep email (2)",
"type": "n8n-nodes-base.set",
"position": [
2704,
464
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "a008a6aa-e391-4684-87d7-42c0a19540ae",
"name": "email",
"type": "string",
"value": "={{(() => {\n const firstName = $json.attendee_name.split(' ')[0];\n const company = $json.company_name;\n const rawDate = new Date($json.meeting_start_time);\n const now = new Date();\n\n const isTomorrow = rawDate.getDate() === (now.getDate() + 1) &&\n rawDate.getMonth() === now.getMonth() &&\n rawDate.getFullYear() === now.getFullYear();\n\n const options = { hour: 'numeric', minute: '2-digit', hour12: true };\n const timeString = rawDate.toLocaleTimeString('en-US', options).toLowerCase().replace(':00', '');\n\n const daysOfWeek = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];\n const dayName = daysOfWeek[rawDate.getDay()];\n\n const friendlyTime = isTomorrow ? `${timeString} tomorrow` : `${timeString} on ${dayName}`;\n\n return `Hey ${firstName},\n\nAppreciate you booking — looking forward to chatting at ${friendlyTime}.\n\nTo make this actually useful for both of us, quick q’s:\n\n– What’s the biggest thing you’re stuck on right now?\n– Are you the main decider on this, or is someone else looped in?\n\nDoesn’t need to be a long reply — just gives me a bit of context so I can prep around what matters for ${company}.\n\nCatch you soon 🙏`;\n})()}}\n"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "e317772f-47fa-4007-8166-3a67e1a60512",
"name": "Wait1",
"type": "n8n-nodes-base.wait",
"position": [
2480,
656
],
"webhookId": "8b5c6e4f-41d6-4817-996d-909ac6f94f8c",
"parameters": {
"unit": "days",
"amount": 1
},
"typeVersion": 1.1
},
{
"id": "210fa6ca-168f-437b-8f18-0ac5e2004521",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-448,
0
],
"parameters": {
"width": 592,
"height": 720,
"content": "# ⭐ Overview \n**Automated Pre-Meeting Email Sequence for Cal.com Bookings**\n\nThis workflow automatically warms up your leads between the moment they book a meeting and the day the meeting happens. It pulls in new Cal.com bookings, checks how many days remain until the meeting, and sends a series of pre-written emails spaced over time. The goal is to increase show-up rates, set expectations, and gently steer the user toward understanding the “connector” or sales-oriented purpose of the call.\n\nInstead of manually sending reminders or hoping your lead reads the booking confirmation, this workflow creates a structured drip sequence leading up to the meeting. Each email is triggered based on time intervals (for example: immediately, 3 days before, 1 day before). You have full control over the email content and timing.\n\n### How it works\n1. Trigger fires when someone books a meeting in Cal.com. \n2. The workflow calculates how many days remain before the call. \n3. Based on timing, the appropriate warm-up emails are sent via Gmail. \n4. Additional Wait nodes space out messages to ensure natural engagement.\n\n### Setup steps\n1. Add your **Cal.com API key** in the Trigger node. \n2. Authenticate your **Google account** for Gmail. \n3. Customize each email message to match your tone and purpose. \n4. Test internally with sample bookings before enabling it live.\n\n### Customization\nAdjust wait durations, timing logic, or create separate sequences for different event types in Cal.com. You can also use other calendar apps like calendly. You just need to change the trigger. "
},
"typeVersion": 1
},
{
"id": "fe992484-d156-46f1-8cff-51380e5fcaee",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
1312,
144
],
"parameters": {
"color": 7,
"width": 1840,
"height": 720,
"content": "## Nurturing Message\nYou can change the message for your needs and duration before sending the messages in here."
},
"typeVersion": 1
},
{
"parameters": {
"operation": "verify",
"email": "={{ $('Calculate time').item.json.attendee_email }}",
"additionalOptions": {}
},
"type": "n8n-nodes-billionverify.billionVerify",
"typeVersion": 1,
"position": [
1000,
464
],
"name": "Verify Email (BillionVerify)",
"credentials": {
"billionVerifyApi": {
"id": "",
"name": "BillionVerify account"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "is-deliverable",
"leftValue": "={{ $json.is_deliverable }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
]
}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1180,
464
],
"name": "IF deliverable"
},
{
"parameters": {
"operation": "verify",
"email": "={{ $('Calculate time').item.json.attendee_email }}",
"additionalOptions": {}
},
"type": "n8n-nodes-billionverify.billionVerify",
"typeVersion": 1,
"position": [
1448,
464
],
"name": "Verify Email (BillionVerify) 2",
"credentials": {
"billionVerifyApi": {
"id": "",
"name": "BillionVerify account"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "is-deliverable",
"leftValue": "={{ $json.is_deliverable }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
]
}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1628,
464
],
"name": "IF deliverable 2"
},
{
"parameters": {
"operation": "verify",
"email": "={{ $('Calculate time').item.json.attendee_email }}",
"additionalOptions": {}
},
"type": "n8n-nodes-billionverify.billionVerify",
"typeVersion": 1,
"position": [
1896,
464
],
"name": "Verify Email (BillionVerify) 3",
"credentials": {
"billionVerifyApi": {
"id": "",
"name": "BillionVerify account"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "is-deliverable",
"leftValue": "={{ $json.is_deliverable }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
]
}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
2076,
464
],
"name": "IF deliverable 3"
},
{
"parameters": {
"operation": "verify",
"email": "={{ $('Calculate time').item.json.attendee_email }}",
"additionalOptions": {}
},
"type": "n8n-nodes-billionverify.billionVerify",
"typeVersion": 1,
"position": [
1224,
272
],
"name": "Verify Email (BillionVerify) 4",
"credentials": {
"billionVerifyApi": {
"id": "",
"name": "BillionVerify account"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "is-deliverable",
"leftValue": "={{ $json.is_deliverable }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
]
}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1404,
272
],
"name": "IF deliverable 4"
},
{
"parameters": {
"operation": "verify",
"email": "={{ $('Calculate time').item.json.attendee_email }}",
"additionalOptions": {}
},
"type": "n8n-nodes-billionverify.billionVerify",
"typeVersion": 1,
"position": [
1000,
656
],
"name": "Verify Email (BillionVerify) 5",
"credentials": {
"billionVerifyApi": {
"id": "",
"name": "BillionVerify account"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "is-deliverable",
"leftValue": "={{ $json.is_deliverable }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
]
}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1180,
656
],
"name": "IF deliverable 5"
},
{
"parameters": {
"operation": "verify",
"email": "={{ $('Calculate time').item.json.attendee_email }}",
"additionalOptions": {}
},
"type": "n8n-nodes-billionverify.billionVerify",
"typeVersion": 1,
"position": [
1448,
656
],
"name": "Verify Email (BillionVerify) 6",
"credentials": {
"billionVerifyApi": {
"id": "",
"name": "BillionVerify account"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "is-deliverable",
"leftValue": "={{ $json.is_deliverable }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
]
}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1628,
656
],
"name": "IF deliverable 6"
},
{
"parameters": {
"operation": "verify",
"email": "={{ $('Calculate time').item.json.attendee_email }}",
"additionalOptions": {}
},
"type": "n8n-nodes-billionverify.billionVerify",
"typeVersion": 1,
"position": [
1896,
656
],
"name": "Verify Email (BillionVerify) 7",
"credentials": {
"billionVerifyApi": {
"id": "",
"name": "BillionVerify account"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "is-deliverable",
"leftValue": "={{ $json.is_deliverable }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
]
}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
2076,
656
],
"name": "IF deliverable 7"
},
{
"parameters": {
"operation": "verify",
"email": "={{ $('Calculate time').item.json.attendee_email }}",
"additionalOptions": {}
},
"type": "n8n-nodes-billionverify.billionVerify",
"typeVersion": 1,
"position": [
2568,
464
],
"name": "Verify Email (BillionVerify) 8",
"credentials": {
"billionVerifyApi": {
"id": "",
"name": "BillionVerify account"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "is-deliverable",
"leftValue": "={{ $json.is_deliverable }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
]
}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
2748,
464
],
"name": "IF deliverable 8"
},
{
"parameters": {
"operation": "verify",
"email": "={{ $('Calculate time').item.json.attendee_email }}",
"additionalOptions": {}
},
"type": "n8n-nodes-billionverify.billionVerify",
"typeVersion": 1,
"position": [
2568,
656
],
"name": "Verify Email (BillionVerify) 9",
"credentials": {
"billionVerifyApi": {
"id": "",
"name": "BillionVerify account"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "is-deliverable",
"leftValue": "={{ $json.is_deliverable }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
]
}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
2748,
656
],
"name": "IF deliverable 9"
}
],
"connections": {
"If": {
"main": [
[
{
"node": "Calculate time",
"type": "main",
"index": 0
}
]
]
},
"Wait": {
"main": [
[
{
"node": "Quick prep email (2)",
"type": "main",
"index": 0
}
]
]
},
"Wait1": {
"main": [
[
{
"node": "Quick prep email2",
"type": "main",
"index": 0
}
]
]
},
"Wait 1 Day": {
"main": [
[
{
"node": "Verify Email (BillionVerify) 2",
"type": "main",
"index": 0
}
]
]
},
"Casual flex": {
"main": [
[
{
"node": "Wait 1 Day",
"type": "main",
"index": 0
}
]
]
},
"Wait 1 Day1": {
"main": [
[
{
"node": "Verify Email (BillionVerify) 6",
"type": "main",
"index": 0
}
]
]
},
"Casual press": {
"main": [
[
{
"node": "Wait another day",
"type": "main",
"index": 0
}
]
]
},
"Calculate time": {
"main": [
[
{
"node": "Switch: Days Until Meeting",
"type": "main",
"index": 0
}
]
]
},
"Cal.com Trigger": {
"main": [
[
{
"node": "Extract prospect",
"type": "main",
"index": 0
}
]
]
},
"Extract prospect": {
"main": [
[
{
"node": "If",
"type": "main",
"index": 0
}
]
]
},
"Quick prep email": {
"main": [
[
{
"node": "Verify Email (BillionVerify) 4",
"type": "main",
"index": 0
}
]
]
},
"Wait another day": {
"main": [
[
{
"node": "Verify Email (BillionVerify) 3",
"type": "main",
"index": 0
}
]
]
},
"Quick prep email2": {
"main": [
[
{
"node": "Verify Email (BillionVerify) 9",
"type": "main",
"index": 0
}
]
]
},
"Casual flex 8+ days": {
"main": [
[
{
"node": "Wait 1 Day1",
"type": "main",
"index": 0
}
]
]
},
"Casual press 8+days": {
"main": [
[
{
"node": "Wait another day 8+days",
"type": "main",
"index": 0
}
]
]
},
"Quick prep email (2)": {
"main": [
[
{
"node": "Verify Email (BillionVerify) 8",
"type": "main",
"index": 0
}
]
]
},
"Casual knowledge flex": {
"main": [
[
{
"node": "Wait",
"type": "main",
"index": 0
}
]
]
},
"Wait another day 8+days": {
"main": [
[
{
"node": "Verify Email (BillionVerify) 7",
"type": "main",
"index": 0
}
]
]
},
"Switch: Days Until Meeting": {
"main": [
[
{
"node": "Quick prep email",
"type": "main",
"index": 0
}
],
[
{
"node": "Verify Email (BillionVerify)",
"type": "main",
"index": 0
}
],
[
{
"node": "Verify Email (BillionVerify) 5",
"type": "main",
"index": 0
}
]
]
},
"Casual knowledge flex 8+days": {
"main": [
[
{
"node": "Wait1",
"type": "main",
"index": 0
}
]
]
},
"Verify Email (BillionVerify)": {
"main": [
[
{
"node": "IF deliverable",
"type": "main",
"index": 0
}
]
]
},
"IF deliverable": {
"main": [
[
{
"node": "Casual flex",
"type": "main",
"index": 0
}
],
[]
]
},
"Verify Email (BillionVerify) 2": {
"main": [
[
{
"node": "IF deliverable 2",
"type": "main",
"index": 0
}
]
]
},
"IF deliverable 2": {
"main": [
[
{
"node": "Casual press",
"type": "main",
"index": 0
}
],
[]
]
},
"Verify Email (BillionVerify) 3": {
"main": [
[
{
"node": "IF deliverable 3",
"type": "main",
"index": 0
}
]
]
},
"IF deliverable 3": {
"main": [
[
{
"node": "Casual knowledge flex",
"type": "main",
"index": 0
}
],
[]
]
},
"Verify Email (BillionVerify) 4": {
"main": [
[
{
"node": "IF deliverable 4",
"type": "main",
"index": 0
}
]
]
},
"IF deliverable 4": {
"main": [
[
{
"node": "Send it",
"type": "main",
"index": 0
}
],
[]
]
},
"Verify Email (BillionVerify) 5": {
"main": [
[
{
"node": "IF deliverable 5",
"type": "main",
"index": 0
}
]
]
},
"IF deliverable 5": {
"main": [
[
{
"node": "Casual flex 8+ days",
"type": "main",
"index": 0
}
],
[]
]
},
"Verify Email (BillionVerify) 6": {
"main": [
[
{
"node": "IF deliverable 6",
"type": "main",
"index": 0
}
]
]
},
"IF deliverable 6": {
"main": [
[
{
"node": "Casual press 8+days",
"type": "main",
"index": 0
}
],
[]
]
},
"Verify Email (BillionVerify) 7": {
"main": [
[
{
"node": "IF deliverable 7",
"type": "main",
"index": 0
}
]
]
},
"IF deliverable 7": {
"main": [
[
{
"node": "Casual knowledge flex 8+days",
"type": "main",
"index": 0
}
],
[]
]
},
"Verify Email (BillionVerify) 8": {
"main": [
[
{
"node": "IF deliverable 8",
"type": "main",
"index": 0
}
]
]
},
"IF deliverable 8": {
"main": [
[
{
"node": "Quick prep",
"type": "main",
"index": 0
}
],
[]
]
},
"Verify Email (BillionVerify) 9": {
"main": [
[
{
"node": "IF deliverable 9",
"type": "main",
"index": 0
}
]
]
},
"IF deliverable 9": {
"main": [
[
{
"node": "Quick prep +8days",
"type": "main",
"index": 0
}
],
[]
]
}
},
"settings": {
"executionOrder": "v1"
}
}When to use this
- Cleaning a list before a Cal.com send or sync.
- Protecting Cal.com deliverability and sender reputation.
- Keeping bounce rates low so your sending is never throttled.
FAQ
Why verify before sending in Cal.com?
Verifying first keeps your bounce rate low, which protects your sender reputation and your results.
How do I import this workflow?
Download the JSON, then in n8n go to Workflows → Import from File (or paste it). Install the BillionVerify community node and add your API key credential.
What happens to risky or catch-all addresses?
They are routed to the false branch and excluded from the send. You decide whether to retry, review, or drop them.
Add verification to your workflow
Create a free account, grab your API key, and stop bounces before they happen.
Get started free