Lookalikes
Five companies you would love as customers in, 100 more like them out, each with the quote from its own site that shows it fits.
- Kind
- Workflow
- Price
- 36¢ per 100 companies
- Your time
- 10 minutes
- Approval
- Runs on its own
- Deepens
- Outbound
Find 100 companies like my five best customers, spend up to $2, and show me the plan first.
curl -X POST "https: //api.sb/ leaseline.app/ audiences/ expand? mode= try" \
-H "Authorization: Bearer $SB_TOKEN" \
-H "Content-Type: application/ json" \
-d '{"goal": "Find 100 companies like my five best customers, spend up to $2, and show me the plan first.", "budget": {"amount": 2, "currency": "USD"}}'
npx startup-builder do Audience.expand --try --data '{"goal": "Find 100 companies like my five best customers, spend up to $2, and show me the plan first.", "budget": {"amount": 2, "currency": "USD"}}'
{
"name": "do",
"arguments": {
"mode": "try",
"code": "return await $.Audience.expand({ goal: 'Find 100 companies like my five best customers, spend up to $2, and show me the plan first.', budget: { amount: 2, currency: 'USD' } })"
}
}
import { $ } from 'startup-builder'
const result = await $.try.Audience.expand({ goal: 'Find 100 companies like my five best customers, spend up to $2, and show me the plan first.', budget: { amount: 2, currency: 'USD' } })
{
"mode": "try",
"stored": false,
"estimate": "About 36¢ per 100 companies",
"sample": {
"company": "Maple Court Homes",
"similar to": "northgate-rentals.example",
"fit": "strong",
"quote": "“Our team manages 180 rental homes across the Twin Cities”"
}
}
What you get
- 100 companies · Five companies you would love as customers in, 100 more like them out, each with the quote from its own site that shows it fits. · Audience
audience· The kept companies, saved as an account Audience.companies[].similarTo· The seed each company was found from, so you can see which seed draws the best fits.companies[].fit·strong,possibleorunknown, with the quote and its page for a strong fit.bySeed· Strong fits per seed: drop a seed that draws under 10 in 100.
How long: About a minute.
Trigger
It runs when you call it, and each time an order is placed: the new customer becomes a seed.
on.Order.placed((order, $) => $.Audience.expand({ seeds: [order.customer.domain], take: 25 }))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 your seeds | Collection.query | The five seed companies: your top customers by revenue, or the ones you named | Free | Runs on its own |
| 2. Find companies like each seed | Organization.find | One and a half rows for every company wanted, each tagged with its seed | About 6¢ | Runs on its own |
| 3. Ask each site the fit question | Organization.find | fit with a quote code finds verbatim on the page, or unknown | About 30¢ | Runs on its own |
| 4. Save the strong fits as an Audience | Audience.create | An account Audience of take strong fits | Free | Runs on its own |
Calls: Collection.query, then Organization.find, then Audience.create.
About 36¢ per 100 companies, each time it runs, capped at 75¢.
Example: give it a goal
Find 100 companies like my five best customers, spend up to $2, 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 |
|---|---|
| Fewer than 3 seeds are given | It refuses with too-few-seeds and the fix “Name three companies like your best customer”, before it spends. |
| A seed returns no lookalikes | It is skipped and named in bySeed; the others carry on. |
| A site cannot be read | The company reads fit: and stays out; the question costs nothing when no page answers. |
| You call it again the same day with the same seeds | It returns the stored Audience at $0: it is idempotent per seeds and day. |
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-audience-expand-1-<step>-<item>.
Change it
| Ask | Compiles to |
|---|---|
| Find the operations lead at each, with a checked email. | Audience.build |
| Drop any seed that finds fewer than 10 strong fits. | Audience.expand |
| Sync this list to my Meta ad account as an audience. | Integration.sync |
import { $, workflow } from 'startup-builder'
export default workflow({
name: 'audience-expand',
verb: 'Audience.expand',
input: { seeds: "'top-customers' | string[]", take: 'integer', question: 'string?' , people: 'boolean' },
maxCost: 0.75,
execute: async (ctx, { seeds = 'top-customers', take = 100, question, people = false }) => {
const s = seeds === 'top-customers' ? (await ctx.step('seeds', () => $.Collection.query({ from: 'customer', sort: '-arr', limit: 5 }))).map((c) => c.domain) : seeds
if (s.length < 3) return ctx.refuse('too-few-seeds', 'Name three companies like your best customer')
const like = await ctx.step('similar', () => $.Organization.find({ similarTo: s, limit: Math.ceil(take * 1.5), maxCost: 0.1 }))
const fit = await ctx.step('fit', () => $.Organization.find({ hostnames: like.organizations.map((o) => o.hostname), question, maxCost: 0.35 }))
const strong = fit.organizations.filter((o) => o.fit === 'strong' && o.evidence.quoteFound).slice(0, take) // a declared rule
const audience = await ctx.step('save', () => $.Audience.create({ name: `Like my best · ${ctx.today}`, members: strong }))
return people ? ctx.step('people', () => $.Audience.build({ from: audience.$id, titles: ['owner', 'head of operations'], size: take })) : audience
},
})Extend it with extends: and base(), or compose your own from these steps.
Works with
Plays that order it: Portfolio cross-sell, House ads, Sibling audiences.
It serves Find your first customers, and runs in Hands-on. It deepens Outbound.
For agents
For agents: the guide, its approvals, retries and lookups
| Fact | Value |
|---|---|
| Guide | Audience.expand |
| Guide approval | limits |
| Goal | Find 100 companies like my five best customers, spend up to $2, 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.Order.placed((order, |
| Retry | Three times with backoff, then the next route; the same idempotency key never charges, sends or posts twice |
| Idempotency key | leaseline-audience-expand-1 |
| Lookups | A find: input asks unless exactly one record fits |
| Step |
|---|
| 1. Read your seeds |
| 2. Find companies like each seed |
| 3. Ask each site the fit question |
| 4. Save the strong fits as an Audience |
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 your seeds | code | Code | Code checks each result against its declared type | measured | $0.seeds |
| 2. Find companies like each seed | code | Code | Code checks each result against its declared type | measured | $1.seeds |
| 3. Ask each site the fit question | code | Code | Code finds the quoted evidence on the page | measured | $2.organizations, |
| 4. Save the strong fits as an Audience | code | Code | Code reads the result back from where it landed | measured | $3.strong, |
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 | Ads |
| Nouns | Collection, Organization, Audience |
| Events | Collection.queried, Audience.created |
Reference
Inputs, outputs, its Event and its errors
| Name | Type | Default | Meaning |
|---|---|---|---|
seeds | 'top-customers' | Hostname[] | 'top-customers' | Three to five companies to match: your best customers by revenue, or the ones you would most like to sign. |
take | integer | 100 | Strong-fit companies wanted. It stops at take strong fits, not at take rows. |
question | string | the question saved with your ICP | Asked of each company’s own site; a fit needs the quote that answers it. |
people | boolean | false | Hand the kept companies to Build a verified list for a checked email at each. |
maxCost | number | 0.75 | 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 Audience.expanded. Refuses with max-cost before it spends, and with the Data calls and Accounts groups (refusals).