Skip to content

Answer every inbound

Every inbound question answered within 5 minutes from your own pages, with the next step set: a call booked, a trial, or you.

Kind
Agent task
Price
1.1¢ per inbound
Your time
20 minutes
Approval
Runs on its own
Lead.respond
You, to your agent

Answer this week’s unanswered inbound questions, oldest first, spend up to $1, and show me the plan first.

curl -X POST "https://api.sb/leaseline.app/leads/respond?mode=try" \
  -H "Authorization: Bearer $SB_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"goal":"Answer this week’s unanswered inbound questions, oldest first, spend up to $1, and show me the plan first.","budget":{"amount":1,"currency":"USD"}}'
npx startup-builder do Lead.respond --try --data '{"goal":"Answer this week’s unanswered inbound questions, oldest first, spend up to $1, and show me the plan first.","budget":{"amount":1,"currency":"USD"}}'
{
  "name": "do",
  "arguments": {
    "mode": "try",
    "code": "return await $.Lead.respond({ goal: 'Answer this week’s unanswered inbound questions, oldest first, spend up to $1, and show me the plan first.', budget: { amount: 1, currency: 'USD' } })"
  }
}
import { $ } from 'startup-builder'

const result = await $.try.Lead.respond({ goal: 'Answer this week’s unanswered inbound questions, oldest first, spend up to $1, and show me the plan first.', budget: { amount: 1, currency: 'USD' } })
Response
{
  "mode": "try",
  "stored": false,
  "estimate": "About 1.1¢ an inbound",
  "sample": {
    "lead": "Dana W., Harbor Point Residential",
    "decision": "book-with-ae",
    "reason": "340 units; asked about portfolio reporting",
    "answered": "2m 51s",
    "next": "call booked Thu 10:30"
  }
}
Try mode reads, stores nothing and spends nothingAPI referenceIts Nouns

What you get

  • 1 inbound · Every inbound question answered within 5 minutes from your own pages, with the next step set: a call booked, a trial, or you. · Lead
  • decision · The choice: book-with-ae, start-a-trial, nurture, not-a-fit, to-a-person or already-a-customer, its reason and when it reopens.
  • answer · The channel, when it went out, and the minutes it took.
  • next · What it set in motion: a meeting booked, a trial started, a nurture enrolled, or a task for you.

Connect once: Gmail, Google Calendar. How long: The first answer in under 5 minutes; the measured median is 2 minutes 51 seconds.

Trigger

It runs on every routed lead.

TypeScript
on.Lead.routed((lead, $) => $.Lead.respond({ 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 who sent itContact.enrichName, title and employerAbout 0.2¢Runs on its own
2. Read their companyOrganization.enrichHeadcount and location, for the fit ruleAbout 0.2¢Runs on its own
3. Match the question to your pages and offerCollection.queryThe passages that answer it, or noneFreeRuns on its own
4. Decide the next step and record whyLead.qualifyOne of six choices, recorded as a Decision with its reason and when it reopensAbout 0.2¢Runs on its own
5. Write the answerContent.draftAn answer from your pages, with the next step in itAbout 0.4¢Runs on its own
6. Send it and set the next stepConversation.answerThe answer sent; a call booked, a trial started or a nurture enrolledAbout 0.05¢Runs on its own

Calls: Contact.enrich, then Organization.enrich, then Collection.query, then Lead.qualify, then Content.draft, then Conversation.answer.

About 1.1¢ an inbound, each time it runs, capped at 10¢.

Example: give it a goal

Answer this week’s unanswered inbound questions, oldest first, spend up to $1, and show me the plan first.

The plan it shows runs 6 steps, priced before it spends anything; Run it above shows the result it returns.

When something fails

Each failure, and what happens next
WhenThen
Your pages cannot answer the questionThe choice is to-a-person: a task for you, and a reply that says when a person will answer, still inside 5 minutes.
The sender is already a customerIt is routed to the customer’s owner and never answered as a new lead.
The calendar is not connectedIt offers the booking link instead of three times, and tells you once.
One lead writes twiceIt gets one first answer: it is idempotent per lead.

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-respond-1-<step>-<item>.

How it is graded

A second model checks each answer against your approved pages; code times it from arrival and reads back its Decision. It passes when every inbound is answered within 5 minutes from your approved pages, with its Decision recorded. A miss is free: you pay nothing for it.

Change it

AskCompiles to
Send questions about pricing to me instead.Lead.respond
Offer a trial to everyone under 100 units.Lead.route
Brief me before every call it books.Deal.brief
TypeScript
import { $, workflow } from 'startup-builder'

export default workflow({
  name: 'lead-respond',
  verb: 'Lead.respond',
  input: { lead: 'string', within: 'string', pages: "'approved' | string[]" },
  trigger: { event: 'Lead.routed' },
  maxCost: 0.1,
  review: { pattern: 'adversarial', rubric: 'approved Pages only; no claim beyond the Offer; no clash with an open Deal' },
  grade: { evaluator: 'the answer against approved Pages; minutes from arrival', passWhen: 'answered in 5 minutes with a Decision', remedy: 'free' },
  execute: async (ctx, { lead, within = '5m', pages = 'approved' }) => {
    const l = await ctx.step('lead', () => $.Collection.query({ from: 'lead', id: lead, with: ['contact', 'organization'] }))
    const passages = await ctx.step('pages', () => $.Collection.query({ from: 'page', where: pages === 'approved' ? "approved = true" : `url in ${pages}`, match: l.question }))
    const decision = await ctx.step('decide', () => $.Lead.qualify({ lead, from: passages, options: ['book-with-ae', 'start-a-trial', 'nurture', 'not-a-fit', 'to-a-person', 'already-a-customer'] }))
    const answer = await ctx.step('write', () => $.Content.draft({ kind: 'reply', to: l, from: passages, next: decision.choice, maxCost: 0.01 }))
    await ctx.step('send', () => $.Conversation.answer({ lead, body: answer.body, within }))
    if (decision.choice === 'book-with-ae') await ctx.step('book', () => $.Meeting.book({ lead, offer: 3 }))
    return { decision, next: decision.choice }
  },
})

Extend it with extends: 'Lead.respond' and base(), or compose your own from these steps.

Works with

Plays that order it: Agent-to-agent selling, Instant response, lead nurture, Outbound calling, Single sign-on upgrade.

It serves Convert visitors, and runs in Hands-on, Paid. It deepens Answer inbound.

For agents

For agents: the guide, its approvals, retries and lookups
FactValue
GuideLead.respond
Guide approvallimits
GoalAnswer this week’s unanswered inbound questions, oldest first, 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.routed((lead, $) => $.Lead.respond({ lead: lead.$id }))
RetryThree times with backoff, then the next route; the same idempotency key never charges, sends or posts twice
Idempotency keyleaseline-lead-respond-1
LookupsA find: input asks unless exactly one record fits
Step
1. Read who sent it
2. Read their company
3. Match the question to your pages and offer
4. Decide the next step and record why
5. Write the answer
6. Send it and set the next step

The amounts live once, on the hub (approvals).

Who does each step, and how it is checked
StepFunctionDoerCheckerAssuranceInputs
1. Read who sent itcodeCodeCode checks each result against its declared typemeasured$0.lead.email
2. Read their companycodeCodeCode checks each result against its declared typemeasured$1.organization
3. Match the question to your pages and offercodeCodeCode checks each result against its declared typemeasured$0.lead.question, $0.pages
4. Decide the next step and record whyagenticThe AE agentA second model grades the outputgraded$1, $2, $3
5. Write the answeragenticThe AE agentAdversarial review by a second model: Pass, or held: approved pages only, no claim past the offer, no clash with an open dealgraded$3.passages, $4.choice
6. Send it and set the next stepcodeCodeCode reads the result back from where it landedmeasured$5.passed, $4.choice

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, sites and content
NounsContact, Organization, Collection, Lead, Content, Action, Conversation
EventsContact.enriched, Collection.queried, Lead.qualified, Content.drafted, Action.evaluated, Conversation.answered

Reference

Inputs, outputs, its Event and its errors
NameTypeDefaultMeaning
leadLeadRequiredThe inbound: a form, a signup question, a reply to an email or a comment.
withinstring'5m'The promise: the first answer goes out inside this.
pages'approved' | Url[]'approved'The pages an answer may draw on. A claim from anywhere else is cut.
calendarCalendaryour connected calendarWhere a call is booked: three open times offered, the first accepted is held.
maxCostnumber0.10The 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.responded. Refuses with max-cost before it spends, and with the Data calls and Accounts groups (refusals).

Contents