Champion moved
Each month, your champions checked for a new job, and each who moved to a company that fits added to your accounts with why now.
- Kind
- Workflow
- Price
- 42¢ per 200 checked
- Your time
- 8 minutes
- Approval
- Runs on its own
- Deepens
- Retention
Find where my champions moved this quarter, spend up to $2, and show me the plan first.
curl -X POST "https: //api.sb/ leaseline.app/ contacts/ follow? mode= try" \
-H "Authorization: Bearer $SB_TOKEN" \
-H "Content-Type: application/ json" \
-d '{"goal": "Find where my champions moved this quarter, spend up to $2, and show me the plan first.", "budget": {"amount": 2, "currency": "USD"}}'
npx startup-builder do Contact.follow --try --data '{"goal": "Find where my champions moved this quarter, spend up to $2, and show me the plan first.", "budget": {"amount": 2, "currency": "USD"}}'
{
"name": "do",
"arguments": {
"mode": "try",
"code": "return await $.Contact.follow({ goal: 'Find where my champions moved this quarter, spend up to $2, and show me the plan first.', budget: { amount: 2, currency: 'USD' } })"
}
}
import { $ } from 'startup-builder'
const result = await $.try.Contact.follow({ goal: 'Find where my champions moved this quarter, spend up to $2, and show me the plan first.', budget: { amount: 2, currency: 'USD' } })
{
"mode": "try",
"stored": false,
"estimate": "About 42¢ per 200 champions checked",
"sample": {
"person": "Maya C.",
"from": "Cedar Hollow Rentals",
"to": "Lakeshore Living",
"title": "COO",
"fit": "strong"
}
}
What you get
- 200 champions checked · Each month, your champions checked for a new job, and each who moved to a company that fits added to your accounts with why now. · Contact
checked· Champions read this month.moved[]· Each person who changed jobs: from, to, the new title and the new company’s fit.added[]· New companies added to your accounts, with why now: “your champion joined as COO”.
How long: About 3 minutes.
Trigger
It runs monthly, and on each list refresh row marked moved.
every.month(($) => $.Contact.follow({ contacts: 'champions' }))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. List your champions | Collection.query | Each champion, with their last known employer | Free | Runs on its own |
| 2. Read each champion’s profile | Contact.enrich | Current title and employer | About 40¢ | Runs on its own |
| 3. Find who moved | Collection.query | Each person whose employer changed, by the declared rule | Free | Runs on its own |
| 4. Ask each new employer the fit question | Organization.find | fit with the quote code finds on the page | About 1.2¢ | Runs on its own |
| 5. Read each new employer’s profile | Organization.enrich | Headcount and location | About 1.2¢ | Runs on its own |
| 6. Add each fit to your accounts | Audience.update | The company on the account Audience, with why now, which a Play can act on | Free | Runs on its own |
Calls: Collection.query, then Contact.enrich, then Collection.query, then Organization.find, then Organization.enrich, then Audience.update.
About 42¢ per 200 champions checked, each time it runs, capped at $1.00.
Example: give it a goal
Find where my champions moved this quarter, spend up to $2, 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 |
|---|---|
| A profile cannot be read | The champion keeps their last state and is read first next month. |
| The new employer does not fit | The move is recorded on the contact and nothing is added. |
| You call it twice in one month | The month’s reading returns: it is idempotent per contact and month. |
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-contact-follow-1-<step>-<item>.
Change it
| Ask | Compiles to |
|---|---|
| Write each one a note to say congratulations and ask for an intro. | Prospect.reach |
| Follow every user, not only champions. | Contact.follow |
| Check every two weeks instead of monthly. | Contact.follow |
import { $, workflow } from 'startup-builder'
export default workflow({
name: 'contact-follow',
verb: 'Contact.follow',
input: { contacts: "'champions' | string[]", question: 'string?' },
trigger: { schedule: 'monthly' },
maxCost: 1,
execute: async (ctx, { contacts = 'champions', question }) => {
const list = await ctx.step('list', () => $.Collection.query({ from: 'contact', where: contacts === 'champions' ? "role = 'champion'" : `$id in ${contacts}` }))
const now = await ctx.step('profiles', () => $.Contact.enrich({ contacts: list.map((c) => c.$id), fields: ['title', 'organization'], maxCost: 0.45 }))
const moved = now.filter((p) => p.organization.hostname !== p.was.organization.hostname) // the declared rule
const fit = await ctx.step('fit', () => $.Organization.find({ hostnames: moved.map((m) => m.organization.hostname), question, maxCost: 0.05 }))
return ctx.step('add', () => $.Audience.update({ id: 'audiences/audience_accounts' , add: fit.organizations.filter((o) => o.fit === 'strong'), whyNow: 'champion joined' }))
},
})Extend it with extends: and base(), or compose your own from these steps.
Works with
Plays that order it: Champion follow, Signal expansion.
It serves Keep and grow customers, and runs in Keep and grow. It deepens Retention.
For agents
For agents: the guide, its approvals, retries and lookups
| Fact | Value |
|---|---|
| Guide | Contact.follow |
| Guide approval | limits |
| Goal | Find where my champions moved this quarter, 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 | every.month(($) => $.Contact.follow({ contacts: |
| Retry | Three times with backoff, then the next route; the same idempotency key never charges, sends or posts twice |
| Idempotency key | leaseline-contact-follow-1 |
| Lookups | A find: input asks unless exactly one record fits |
| Step |
|---|
| 1. List your champions |
| 2. Read each champion’s profile |
| 3. Find who moved |
| 4. Ask each new employer the fit question |
| 5. Read each new employer’s profile |
| 6. Add each fit to your accounts |
The amounts live once, on the hub (approvals).
Who does each step, and how it is checked
| Step | Function | Doer | Checker | Assurance | Inputs |
|---|---|---|---|---|---|
| 1. List your champions | code | Code | Code checks each result against its declared type | measured | $0.contacts |
| 2. Read each champion’s profile | code | Code | Code checks each result against its declared type | measured | $1.contacts |
| 3. Find who moved | code | Code | Code checks each result against its declared type | measured | $1, |
| 4. Ask each new employer the fit question | code | Code | Code finds the quoted evidence on the page | measured | $3.moved, |
| 5. Read each new employer’s profile | code | Code | Code checks each result against its declared type | measured | $3.moved |
| 6. Add each fit to your accounts | code | Code | Code reads the result back from where it landed | measured | $4.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 | CRM |
| Nouns | Collection, Contact, Organization, Audience |
| Events | Collection.queried, Contact.enriched, Audience.updated |
Reference
Inputs, outputs, its Event and its errors
| Name | Type | Default | Meaning |
|---|---|---|---|
contacts | 'champions' | Contact[] | 'champions' | The people to follow: every contact marked champion, or a list. |
question | string | the question saved with your ICP | Asked of each new employer’s site; a fit needs the quote. |
every | string | '30d' | How often they are read. |
maxCost | number | 1.00 | 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 Contact.followed. Refuses with max-cost before it spends, and with the Data calls and Accounts groups (refusals).