Automate demo research and prep with Twain and n8n

Mohamed ChahinJuly 2, 20267 min read

Updated

Turn booked demos into researched conversations. Twain prepares the prospect brief and pre-demo message, with n8n delivering the work to Slack and Gmail for your rep to review.

A demo lands on the calendar. The rep clicks in cold — no idea who booked, what the company does, or why now. So the first ten minutes go to "So, tell me a bit about yourselves" while the prospect quietly wonders why they're re-explaining their own business to the vendor.

Doing it right means 15–20 minutes of manual research per meeting: the LinkedIn dig, the funding news, the "what changed for them recently" hunt. On a full demo calendar, nobody has that time. So it gets skipped, and the demo pays for it.

This workflow makes prepared the default. The instant a meeting is booked, Twain researches the prospect and hands the rep a ready AE brief plus a drafted pre-demo touch — no scrambling, no new dashboard.

The cold-demo tax

Every unprepared demo bleeds value in three places at once:

  • The first ten minutes are wasted. Generic discovery you could have done beforehand eats the most valuable part of the call.
  • Show and convert rates slip. Prospects who feel researched show up and lean in. Prospects who feel like a number ghost or stall.
  • Prep doesn't scale. 15–20 minutes per meeting times a calendar full of demos is hours a week of research nobody has — so it quietly stops happening.

The fix isn't "tell reps to prep harder." It's removing the manual work so prep happens whether or not anyone has the time.

Walk in prepared, every time

The moment a meeting is booked — from Calendly, Google Calendar, Chili Piper, HubSpot Meetings, or anything else — the workflow fires automatically.

Twain researches the prospect and company, then drafts a pre-demo sequence. n8n puts Twain's reasoning and research signals into a Slack brief and saves the first email as a Gmail draft. The rep has something specific to read, check, and use before the call.

Instead of opening tabs to assemble a brief from scratch, the rep can spend prep time on the questions the research raises.

What it does for your numbers

This is a GTM lever, not a productivity toy. The payoff lands where revenue teams already measure:

  • Better demos. Reps open with "I saw you just expanded into X" instead of "what does your company do?" The call starts where it should — at value.
  • Higher show and convert rates. A relevant pre-demo touch keeps the meeting warm between booking and call, and a researched rep closes more of the ones who show.
  • Zero pre-call scrambling. No 11:55 panic-Googling before the noon demo. The brief is already waiting.
  • Hours back across the team. At ~15–20 minutes saved per demo, a rep running 10 demos a week recovers two to three hours — time that goes back into selling.
  • Demand-gen ROI that doesn't leak. You paid to book the meeting. This protects that spend by making sure the meeting actually lands.

What the rep actually gets

Not a "demo booked" ping. A working brief the rep can act on:

  • The full Thesis — Twain's qualification and reasoning for this prospect.
  • Research signals — the current insights Twain found about the person and company, when available.
  • A drafted pre-demo email — written to the prospect, sitting in the rep's Gmail drafts, ready to review and send.

The card carries the person, title, company, meeting time, and the deal when the booking brought one — so the whole picture is in one place, in Slack, the moment the meeting is set.

Works with whatever you book in

Teams book demos in different places, and this template meets you where you already are. Calendly, Google Calendar, Chili Piper, HubSpot Meetings, or a generic webhook for anything else — every source feeds the same path.

Whatever the booking tool, the workflow normalizes it down to the same handful of fields — name, work email, meeting time, LinkedIn if it's there, deal id if it's there — and everything downstream stops caring where the booking came from. No rip-and-replace of your stack to get prepared demos; it bolts onto the booking flow you already run.

It also only fires on demos, not every meeting on a calendar. With Google Calendar you have two easy options: point the trigger at a dedicated Demo calendar so nothing else can set it off —

— or keep using a shared calendar and add a Match Term so only events whose title matches (e.g. demo booked) trigger the workflow.

Either way — dedicated calendar, match term, or the built-in keyword filter downstream — you decide what counts as a demo; everything else is ignored.

A human still approves every send

Nothing goes out on its own. The pre-demo email lands as a draft in the rep's inbox; the brief lands as a card in Slack. The rep reviews, tweaks if needed, and sends — or doesn't.

That's deliberate. AI does the research and the first draft; the rep stays the author and keeps their voice on every touch. For teams wary of automation messaging prospects on their behalf, this is the version that earns trust: all the speed, none of the "an AI emailed my prospect without me" risk.

Import the template

Prefer to import rather than rebuild? Copy the sanitized JSON below — secrets removed, live IDs replaced with placeholders. You attach your own connections and paste your demo campaign_id after import.

{
  "name": "Twain — Demo-booking Research & Pre-demo Touch",
  "settings": {
    "executionOrder": "v1",
    "availableInMCP": true,
    "binaryMode": "separate"
  },
  "nodes": [
    {
      "id": "cb631b80-e444-40c3-b46c-3826de3376cb",
      "name": "Generic Booking (Webhook)",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [240, 180],
      "parameters": {
        "httpMethod": "POST",
        "path": "demo-booked",
        "options": {}
      }
    },
    {
      "id": "2aeb90b4-fe40-49b4-b35d-74873c292a95",
      "name": "Dedupe Bookings",
      "type": "n8n-nodes-base.removeDuplicates",
      "typeVersion": 2,
      "position": [1760, 520],
      "parameters": {
        "operation": "removeItemsSeenInPreviousExecutions",
        "dedupeValue": "={{ $json.uid }}",
        "options": {
          "scope": "node",
          "historySize": 100000
        }
      }
    },
    {
      "id": "0603e3d8-a972-4b8a-ad5b-b62728154424",
      "name": "Has usable contact?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [3660, 520],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 3
          },
          "conditions": [
            {
              "id": "usable-1",
              "leftValue": "={{ Boolean($json.has_work_email) || Boolean($json.has_linkedin_url) }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      }
    },
    {
      "id": "3e043ff3-7e06-40c7-bebc-a32686895d2a",
      "name": "Slack: Skipped (cannot research)",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [4040, 740],
      "onError": "continueRegularOutput",
      "parameters": {
        "authentication": "accessToken",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "n8n-workflows",
          "mode": "name"
        },
        "text": "=:no_entry_sign:  *Twain — Demo-booking Research & Pre-demo Touch — skipped*\n\n*{{ $('Normalize Booking').item.json.displayName || $('Normalize Booking').item.json.email || 'Unknown invitee' }}*\n:email:  {{ $('Normalize Booking').item.json.email || 'No email provided' }}\n\nNo work email, and the LinkedIn lookup didn't return a profile URL for this lead — so there's nothing to research. Enable a LinkedIn Lookup provider (or add a work email / LinkedIn URL upstream) to catch leads like this.\n\n<https://www.twain.ai/w|Open Twain workspace>",
        "otherOptions": {
          "includeLinkToWorkflow": false,
          "unfurl_links": false,
          "unfurl_media": false
        }
      }
    },
    {
      "id": "0aa5c900-bff5-409d-adb5-9d280c2a9d11",
      "name": "Twain Generate Sequence",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [4040, 520],
      "onError": "continueErrorOutput",
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 5000,
      "parameters": {
        "method": "POST",
        "url": "https://public.api.twain.ai/v2/Generate/Sequence",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\n  campaign_id: $('Normalize Booking').item.json.campaign_id,\n  contact: {\n    ...($('Normalize Booking').item.json.has_work_email ? { work_email: $('Normalize Booking').item.json.email } : {}),\n    ...($json.has_linkedin_url ? { linkedin_profile_url: $json.linkedin_url } : {}),\n  },\n  mode: \"HIGH\",\n  add_contact_to_campaign: true,\n} }}",
        "options": {
          "timeout": 180000
        }
      }
    },
    {
      "id": "484e965e-59bf-47f4-92e0-d2773067d258",
      "name": "Build Pre-demo Brief",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [4420, 520],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const api = $json;\nconst norm = $(\"Normalize Booking\").item.json;\nconst cfg = $('Demo Settings').first().json;\nconst messages = Array.isArray(api.messages) ? api.messages : [];\n\n// --- Parse configurable step->channel split (1-based indices) ---\nconst parseSteps = (s) =>\n  String(s ?? \"\")\n    .split(\",\")\n    .map((x) => parseInt(String(x).trim(), 10))\n    .filter((n) => Number.isInteger(n) && n >= 1);\nlet emailSteps = parseSteps(cfg.email_steps);\nconst linkedinSteps = parseSteps(cfg.linkedin_steps);\n// Fall back to step 1 for email if none configured.\nif (emailSteps.length === 0) emailSteps = [1];\n\nconst stepMsg = (i1) => messages[i1 - 1] || {}; // i1 is 1-based\n\nconst research = api.research || {};\nconst person = research.person || {};\nconst company = research.company || {};\nconst thesis = api.thesis && typeof api.thesis.markdown === \"string\" ? api.thesis.markdown.trim() : \"\";\nconst insights = Array.isArray(research.insights)\n  ? research.insights.map((item) => String(item && item.insight || \"\").trim()).filter(Boolean)\n  : [];\nconst warnings = api.warnings || {};\nconst categories = Array.isArray(warnings.categories) ? warnings.categories : [];\nconst esc = (s) =>\n  String(s ?? \"\")\n    .replace(/&/g, \"&amp;\")\n    .replace(/</g, \"&lt;\")\n    .replace(/>/g, \"&gt;\");\nconst toHtml = (s) => (s ? esc(s).split(\"\\n\").join(\"<br>\") : \"\");\n\n// --- Email step(s): Gmail draft uses the FIRST email step ---\nconst primaryEmailIdx = emailSteps[0]; // 1-based\nconst primary = stepMsg(primaryEmailIdx);\nconst subject = String(\n  primary.subject_line ||\n    api.subject_line ||\n    \"Prep for our demo, \" + (person.name || norm.displayName || \"there\"),\n);\nconst firstBody = String(primary.message || \"\");\nconst bodyHtml = toHtml(firstBody);\n\n// Preview of ALL email steps (for Slack), escape-safe single string.\nconst email_preview =\n  emailSteps\n    .map((i1) => {\n      const m = stepMsg(i1);\n      const subj = m.subject_line ? \"Subject: \" + m.subject_line + \"\\n\" : \"\";\n      return subj + (m.message || \"\");\n    })\n    .filter(Boolean)\n    .join(\"\\n\\n--------\\n\\n\") || \"No email steps.\";\n\n// --- LinkedIn step(s) ---\nconst linkedin_messages = linkedinSteps.map((i1) => ({\n  step: i1,\n  message: String(stepMsg(i1).message || \"\"),\n}));\nconst has_linkedin_steps = linkedin_messages.length > 0;\nconst linkedin_preview =\n  linkedin_messages.map((m) => m.message).join(\"\\n\\n--------\\n\\n\") ||\n  \"No LinkedIn steps.\";\n\n// --- Full sequence preview (all steps) ---\nconst sequencePreview = messages\n  .map((m, i) => {\n    const subj = m.subject_line ? \"Subject: \" + m.subject_line + \"\\n\" : \"\";\n    return subj + (m.message || \"\");\n  })\n  .join(\"\\n\\n--------\\n\\n\");\n\n// --- AE brief (current HIGH response) ---\nconst briefParts = [];\nif (thesis) briefParts.push(\":dart:  *Thesis*\\n\" + thesis);\nif (insights.length) briefParts.push(\":mag:  *Research signals*\\n- \" + insights.join(\"\\n- \"));\nconst ae_brief = briefParts.join(\"\\n\\n\");\n\n// --- Meeting meta ---\nlet mt = \"\";\ntry {\n  mt = norm.meeting_time ? new Date(norm.meeting_time).toUTCString() : \"\";\n} catch (e) {\n  mt = String(norm.meeting_time || \"\");\n}\nconst metaParts = [];\nif (mt) metaParts.push(\":clock3:  \" + mt);\nif (norm.deal_id || \"\") metaParts.push(\":handshake:  Deal `\" + norm.deal_id + \"`\");\nconst meta_line = metaParts.join(\"\\n\");\n\nconst resolvedLi = (research.person && research.person.linkedin_profile_url) || \"\";\nconst linkedin_url = resolvedLi || norm.linkedin_url || \"\";\nconst linkedin_line = linkedin_url ? \":link:  <\" + linkedin_url + \"|LinkedIn profile>\" : \"\";\n// Prebuilt escape-safe warnings line (single token for Slack cards; empty when no warnings).\nconst warning_line =\n  warnings && warnings.has_warnings\n    ? \":warning:  *Warnings:* \" +\n      ((categories || []).join(\", \") || \"flagged\") +\n      (warnings.summary ? \"  —  \" + warnings.summary : \"\")\n    : \"\";\n\nreturn {\n  to_email: norm.email || \"\",\n  has_work_email: norm.has_work_email === true,\n  // Primary email step (drives the Gmail draft + email Slack card)\n  subject: subject,\n  body_html: bodyHtml,\n  body_text: firstBody,\n  email_preview: email_preview,\n  email_steps: emailSteps,\n  // LinkedIn steps (drive HeyReach + LinkedIn Slack card)\n  linkedin_messages: linkedin_messages,\n  linkedin_preview: linkedin_preview,\n  has_linkedin_steps: has_linkedin_steps,\n  linkedin_steps: linkedinSteps,\n  heyreach_campaign_id: cfg.heyreach_campaign_id || \"\",\n  // Shared\n  sequence_preview: sequencePreview || \"No messages returned.\",\n  step_count: messages.length,\n  person_name: person.name || norm.displayName || norm.email || \"Unknown\",\n  person_title: person.title || \"\",\n  company_name: company.name || \"\",\n  ae_brief: ae_brief || \"No structured research returned yet — open the lead in Twain for full context.\",\n  source: norm.source || \"\",\n  deal_id: norm.deal_id || \"\",\n  meta_line: meta_line,\n  linkedin_url: linkedin_url,\n  linkedin_line: linkedin_line,\n  meeting_time: norm.meeting_time || \"\",\n  meeting_time_display: mt,\n  link: api.link || \"\",\n  warnings_line: categories.length ? categories.join(\", \") : \"\",\n  warnings_summary: warnings.summary || \"\",\n  warning_line: warning_line,\n};\n"
      }
    },
    {
      "id": "390f2e94-8cd9-4ff3-b93f-cbf03333c01c",
      "name": "Has Work Email?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [4800, 520],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 3
          },
          "conditions": [
            {
              "leftValue": "={{ $json.has_work_email }}",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "rightValue": "",
              "id": "hwe-1"
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      }
    },
    {
      "id": "709019b0-e019-4e6a-a004-43202761d2d1",
      "name": "Gmail: Create Pre-demo Draft",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [5180, 410],
      "onError": "continueErrorOutput",
      "parameters": {
        "resource": "draft",
        "subject": "={{ $('Build Pre-demo Brief').item.json.subject }}",
        "emailType": "html",
        "message": "={{ $('Build Pre-demo Brief').item.json.body_html }}",
        "options": {
          "sendTo": "={{ $('Build Pre-demo Brief').item.json.to_email }}"
        }
      }
    },
    {
      "id": "47a73edc-8d01-4388-8a99-b8198ecf22cd",
      "name": "Slack: Pre-demo brief + draft ready",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [5560, 410],
      "onError": "continueRegularOutput",
      "parameters": {
        "authentication": "accessToken",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "n8n-workflows",
          "mode": "name"
        },
        "text": "=:calendar:  *Demo booked — pre-demo brief ready*\n\n*{{ $('Build Pre-demo Brief').item.json.person_name }}*{{ [$('Build Pre-demo Brief').item.json.person_title, $('Build Pre-demo Brief').item.json.company_name].filter(Boolean).length ? '  ·  ' + [$('Build Pre-demo Brief').item.json.person_title, $('Build Pre-demo Brief').item.json.company_name].filter(Boolean).join(' @ ') : '' }}\n\n:email:  {{ $('Build Pre-demo Brief').item.json.to_email }}\n{{ $('Build Pre-demo Brief').item.json.linkedin_line }}\n\n{{ $('Build Pre-demo Brief').item.json.meta_line }}\n{{ $('Build Pre-demo Brief').item.json.warning_line }}\n\n{{ $('Build Pre-demo Brief').item.json.ae_brief }}\n\n:memo:  *_{{ $('Build Pre-demo Brief').item.json.subject }}_*\n```\n{{ ($('Build Pre-demo Brief').item.json.body_text || '').slice(0, 700) }}{{ ($('Build Pre-demo Brief').item.json.body_text || '').length > 700 ? '…' : '' }}\n```\n\n:inbox_tray:  Saved as a Gmail draft — open *Drafts* to review and send.\n<{{ $('Build Pre-demo Brief').item.json.link }}|Open lead in Twain>",
        "otherOptions": {
          "includeLinkToWorkflow": false,
          "unfurl_links": false,
          "unfurl_media": false
        }
      }
    },
    {
      "id": "79d73607-1106-4b31-9add-4044575473ca",
      "name": "Slack: Brief ready, no draft",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [5560, 630],
      "onError": "continueRegularOutput",
      "parameters": {
        "authentication": "accessToken",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "n8n-workflows",
          "mode": "name"
        },
        "text": "=:warning:  *Pre-demo brief ready — no Gmail draft created*\n\n*{{ $('Build Pre-demo Brief').item.json.person_name }}*{{ [$('Build Pre-demo Brief').item.json.person_title, $('Build Pre-demo Brief').item.json.company_name].filter(Boolean).length ? '  ·  ' + [$('Build Pre-demo Brief').item.json.person_title, $('Build Pre-demo Brief').item.json.company_name].filter(Boolean).join(' @ ') : '' }}\n\n:email:  {{ $('Build Pre-demo Brief').item.json.to_email }}\n{{ $('Build Pre-demo Brief').item.json.linkedin_line }}\n\n{{ $('Build Pre-demo Brief').item.json.meta_line }}\n{{ $('Build Pre-demo Brief').item.json.warning_line }}\n\n{{ $('Build Pre-demo Brief').item.json.ae_brief }}\n\nGmail isn't connected (or the draft step failed), so nothing was saved to Drafts. Connect Gmail to auto-draft, or copy the message below to send manually.\n\n:memo:  *_{{ $('Build Pre-demo Brief').item.json.subject }}_*\n```\n{{ ($('Build Pre-demo Brief').item.json.body_text || '').slice(0, 900) }}{{ ($('Build Pre-demo Brief').item.json.body_text || '').length > 900 ? '…' : '' }}\n```\n\n<{{ $('Build Pre-demo Brief').item.json.link }}|Open lead in Twain>",
        "otherOptions": {
          "includeLinkToWorkflow": false,
          "unfurl_links": false,
          "unfurl_media": false
        }
      }
    },
    {
      "id": "1c11b8e7-ec7a-4481-adc4-9dffcfe8945e",
      "name": "Slack: Brief ready (LinkedIn only)",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [5180, 630],
      "onError": "continueRegularOutput",
      "parameters": {
        "authentication": "accessToken",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "n8n-workflows",
          "mode": "name"
        },
        "text": "=:wave:  *Demo booked — pre-demo brief ready (LinkedIn only)*\n\n*{{ $('Build Pre-demo Brief').item.json.person_name }}*{{ [$('Build Pre-demo Brief').item.json.person_title, $('Build Pre-demo Brief').item.json.company_name].filter(Boolean).length ? '  ·  ' + [$('Build Pre-demo Brief').item.json.person_title, $('Build Pre-demo Brief').item.json.company_name].filter(Boolean).join(' @ ') : '' }}\n\n{{ $('Build Pre-demo Brief').item.json.linkedin_line }}\n\n{{ $('Build Pre-demo Brief').item.json.meta_line }}\n{{ $('Build Pre-demo Brief').item.json.warning_line }}\n\n{{ $('Build Pre-demo Brief').item.json.ae_brief }}\n\nNo work email on file — send this via LinkedIn ahead of the demo.\n\n:speech_balloon:  *_Sequence · {{ $('Build Pre-demo Brief').item.json.step_count }} step{{ $('Build Pre-demo Brief').item.json.step_count == 1 ? '' : 's' }}_*\n```\n{{ ($('Build Pre-demo Brief').item.json.sequence_preview || '').slice(0, 1400) }}{{ ($('Build Pre-demo Brief').item.json.sequence_preview || '').length > 1400 ? '…' : '' }}\n```\n\n<{{ $('Build Pre-demo Brief').item.json.link }}|Open lead in Twain>",
        "otherOptions": {
          "includeLinkToWorkflow": false,
          "unfurl_links": false,
          "unfurl_media": false
        }
      }
    },
    {
      "id": "da26fe7e-e8bc-4cf6-ad0e-7774145b6492",
      "name": "Slack: Twain error",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [4420, 740],
      "onError": "continueRegularOutput",
      "parameters": {
        "authentication": "accessToken",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "n8n-workflows",
          "mode": "name"
        },
        "text": "=:rotating_light:  *Twain sequence generation failed*\n\n*{{ $('Normalize Booking').item.json.displayName || $('Normalize Booking').item.json.email || 'Unknown invitee' }}*\n:email:  {{ $('Normalize Booking').item.json.email || 'No email provided' }}\n\n*Error:*\n{{ String($json.error?.message ?? $json.message ?? 'Unknown error').replace(/^\\s*\\d{3}\\s*-\\s*/, '').trim() }}\n\n<https://www.twain.ai/w|Open Twain workspace>",
        "otherOptions": {
          "includeLinkToWorkflow": false,
          "unfurl_links": false,
          "unfurl_media": false
        }
      }
    },
    {
      "id": "e92693d2-9527-4bcb-a3ba-1a60a958b0d7",
      "name": "Normalize Booking",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [1000, 520],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const src = $json.body ?? $json ?? {};\nconst PERSONAL = [\"gmail.com\",\"googlemail.com\",\"outlook.com\",\"hotmail.com\",\"live.com\",\"msn.com\",\"yahoo.com\",\"ymail.com\",\"icloud.com\",\"me.com\",\"mac.com\",\"proton.me\",\"protonmail.com\",\"aol.com\",\"gmx.com\",\"gmx.net\",\"zoho.com\",\"pm.me\",\"mail.com\",\"yandex.com\"];\nconst DEMO_TERMS = [\"demo\"];\nconst str = (v) => (v === undefined || v === null) ? \"\" : String(v);\nconst firstNonEmpty = (...vals) => { for (const v of vals) { const s = str(v).trim(); if (s) return s; } return \"\"; };\n\n// --- Detect provider (order matters: most specific first) ---\nlet source = \"generic\";\nif (src.payload && (src.payload.scheduled_event || src.payload.uri || src.event === \"invitee.created\" || src.payload.email)) source = \"calendly\";\nelse if (src.guest || src.slot || (src.event && src.event.start) || src.crm) source = \"chili_piper\";\nelse if (src.properties || src.dealId !== undefined || src.engagementId !== undefined || src.objectId !== undefined) source = \"hubspot_meetings\";\nelse if (src.kind === \"calendar#event\" || (src.start && Array.isArray(src.attendees)) || src.iCalUID) source = \"google_calendar\";\n\n// --- Calendly LinkedIn from Q&A (best effort) ---\nconst qaLinkedin = () => {\n  const qa = src.payload && Array.isArray(src.payload.questions_and_answers) ? src.payload.questions_and_answers : [];\n  for (const item of qa) {\n    const q = str(item && (item.question || item.q)).toLowerCase();\n    if (q.includes(\"linkedin\")) return str(item && (item.answer || item.a)).trim();\n  }\n  return \"\";\n};\n\n// --- Google Calendar: pick the prospect attendee (not the host/organizer) ---\nconst gcalAttendee = () => {\n  const att = Array.isArray(src.attendees) ? src.attendees : [];\n  const prospect = att.find((a) => a && a.self !== true && a.organizer !== true && a.responseStatus !== \"declined\");\n  return prospect || att[0] || null;\n};\nconst gcalLinkedin = () => {\n  const liRe = /https?:\\/\\/([\\w-]+\\.)*linkedin\\.com\\/(in|pub|company)\\/[^\\s\"'<>)\\]]+/i;\n  const liScrape = (String(src.description||\"\") + \" \" + String(src.location||\"\")).match(liRe);\n  return liScrape ? liScrape[0] : \"\";\n};\n\n// --- Per-source extraction with generic-flat fallback (all off `src`) ---\nconst p = src.payload || {};\nconst props = src.properties || {};\nconst gAtt = source === \"google_calendar\" ? gcalAttendee() : null;\n\nconst email = firstNonEmpty(\n  p.email,\n  src.guest && src.guest.email, src.fields && src.fields.email,\n  props.email,\n  gAtt && gAtt.email, source === \"google_calendar\" ? (src.creator && src.creator.email) : \"\",\n  src.email\n).toLowerCase();\n\nconst name = firstNonEmpty(\n  p.name,\n  src.guest && src.guest.name, src.fields && src.fields.name,\n  src.firstName && src.lastName ? (src.firstName + \" \" + src.lastName) : \"\",\n  props.firstname && props.lastname ? (props.firstname + \" \" + props.lastname) : \"\",\n  props.firstname,\n  gAtt && gAtt.displayName,\n  src.name, src.displayName, src.invitee_name\n);\n\nconst meeting_time = firstNonEmpty(\n  p.scheduled_event && p.scheduled_event.start_time,\n  src.event && src.event.start, src.slot && src.slot.start, src.startTime,\n  props.hs_meeting_start_time,\n  src.start && (src.start.dateTime || src.start.date),\n  src.meeting_time, src.start_time\n);\n\nconst meeting_title = firstNonEmpty(\n  p.scheduled_event && p.scheduled_event.name,\n  source === \"google_calendar\" ? src.summary : \"\",\n  src.event && src.event.name, src.meetingType, src.fields && src.fields.meetingType, src.slot && src.slot.name,\n  props.hs_meeting_title, props.meeting_name, src.subject,\n  src.meeting_type, src.title, src.event_type, src.eventType\n);\n\nconst linkedin_url = firstNonEmpty(\n  source === \"calendly\" ? qaLinkedin() : \"\",\n  source === \"google_calendar\" ? gcalLinkedin() : \"\",\n  p.linkedin, p.linkedin_url, p.linkedin_profile_url,\n  src.guest && src.guest.linkedin,\n  src.linkedin, src.linkedin_url, src.linkedin_profile_url,\n  props.linkedin, props.linkedin_url\n);\n\nconst deal_id = firstNonEmpty(\n  src.crm && src.crm.dealId,\n  src.dealId, props.deal_id, src.associatedDealId,\n  p.deal_id,\n  src.deal_id\n);\n\nconst stable_id = firstNonEmpty(\n  p.scheduled_event && p.scheduled_event.uri, p.uri,\n  src.bookingId, src.eventId, src.event && src.event.id,\n  src.engagementId, src.objectId, props.hs_object_id,\n  source === \"google_calendar\" ? (src.id || src.iCalUID) : \"\",\n  src.id, src.uid, src.booking_id, src.event_id\n);\n\nconst uid = stable_id || (email + \"|\" + meeting_time);\nconst email_domain = (email.split(\"@\")[1] || \"\").toLowerCase();\nconst is_personal_email = PERSONAL.includes(email_domain);\nconst has_work_email = Boolean(email) && !is_personal_email;\nconst has_linkedin_url = Boolean(linkedin_url);\n\nconst t = meeting_title.toLowerCase();\nconst is_demo = !meeting_title ? true : DEMO_TERMS.some((term) => t.includes(term));\n\nreturn {\n  source: source,\n  uid: uid,\n  email: email,\n  displayName: name,\n  meeting_time: meeting_time,\n  meeting_title: meeting_title,\n  is_demo: is_demo,\n  campaign_id: ($('Demo Settings').first().json.campaign_id || \"REPLACE_WITH_DEMO_CAMPAIGN_ID\"),\n  linkedin_url: linkedin_url,\n  deal_id: deal_id,\n  email_domain: email_domain,\n  is_personal_email: is_personal_email,\n  has_linkedin_url: has_linkedin_url,\n  has_work_email: has_work_email\n};\n"
      }
    },
    {
      "id": "001af38b-93ed-490e-916d-44ecb3d1aa1c",
      "name": "Calendly Trigger",
      "type": "n8n-nodes-base.calendlyTrigger",
      "typeVersion": 2,
      "position": [240, 350],
      "disabled": true,
      "parameters": {
        "events": ["invitee.created"]
      }
    },
    {
      "id": "e5404f3d-2b7b-4361-bdf8-f88787437cc9",
      "name": "Chili Piper Booking",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [240, 520],
      "parameters": {
        "httpMethod": "POST",
        "path": "booking/chili-piper",
        "options": {}
      }
    },
    {
      "id": "7d7528d4-e0bb-45fa-a395-fc72b16b7a70",
      "name": "HubSpot Meeting Booked",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [240, 690],
      "parameters": {
        "httpMethod": "POST",
        "path": "booking/hubspot",
        "options": {}
      }
    },
    {
      "id": "fb1db403-9f17-4af5-bb22-13ade33675df",
      "name": "Google Calendar Trigger",
      "type": "n8n-nodes-base.googleCalendarTrigger",
      "typeVersion": 1,
      "position": [240, 860],
      "parameters": {
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "calendarId": {
          "__rl": true,
          "value": "REPLACE_WITH_DEMO_CALENDAR_ID",
          "mode": "list",
          "cachedResultName": "Demo"
        },
        "triggerOn": "eventCreated",
        "options": {}
      }
    },
    {
      "id": "ca1dc18f-8ac9-4092-a7ad-2a61707f4814",
      "name": "Demo meetings only",
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2.3,
      "position": [1380, 520],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 3
          },
          "conditions": [
            {
              "leftValue": "={{ $json.is_demo }}",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "rightValue": "",
              "id": "demo-filter-1"
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      }
    },
    {
      "id": "5a458db9-4979-46a8-838a-67af46c49291",
      "name": "Demo Settings",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [620, 520],
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "campaign_id",
              "name": "campaign_id",
              "value": "REPLACE_WITH_DEMO_CAMPAIGN_ID",
              "type": "string"
            },
            {
              "id": "email_steps",
              "name": "email_steps",
              "value": "1",
              "type": "string"
            },
            {
              "id": "linkedin_steps",
              "name": "linkedin_steps",
              "value": "2",
              "type": "string"
            },
            {
              "id": "heyreach_campaign_id",
              "name": "heyreach_campaign_id",
              "value": "REPLACE_WITH_HEYREACH_CAMPAIGN_ID",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      }
    },
    {
      "id": "ab8b1915-f4b0-4114-b420-0c5387caa5a5",
      "name": "Needs LinkedIn Lookup?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [2140, 520],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "needs-li-1",
              "leftValue": "={{ !$('Normalize Booking').item.json.has_linkedin_url && Boolean($('Normalize Booking').item.json.email) }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      }
    },
    {
      "id": "9ebc71d0-8bb7-40c9-8f6a-1a117dbd391a",
      "name": "LinkedIn Lookup — Findymail",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [2520, 300],
      "onError": "continueRegularOutput",
      "retryOnFail": true,
      "maxTries": 2,
      "waitBetweenTries": 2000,
      "parameters": {
        "method": "POST",
        "url": "https://app.findymail.com/api/search/reverse-email",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ { email: $('Normalize Booking').item.json.email, with_profile: true } }}",
        "options": {
          "timeout": 30000
        }
      }
    },
    {
      "id": "f32682b7-1eb5-4cc2-bf95-2b6e214ca758",
      "name": "Apply Resolved LinkedIn",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [2900, 520],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Resolve LinkedIn URL from the provider response.\n// Adjust the field below to match YOUR provider's response shape.\nconst resp = $json || {};\nconst linkedin_url = resp.linkedin_url ?? resp.linkedin_profile_url ?? resp.url ?? resp.contact?.linkedin_url ?? resp.profile?.linkedin_url ?? resp.person?.linkedin_url ?? resp.data?.linkedin_url ?? resp.response?.linkedin_url ?? \"\";\n\n// Carry forward all normalized fields, overriding only the LinkedIn ones.\nconst n = $('Normalize Booking').item.json;\nreturn {\n  ...n,\n  linkedin_url: linkedin_url,\n  has_linkedin_url: Boolean(linkedin_url)\n};\n"
      }
    },
    {
      "id": "d8a84847-73a3-420e-8933-7c814f19540b",
      "name": "Resolved Contact",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [3280, 520],
      "parameters": {}
    },
    {
      "id": "f2f37ca3-abee-41ab-b01b-06189b668bb4",
      "name": "Has LinkedIn steps?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [5940, 520],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 3
          },
          "conditions": [
            {
              "id": "has-li-steps-1",
              "leftValue": "={{ $('Build Pre-demo Brief').item.json.has_linkedin_steps }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      }
    },
    {
      "id": "c85bea94-b794-4e23-85c2-8d08ce8c729b",
      "name": "HeyReach: Add to Campaign",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [6320, 520],
      "onError": "continueRegularOutput",
      "parameters": {
        "method": "POST",
        "url": "https://api.heyreach.io/api/public/campaign/AddLeadsToCampaignV2",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\n  campaignId: Number($('Build Pre-demo Brief').item.json.heyreach_campaign_id) || $('Build Pre-demo Brief').item.json.heyreach_campaign_id,\n  accountLeadPairs: [{\n    lead: {\n      profileUrl: $('Build Pre-demo Brief').item.json.linkedin_url,\n      firstName: ($('Build Pre-demo Brief').item.json.person_name || \"\").trim().split(\" \")[0] || \"\",\n      lastName: ($('Build Pre-demo Brief').item.json.person_name || \"\").trim().split(\" \").slice(1).join(\" \") || \"\",\n      companyName: $('Build Pre-demo Brief').item.json.company_name || \"\",\n      emailAddress: $('Build Pre-demo Brief').item.json.to_email || \"\",\n      customUserFields: [{ name: \"twain_message\", value: $('Build Pre-demo Brief').item.json.linkedin_preview || \"\" }]\n    }\n  }]\n} }}",
        "options": {
          "timeout": 30000
        }
      }
    },
    {
      "id": "c559eed6-e935-438f-affa-89804a6b6d5c",
      "name": "Slack: LinkedIn steps queued (HeyReach)",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [6700, 520],
      "parameters": {
        "authentication": "accessToken",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "n8n-workflows",
          "mode": "name"
        },
        "text": "=:link:  *LinkedIn step(s) queued — HeyReach*\n\n*{{ $('Build Pre-demo Brief').item.json.person_name }}*{{ [$('Build Pre-demo Brief').item.json.person_title, $('Build Pre-demo Brief').item.json.company_name].filter(Boolean).length ? '  ·  ' + [$('Build Pre-demo Brief').item.json.person_title, $('Build Pre-demo Brief').item.json.company_name].filter(Boolean).join(' @ ') : '' }}\n\n{{ $('Build Pre-demo Brief').item.json.linkedin_line }}\n\n:speech_balloon:  *_LinkedIn message_*\n```\n{{ ($('Build Pre-demo Brief').item.json.linkedin_preview || '').slice(0, 1400) }}{{ ($('Build Pre-demo Brief').item.json.linkedin_preview || '').length > 1400 ? '…' : '' }}\n```\n\n{{ $('HeyReach: Add to Campaign').isExecuted ? ':white_check_mark:  Pushed to HeyReach campaign `' + ($('Build Pre-demo Brief').item.json.heyreach_campaign_id || '') + '`.' : ':wrench:  *Configure HeyReach to enable* — add the `X-API-KEY` credential + `heyreach_campaign_id`, then enable `HeyReach: Add to Campaign`. Until then, send these manually.' }}\n<{{ $('Build Pre-demo Brief').item.json.link }}|Open lead in Twain>",
        "otherOptions": {
          "includeLinkToWorkflow": false,
          "unfurl_links": false,
          "unfurl_media": false,
          "thread_ts": {
            "replyValues": {
              "thread_ts": "={{ $('Slack: Pre-demo brief + draft ready').isExecuted ? $('Slack: Pre-demo brief + draft ready').item.json.message_timestamp : ($('Slack: Brief ready, no draft').isExecuted ? $('Slack: Brief ready, no draft').item.json.message_timestamp : $('Slack: Brief ready (LinkedIn only)').item.json.message_timestamp) }}"
            }
          }
        }
      }
    },
    {
      "id": "a63b7bfd-5df4-4082-ab21-102d233e8a2a",
      "name": "LinkedIn Lookup — Apollo",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [2520, 80],
      "disabled": true,
      "onError": "continueRegularOutput",
      "parameters": {
        "method": "POST",
        "url": "https://api.apollo.io/api/v1/people/match",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ { email: $('Normalize Booking').item.json.email } }}",
        "options": {
          "timeout": 30000
        }
      }
    },
    {
      "id": "b1a9bc6b-bffd-4bca-ac35-a983d00aca38",
      "name": "LinkedIn Lookup — People Data Labs",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [2520, -140],
      "disabled": true,
      "onError": "continueRegularOutput",
      "parameters": {
        "url": "=https://api.peopledatalabs.com/v5/person/enrich?email={{ encodeURIComponent($('Normalize Booking').item.json.email) }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "options": {
          "timeout": 30000
        }
      }
    },
    {
      "id": "757d37b3-bdd0-4991-b373-ff2fdc6eb986",
      "name": "LinkedIn Lookup — Proxycurl",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [2520, -360],
      "disabled": true,
      "onError": "continueRegularOutput",
      "parameters": {
        "url": "=https://nubela.co/proxycurl/api/linkedin/profile/resolve/email?email={{ encodeURIComponent($('Normalize Booking').item.json.email) }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "options": {
          "timeout": 30000
        }
      }
    }
  ],
  "connections": {
    "Twain Generate Sequence": {
      "main": [
        [
          {
            "node": "Build Pre-demo Brief",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Slack: Twain error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Pre-demo Brief": {
      "main": [
        [
          {
            "node": "Has Work Email?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Work Email?": {
      "main": [
        [
          {
            "node": "Gmail: Create Pre-demo Draft",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Slack: Brief ready (LinkedIn only)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gmail: Create Pre-demo Draft": {
      "main": [
        [
          {
            "node": "Slack: Pre-demo brief + draft ready",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Slack: Brief ready, no draft",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Booking": {
      "main": [
        [
          {
            "node": "Demo meetings only",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Demo meetings only": {
      "main": [
        [
          {
            "node": "Dedupe Bookings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calendly Trigger": {
      "main": [
        [
          {
            "node": "Demo Settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Calendar Trigger": {
      "main": [
        [
          {
            "node": "Demo Settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Chili Piper Booking": {
      "main": [
        [
          {
            "node": "Demo Settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HubSpot Meeting Booked": {
      "main": [
        [
          {
            "node": "Demo Settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generic Booking (Webhook)": {
      "main": [
        [
          {
            "node": "Demo Settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Demo Settings": {
      "main": [
        [
          {
            "node": "Normalize Booking",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Needs LinkedIn Lookup?": {
      "main": [
        [
          {
            "node": "LinkedIn Lookup — Findymail",
            "type": "main",
            "index": 0
          },
          {
            "node": "LinkedIn Lookup — Apollo",
            "type": "main",
            "index": 0
          },
          {
            "node": "LinkedIn Lookup — People Data Labs",
            "type": "main",
            "index": 0
          },
          {
            "node": "LinkedIn Lookup — Proxycurl",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Resolved Contact",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Apply Resolved LinkedIn": {
      "main": [
        [
          {
            "node": "Resolved Contact",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has LinkedIn steps?": {
      "main": [
        [
          {
            "node": "HeyReach: Add to Campaign",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HeyReach: Add to Campaign": {
      "main": [
        [
          {
            "node": "Slack: LinkedIn steps queued (HeyReach)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LinkedIn Lookup — Findymail": {
      "main": [
        [
          {
            "node": "Apply Resolved LinkedIn",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LinkedIn Lookup — Apollo": {
      "main": [
        [
          {
            "node": "Apply Resolved LinkedIn",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LinkedIn Lookup — People Data Labs": {
      "main": [
        [
          {
            "node": "Apply Resolved LinkedIn",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LinkedIn Lookup — Proxycurl": {
      "main": [
        [
          {
            "node": "Apply Resolved LinkedIn",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack: Pre-demo brief + draft ready": {
      "main": [
        [
          {
            "node": "Has LinkedIn steps?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack: Brief ready, no draft": {
      "main": [
        [
          {
            "node": "Has LinkedIn steps?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack: Brief ready (LinkedIn only)": {
      "main": [
        [
          {
            "node": "Has LinkedIn steps?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dedupe Bookings": {
      "main": [
        [
          {
            "node": "Needs LinkedIn Lookup?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Resolved Contact": {
      "main": [
        [
          {
            "node": "Has usable contact?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has usable contact?": {
      "main": [
        [
          {
            "node": "Twain Generate Sequence",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Slack: Skipped (cannot research)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

After import you connect your own Twain API key (Header Auth), the Gmail mailbox drafts should land in, Slack, and any booking-tool OAuth you use — then paste your demo campaign's campaign_id into the one labeled field in the Demo Settings node. The template calls the synchronous Generate/Sequence endpoint in High mode, currently 2 Twain credits for a new generation. The Campaign Brief is where you decide how the sequence reads; every future booking inherits it.

Who this is for

This earns its place anywhere a prepared demo moves the number:

  • AE-owned and sales-assist motions — turn the cold first ten minutes into a real conversation, every call.
  • Founder-led sales — when the founder is the closer, this is the research team they don't have headcount for.
  • RevOps and demand-gen — protect the cost-per-meeting you're already paying by making sure booked demos actually land.
  • Teams on any booking stack — Calendly, Chili Piper, HubSpot Meetings, or the Google Calendar catch-all. Same brief, whatever the source.

Before a meeting is even on the calendar, the signup fit triage workflow identifies which inbound leads deserve a fast human response. Once a qualified lead books, this workflow takes over: the rep gets the brief, and a human still approves every send.