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
- Deepens
- Answer inbound
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' } })
{
"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"
}
}
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-personoralready-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.
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.
| Step | Verb | Returns | Price | Approval |
|---|---|---|---|---|
| 1. Read who sent it | Contact.enrich | Name, title and employer | About 0.2¢ | Runs on its own |
| 2. Read their company | Organization.enrich | Headcount and location, for the fit rule | About 0.2¢ | Runs on its own |
| 3. Match the question to your pages and offer | Collection.query | The passages that answer it, or none | Free | Runs on its own |
| 4. Decide the next step and record why | Lead.qualify | One of six choices, recorded as a Decision with its reason and when it reopens | About 0.2¢ | Runs on its own |
| 5. Write the answer | Content.draft | An answer from your pages, with the next step in it | About 0.4¢ | Runs on its own |
| 6. Send it and set the next step | Conversation.answer | The answer sent; a call booked, a trial started or a nurture enrolled | About 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
| When | Then |
|---|---|
| Your pages cannot answer the question | The 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 customer | It is routed to the customer’s owner and never answered as a new lead. |
| The calendar is not connected | It offers the booking link instead of three times, and tells you once. |
| One lead writes twice | It 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
| Ask | Compiles 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 |
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: 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
| Fact | Value |
|---|---|
| Guide | Lead.respond |
| Guide approval | limits |
| Goal | Answer 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. |
| Trigger | on.Lead.routed((lead, |
| Retry | Three times with backoff, then the next route; the same idempotency key never charges, sends or posts twice |
| Idempotency key | leaseline-lead-respond-1 |
| Lookups | A 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
| Step | Function | Doer | Checker | Assurance | Inputs |
|---|---|---|---|---|---|
| 1. Read who sent it | code | Code | Code checks each result against its declared type | measured | $0.lead.email |
| 2. Read their company | code | Code | Code checks each result against its declared type | measured | $1.organization |
| 3. Match the question to your pages and offer | code | Code | Code checks each result against its declared type | measured | $0.lead.question, |
| 4. Decide the next step and record why | agentic | The AE agent | A second model grades the output | graded | $1, |
| 5. Write the answer | agentic | The AE agent | Adversarial review by a second model: Pass, or held: approved pages only, no claim past the offer, no clash with an open deal | graded | $3.passages, |
| 6. Send it and set the next step | code | Code | Code reads the result back from where it landed | measured | $5.passed, |
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, sites and content |
| Nouns | Contact, Organization, Collection, Lead, Content, Action, Conversation |
| Events | Contact.enriched, Collection.queried, Lead.qualified, Content.drafted, Action.evaluated, Conversation.answered |
Reference
Inputs, outputs, its Event and its errors
| Name | Type | Default | Meaning |
|---|---|---|---|
lead | Lead | Required | The inbound: a form, a signup question, a reply to an email or a comment. |
within | string | '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. |
calendar | Calendar | your connected calendar | Where a call is booked: three open times offered, the first accepted is held. |
maxCost | number | 0.10 | 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.responded. Refuses with max-cost before it spends, and with the Data calls and Accounts groups (refusals).