Referral ask
Each week, your five happiest customers asked for one introduction each, by a note naming the outcome they got from you.
- Kind
- Agent task
- Price
- 2.4¢ per 5 asks
- Your time
- 10 minutes
- Approval
- Runs on its own
- Deepens
- Retention
Ask my five happiest customers for an introduction, spend up to 25¢, and show me the plan first.
curl -X POST "https: //api.sb/ leaseline.app/ referrals/ ask? mode= try" \
-H "Authorization: Bearer $SB_TOKEN" \
-H "Content-Type: application/ json" \
-d '{"goal": "Ask my five happiest customers for an introduction, spend up to 25¢, and show me the plan first.", "budget": {"amount": 0.25, "currency": "USD"}}'
npx startup-builder do Referral.ask --try --data '{"goal": "Ask my five happiest customers for an introduction, spend up to 25¢, and show me the plan first.", "budget": {"amount": 0.25, "currency": "USD"}}'
{
"name": "do",
"arguments": {
"mode": "try",
"code": "return await $.Referral.ask({ goal: 'Ask my five happiest customers for an introduction, spend up to 25¢, and show me the plan first.', budget: { amount: 0.25, currency: 'USD' } })"
}
}
import { $ } from 'startup-builder'
const result = await $.try.Referral.ask({ goal: 'Ask my five happiest customers for an introduction, spend up to 25¢, and show me the plan first.', budget: { amount: 0.25, currency: 'USD' } })
{
"mode": "try",
"stored": false,
"estimate": "About 2.4¢ per 5 asks",
"sample": {
"customer": "Northgate Rentals",
"outcome": "31 renewals tracked, 0 missed",
"note": "You have not missed a renewal since March. Know another manager still on a spreadsheet? ",
"referral": "one, day 6"
}
}
What you get
- 5 asks · Each week, your five happiest customers asked for one introduction each, by a note naming the outcome they got from you. · Referral
asked[]· Each customer asked, the outcome the note names, and the note.referrals[]· Introductions that came back, each a Referral with its Acquisition.skipped· customers passed over and why, such as an open support issue.
How long: Seconds to send; replies over the week.
Trigger
It runs weekly, and when a customer publishes a review.
every.week(($) => $.Referral.ask({ take: 5 }))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. Rank customers by outcomes and replies | Metric.query | Customers past their first outcome, by outcomes delivered and the tone of recent replies | Free | Runs on its own |
| 2. Choose whom to ask | Collection.query | Five customers, each with the reason they were picked | About 0.2¢ | Runs on its own |
| 3. Write each ask | Content.draft | A two-sentence note naming the outcome they got and asking for one introduction | About 2¢ | Runs on its own |
| 4. Send them | Message.schedule | Each ask sent; a reply with a name becomes a Referral | About 0.25¢ | Runs on its own |
Calls: Metric.query, then Collection.query, then Content.draft, then Message.schedule.
About 2.4¢ per 5 asks, each time it runs, capped at 10¢.
Example: give it a goal
Ask my five happiest customers for an introduction, spend up to 25¢, and show me the plan first.
The plan it shows runs 4 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 |
|---|---|
| A customer has an open issue | They are never asked that week; the next customer by outcomes takes the slot. |
| An ask fails the grade | It is rewritten once; the redo is free. |
| You call it twice in one quarter for one customer | One ask per customer per quarter: it is idempotent. |
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-referral-ask-1-<step>-<item>.
How it is graded
A second model checks each ask against the customer’s record and your referral Policy. It passes when the outcome named is theirs and true, the reward matches the Policy, and the note asks for exactly one introduction. A miss is redone at no charge.
Change it
| Ask | Compiles to |
|---|---|
| Offer a month free for each introduction that becomes a customer. | Referral.ask |
| Ask after each review they publish. | Referral.ask |
| Write to each person they introduce within an hour. | Prospect.reach |
import { $, workflow } from 'startup-builder'
export default workflow({
name: 'referral-ask',
verb: 'Referral.ask',
input: { take: 'integer', offer: 'string?' },
trigger: { schedule: 'weekly' },
maxCost: 0.1,
grade: { evaluator: 'the Customer’s record; the referral Policy' , passWhen: 'their true outcome; the Policy’s reward; one introduction asked' , remedy: 'redo' },
execute: async (ctx, { take = 5, offer }) => {
const ranked = await ctx.step('rank', () => $.Metric.query({ metrics: ['outcomes-delivered', 'reply-sentiment'], by: 'customer', where: 'firstOutcome = true and openIssues = 0' }))
const notes = await ctx.step('write', () => $.Content.draft({ kind: 'referral-ask', customers: ranked.slice(0, take), offer, maxCost: 0.03 }))
for (const n of notes) await ctx.step(`send: ${n.customer}`, () => $.Message.schedule({ to: n.customer, body: n.body }))
return { asked: notes }
},
})Extend it with extends: and base(), or compose your own from these steps.
Works with
Plays that order it: Commission partners, customer referrals, Made-with line.
It serves Keep and grow customers, and runs in Hands-on, Keep and grow. It deepens Retention.
For agents
For agents: the guide, its approvals, retries and lookups
| Fact | Value |
|---|---|
| Guide | Referral.ask |
| Guide approval | none |
| Goal | Ask my five happiest customers for an introduction, spend up to 25¢, 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 | every.week(($) => $.Referral.ask({ take: |
| Retry | Three times with backoff, then the next route; the same idempotency key never charges, sends or posts twice |
| Idempotency key | leaseline-referral-ask-1 |
| Lookups | A find: input asks unless exactly one record fits |
| Step |
|---|
| 1. Rank customers by outcomes and replies |
| 2. Choose whom to ask |
| 3. Write each ask |
| 4. Send them |
The amounts live once, on the hub (approvals).
Who does each step, and how it is checked
| Step | Function | Doer | Checker | Assurance | Inputs |
|---|---|---|---|---|---|
| 1. Rank customers by outcomes and replies | code | Code | Code checks each result against its declared type | measured | $0.minOutcome |
| 2. Choose whom to ask | agentic | The CSM agent | A second model grades the output | graded | $1, |
| 3. Write each ask | agentic | The CSM agent | Adversarial review by a second model: Pass, or held: the outcome is theirs and true, and the reward matches the Policy | graded | $2, |
| 4. Send them | code | Code | Code reads the result back from where it landed | measured | $3.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 | Analytics and experiments, Mailboxes, sites and content |
| Nouns | Metric, Collection, Content, Action, Message |
| Events | Metric.queried, Collection.queried, Content.drafted, Action.evaluated, Message.scheduled |
Reference
Inputs, outputs, its Event and its errors
| Name | Type | Default | Meaning |
|---|---|---|---|
take | integer | 5 | Customers asked a week. |
minOutcome | string | their first outcome delivered | Only customers past it are asked. |
offer | string | Required | What the referrer gets, from your referral Policy, if anything. |
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 Referral.asked. Refuses with max-cost before it spends, and with the Data calls and Accounts groups (refusals).