Skip to content

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
Lead.route
You, to your agent

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' } })
Response
{
  "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"
  }
}
Try mode reads, stores nothing and spends nothingAPI referenceIts Nouns

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, maybe or not, by the Policy, with the field that decided it.
  • route · The next step: booking (a call), trial, nurture or person for a question only a person can answer.

How long: Under 10 seconds.

Trigger

It runs on every new lead.

TypeScript
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.

StepVerbReturnsPriceApproval
1. Read the person behind the emailContact.enrichName, title, seniority and employerAbout 0.2¢Runs on its own
2. Find the company behind the domainOrganization.resolveThe company’s hostname; a free-mail address skips this stepFreeRuns on its own
3. Read the company’s profileOrganization.enrichHeadcount, location and industryAbout 0.2¢Runs on its own
4. Score fit by the PolicyLead.qualifyfit, maybe or not, with the deciding fieldFreeRuns on its own
5. Route by the ruleLead.assignThe lead assigned, with the booking or trial link attachedFreeRuns 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
WhenThen
The lead used a free-mail addressThe company step is skipped and the lead routes on its form answers alone.
No source knows the personThe row reads unknown for the person and still routes by the company; the miss is free.
You route the same lead twiceIt 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

AskCompiles 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
TypeScript
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: 'Lead.route' 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
FactValue
GuideLead.route
Guide approvallimits
GoalRe-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.
Triggeron.Lead.created((lead, $) => $.Lead.route({ lead: lead.$id }))
RetryThree times with backoff, then the next route; the same idempotency key never charges, sends or posts twice
Idempotency keyleaseline-lead-route-1
LookupsA 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
StepFunctionDoerCheckerAssuranceInputs
1. Read the person behind the emailcodeCodeCode checks each result against its declared typemeasured$0.lead.email
2. Find the company behind the domaincodeCodeCode checks each result against its declared typemeasured$0.lead.email
3. Read the company’s profilecodeCodeCode checks each result against its declared typemeasured$2.hostname
4. Score fit by the PolicycodeCodeCode reads the result back from where it landedmeasured$1, $3, $0.rule
5. Route by the rulecodeCodeCode reads the result back from where it landedmeasured$4.fit, $0.rule

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
KindWhat it touches
SystemsCRM
NounsContact, Organization, Lead
EventsContact.enriched, Lead.qualified, Lead.assigned

Reference

Inputs, outputs, its Event and its errors
NameTypeDefaultMeaning
leadLeadRequiredThe new lead, from a form, a signup, a reply or a lead ad.
rulePolicyyour Lead fit PolicyThe declared fit rule and routes, such as “over 200 units books a call; under that starts a trial”.
maxCostnumber0.05The 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).

Contents