Score and route every new demo request
Reads new Typeform demo requests, enriches them with Apollo, scores fit and picks a route with Jev, and logs them in HubSpot.
Outcome
A table of every new request with its fit level, route, Jev's confidence and the person and company behind it.
Each real lead created or updated in HubSpot with its fit level and a note on its route, and spam, vendors and job seekers left out.
A single-use booking link and a drafted reply for each sales lead with a good enough fit.
A Slack post for each lead routed to sales.
How it works
- 1
Pull new requests
TypeformRead the completed responses to
formsubmitted aftersinceand more than 30 minutes ago, since the newest may not be there yet. Keep each response's answers toform_fields, and tell the user that 30-minute cutoff to use as the nextsince. - 2
Enrich the people
ApolloMatch up to
max_enrichmentswork emails; skip personal email domains. Keep each person's title and employer. - 3
Size their companies
ApolloLook up each work email's domain once, within what is left of
max_enrichments. Keep each company's industry and employee count. - 4
Score and route
TypeSafe AISend each request's answers and enrichment as the state, with a score
fitwhose levels describe the match withideal_customer: no fit (matches none of it), weak (matches some), good (matches most) and ideal (matches all); a choicerouteof sales (a team evaluating or ready to buy), self_serve (a small team that wants to try it), partner (wants to integrate, resell or refer), existing_customer (a customer asking for help), job_seeker (asks about jobs), vendor_pitch (sells something to you), spam (junk or nonsense) and other; and a noulactive_evaluation(names a deadline, a budget or a tool they are replacing). Keep each answer with its confidence or probability. - 5
Check the unsure ones with the user
Your agentShow the user every request whose route or fit confidence is below
min_confidence, with its two most likely routes, and keep what the user picks. - 6
Log the leads
HubSpotAfter the user approves, upsert every request not routed to job_seeker, vendor_pitch or spam by email, with its name, company, title and most likely fit level in
fit_property. Keep each contact's ID. - 7
Note why
HubSpotAdd a note to each contact ID with the route, the fit level,
active_evaluationand their message. - 8
Make booking links
Calendly
For each lead routed to sales with a fit of
min_fitor better, create a single-use link onevent_type. Keep each link. - 9
Write the replies
Your agentDraft a two-sentence reply for each lead with a booking link that answers their message in one line and offers the link, for a rep to send, leaving a [fill in] for any fact or commitment you don't have. Show the drafts to the user.
- 10
Alert sales
SlackPost each sales lead to
sales_channelwith their name, email, title, company, size and industry, the fit level,active_evaluation, the message, the booking link and the drafted reply.
You'll be asked for
The Typeform demo request form, by name
e.g. Book a demo
The refs of its email, name, company and message questions
e.g. email, full_name, company, what_do_you_need
The cutoff the last run reported, so only newer requests are read
e.g. 2026-09-29 08:00 UTC
Who you sell to, in a sentence or two
e.g. B2B software companies of 50 to 1,000 employees with a sales team
The lowest fit level that earns a booking link
e.g. good
The most Apollo lookups in one run, people and companies together, since each match costs a credit
e.g. 50
How sure Jev must be before its answer is used without you
e.g. 0.8; a yes-or-no answer counts as yes at or above it and as no at or below 1 minus it
The HubSpot contact property that holds the fit level, created once as a number from 0 to 3
e.g. jev_fit_level
The Calendly event type sales books demos on
e.g. 30 minute demo
The Slack channel for leads routed to sales
e.g. #inbound-leads
The file your agent runs
inbound-lead-routing.md
Score and route every new demo request
Reads new Typeform demo requests, enriches them with Apollo, scores fit and picks a route with Jev, and logs them in HubSpot.
Set up the tools below, then run the steps in order for the user, carrying each step's results into the next. The run is done when the user has the outcome below.
Outcome
- A table of every new request with its fit level, route, Jev's confidence and the person and company behind it.
- Each real lead created or updated in HubSpot with its fit level and a note on its route, and spam, vendors and job seekers left out.
- A single-use booking link and a drafted reply for each sales lead with a good enough fit.
- A Slack post for each lead routed to sales.
Inputs
Ask the user for these before you start.
form: the Typeform demo request form, by name, e.g. Book a demoform_fields: the refs of its email, name, company and message questions, e.g. email, full_name, company, what_do_you_needsince: the cutoff the last run reported, so only newer requests are read, e.g. 2026-09-29 08:00 UTCideal_customer: who you sell to, in a sentence or two, e.g. B2B software companies of 50 to 1,000 employees with a sales teammin_fit: the lowest fit level that earns a booking link, e.g. goodmax_enrichments: the most Apollo lookups in one run, people and companies together, since each match costs a credit, e.g. 50min_confidence: how sure Jev must be before its answer is used without you, e.g. 0.8; a yes-or-no answer counts as yes at or above it and as no at or below 1 minus itfit_property: the HubSpot contact property that holds the fit level, created once as a number from 0 to 3, e.g. jev_fit_levelevent_type: the Calendly event type sales books demos on, e.g. 30 minute demosales_channel: the Slack channel for leads routed to sales, e.g. #inbound-leads
Set up
Retrieve responses (Typeform, tool:typeform/retrieve-responses)
Use the API.
- Base URL: https://api.typeform.com
- Endpoint:
GET /forms/{form_id}/responses - Auth: send the header
Authorization: Bearer $TYPEFORM_API_KEY - Get a key: https://admin.typeform.com/user/tokens
Note: Forms with more than 1,000 responses need narrower since and until ranges or the before and after cursors. Responses from the last 30 minutes or so may be missing: use a webhook for real-time leads.
Apollo (tool:apollo/enrich-person, tool:apollo/enrich-company)
Use the first option your agent supports.
Notes:
- Enrich a person: A match costs 1 credit, plus 8 for a mobile phone; no match costs nothing. Personal emails and phone numbers are off unless you set
reveal_personal_emailsorreveal_phone_number, and phone numbers arrive later at awebhook_url. - Enrich a company: Costs 1 credit per company. To enrich up to 10 companies in one call, use bulk organization enrichment instead.
MCP (official, remote)
Add this server to your agent's MCP settings, then sign in when asked.
{ "mcpServers": { "apollo": { "url": "https://mcp.apollo.io/mcp" } } }
- Enrich a person: call the MCP tool
apollo_people_match - Enrich a company: call the MCP tool
apollo_organizations_enrich
CLI (official)
Install the command and sign in with it, then confirm it runs.
brew install apolloio/apollo-io-cli/apollo-io-cli
apollo --version
- Enrich a person: run
apollo people enrich - Enrich a company: run
apollo companies enrich
Answer typed questions (TypeSafe AI, tool:typesafe/answer-typed-questions)
Use the API.
- Base URL: https://api.typesafe.ai
- Endpoint:
POST /v1/systemone - Auth: send the header
Authorization: Bearer $TYPESAFE_API_KEY - Get a key: https://console.typesafe.ai
Note: Put every question about one record in one request. GET /v1/models lists the models and is the cheapest check of a key; a request over the rate limit gets a 429 with a Retry-After header.
Note: Send state, model (jev-latest) and named questions, each with type, instructions and criteria: a choice maps labels to descriptions, a score lists its levels in order, a noul's is optional. Read a score by its most likely level; send unsure answers to a person.
HubSpot (tool:hubspot/upsert-contacts, tool:hubspot/create-note)
For each call, use the first option your agent supports that lists it.
Notes:
- Create or update contacts: Matches each contact on
idProperty,emailor a custom unique property. On MCP,manage_crm_objectswaits for the user to confirm the proposed changes before it writes. - Create a note:
hs_timestampis required; attach the note to existing records with anassociationsobject.
MCP (official, remote)
Add this server to your agent's MCP settings, then sign in when asked.
{ "mcpServers": { "hubspot": { "url": "https://mcp.hubspot.com" } } }
- Create or update contacts: call the MCP tool
manage_crm_objects
API (official)
- Base URL: https://api.hubapi.com
- Create or update contacts:
POST /crm/objects/2026-09/contacts/batch/upsert - Create a note:
POST /crm/objects/2026-09/notes - Auth: send the header
Authorization: Bearer $HUBSPOT_API_KEY
Note: Use a service key or an app's static access token with the CRM scopes the calls need; paths carry a dated version such as 2026-09.
Create a single-use scheduling link (Calendly, tool:calendly/create-scheduling-link)
Use the first option your agent supports.
Note: Needs the event type's URI as owner, with owner_type: EventType and max_event_count: 1: list event types first to find it. Create one link per prospect.
MCP (official, remote)
Add this server to your agent's MCP settings, then sign in when asked.
{ "mcpServers": { "calendly": { "url": "https://mcp.calendly.com" } } }
Call the MCP tool scheduling_links-create_single_use_scheduling_link.
API (official)
- Base URL: https://api.calendly.com
- Endpoint:
POST /scheduling_links - Auth: send the header
Authorization: Bearer $CALENDLY_API_KEY - Get a key: https://calendly.com/integrations/api_webhooks
Post a message (Slack, tool:slack/post-message)
Use the first option your agent supports.
Note: Needs the chat:write scope. Over MCP it posts as the signed-in user; the API and CLI post as the app's bot.
MCP (official, remote)
Add this server to your agent's MCP settings, then sign in when asked.
{ "mcpServers": { "slack": { "url": "https://mcp.slack.com/mcp" } } }
Call the MCP tool slack_send_message.
CLI (official)
Install the command, then confirm it runs.
curl -fsSL https://downloads.slack-edge.com/slack-cli/install.sh | bash
slack --version
Run slack api chat.postMessage.
Set $SLACK_BOT_TOKEN in your environment first (get a key: https://api.slack.com/apps).
Before step 1, confirm access to each service with its cheapest read-only call, like a list or a search. Never send or change anything to test access.
Steps
- Pull new requests with Retrieve responses (Typeform). Read the completed responses to
formsubmitted aftersinceand more than 30 minutes ago, since the newest may not be there yet. Keep each response's answers toform_fields, and tell the user that 30-minute cutoff to use as the nextsince. - Enrich the people with Enrich a person (Apollo). Match up to
max_enrichmentswork emails; skip personal email domains. Keep each person's title and employer. - Size their companies with Enrich a company (Apollo). Look up each work email's domain once, within what is left of
max_enrichments. Keep each company's industry and employee count. - Score and route with Answer typed questions (TypeSafe AI). Send each request's answers and enrichment as the state, with a score
fitwhose levels describe the match withideal_customer: no fit (matches none of it), weak (matches some), good (matches most) and ideal (matches all); a choicerouteof sales (a team evaluating or ready to buy), self_serve (a small team that wants to try it), partner (wants to integrate, resell or refer), existing_customer (a customer asking for help), job_seeker (asks about jobs), vendor_pitch (sells something to you), spam (junk or nonsense) and other; and a noulactive_evaluation(names a deadline, a budget or a tool they are replacing). Keep each answer with its confidence or probability. - Check the unsure ones with the user yourself. Show the user every request whose route or fit confidence is below
min_confidence, with its two most likely routes, and keep what the user picks. - Log the leads with Create or update contacts (HubSpot). After the user approves, upsert every request not routed to job_seeker, vendor_pitch or spam by email, with its name, company, title and most likely fit level in
fit_property. Keep each contact's ID. - Note why with Create a note (HubSpot). Add a note to each contact ID with the route, the fit level,
active_evaluationand their message. - Make booking links with Create a single-use scheduling link (Calendly). For each lead routed to sales with a fit of
min_fitor better, create a single-use link onevent_type. Keep each link. - Write the replies yourself. Draft a two-sentence reply for each lead with a booking link that answers their message in one line and offers the link, for a rep to send, leaving a [fill in] for any fact or commitment you don't have. Show the drafts to the user.
- Alert sales with Post a message (Slack). Post each sales lead to
sales_channelwith their name, email, title, company, size and industry, the fit level,active_evaluation, the message, the booking link and the drafted reply.
Notes
Read each score by its most likely level, not its expected value. To rank leads within a level, sort by the summed probability of good and ideal. List the self_serve leads for whoever runs your onboarding emails rather than a rep. Run it every hour with since set to the cutoff the previous run reported, so each lead is routed within about 90 minutes.
Rules
- Use only the services set up above. The read-only calls they need, like listing ids or polling for results, are fine.
- Ask the user before anything that sends messages, costs money, or changes data, and say how many records it touches. One approval covers a batch the user has seen.
- Never print API keys.
