---
ref: workflow:inbound-lead-routing
title: Score and route every new demo request
author: thedogwiththedataonit
tools: [tool:typeform/retrieve-responses, tool:apollo/enrich-person, tool:apollo/enrich-company, tool:typesafe/answer-typed-questions, tool:hubspot/upsert-contacts, tool:hubspot/create-note, tool:calendly/create-scheduling-link, tool:slack/post-message]
tags: [capability:book-meetings, capability:classify-signals, capability:collect-responses, capability:enrich-companies, capability:enrich-contacts, capability:manage-crm, capability:route-alerts, channel:chat, channel:email, has:api, motion:inbound]
updated: 2026-09-29
---

# 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 demo
- `form_fields`: the refs of its email, name, company and message questions, e.g. email, full_name, company, what_do_you_need
- `since`: the cutoff the last run reported, so only newer requests are read, e.g. 2026-09-29 08:00 UTC
- `ideal_customer`: who you sell to, in a sentence or two, e.g. B2B software companies of 50 to 1,000 employees with a sales team
- `min_fit`: the lowest fit level that earns a booking link, e.g. good
- `max_enrichments`: the most Apollo lookups in one run, people and companies together, since each match costs a credit, e.g. 50
- `min_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 it
- `fit_property`: the HubSpot contact property that holds the fit level, created once as a number from 0 to 3, e.g. jev_fit_level
- `event_type`: the Calendly event type sales books demos on, e.g. 30 minute demo
- `sales_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_emails` or `reveal_phone_number`, and phone numbers arrive later at a `webhook_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.

```json
{ "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.

```sh
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`, `email` or a custom unique property. On MCP, `manage_crm_objects` waits for the user to confirm the proposed changes before it writes.
- Create a note: `hs_timestamp` is required; attach the note to existing records with an `associations` object.

#### MCP (official, remote)

Add this server to your agent's MCP settings, then sign in when asked.

```json
{ "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.

```json
{ "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.

```json
{ "mcpServers": { "slack": { "url": "https://mcp.slack.com/mcp" } } }
```

Call the MCP tool `slack_send_message`.

#### CLI (official)

Install the command, then confirm it runs.

```sh
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

1. **Pull new requests** with Retrieve responses (Typeform). Read the completed responses to `form` submitted after `since` and more than 30 minutes ago, since the newest may not be there yet. Keep each response's answers to `form_fields`, and tell the user that 30-minute cutoff to use as the next `since`.
2. **Enrich the people** with Enrich a person (Apollo). Match up to `max_enrichments` work emails; skip personal email domains. Keep each person's title and employer.
3. **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.
4. **Score and route** with Answer typed questions (TypeSafe AI). Send each request's answers and enrichment as the state, with a score `fit` whose levels describe the match with `ideal_customer`: no fit (matches none of it), weak (matches some), good (matches most) and ideal (matches all); a choice `route` of 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 noul `active_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** 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.
6. **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.
7. **Note why** with Create a note (HubSpot). Add a note to each contact ID with the route, the fit level, `active_evaluation` and their message.
8. **Make booking links** with Create a single-use scheduling link (Calendly). For each lead routed to sales with a fit of `min_fit` or better, create a single-use link on `event_type`. Keep each link.
9. **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.
10. **Alert sales** with Post a message (Slack). Post each sales lead to `sales_channel` with 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.
