Inbound routing
A new signup or form fill in, who they are, where they work, whether they fit, and the next step out, in under 10 seconds.
- Kind
- Workflow
- Price
- 0.4¢ per inbound
- Your time
- 11 minutes
- Approval
- Runs on its own
- Deepens
- Answer inbound
Re-route every inbound from the last week with my new fit rule, spend up to $1, and show me the plan first.
curl -X POST "https: //api.sb/ leaseline.app/ leads/ route? mode= try" \
-H "Authorization: Bearer $SB_TOKEN" \
-H "Content-Type: application/ json" \
-d '{"goal": "Re-route every inbound from the last week with my new fit rule, spend up to $1, and show me the plan first.", "budget": {"amount": 1, "currency": "USD"}}'
npx startup-builder do Lead.route --try --data '{"goal": "Re-route every inbound from the last week with my new fit rule, spend up to $1, and show me the plan first.", "budget": {"amount": 1, "currency": "USD"}}'
{
"name": "do",
"arguments": {
"mode": "try",
"code": "return await $.Lead.route({ goal: 'Re-route every inbound from the last week with my new fit rule, spend up to $1, and show me the plan first.', budget: { amount: 1, currency: 'USD' } })"
}
}
import { $ } from 'startup-builder'
const result = await $.try.Lead.route({ goal: 'Re-route every inbound from the last week with my new fit rule, spend up to $1, and show me the plan first.', budget: { amount: 1, currency: 'USD' } })
{
"mode": "try",
"stored": false,
"estimate": "About 0.4¢ an inbound",
"sample": {
"lead": "Dana W., Director of Operations",
"company": "Harbor Point Residential",
"units": "340",
"fit": "fit",
"route": "booking"
}
}
What you get
- 1 inbound · A new signup or form fill in, who they are, where they work, whether they fit, and the next step out, in under 10 seconds. · Lead
person· Name, title and seniority, from the work email.organization· The company behind the domain, with headcount and what your fit rule reads.fit·fit,maybeornot, by the Policy, with the field that decided it.route· The next step:booking(a call),trial,nurtureorpersonfor a question only a person can answer.
How long: Under 10 seconds.
Trigger
It runs on every new lead.
on.Lead.created((lead, $) => $.Lead.route({ lead: lead.$id }))Steps
Your agent does these steps, one row per step; each step’s check is part of its row and its price.
| Step | Verb | Returns | Price | Approval |
|---|---|---|---|---|
| 1. Read the person behind the email | Contact.enrich | Name, title, seniority and employer | About 0.2¢ | Runs on its own |
| 2. Find the company behind the domain | Organization.resolve | The company’s hostname; a free-mail address skips this step | Free | Runs on its own |
| 3. Read the company’s profile | Organization.enrich | Headcount, location and industry | About 0.2¢ | Runs on its own |
| 4. Score fit by the Policy | Lead.qualify | fit, maybe or not, with the deciding field | Free | Runs on its own |
| 5. Route by the rule | Lead.assign | The lead assigned, with the booking or trial link attached | Free | Runs on its own |
Calls: Contact.enrich, then Organization.resolve, then Organization.enrich, then Lead.qualify, then Lead.assign.
About 0.4¢ an inbound, each time it runs, capped at 5¢.
Example: give it a goal
Re-route every inbound from the last week with my new fit rule, spend up to $1, and show me the plan first.
The plan it shows runs 5 steps, priced before it spends anything; Run it above shows the result it returns.
When something fails
Each failure, and what happens next
| When | Then |
|---|---|
| The lead used a free-mail address | The company step is skipped and the lead routes on its form answers alone. |
| No source knows the person | The row reads unknown for the person and still routes by the company; the miss is free. |
| You route the same lead twice | It returns the first route: it is idempotent per lead. Re-routing after a rule change is a new call. |
Each step retries three times, then falls through to the next route; one item's failure lands in skipped with its reason, never failing the call. Before a step would cross maxCost it stops and returns what it has, with a resume link. Each step's idempotency key is the call's key plus the step and the item: leaseline-lead-route-1-<step>-<item>.
Change it
| Ask | Compiles to |
|---|---|
| Send every lead over 100 units to a call, not a trial. | Lead.route |
| Answer each one within five minutes too. | Lead.respond |
| Re-route last week’s leads with the new rule. | Lead.route |
import { $, workflow } from 'startup-builder'
export default workflow({
name: 'lead-route',
verb: 'Lead.route',
input: { lead: 'string' },
trigger: { event: 'Lead.created' },
maxCost: 0.05,
execute: async (ctx, { lead }) => {
const l = await ctx.step('lead', () => $.Collection.query({ from: 'lead', id: lead }))
const who = await ctx.step('person', () => $.Contact.enrich({ email: l.email, fields: ['title', 'organization', 'seniority'], maxCost: 0.01 }))
const org = isFreeMail(l.email) ? undefined : await ctx.step('company', () => $.Organization.resolve({ email: l.email }))
const profile = org?.match ? await ctx.step('profile', () => $.Organization.enrich({ hostname: org.match.hostname, fields: ['profile'], maxCost: 0.01 })) : undefined
const q = await ctx.step('qualify', () => $.Lead.qualify({ lead, from: { who, profile } })) // the fit Policy
return ctx.step('assign', () => $.Lead.assign({ lead, route: q.fit === 'fit' ? 'booking' : q.fit === 'maybe' ? 'trial' : 'nurture' }))
},
})Extend it with extends: and base(), or compose your own from these steps.
Works with
Plays that order it: Agent-to-agent selling, Events and webinars, Family opt-in, Instant response, lead nurture, Outbound calling, Product-led growth, Waitlist interviews.
It serves Convert visitors. It deepens Answer inbound.
For agents
For agents: the guide, its approvals, retries and lookups
| Fact | Value |
|---|---|
| Guide | Lead.route |
| Guide approval | limits |
| Goal | Re-route every inbound from the last week with my new fit rule, spend up to $1, and show me the plan first. It may change counts, the order of steps, soft filters and the route a read takes, and it stops early when the goal is met. It never relaxes your words, raises its own budget, answers its own approval, calls a Play or acts outside your standing approval. |
| Trigger | on.Lead.created((lead, |
| Retry | Three times with backoff, then the next route; the same idempotency key never charges, sends or posts twice |
| Idempotency key | leaseline-lead-route-1 |
| Lookups | A find: input asks unless exactly one record fits |
| Step |
|---|
| 1. Read the person behind the email |
| 2. Find the company behind the domain |
| 3. Read the company’s profile |
| 4. Score fit by the Policy |
| 5. Route by the rule |
The amounts live once, on the hub (approvals).
Who does each step, and how it is checked
| Step | Function | Doer | Checker | Assurance | Inputs |
|---|---|---|---|---|---|
| 1. Read the person behind the email | code | Code | Code checks each result against its declared type | measured | $0.lead.email |
| 2. Find the company behind the domain | code | Code | Code checks each result against its declared type | measured | $0.lead.email |
| 3. Read the company’s profile | code | Code | Code checks each result against its declared type | measured | $2.hostname |
| 4. Score fit by the Policy | code | Code | Code reads the result back from where it landed | measured | $1, |
| 5. Route by the rule | code | Code | Code reads the result back from where it landed | measured | $4.fit, |
The doer is never the checker, and the weakest assurance on the path is the whole call’s.
Under the hood
Under the hood: the Systems, Nouns and Events it touches
| Kind | What it touches |
|---|---|
| Systems | CRM |
| Nouns | Contact, Organization, Lead |
| Events | Contact.enriched, Lead.qualified, Lead.assigned |
Reference
Inputs, outputs, its Event and its errors
| Name | Type | Default | Meaning |
|---|---|---|---|
lead | Lead | Required | The new lead, from a form, a signup, a reply or a lead ad. |
rule | Policy | your Lead fit Policy | The declared fit rule and routes, such as “over 200 units books a call; under that starts a trial”. |
maxCost | number | 0.05 | The most this call spends each time it runs, in USD. It stops before a step would cross it and returns what it has, with a resume link. |
Emits Lead.routed. Refuses with max-cost before it spends, and with the Data calls and Accounts groups (refusals).