This workflow corresponds to n8n.io template #15874 — we link there as the canonical source.
This workflow follows the Gmail → Google Calendar recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Calendly Sales Meeting Automation",
"nodes": [
{
"id": "085a7097-43e7-42af-aa7a-192818aa1bdb",
"name": "Update HubSpot Contact",
"type": "n8n-nodes-base.httpRequest",
"position": [
-2144,
-144
],
"parameters": {
"url": "=https://api.hubapi.com/crm/v3/objects/contacts/{{ $('HubSpot - Search Contact by Email').item.json.results[0].id }}",
"method": "PATCH",
"options": {},
"sendBody": true,
"sendHeaders": true,
"authentication": "genericCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "properties.firstname",
"value": "={{ $('Format Booking Data').item.json.firstName }}"
},
{
"name": "properties.lastname",
"value": "={{ $('Format Booking Data').item.json.lastName }}"
},
{
"name": "properties.phone",
"value": "={{ $('Format Booking Data').item.json.inviteePhone }}"
},
{
"name": "properties.company",
"value": "={{ $('Format Booking Data').item.json.inviteeOrg }}"
},
{
"name": "properties.leadsource",
"value": "Calendly"
},
{
"name": "properties.lifecyclestage",
"value": "lead"
}
]
},
"genericAuthType": "httpHeaderAuth",
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.3
},
{
"id": "3108b381-b595-410e-b348-21f08e7fb4ca",
"name": "Create HubSpot Contact",
"type": "n8n-nodes-base.httpRequest",
"position": [
-2144,
48
],
"parameters": {
"url": "=https://api.hubapi.com/crm/v3/objects/contacts",
"method": "POST",
"options": {},
"sendBody": true,
"sendHeaders": true,
"authentication": "genericCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "properties.firstname",
"value": "={{ $('Format Booking Data').item.json.firstName }}"
},
{
"name": "properties.lastname",
"value": "={{ $('Format Booking Data').item.json.lastName }}"
},
{
"name": "properties.email",
"value": "={{ $('Format Booking Data').item.json.inviteeEmail }}"
},
{
"name": "properties.phone",
"value": "={{ $('Format Booking Data').item.json.inviteePhone }}"
},
{
"name": "properties.company",
"value": "={{ $('Format Booking Data').item.json.inviteeOrg }}"
},
{
"name": "properties.leadsource",
"value": "Calendly"
},
{
"name": "properties.lifecyclestage",
"value": "lead"
}
]
},
"genericAuthType": "httpHeaderAuth",
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.3
},
{
"id": "3e14d874-cfa6-417b-833b-1984899ac560",
"name": "IF - Contact Exists in HubSpot?",
"type": "n8n-nodes-base.if",
"position": [
-2416,
-48
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "cond_exists",
"operator": {
"type": "number",
"operation": "gt"
},
"leftValue": "={{ $json.results.length }}",
"rightValue": 0
}
]
}
},
"typeVersion": 2.2
},
{
"id": "634a4767-fc14-4f61-b72e-371c512012ca",
"name": "HubSpot - Search Contact by Email",
"type": "n8n-nodes-base.httpRequest",
"position": [
-2624,
-48
],
"parameters": {
"url": "https://api.hubapi.com/crm/v3/objects/contacts/search",
"method": "POST",
"options": {},
"jsonBody": "={\n \"filterGroups\": [\n {\n \"filters\": [\n {\n \"propertyName\": \"email\",\n \"operator\": \"EQ\",\n \"value\": \"{{ $('Format Booking Data').item.json.inviteeEmail }}\"\n }\n ]\n }\n ],\n \"limit\": 1\n}",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.3
},
{
"id": "439c65b2-2535-469b-8f82-131c8b0810ea",
"name": "HubSpot Log Note",
"type": "n8n-nodes-base.httpRequest",
"position": [
-240,
-176
],
"parameters": {
"url": "https://api.hubapi.com/crm/v3/objects/notes",
"method": "POST",
"options": {},
"sendBody": true,
"sendHeaders": true,
"authentication": "genericCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "properties.hs_note_body",
"value": "=\ud83d\uddd3 Calendly Booking Received\n\n\ud83d\udc64 Prospect: {{ $('Format Booking Data').item.json.inviteeName }}\n\n\ud83d\udce7 Email: {{ $('Format Booking Data').item.json.inviteeEmail }}\n\n\ud83c\udfe2 Company: {{ $('Format Booking Data').item.json.inviteeOrg }}\n\n\ud83d\udcde Phone: {{ $('Format Booking Data').item.json.inviteePhone }}\n\n\ud83d\udccb Event: {{ $('Format Booking Data').item.json.eventTypeName }}\n\n\ud83d\udd50 Scheduled: {{ $('Format Booking Data').item.json.startTimeFormatted }}\n\n\ud83c\udfa5 Join URL: {{ $('Format Booking Data').item.json.meetingJoinUrl }}\n\n\ud83d\udd01 Reschedule: {{ $('Format Booking Data').item.json.rescheduleUrl }}\n\n\u274c Cancel: {{ $('Format Booking Data').item.json.cancelUrl }}\n\n\u2705 ClickUp Prep Task: {{ $('ClickUp \u2014 Create Prep Task').item.json.url }}"
},
{
"name": "properties.hs_timestamp",
"value": "={{ Date.now() }}"
},
{
"name": "associations[0].to.id",
"value": "={{ $('Set \u2014 Contact ID').item.json.contactId }}"
},
{
"name": "associations[0].types[0].associationCategory",
"value": "HUBSPOT_DEFINED"
},
{
"name": "associations[0].types[0].associationTypeId",
"value": "202"
},
{
"name": "associations[1].to.id",
"value": "={{ $('HubSpot - Create a Deal').item.json.id }}"
},
{
"name": "associations[1].types[0].associationCategory",
"value": "HUBSPOT_DEFINED"
},
{
"name": "associations[1].types[0].associationTypeId",
"value": "214"
}
]
},
"genericAuthType": "httpHeaderAuth",
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.3
},
{
"id": "e2397ea7-20d9-4c00-a400-5a39ba23a239",
"name": "Calendly \u2014 New Booking",
"type": "n8n-nodes-base.calendlyTrigger",
"notes": "SETUP: 1) Create a Calendly API credential in n8n using your Personal Access Token. 2) n8n will auto-register the webhook with Calendly when the workflow is activated.",
"position": [
-3312,
-48
],
"parameters": {
"events": [
"invitee.created"
],
"authentication": "oAuth2"
},
"credentials": {
"calendlyOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "74de6104-05e8-4f18-8dc1-eb45d34933fd",
"name": "Set \u2014 Contact ID",
"type": "n8n-nodes-base.set",
"notes": "Normalises the contact ID from either the Create or Update branch so downstream nodes always have a consistent $json.contactId reference regardless of which branch ran.",
"position": [
-1904,
-48
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "norm-contactId",
"name": "contactId",
"type": "string",
"value": "={{ $json.id }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "79b4f907-f55b-4106-bc5b-dc4c43111702",
"name": "ClickUp \u2014 Create Prep Task",
"type": "n8n-nodes-base.clickUp",
"notes": "SETUP: Replace YOUR_CLICKUP_LIST_ID with the numeric ID of the ClickUp list where prep tasks should land. Priority 2 = High. Due date is set to 2 hours before the meeting start time.",
"position": [
-848,
-48
],
"parameters": {
"list": "YOUR_CLICKUP_LIST_ID",
"name": "={{ '\ud83d\udd0d Pre-Call Research: ' + $('Format Booking Data').item.json.inviteeName }}",
"team": "YOUR_CLICKUP_TEAM_ID",
"space": "YOUR_CLICKUP_SPACE_ID",
"folderless": true,
"additionalFields": {
"content": "={{ `Pre-Call Briefing\\n\\nProspect: ${$('Format Booking Data').item.json.inviteeName}\\nEmail: ${$('Format Booking Data').item.json.inviteeEmail}\\nCompany: ${$('Format Booking Data').item.json.inviteeOrg || 'TBD \u2014 research required'}\\nMeeting: ${$('Format Booking Data').item.json.eventTypeName}\\nScheduled: ${$('Format Booking Data').item.json.startTimeFormatted}\\nJoin URL: ${$('Format Booking Data').item.json.meetingJoinUrl}\\nHubSpot Deal | Deal ID: ${$json.id}\\n\\n---\\n\\n\u2705 Research Checklist\\n\\n- [ ] Review LinkedIn profile \u2014 look for tenure, role scope, recent posts\\n- [ ] Research company: size, funding stage, tech stack, recent news\\n- [ ] Check HubSpot for any prior interactions or open deals\\n- [ ] Identify 2-3 pain points likely relevant to our solution\\n- [ ] Select 1-2 relevant customer case studies to reference\\n- [ ] Prepare 5 tailored discovery questions\\n- [ ] Review competitor solutions they may currently use\\n- [ ] Set personal meeting reminder 15 min before call\\n\\n---\\n\\n\ud83d\udcdd Pre-Call Notes\\n\\n*(Add your notes here before the call)*` }}",
"dueDate": "={{ $('Format Booking Data').item.json.taskDueDateMs }}",
"priority": 2
}
},
"credentials": {
"clickUpApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "96a5a9ba-f633-4bcc-b8b0-52687cabcd2f",
"name": "Gmail \u2014 Send Confirmation",
"type": "n8n-nodes-base.gmail",
"notes": "SETUP: Replace replyTo with your sales team's email address.",
"position": [
-480,
-48
],
"parameters": {
"sendTo": "={{ $('Format Booking Data').item.json.inviteeEmail }}",
"message": "={{ `<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\"><title>Call Confirmed</title></head><body style=\"margin:0;padding:0;background-color:#F1F5F9;font-family:'Helvetica Neue',Arial,sans-serif;\"><table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"padding:40px 20px;\"><tr><td align=\"center\"><table width=\"580\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:#ffffff;border-radius:16px;overflow:hidden;border:1px solid #E2E8F0;max-width:580px;\"><tr><td style=\"background:#0F172A;padding:44px 40px 36px;text-align:center;\"><div style=\"width:56px;height:56px;background:rgba(255,255,255,0.1);border-radius:50%;display:inline-block;text-align:center;line-height:56px;font-size:26px;margin-bottom:18px;\">\ud83d\udcc5</div><h1 style=\"color:#ffffff;margin:0 0 6px;font-size:22px;font-weight:600;letter-spacing:-0.3px;\">Your call is confirmed</h1><p style=\"color:#94A3B8;margin:0;font-size:14px;line-height:1.6;\">We're looking forward to speaking with you, <strong style=\"color:#E2E8F0;\">${$('Format Booking Data').item.json.firstName}</strong></p></td></tr><tr><td style=\"padding:32px 36px;\"><table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:#F8FAFC;border-radius:12px;border:1px solid #E2E8F0;margin-bottom:28px;\"><tr><td style=\"padding:20px 24px 0;\"><p style=\"margin:0 0 14px;font-size:11px;font-weight:700;letter-spacing:1.8px;color:#94A3B8;text-transform:uppercase;\">Meeting details</p></td></tr><tr><td style=\"padding:0 24px;\"><table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"><tr><td style=\"padding:10px 0;color:#64748B;font-size:13px;width:38%;border-bottom:1px solid #E2E8F0;\">\ud83d\udcc5 Date & time</td><td style=\"padding:10px 0;font-size:13px;font-weight:600;color:#0F172A;border-bottom:1px solid #E2E8F0;\">${$('Format Booking Data').item.json.startTimeFormatted}</td></tr><tr><td style=\"padding:10px 0;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;\">\ud83d\udccb Session</td><td style=\"padding:10px 0;font-size:13px;font-weight:600;color:#0F172A;border-bottom:1px solid #E2E8F0;\">${$('Format Booking Data').item.json.eventTypeName}</td></tr><tr><td style=\"padding:10px 0;color:#64748B;font-size:13px;border-bottom:1px solid #E2E8F0;\">\u23f1 Duration</td><td style=\"padding:10px 0;font-size:13px;font-weight:600;color:#0F172A;border-bottom:1px solid #E2E8F0;\">30 minutes</td></tr><tr><td style=\"padding:10px 0;color:#64748B;font-size:13px;\">\ud83c\udfa5 Platform</td><td style=\"padding:10px 0;font-size:13px;font-weight:600;color:#0F172A;\">Zoom</td></tr></table></td></tr><tr><td style=\"padding:0 24px 20px;\"></td></tr></table><table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin-bottom:28px;\"><tr><td align=\"center\"><a href=\"${$('Format Booking Data').item.json.meetingJoinUrl}\" style=\"background:#0F172A;color:#ffffff;text-decoration:none;padding:13px 36px;border-radius:10px;font-size:14px;font-weight:600;display:inline-block;letter-spacing:0.2px;\">\ud83c\udfa5 Join meeting</a></td></tr></table><table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:#F0FDF4;border-radius:12px;border:1px solid #BBF7D0;margin-bottom:28px;\"><tr><td style=\"padding:20px 24px 8px;\"><p style=\"margin:0 0 14px;font-size:11px;font-weight:700;letter-spacing:1.8px;color:#15803D;text-transform:uppercase;\">Before your call</p></td></tr><tr><td style=\"padding:0 24px 20px;\"><p style=\"margin:0 0 10px;font-size:13px;color:#166534;line-height:1.6;\">\u2713 Check your inbox for a calendar invite with the meeting link</p><p style=\"margin:0 0 10px;font-size:13px;color:#166534;line-height:1.6;\">\u2713 Join 2\u20133 minutes early so we can start on time</p><p style=\"margin:0;font-size:13px;color:#166534;line-height:1.6;\">\u2713 Have any questions or agenda items ready to share</p></td></tr></table><p style=\"color:#94A3B8;font-size:13px;text-align:center;margin:0 0 14px;\">Need to make changes?</p><table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"><tr><td align=\"center\"><a href=\"${$('Format Booking Data').item.json.rescheduleUrl}\" style=\"color:#0F172A;text-decoration:none;padding:8px 20px;border:1.5px solid #CBD5E1;border-radius:8px;font-size:13px;font-weight:500;display:inline-block;margin:0 6px;\">\ud83d\udcc6 Reschedule</a><a href=\"${$('Format Booking Data').item.json.cancelUrl}\" style=\"color:#94A3B8;text-decoration:none;padding:8px 20px;border:1.5px solid #E2E8F0;border-radius:8px;font-size:13px;font-weight:500;display:inline-block;margin:0 6px;\">\u2715 Cancel</a></td></tr></table></td></tr><tr><td style=\"border-top:1px solid #E2E8F0;padding:20px 36px;text-align:center;\"><p style=\"color:#94A3B8;font-size:12px;margin:0;line-height:1.7;\">Questions before the call? Simply reply to this email.<br>We'll get back to you as soon as possible.</p></td></tr></table></td></tr></table></body></html>` }}",
"options": {
"replyTo": "user@example.com",
"appendAttribution": false
},
"subject": "={{ '\u2705 Confirmed: ' + $('Format Booking Data').item.json.eventTypeName + ' on ' + new Date($('Format Booking Data').item.json.startTime).toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric' }) }}"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "7f4f9fdb-67a9-40fa-834f-823dbaeafb5e",
"name": "Slack \u2014 Notify Sales Channel",
"type": "n8n-nodes-base.slack",
"notes": "SETUP: Replace YOUR_SLACK_CHANNEL_ID with your #sales or #inbound-leads channel ID.",
"position": [
-32,
-176
],
"parameters": {
"text": "={{ ':phone: *New Sales Call Booked!*\\n\\n' + ':bust_in_silhouette: *Prospect:* ' + $('Format Booking Data').item.json.inviteeName + '\\n' + ':email: *Email:* ' + $('Format Booking Data').item.json.inviteeEmail + '\\n' + ':office: *Company:* ' + ($('Format Booking Data').item.json.inviteeOrg || '_Unknown \u2014 check prep task_') + '\\n' + ':calendar: *Session:* ' + $('Format Booking Data').item.json.eventTypeName + '\\n' + ':clock3: *Time:* ' + $('Format Booking Data').item.json.startTimeFormatted + '\\n' + ':video_camera: *Join:* ' + $('Format Booking Data').item.json.meetingJoinUrl + '\\n\\n' + ':white_check_mark: HubSpot deal created \u00b7 ' + ':white_check_mark: ClickUp prep task created \u00b7 ' + ':white_check_mark: Confirmation email sent' }}",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "YOUR_SLACK_CHANNEL_ID"
},
"otherOptions": {
"mrkdwn": true,
"includeLinkToWorkflow": false
}
},
"credentials": {
"slackApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "5793ff05-1d77-4eda-a036-e021bbf17d50",
"name": "HubSpot - Create a Deal",
"type": "n8n-nodes-base.httpRequest",
"position": [
-1216,
-48
],
"parameters": {
"url": "https://api.hubapi.com/crm/v3/objects/deals",
"method": "POST",
"options": {},
"sendBody": true,
"sendHeaders": true,
"authentication": "genericCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "properties.dealname",
"value": "={{ $('Format Booking Data').item.json.dealName }}"
},
{
"name": "properties.pipeline",
"value": "=default"
},
{
"name": "properties.dealstage",
"value": "appointmentscheduled"
},
{
"name": "properties.closedate",
"value": "={{ $('Format Booking Data').item.json.startTime }}"
},
{
"name": "properties.deal_currency_code",
"value": "USD"
},
{
"name": "properties.hs_priority",
"value": "high"
},
{
"name": "properties.lead_source",
"value": "=Calendly"
},
{
"name": "properties.hs_next_step",
"value": "=Complete pre-call research in ClickUp before the meeting"
},
{
"name": "properties.hs_lastmodifieddate",
"value": "={{ Date.now() }}"
},
{
"name": "associations[0].to.id",
"value": "={{ $('Set \u2014 Contact ID').item.json.contactId }}"
},
{
"name": "associations[0].types[0].associationCategory",
"value": "HUBSPOT_DEFINED"
},
{
"name": "associations[0].types[0].associationTypeId",
"value": "3"
},
{
"name": "properties.hubspot_owner_id",
"value": "={{ $json.results[0].id }}"
},
{
"name": "properties.description",
"value": "=Calendly booking received.\n\nEvent: {{ $('Format Booking Data').item.json.eventTypeName }}\nScheduled: {{ $('Format Booking Data').item.json.startTimeFormatted }}\nMeeting: {{ $('Format Booking Data').item.json.meetingJoinUrl }}"
}
]
},
"genericAuthType": "httpHeaderAuth",
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.3
},
{
"id": "af08d995-a766-4097-92f0-7ed739f61e40",
"name": "HubSpot \u2014 Get Owner ID",
"type": "n8n-nodes-base.httpRequest",
"position": [
-1440,
-48
],
"parameters": {
"url": "https://api.hubapi.com/crm/v3/owners?limit=100",
"options": {},
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.3
},
{
"id": "8d7aaa55-a700-4bc2-b076-377afb1d11b5",
"name": "Format Booking Data",
"type": "n8n-nodes-base.code",
"position": [
-3088,
-48
],
"parameters": {
"jsCode": "const payload = $input.first().json.payload ?? $input.first().json;\n\n// \u2500\u2500 Helper: safely dig into nested objects \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction get(obj, ...keys) {\n return keys.reduce((o, k) => (o != null ? o[k] : undefined), obj);\n}\n\n// \u2500\u2500 Helper: search Q&A array by keyword \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction findAnswer(qna, keyword) {\n if (!Array.isArray(qna)) return '';\n const match = qna.find(q =>\n (q.question ?? '').toLowerCase().includes(keyword.toLowerCase())\n );\n return match?.answer ?? '';\n}\n\n// \u2500\u2500 Invitee details \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst fullName =\n get(payload, 'name') ??\n get(payload, 'invitee', 'name') ??\n get(payload, 'full_name') ?? '';\n\nconst email =\n get(payload, 'email') ??\n get(payload, 'invitee', 'email') ?? '';\n\nconst firstName =\n get(payload, 'first_name') ??\n get(payload, 'invitee', 'first_name') ??\n fullName.split(' ')[0] ?? '';\n\nconst lastName =\n get(payload, 'last_name') ??\n get(payload, 'invitee', 'last_name') ??\n fullName.split(' ').slice(1).join(' ') ?? '';\n\nconst cancelUrl =\n get(payload, 'cancel_url') ??\n get(payload, 'invitee', 'cancel_url') ?? '';\n\nconst rescheduleUrl =\n get(payload, 'reschedule_url') ??\n get(payload, 'invitee', 'reschedule_url') ?? '';\n\n// \u2500\u2500 Q&A \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst qna =\n get(payload, 'questions_and_answers') ??\n get(payload, 'invitee', 'questions_and_answers') ??\n get(payload, 'form_submission', 'answers') ?? [];\n\nconst inviteeOrg = findAnswer(qna, 'company') || findAnswer(qna, 'organization') || findAnswer(qna, 'org');\nconst inviteePhone = findAnswer(qna, 'phone') || findAnswer(qna, 'mobile') || findAnswer(qna, 'contact');\n\n// \u2500\u2500 Event / scheduled details \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst event =\n get(payload, 'scheduled_event') ??\n get(payload, 'event') ?? {};\n\nconst eventTypeName =\n get(event, 'name') ??\n get(payload, 'event_type', 'name') ??\n get(payload, 'event_type_name') ?? 'Sales Call';\n\nconst startTime =\n get(event, 'start_time') ??\n get(payload, 'start_time') ?? new Date().toISOString();\n\nconst startTimeFormatted = new Date(startTime).toLocaleString('en-US', {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n timeZoneName: 'short'\n});\n\n// \u2500\u2500 Meeting join URL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst location = get(event, 'location') ?? get(payload, 'location') ?? {};\nconst meetingJoinUrl =\n get(location, 'join_url') ??\n get(location, 'data') ??\n get(location, 'url') ??\n get(event, 'join_url') ??\n 'See your calendar invite for the meeting link';\n\n// \u2500\u2500 Due date for ClickUp task (2 hrs before meeting) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst taskDueDateMs = new Date(startTime).getTime() - (2 * 60 * 60 * 1000);\n\n// \u2500\u2500 Reminder timestamps \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst reminder24hAt = new Date(new Date(startTime).getTime() - 24 * 60 * 60 * 1000).toISOString();\nconst reminder1hAt = new Date(new Date(startTime).getTime() - 1 * 60 * 60 * 1000).toISOString();\n\n// \u2500\u2500 Output \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nreturn [{\n json: {\n inviteeName: fullName,\n inviteeEmail: email,\n firstName,\n lastName,\n inviteeOrg,\n inviteePhone,\n eventTypeName,\n startTime,\n startTimeFormatted,\n meetingJoinUrl,\n cancelUrl,\n rescheduleUrl,\n dealName: `${fullName} \u2014 ${eventTypeName}`,\n taskDueDateMs,\n reminder24hAt,\n reminder1hAt,\n _raw: payload\n }\n}];"
},
"typeVersion": 2
},
{
"id": "744905ac-883a-49bb-9609-c17d97ec5443",
"name": "Google Sheets \u2014 Log Booking",
"type": "n8n-nodes-base.googleSheets",
"notes": "SETUP: 1) Replace YOUR_GOOGLE_SHEET_ID with the ID from your spreadsheet URL (docs.google.com/spreadsheets/d/SHEET_ID/edit). 2) Create a sheet tab named 'Bookings' with matching column headers in row 1. 3) Connect a Google Sheets OAuth2 credential.",
"position": [
-2864,
-48
],
"parameters": {
"columns": {
"value": {
"Name": "={{ $('Format Booking Data').item.json.inviteeName }}",
"Email": "={{ $('Format Booking Data').item.json.inviteeEmail }}",
"Phone": "={{ $('Format Booking Data').item.json.inviteePhone }}",
"Source": "Calendly",
"Company": "={{ $('Format Booking Data').item.json.inviteeOrg }}",
"Deal Name": "={{ $('Format Booking Data').item.json.dealName }}",
"Logged At": "={{ new Date().toISOString() }}",
"Cancel URL": "={{ $('Format Booking Data').item.json.cancelUrl }}",
"Event Type": "={{ $('Format Booking Data').item.json.eventTypeName }}",
"Meeting URL": "={{ $('Format Booking Data').item.json.meetingJoinUrl }}",
"Meeting Time": "={{ $('Format Booking Data').item.json.startTimeFormatted }}",
"Reschedule URL": "={{ $('Format Booking Data').item.json.rescheduleUrl }}"
},
"schema": [
{
"id": "Logged At",
"type": "string",
"display": true,
"required": false,
"displayName": "Logged At",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Name",
"type": "string",
"display": true,
"required": false,
"displayName": "Name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Email",
"type": "string",
"display": true,
"required": false,
"displayName": "Email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Company",
"type": "string",
"display": true,
"required": false,
"displayName": "Company",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Phone",
"type": "string",
"display": true,
"required": false,
"displayName": "Phone",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Event Type",
"type": "string",
"display": true,
"required": false,
"displayName": "Event Type",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Meeting Time",
"type": "string",
"display": true,
"required": false,
"displayName": "Meeting Time",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Meeting URL",
"type": "string",
"display": true,
"required": false,
"displayName": "Meeting URL",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Reschedule URL",
"type": "string",
"display": true,
"required": false,
"displayName": "Reschedule URL",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Cancel URL",
"type": "string",
"display": true,
"required": false,
"displayName": "Cancel URL",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Deal Name",
"type": "string",
"display": true,
"required": false,
"displayName": "Deal Name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Source",
"type": "string",
"display": true,
"required": false,
"displayName": "Source",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "YOUR_GOOGLE_SHEET_ID"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.4
},
{
"id": "6cad8195-493c-4320-900e-e6f866cc0603",
"name": "Google Calendar \u2014 Create Rep Event",
"type": "n8n-nodes-base.googleCalendar",
"notes": "SETUP: Connect your sales rep's Google Calendar OAuth2 credential. The event is created on their primary calendar with 60-min and 15-min popup reminders plus a 24-hour email reminder. Duration is hardcoded to 30 min \u2014 adjust the +30*60*1000 offset to match your actual event length.",
"position": [
-1664,
-48
],
"parameters": {
"end": "={{ new Date(new Date($('Format Booking Data').item.json.startTime).getTime() + 30 * 60 * 1000).toISOString() }}",
"start": "={{ $('Format Booking Data').item.json.startTime }}",
"calendar": {
"__rl": true,
"mode": "list",
"value": "user@example.com"
},
"additionalFields": {
"summary": "={{ '\ud83d\udcde Sales Call: ' + $('Format Booking Data').item.json.inviteeName + ' (' + ($('Format Booking Data').item.json.inviteeOrg || 'Unknown Co.') + ')' }}",
"location": "={{ $('Format Booking Data').item.json.meetingJoinUrl }}",
"description": "={{ '\ud83d\udd17 Join URL: ' + $('Format Booking Data').item.json.meetingJoinUrl + '\\n\\n' + '\ud83d\udc64 Prospect: ' + $('Format Booking Data').item.json.inviteeName + '\\n' + '\ud83d\udce7 Email: ' + $('Format Booking Data').item.json.inviteeEmail + '\\n' + '\ud83c\udfe2 Company: ' + ($('Format Booking Data').item.json.inviteeOrg || 'TBD') + '\\n' + '\ud83d\udcde Phone: ' + ($('Format Booking Data').item.json.inviteePhone || 'TBD') + '\\n\\n' + '\ud83d\udccb Event: ' + $('Format Booking Data').item.json.eventTypeName + '\\n\\n' + '\ud83d\udd01 Reschedule: ' + $('Format Booking Data').item.json.rescheduleUrl + '\\n' + '\u274c Cancel: ' + $('Format Booking Data').item.json.cancelUrl + '\\n\\n' + '---\\nAuto-created by n8n Calendly workflow.' }}",
"sendUpdates": "none"
}
},
"credentials": {
"googleCalendarOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "1006affa-3580-4df2-abf1-d5fc042b56df",
"name": "Wait \u2014 Until 24h Before Meeting",
"type": "n8n-nodes-base.wait",
"notes": "Pauses execution until exactly 24 hours before the scheduled meeting. Uses the reminder24hAt timestamp computed in Format Booking Data.",
"position": [
240,
-48
],
"parameters": {
"resume": "specificTime",
"dateTime": "={{ $('Format Booking Data').item.json.reminder24hAt }}"
},
"typeVersion": 1.1
},
{
"id": "33ceac7a-7c00-4300-aa1b-8832cebe49c0",
"name": "Gmail \u2014 Send 24h Reminder",
"type": "n8n-nodes-base.gmail",
"notes": "SETUP: Same Gmail OAuth credential as the confirmation email. Sent exactly 24 hours before the meeting time.",
"position": [
448,
-48
],
"parameters": {
"sendTo": "={{ $('Format Booking Data').item.json.inviteeEmail }}",
"message": "={{ `<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\"><title>24-Hour Reminder</title></head><body style=\"margin:0;padding:0;background-color:#F1F5F9;font-family:'Helvetica Neue',Arial,sans-serif;\"><table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"padding:40px 20px;\"><tr><td align=\"center\"><table width=\"580\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:#ffffff;border-radius:16px;overflow:hidden;border:1px solid #E2E8F0;max-width:580px;\">\n\n<tr><td style=\"background:#1E3A5F;padding:40px 40px 32px;text-align:center;\">\n<div style=\"font-size:36px;margin-bottom:12px;\">\u23f0</div>\n<h1 style=\"color:#ffffff;margin:0 0 8px;font-size:20px;font-weight:600;\">Your call is tomorrow</h1>\n<p style=\"color:#93C5FD;margin:0;font-size:14px;\">Just a friendly heads-up, ${$('Format Booking Data').item.json.firstName}</p>\n</td></tr>\n\n<tr><td style=\"padding:32px 36px;\">\n<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:#F8FAFC;border-radius:12px;border:1px solid #E2E8F0;margin-bottom:24px;\">\n<tr><td style=\"padding:16px 24px 0;\"><p style=\"margin:0 0 12px;font-size:11px;font-weight:700;letter-spacing:1.6px;color:#94A3B8;text-transform:uppercase;\">Meeting details</p></td></tr>\n<tr><td style=\"padding:0 24px 16px;\"><table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n<tr><td style=\"padding:8px 0;color:#64748B;font-size:13px;width:38%;border-bottom:1px solid #E2E8F0;\">\ud83d\udcc5 When</td><td style=\"padding:8px 0;font-size:13px;font-weight:600;color:#0F172A;border-bottom:1px solid #E2E8F0;\">${$('Format Booking Data').item.json.startTimeFormatted}</td></tr>\n<tr><td style=\"padding:8px 0;color:#64748B;font-size:13px;\">\ud83d\udccb Session</td><td style=\"padding:8px 0;font-size:13px;font-weight:600;color:#0F172A;\">${$('Format Booking Data').item.json.eventTypeName}</td></tr>\n</table></td></tr>\n</table>\n\n<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin-bottom:24px;\"><tr><td align=\"center\">\n<a href=\"${$('Format Booking Data').item.json.meetingJoinUrl}\" style=\"background:#1E3A5F;color:#ffffff;text-decoration:none;padding:12px 32px;border-radius:10px;font-size:14px;font-weight:600;display:inline-block;\">\ud83c\udfa5 Save meeting link</a>\n</td></tr></table>\n\n<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:#FFF7ED;border-radius:12px;border:1px solid #FED7AA;margin-bottom:24px;\">\n<tr><td style=\"padding:16px 24px;\"><p style=\"margin:0 0 8px;font-size:11px;font-weight:700;letter-spacing:1.6px;color:#C2410C;text-transform:uppercase;\">Quick checklist</p><p style=\"margin:0 0 6px;font-size:13px;color:#9A3412;line-height:1.6;\">\u2713 Add the meeting link to your calendar</p><p style=\"margin:0 0 6px;font-size:13px;color:#9A3412;line-height:1.6;\">\u2713 Test your camera and microphone</p><p style=\"margin:0;font-size:13px;color:#9A3412;line-height:1.6;\">\u2713 Prepare any questions you'd like to discuss</p></td></tr>\n</table>\n\n<p style=\"color:#94A3B8;font-size:13px;text-align:center;margin:0 0 12px;\">Need to make changes?</p>\n<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"><tr><td align=\"center\">\n<a href=\"${$('Format Booking Data').item.json.rescheduleUrl}\" style=\"color:#0F172A;text-decoration:none;padding:8px 18px;border:1.5px solid #CBD5E1;border-radius:8px;font-size:13px;font-weight:500;display:inline-block;margin:0 5px;\">\ud83d\udcc6 Reschedule</a>\n<a href=\"${$('Format Booking Data').item.json.cancelUrl}\" style=\"color:#94A3B8;text-decoration:none;padding:8px 18px;border:1.5px solid #E2E8F0;border-radius:8px;font-size:13px;font-weight:500;display:inline-block;margin:0 5px;\">\u2715 Cancel</a>\n</td></tr></table>\n\n</td></tr>\n<tr><td style=\"border-top:1px solid #E2E8F0;padding:18px 36px;text-align:center;\"><p style=\"color:#94A3B8;font-size:12px;margin:0;\">Questions? Reply to this email \u2014 we're here to help.</p></td></tr>\n</table></td></tr></table></body></html>` }}",
"options": {
"replyTo": "user@example.com",
"appendAttribution": false
},
"subject": "={{ '\u23f0 Reminder: Your call is tomorrow \u2014 ' + $('Format Booking Data').item.json.eventTypeName }}"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "0f2ebbe4-2ada-4bd0-8454-8c648d419336",
"name": "Wait \u2014 Until 1h Before Meeting",
"type": "n8n-nodes-base.wait",
"notes": "Pauses execution until exactly 1 hour before the scheduled meeting.",
"position": [
640,
-48
],
"parameters": {
"resume": "specificTime",
"dateTime": "={{ $('Format Booking Data').item.json.reminder1hAt }}"
},
"typeVersion": 1.1
},
{
"id": "9e3f34cb-c2e3-46a2-a6ac-4b24192c65d2",
"name": "Gmail \u2014 Send 1h Reminder",
"type": "n8n-nodes-base.gmail",
"notes": "SETUP: Same Gmail OAuth credential. Final reminder sent 1 hour before the call with a prominent join button.",
"position": [
832,
-48
],
"parameters": {
"sendTo": "={{ $('Format Booking Data').item.json.inviteeEmail }}",
"message": "={{ `<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\"><title>1-Hour Reminder</title></head><body style=\"margin:0;padding:0;background-color:#F1F5F9;font-family:'Helvetica Neue',Arial,sans-serif;\"><table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"padding:40px 20px;\"><tr><td align=\"center\"><table width=\"580\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:#ffffff;border-radius:16px;overflow:hidden;border:1px solid #E2E8F0;max-width:580px;\">\n\n<tr><td style=\"background:#0F4C35;padding:40px 40px 32px;text-align:center;\">\n<div style=\"font-size:36px;margin-bottom:12px;\">\ud83d\ude80</div>\n<h1 style=\"color:#ffffff;margin:0 0 8px;font-size:20px;font-weight:600;\">Your call starts in 1 hour</h1>\n<p style=\"color:#6EE7B7;margin:0;font-size:14px;\">Almost time \u2014 we can't wait to connect, ${$('Format Booking Data').item.json.firstName}</p>\n</td></tr>\n\n<tr><td style=\"padding:32px 36px;\">\n\n<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin-bottom:28px;\"><tr><td align=\"center\">\n<a href=\"${$('Format Booking Data').item.json.meetingJoinUrl}\" style=\"background:#0F4C35;color:#ffffff;text-decoration:none;padding:14px 40px;border-radius:10px;font-size:15px;font-weight:700;display:inline-block;letter-spacing:0.3px;\">\ud83c\udfa5 Join meeting now</a>\n</td></tr></table>\n\n<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:#F8FAFC;border-radius:12px;border:1px solid #E2E8F0;margin-bottom:24px;\">\n<tr><td style=\"padding:16px 24px 0;\"><p style=\"margin:0 0 12px;font-size:11px;font-weight:700;letter-spacing:1.6px;color:#94A3B8;text-transform:uppercase;\">Your appointment</p></td></tr>\n<tr><td style=\"padding:0 24px 16px;\"><table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n<tr><td style=\"padding:8px 0;color:#64748B;font-size:13px;width:38%;border-bottom:1px solid #E2E8F0;\">\ud83d\udcc5 When</td><td style=\"padding:8px 0;font-size:13px;font-weight:600;color:#0F172A;border-bottom:1px solid #E2E8F0;\">${$('Format Booking Data').item.json.startTimeFormatted}</td></tr>\n<tr><td style=\"padding:8px 0;color:#64748B;font-size:13px;\">\ud83d\udccb Session</td><td style=\"padding:8px 0;font-size:13px;font-weight:600;color:#0F172A;\">${$('Format Booking Data').item.json.eventTypeName}</td></tr>\n</table></td></tr>\n</table>\n\n<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:#ECFDF5;border-radius:12px;border:1px solid #A7F3D0;margin-bottom:24px;\">\n<tr><td style=\"padding:16px 24px;\"><p style=\"margin:0 0 8px;font-size:11px;font-weight:700;letter-spacing:1.6px;color:#065F46;text-transform:uppercase;\">Last-minute tips</p><p style=\"margin:0 0 6px;font-size:13px;color:#064E3B;line-height:1.6;\">\u2713 Join 2 minutes early to check your audio/video</p><p style=\"margin:0 0 6px;font-size:13px;color:#064E3B;line-height:1.6;\">\u2713 Find a quiet spot with good lighting</p><p style=\"margin:0;font-size:13px;color:#064E3B;line-height:1.6;\">\u2713 Have your questions ready \u2014 we'll cover everything!</p></td></tr>\n</table>\n\n<p style=\"color:#94A3B8;font-size:13px;text-align:center;margin:0 0 12px;\">Still need to reschedule?</p>\n<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\"><tr><td align=\"center\">\n<a href=\"${$('Format Booking Data').item.json.rescheduleUrl}\" style=\"color:#0F172A;text-decoration:none;padding:8px 18px;border:1.5px solid #CBD5E1;border-radius:8px;font-size:13px;font-weight:500;display:inline-block;margin:0 5px;\">\ud83d\udcc6 Reschedule</a>\n</td></tr></table>\n\n</td></tr>\n<tr><td style=\"border-top:1px solid #E2E8F0;padding:18px 36px;text-align:center;\"><p style=\"color:#94A3B8;font-size:12px;margin:0;\">See you very soon! Reply to this email if you need anything.</p></td></tr>\n</table></td></tr></table></body></html>` }}",
"options": {
"replyTo": "user@example.com",
"appendAttribution": false
},
"subject": "={{ '\ud83d\ude80 Starting in 1 hour: ' + $('Format Booking Data').item.json.eventTypeName }}"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "e4bc8727-8d69-49f3-992d-336ec880740c",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3968,
-400
],
"parameters": {
"width": 560,
"height": 640,
"content": "## Calendly Sales Meeting Automation \nThis workflow automatically manages a new meeting booking from start to finish. When a person books a meeting through Calendly, the workflow saves the booking details, updates customer records, creates tasks for the sales team, sends emails, and reminds the customer before the meeting. It helps the team stay organized and ensures no meeting is forgotten. Everything works automatically without manual work. The workflow also keeps all meeting information in one place so the sales team can easily prepare before the call.\n\n### How it Works\n\n\t\u2022\tA customer books a meeting through Calendly.\n\t\u2022\tThe workflow collects and organizes all booking details.\n\t\u2022\tThe booking information is saved in Google Sheets.\n\t\u2022\tThe workflow checks if the customer already exists in HubSpot.\n\t\u2022\tA customer profile and sales deal are created or updated automatically.\n\t\u2022\tA preparation task is created for the sales team in ClickUp.\n\t\u2022\tConfirmation and reminder emails are automatically sent to the customer before the meeting.\n\n### Setup Steps\n\n\t1.\tConnect your Calendly account to receive new booking details automatically.\n\t2.\tConnect HubSpot so customer and deal information can be stored properly.\n\t3.\tConnect Google Sheets and create a sheet to save booking records.\n\t4.\tConnect Gmail for sending confirmation and reminder emails.\n\t5.\tConnect Slack and ClickUp for team notifications and meeting preparation tasks."
},
"typeVersion": 1
},
{
"id": "1aadf34b-5a8e-478f-8677-6742c2b83b0d",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3376,
-400
],
"parameters": {
"color": 7,
"width": 656,
"height": 640,
"content": "## Step 1 : Receive and Prepare Booking Details\n\nThis step starts when a customer books a meeting in Calendly. The workflow collects the customer\u2019s name, email, company, phone number, meeting time, and meeting links. After organizing the information, all booking details are saved into Google Sheets for record keeping. This helps the business keep a proper history of all scheduled meetings."
},
"typeVersion": 1
},
{
"id": "de6e4893-c313-4269-922c-5f7885d44613",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2688,
-400
],
"parameters": {
"color": 7,
"width": 432,
"height": 640,
"content": "## Step 2 : Check Existing Customer\n\nThis step checks whether the customer already exists in HubSpot using their email address. The workflow searches the customer information and decides whether the record should be updated or a new one should be created. This helps avoid duplicate customer entries and keeps the system organized."
},
"typeVersion": 1
},
{
"id": "5fe054cb-147e-44b9-8c02-ced072513d8f",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2224,
-400
],
"parameters": {
"color": 7,
"width": 464,
"height": 640,
"content": "## Step 3 : Create or Update Customer Record\n\nIf the customer already exists, their information is updated with the latest booking details. If the customer is new, a new customer profile is created automatically in HubSpot. After that, the workflow stores the customer ID so the next steps can use the correct customer information easily."
},
"typeVersion": 1
},
{
"id": "ce7ed279-f1af-425a-bc57-cfab554b7dca",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1728,
-400
],
"parameters": {
"color": 7,
"width": 672,
"height": 640,
"content": "## Step 4 : Create Calendar Event and Sales Deal\n\nThis step creates the meeting event in the sales representative\u2019s Google Calendar. After that, a sales deal is created in HubSpot to track the meeting opportunity and customer progress. This helps the sales team stay prepared and manage upcoming meetings properly. "
},
"typeVersion": 1
},
{
"id": "47043c39-152b-44dd-a265-4a128eabca7b",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1024,
-400
],
"parameters": {
"color": 7,
"width": 464,
"height": 640,
"content": "## Step 5 : Create Meeting Preparation Task\n\nThis step creates a preparation task for the sales team in ClickUp. The task includes customer details, meeting information, and a checklist to help the team prepare before the call. This makes it easier for the sales representative to research the customer and stay organized."
},
"typeVersion": 1
},
{
"id": "3fc68ecb-918d-4eb0-af49-88d1d8c374e7",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
-528,
-400
],
"parameters": {
"color": 7,
"width": 672,
"height": 640,
"content": "## Step 6 : Send Confirmation and Team Updates\n\nThis step sends a confirmation email to the customer after the booking is completed.\nThe email contains the meeting date, meeting link, and options to reschedule or cancel the meeting. At the same time, the sales team receives a notification in Slack and the meeting activity is saved in HubSpot notes."
},
"typeVersion": 1
},
{
"id": "9c5d445d-55f0-4661-830c-9b2defcb9250",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
176,
-400
],
"parameters": {
"color": 7,
"width": 848,
"height": 640,
"content": "## Step 7 : Send Reminder Emails Before Meeting\n\nThis step automatically sends reminder emails before the meeting starts. The first reminder is sent 24 hours before the meeting, and the second reminder is sent 1 hour before the meeting. These reminders help the customer remember the meeting and quickly join the call on time."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"connections": {
"HubSpot Log Note": {
"main": [
[
{
"node": "Slack \u2014 Notify Sales Channel",
"type": "main",
"index": 0
}
]
]
},
"Set \u2014 Contact ID": {
"main": [
[
{
"node": "Google Calendar \u2014 Create Rep Event",
"type": "main",
"index": 0
}
]
]
},
"Format Booking Data": {
"main": [
[
{
"node": "Google Sheets \u2014 Log Booking",
"type": "main",
"index": 0
}
]
]
},
"Create HubSpot Contact": {
"main": [
[
{
"node": "Set \u2014 Contact ID",
"type": "main",
"index": 0
}
]
]
},
"Update HubSpot Contact": {
"main": [
[
{
"node": "Set \u2014 Contact ID",
"type": "main",
"index": 0
}
]
]
},
"HubSpot - Create a Deal": {
"main": [
[
{
"node": "ClickUp \u2014 Create Prep Task",
"type": "main",
"index": 0
}
]
]
},
"Calendly \u2014 New Booking": {
"main": [
[
{
"node": "Format Booking Data",
"type": "main",
"index": 0
}
]
]
},
"HubSpot \u2014 Get Owner ID": {
"main": [
[
{
"node": "HubSpot - Create a Deal",
"type": "main",
"index": 0
}
]
]
},
"Gmail \u2014 Send 24h Reminder": {
"main": [
[
{
"node": "Wait \u2014 Until 1h Before Meeting",
"type": "main",
"index": 0
}
]
]
},
"Gmail \u2014 Send Confirmation": {
"main": [
[
{
"node": "HubSpot Log Note",
"type": "main",
"index": 0
},
{
"node": "Wait \u2014 Until 24h Before Meeting",
"type": "main",
"index": 0
}
]
]
},
"ClickUp \u2014 Create Prep Task": {
"main": [
[
{
"node": "Gmail \u2014 Send Confirmation",
"type": "main",
"index": 0
}
]
]
},
"Google Sheets \u2014 Log Booking": {
"main": [
[
{
"node": "HubSpot - Search Contact by Email",
"type": "main",
"index": 0
}
]
]
},
"IF - Contact Exists in HubSpot?": {
"main": [
[
{
"node": "Update HubSpot Contact",
"type": "main",
"index": 0
}
],
[
{
"node": "Create HubSpot Contact",
"type": "main",
"index": 0
}
]
]
},
"Wait \u2014 Until 1h Before Meeting": {
"main": [
[
{
"node": "Gmail \u2014 Send 1h Reminder",
"type": "main",
"index": 0
}
]
]
},
"HubSpot - Search Contact by Email": {
"main": [
[
{
"node": "IF - Contact Exists in HubSpot?",
"type": "main",
"index": 0
}
]
]
},
"Wait \u2014 Until 24h Before Meeting": {
"main": [
[
{
"node": "Gmail \u2014 Send 24h Reminder",
"type": "main",
"index": 0
}
]
]
},
"Google Calendar \u2014 Create Rep Event": {
"main": [
[
{
"node": "HubSpot \u2014 Get Owner ID",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
calendlyOAuth2ApiclickUpApigmailOAuth2googleCalendarOAuth2ApigoogleSheetsOAuth2ApihttpHeaderAuthslackApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow automatically manages every new Calendly booking from start to finish. When a prospect books a meeting, it captures all details, syncs everything to HubSpot, creates a prep task for the sales rep, sends confirmation and reminder emails, and notifies the team — all…
Source: https://n8n.io/workflows/15874/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Calendar and Meeting Prep Workflow. Uses googleCalendar, gmail, clickUp, httpRequest. Event-driven trigger; 38 nodes.
Categories: Payments, Project Operations, Client Onboarding
Sync your Google Calendar events with Google Sheets and get daily Slack summaries with meeting statistics. FEATURES:
Fluidflow Licensing Framework. Uses httpRequest, xero, googleSheetsTrigger, slack. Event-driven trigger; 25 nodes.
Stop chasing blurry receipts and manually typing expense data. This workflow creates an intelligent, "snap-and-submit" reimbursement pipeline that hosts photos via UploadToURL, extracts deep data via