How SalesBuilder is built
Six layers, from who you reach to what you try next, on three base records: each calls only the layers below it.
Six layers sit on three base records. A layer calls only the layers below it, and Events flow up.
You, to your agent
Show me one call from each layer for our first cold email, as a dry run.
curl -X POST "https://api.sb/leaseline.app/contacts/contact_22/enrich?mode=try" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
curl -X POST "https://api.sb/leaseline.app/messages/message_3/schedule?mode=try" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"at":"2026-11-03T14:00:00Z"}'
curl -X POST "https://api.sb/leaseline.app/contacts/contact_22/enroll?mode=try" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"sequence":"founder-thread"}'
curl -X POST https://api.sb/leaseline.app/tasks/next \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
curl -X POST "https://api.sb/leaseline.app/play-mixes/compile?mode=try" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"startup":"leaseline.app"}'import { $ } from 'startup-builder'
const sb = $.with({ namespace: 'leaseline.app' })
await sb.Contact.enrich({ contact: 'contact_22' }, { mode: 'try' }) // Lists: who it goes to
await sb.Message.schedule({ message: 'message_3', at: '2026-11-03T14: 00: 00Z' }, { mode: 'try' }) // Sending: from dana@leaseline.app
await sb.Contact.enroll({ contact: 'contact_22', sequence: 'founder-thread' }, { mode: 'try' }) // Sequences: follow-ups that stop when they reply
const { task } = await sb.Task.next() // Team: your SDR agent’s next Task
return await sb.PlayMix.compile({ startup: 'leaseline.app' }, { mode: 'try' }) // Plays: what to try next
npx startup-builder do Contact.enrich --json '{"contact":"contact_22"}' --try
npx startup-builder do Message.schedule --json '{"message":"message_3","at":"2026-11-03T14:00:00Z"}' --try
npx startup-builder do Contact.enroll --json '{"contact":"contact_22","sequence":"founder-thread"}' --try
npx startup-builder do Task.next
npx startup-builder do PlayMix.compile --json '{"startup":"leaseline.app"}' --try{
"name": "do",
"arguments": {
"code": "const sb = $.with({ namespace: 'leaseline.app' })\nawait sb.Contact.enrich({ contact: 'contact_22' }, { mode: 'try' }) // Lists: who it goes to\nawait sb.Message.schedule({ message: 'message_3', at: '2026-11-03T14:00:00Z' }, { mode: 'try' }) // Sending: from dana@leaseline.app\nawait sb.Contact.enroll({ contact: 'contact_22', sequence: 'founder-thread' }, { mode: 'try' }) // Sequences: follow-ups that stop when they reply\nconst { task } = await sb.Task.next() // Team: your SDR agent’s next Task\nreturn await sb.PlayMix.compile({ startup: 'leaseline.app' }, { mode: 'try' }) // Plays: what to try next",
"mode": "try"
}
}Response200
application/json{
"$id": "https: //api.sb/ leaseline.app/ play-mixes/ play-mix_1",
"$type": "PlayMix",
"customers": 0,
"hours": 3,
"budget": 0,
"plays": [
{
"play": "warm-intros",
"rank": 1
},
{
"play": "cold-email",
"rank": 2
},
{
"play": "signup-interviews",
"rank": 3
}
]
}
sampleA layer calls only the layers below it, and Events flow up: a Sequence never calls a Play, it emits an Event that a Play hears.
Lists
| It answers | Its files | Its calls | Not live yet |
|---|---|---|---|
| Who you sell to | audiences/ | POST /{namespace}/contacts/{contact}/enrich, POST /{namespace}/contacts/{contact}/merge, POST /{namespace}/visitors/{visitor}/identify, POST /{namespace}/leads/{lead}/qualify, POST /{namespace}/leads/{lead}/assign, POST /{namespace}/contacts/{contact}/suppress | Organization.enrich (Not live yet, gaps #2), Contact.import (Not live yet, gaps #2), Prospect.find (Not live yet, gaps #2) |
| Pages in this layer |
|---|
| Find your first customers |
Content
| It answers | Its files | Its calls | Not live yet |
|---|---|---|---|
| What you say | content/ | POST /{namespace}/content/draft, POST /{namespace}/content/{content}/publish, POST /{namespace}/posts/{post}/publish, POST /{namespace}/videos/draft, POST /{namespace}/keywords/{keyword}/measure, POST /{namespace}/reviews/{review}/publish | Content.refresh (Not live yet, gaps #2), Content.withdraw (Not live yet, gaps #2) |
| Pages in this layer |
|---|
| Research demand, Check AI answers, Size up competitors, Make content, Publish and post, Get indexed, Earn first links, Directory submissions, Launch circuit, Run ads, Measure |
Sending
| It answers | Its files | Its calls | Not live yet |
|---|---|---|---|
| Where it runs | systems/, domains/ | POST /{namespace}/domains/{domain}/authenticate, POST /{namespace}/mailboxes/{mailbox}/warm, POST /{namespace}/handles/{handle}/connect, POST /{namespace}/messages/{message}/schedule, POST /{namespace}/messages/{message}/broadcast, POST /{namespace}/posts/{post}/schedule | mail and gifts |
| Pages in this layer |
|---|
| Reach out, Instant response, Replies and routing, Signup flows, Meetings, Sequences, When to reach out, Work old leads |
Sequences
| It answers | Its files | Its calls | Not live yet |
|---|---|---|---|
| When it happens | sequences/, funnels/, workflows/ | POST /{namespace}/contacts/{contact}/enroll, POST /{namespace}/enrollments/{enrollment}/pause, POST /{namespace}/leads/{lead}/nurture, POST /{namespace}/meetings/book, POST /{namespace}/deals/{deal}/advance, POST /{namespace}/renewals/open | Referral.invite (Not live yet, gaps #3) |
Team
| It answers | Its files | Its calls | Not live yet |
|---|---|---|---|
| Who does it | agents/ | POST /{namespace}/roles/{role}/staff, POST /{namespace}/mandates/authorize, POST /{namespace}/approvals/{approval}/approve, POST /{namespace}/tasks/next, POST /{namespace}/actions/{action}/redirect, POST /{namespace}/actions/{action}/evaluate | round-robin across agents |
| Pages in this layer |
|---|
| How your agents act |
Plays
| It answers | Its files | Its calls | Not live yet |
|---|---|---|---|
| What to try next | plays/ | POST /{namespace}/play-mixes/compile, POST /{namespace}/experiments/{experiment}/start, POST /{namespace}/experiments/{experiment}/conclude, POST /{namespace}/processes/{process}/pause | Campaign.start (Not live yet, gaps #2), Campaign.pause (Not live yet, gaps #2) |
The base
| The base | Its files | Its calls | Where it lives |
|---|---|---|---|
| What you sell | offers/ | POST /{namespace}/offers/{offer}/publish, POST /{namespace}/offers/{offer}/trial, POST /{namespace}/orders/place, POST /{namespace}/quotes/{quote}/send | Offers and trials |
| Who has paid | the Customer records | POST /{namespace}/customers/{customer}/activate, POST /{namespace}/subscriptions/{subscription}/keep, POST /{namespace}/subscriptions/{subscription}/resize, POST /{namespace}/invoices/{invoice}/collect | Customers |
| How you score | metrics/ | POST /{namespace}/metrics/define, POST /{namespace}/metrics/query, POST /{namespace}/metrics/{metric}/watch | Revenue |
Compiled, not written: edit its page, then regenerate. Every step runs from a sentence or a line of code. In your words: the CRM words you know, and where each lives here.