Build a Lead Qualification Agent
Use form triggers, scheduler, HubSpot, Gmail, Calendly, and variables to automatically qualify leads and book meetings — no code required.
Build a Lead Qualification Agent with Pipecat
I used to handle lead qualification the same way every time.
New form submission comes in. Check the email domain. Google the company. Open HubSpot. Add the contact. Write a follow-up email. Attach a Calendly link if they look promising.
Fifteen minutes per lead, every time, indefinitely.
Then I built the same workflow on Pipecat in about 20 minutes — and now it runs automatically, round the clock, without me touching it.
Here's exactly how to build it.
What You'll Build
A prospect fills out a contact form. That submission triggers a Pipecat workflow that:
- Researches the company using web search
- Scores the lead with an LLM (structured output, 1–10 score with reasoning)
- Routes hot vs. cold through a Conditional node
- Creates a HubSpot contact and deal (hot leads only)
- Fetches a Calendly scheduling link and sends a personalized Gmail
The whole thing runs in under 30 seconds. No code. No servers. Just a visual canvas and a shareable form URL.
Step 0 — Variables (do this first)
Before touching the canvas, click the Variables button in the toolbar to open the Variables panel.
Variables let you store API keys and tokens that you'll reuse across nodes. Reference them anywhere in the canvas as {{var.name}}. Toggle Secret and they're masked in the UI — change a token in one place and every node that references it updates automatically.
Add these three:
| Name | Value | Secret |
|---|---|---|
hubspot_token | Your HubSpot private app token | Yes |
calendly_token | Your Calendly personal token | Yes |
gmail_token | Your Gmail OAuth 2.0 access token | Yes |
[!TIP] Use the copy button next to each variable name to grab
{{var.hubspot_token}}— then paste it directly into any node config field that asks for an API key.
Getting your Gmail OAuth token
Gmail requires an OAuth 2.0 access token, not an API key. The fastest way to get one for testing is the Google OAuth Playground:
- Go to https://developers.google.com/oauthplayground
- In the left panel, scroll to Gmail API v1 and select
https://www.googleapis.com/auth/gmail.send - Click Authorize APIs and sign in with the Google account you want to send from
- Click Exchange authorization code for tokens
- Copy the
access_tokenvalue — paste it as the value ofgmail_tokenabove
[!IMPORTANT] OAuth access tokens expire after 1 hour. For a production workflow you'll want to store the
refresh_tokenand add a token-refresh step before the Gmail node. For testing and low-volume workflows the Playground token is fine — just re-paste a fresh one when it expires.
Step 1 — Form Trigger
Click the trigger icon in the toolbar to open the Trigger panel. Switch to the Form tab and toggle it on.
Fill in the appearance:
- Title: Request a Demo
- Description: Tell us a bit about yourself and we'll be in touch within the hour.
- Accent colour: your brand color
- Submit label: Request Demo
- Success message: Thanks — we'll be in touch shortly.
Then add your form fields:
| Label | Type | Required |
|---|---|---|
| Full Name | Text | Yes |
| Work Email | Yes | |
| Company | Text | Yes |
| Role / Title | Text | No |
| What are you trying to solve? | Text | No |
Once saved, Pipecat generates a public URL: app.pipecat.in/form/your-workflow-slug. Share it anywhere — your landing page, email signature, or embed as an iframe. Every submission runs the workflow automatically.
The form data is passed into the workflow as structured input. You'll reference fields like input.company and input.email in downstream nodes.
Step 2 — Research Node
Drop a Research node onto the canvas and connect it from the Input node.
Set the query to:
{{input.company}} company overview funding size industry
This node runs a web search and returns a structured summary of the company. The LLM downstream sees "Series B SaaS company, 80 employees, raised $12M in 2023, targets mid-market HR teams" — not just a name.
Step 3 — LLM Node (Qualification)
Add an LLM node and wire it after the Research node. This is the brain of the workflow.
System prompt:
You are a B2B lead qualification agent.
Given a prospect's form submission and company research, score the lead
from 1-10 based on:
- Company size (10+ employees = higher score)
- Role seniority (decision-maker = higher score)
- Problem specificity (clear pain point = higher score)
- Industry fit (SaaS, tech, professional services = higher score)
Be concise and direct in your reasoning.
Enable structured output on this node and define the schema:
| Field | Type | Description |
|---|---|---|
score | number | Lead score from 1 to 10 |
tier | string | "hot", "warm", or "cold" |
reason | string | 1–2 sentences explaining the score |
personalized_opener | string | Opening line for the follow-up email, specific to their situation |
The personalized_opener field is what makes the follow-up email feel human. The model writes it based on what the prospect said they're trying to solve — not a generic "Thanks for your interest."
[!IMPORTANT] Upstream nodes must use structured output for the Conditional node in the next step to access fields via
input.field. Always define an output schema on your LLM node.
Step 4 — Conditional Node
Add a Conditional node below the LLM node.
Set the condition:
input.score >= 7
Hot leads (score ≥ 7) flow down the true branch into HubSpot, Calendly, and Gmail. Cold and warm leads route to the false branch for a lighter reply.
Wire: LLM node → Conditional node.
Step 5 — HubSpot Nodes (hot branch)
On the true branch, add a HubSpot node.
Action: Create Contact
email → {{input.email}}
firstname → {{input.name}}
company → {{input.company}}
jobtitle → {{input.role}}
lead_score → {{input.score}}
token → {{var.hubspot_token}}
Chain a second HubSpot node to create a deal:
Action: Create Deal
dealname → Demo Request — {{input.company}}
pipeline → default
dealstage → appointmentscheduled
token → {{var.hubspot_token}}
Notice you're using {{var.hubspot_token}} in both — not pasting the raw token into each node.
Wire: Conditional (true) → HubSpot Create Contact → HubSpot Create Deal.
Step 6 — Calendly Node
Add a Calendly node on the hot branch.
Action: Get Scheduling Link
token → {{var.calendly_token}}
event_type_uri → (your event type URI from Calendly)
This returns a one-time scheduling link for the demo call event type. The link flows into the Gmail node in the next step.
Wire: HubSpot Create Deal → Calendly.
Step 7 — Gmail Node (hot lead)
Add a Gmail node on the hot branch.
Action: Send Email
to → {{input.email}}
subject → Re: Demo request from {{input.company}}
token → {{var.gmail_token}}
body →
Hi {{input.name}},
{{input.personalized_opener}}
I'd love to show you how Pipecat handles this — grab 30 minutes on my
calendar here:
{{input.calendly_link}}
Looking forward to it.
The {{input.personalized_opener}} is the field the LLM wrote in Step 3. Every email is different and specific to what the prospect said they're trying to solve.
Wire: Calendly → Gmail.
Step 8 — Gmail Node (cold / warm branch)
On the false branch from the Conditional, add another Gmail node.
to → {{input.email}}
subject → Thanks for reaching out
token → {{var.gmail_token}}
body →
Hi {{input.name}},
Thanks for getting in touch — we'll review your request and follow up
within 24 hours if there's a good fit.
In the meantime, you might find our documentation useful:
https://docs.pipecat.in
No Calendly link, no HubSpot deal. They still get an immediate response, and they land in your inbox for manual review later.
The Full Graph
[Form Trigger]
│
[Research]
│
[LLM — structured output: score, tier, reason, opener]
│
[Conditional: input.score >= 7]
/ \
[true] [false]
│ │
[HubSpot [Gmail
Create Contact] cold reply]
│
[HubSpot
Create Deal]
│
[Calendly
Get Link]
│
[Gmail
hot reply]
Nodes execute in topological order. The two branches after the Conditional are independent and don't block each other. If two nodes at any point have no dependency on each other, Pipecat runs them concurrently.
Step 9 — Publish and Test
Hit Publish. Open your form URL. Submit a test entry.
The canvas lights up node by node — grey (waiting), blue (running), green (done), red (error). Watch the Research node pull company data, the LLM node score the lead, and the Conditional branch take the right path.
For a hot-path test, use something like "VP of Engineering at Stripe" — that should score 8–10. For cold-path, try "student" with a personal Gmail — that should score 1–3. Confirm each branch fires correctly.
Bonus — Adding a Scheduled Digest
Open the Trigger panel again, switch to the Schedule tab, and enable it.
Select a preset (every 15 min / hourly / daily 9 AM / weekdays 9 AM) or write a custom cron expression. Set an optional fixed input that gets passed as the workflow's input on each scheduled run.
Build a separate lightweight workflow — LLM node that queries your HubSpot via an HTTP tool and emails you a morning digest of the last 24 hours of leads. Same canvas, different trigger, same variables.
What Each Feature Actually Solves
Variables — before this, you'd paste API tokens directly into node configs. Same token in 5 places, no way to rotate it without hunting them all down. Now: one place, masked, referenceable with {{var.name}} everywhere.
Form Trigger — invokes a workflow without exposing an API key. Generates a standalone URL anyone can fill out. Every submission runs the workflow automatically.
Scheduler Trigger — define a recurring schedule inside the workflow itself. No external cron job needed. Presets cover 90% of cases; custom cron covers the rest.
HubSpot — create contacts, update contacts, create deals, add notes. No more copy-pasting form submissions into your CRM.
Gmail — send, read inbox, search. Uses an OAuth 2.0 access token — get one from the Google OAuth Playground in about 60 seconds. The send action is the useful one for automation workflows.
Calendly — get a scheduling link for a specific event type. One action, but it's the integration point that turns a qualified lead reply from informational to actionable.
Conditional — branch your workflow based on any field from a structured upstream output. Only works when upstream nodes produce structured output — which is a good reason to always define output schemas on your LLM nodes.
Next Tutorial
Continue learning with: