Skip to content

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
Referral.ask
You, to your agent

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

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.

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

StepVerbReturnsPriceApproval
1. Rank customers by outcomes and repliesMetric.queryCustomers past their first outcome, by outcomes delivered and the tone of recent repliesFreeRuns on its own
2. Choose whom to askCollection.queryFive customers, each with the reason they were pickedAbout 0.2¢Runs on its own
3. Write each askContent.draftA two-sentence note naming the outcome they got and asking for one introductionAbout 2¢Runs on its own
4. Send themMessage.scheduleEach ask sent; a reply with a name becomes a ReferralAbout 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
WhenThen
A customer has an open issueThey are never asked that week; the next customer by outcomes takes the slot.
An ask fails the gradeIt is rewritten once; the redo is free.
You call it twice in one quarter for one customerOne 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

AskCompiles 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
TypeScript
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: 'Referral.ask' 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
FactValue
GuideReferral.ask
Guide approvalnone
GoalAsk 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.
Triggerevery.week(($) => $.Referral.ask({ take: 5 }))
RetryThree times with backoff, then the next route; the same idempotency key never charges, sends or posts twice
Idempotency keyleaseline-referral-ask-1
LookupsA 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
StepFunctionDoerCheckerAssuranceInputs
1. Rank customers by outcomes and repliescodeCodeCode checks each result against its declared typemeasured$0.minOutcome
2. Choose whom to askagenticThe CSM agentA second model grades the outputgraded$1, $0.take
3. Write each askagenticThe CSM agentAdversarial review by a second model: Pass, or held: the outcome is theirs and true, and the reward matches the Policygraded$2, $0.offer
4. Send themcodeCodeCode reads the result back from where it landedmeasured$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
KindWhat it touches
SystemsAnalytics and experiments, Mailboxes, sites and content
NounsMetric, Collection, Content, Action, Message
EventsMetric.queried, Collection.queried, Content.drafted, Action.evaluated, Message.scheduled

Reference

Inputs, outputs, its Event and its errors
NameTypeDefaultMeaning
takeinteger5Customers asked a week.
minOutcomestringtheir first outcome deliveredOnly customers past it are asked.
offerstringRequiredWhat the referrer gets, from your referral Policy, if anything.
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 Referral.asked. Refuses with max-cost before it spends, and with the Data calls and Accounts groups (refusals).

Contents