Account visits
Finds the fitting companies behind visits to your own buying pages and writes to the right person there, from facts, never from the visit.
- Channel
- Who’s visiting
- Kind
- Outreach
Be our SDR for site visits: when a property manager that fits reads our pricing or setup pages, find who owns renewals there and write once, from a public fact, never from the visit. Top accounts go out as me. Show me what’s missing; don’t send.
# Not live yet (gaps #2)
curl -X POST "https://api.sb/leaseline.app/campaigns?mode=try" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"play":"account-visits"}'import { $ } from 'startup-builder'
return await $.Campaign.create({ play: 'account-visits' }, { mode: 'try' }) // dry run on the day-0 Startup, 2026-08-24 · Not live yet, gaps #2
npx startup-builder do Campaign.create --json '{"play":"account-visits"}' --try # Not live yet (gaps #2){
"name": "do",
"arguments": {
"code": "return await $.Campaign.create({ play: 'account-visits' }, { mode: 'try' }) // dry run on the day-0 Startup, 2026-08-24 · Not live yet, gaps #2",
"mode": "try"
}
}application/json{
"type": "https: //startupbuilder.dev/ errors/ refused",
"title": "Refused",
"status": 422,
"detail": "account-visits can’t start yet: 2 checks fail and 1 warns.",
"checks": [
{
"check": "owned-by-a-person",
"status": "pass",
"observed": "0 of the 104 fitting visiting companies have a Lead or open Deal a person owns",
"doc": "/plays/ account-visits#check-owned-by-a-person",
"then": "Task.assign"
},
{
…
Hand the latest company that read our buying pages to the SDR agent and show me what it chose and why. Don’t send.
curl "https://api.sb/leaseline.app/events?where=name%20in%20(%22Visit.recorded%22%2C%20%22Visitor.identified%22)&limit=1" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
# Not live yet (gaps #7)
curl -X POST "https://api.sb/leaseline.app/triggers/fire?mode=try" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"like":"event_1412"}'
# Not live yet (gaps #7)
curl "https://api.sb/leaseline.app/decisions?where=on%20%3D%20%22organization_131%22" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
# Not live yet (gaps #7)
curl "https://api.sb/leaseline.app/actions?where=on%20%3D%20%22organization_131%22" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"import { $ } from 'startup-builder'
const { events: [last] } = await $.Event.list({ where: 'name in ("Visit.recorded", "Visitor.identified")', limit: 1 }) // { play } compiled to this Play’s Trigger Events
const { subject } = await $.Trigger.fire({ like: last.$id }, { mode: 'try' }) // the same Event again, in try mode · Not live yet, gaps #7
const { decisions: [decision] } = await $.Decision.list({ where: `on = "${subject}"` }) // what it chose, and why · Not live yet, gaps #7
const { actions } = await $.Action.list({ where: `on = "${subject}"` }) // what it did, with its checks · Not live yet, gaps #7
return { decision, actions }
npx startup-builder do Event.list --json '{"where":"name in (\"Visit.recorded\", \"Visitor.identified\")","limit":1}'
npx startup-builder do Trigger.fire --json '{"like":"event_1412"}' --try # Not live yet (gaps #7)
npx startup-builder do Decision.list --json '{"where":"on = \"organization_131\""}' # Not live yet (gaps #7)
npx startup-builder do Action.list --json '{"where":"on = \"organization_131\""}' # Not live yet (gaps #7){
"name": "do",
"arguments": {
"code": "const { events: [last] } = await $.Event.list({ where: 'name in (\"Visit.recorded\", \"Visitor.identified\")', limit: 1 }) // { play } compiled to this Play’s Trigger Events\nconst { subject } = await $.Trigger.fire({ like: last.$id }, { mode: 'try' }) // the same Event again, in try mode · Not live yet, gaps #7\nconst { decisions: [decision] } = await $.Decision.list({ where: `on = \"${subject}\"` }) // what it chose, and why · Not live yet, gaps #7\nconst { actions } = await $.Action.list({ where: `on = \"${subject}\"` }) // what it did, with its checks · Not live yet, gaps #7\nreturn { decision, actions }",
"mode": "try"
}
}application/json{
"decision": {
"$id": "https: //api.sb/ leaseline.app/ decisions/ decision_455",
"$type": "Decision",
"choice": "write-to-them",
"reason": "Fernhill Property Group read pricing on 2 days in 4 (7 points, confirmed on 2 networks), and its careers page lists 2 leasing coordinator openings",
"stakes": "low",
"options": [
"write-to-them",
"write-as-you",
"ads-only",
"to-their-csm",
"not-now",
"let-it-go"
…
sample · 4 Not live yetPreviewOn POST /{namespace}/processes/{process}/enableOff POST /{namespace}/processes/{process}/pauseTake this one POST /{namespace}/tasks/{task}/claim
action_3 is touch 1 to Marta Silva (contact_14), Fernhill’s head of operations, held until 08:40 her time; touch 2 waits in the same thread for 6 days and drops if the stack decays first.
18.2 qualified responses per 100 against 2.9 before · before-after,
| Try another: the visiting company | Choice | Reason |
|---|---|---|
| Fernhill Property Group: pricing on 2 days in 4, 2 leasing roles open | write-to-them | 7 points, confirmed, with a public fact to write from |
| Calloway Residential: pricing and the security page, 900 units | write-as-you | a top-tier account: it goes out as Dana |
| Northgate Lettings: one setup-docs read, a probable match | ads-only | 3 points and probable: it joins the company Audience only |
| Juniper Homes: an active Customer on Pro | to-their-csm | already a Customer |
- 30 min week 1, then 15 a week
- Your time
- Day 6: first qualified reply
- First signal
- 500+ visitors a month, 4+ fitting companies
- Use it when
Where it sits: Stranger → Lead → Trialing → Activated → At risk → Churned → Won back · Visit reply lift → New Leads → New Customers ↑ → ARR added each week → ARR
Worth’s formula: about +4.8 qualified responses per 100 (Other Startups’ results; no lift of your own yet) × 17 companies written to a month × 0.45 responses to Deals or trials × 0.30 opens to paid × $1,200 first order ARR ≈ +$132. Under First signal: At 4 companies a week, 90 days can tell only +22 per 100 or more, so your file starts at holdout: and reads before-after.
sample · api.sb/
Start it this week
npx startup-builder add play account-visits # 1 · writes plays/account-visits.mdx (Not live yet, gaps #5)
# 2 · dry run; below, its 422 on the day-0 Startup (clock at 2026-08-24)
curl -X POST "https://api.sb/leaseline.app/campaigns?mode=try" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"play":"account-visits"}'
# 3 · keep it, at You approve each one for the first 20 drafts
curl -X POST https://api.sb/leaseline.app/campaigns \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"play":"account-visits"}'
# on
curl -X POST https://api.sb/leaseline.app/processes/outbound/enable \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
# off: lookups stop, nothing sends, the company Audience freezes
curl -X POST https://api.sb/leaseline.app/processes/outbound/pause \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"| Check | Fix | Then |
|---|---|---|
owned-by-a-person pass · Not live yet, gaps #8 | 0 of the 104 fitting visiting companies have a Lead or open Deal a person owns | POST /{namespace}/tasks/{task}/assign |
use-when pass · Not live yet, gaps #8 | 3,410 visitors in 30 days, 17 fitting companies at 5 points or more | POST /{namespace}/metrics/query |
baseline warn · Not live yet, gaps #8 | n 104: it reads before-after, not proof, until a held-back half can read. | POST /{namespace}/metrics/query |
visit-capture fail | Serve your Site from your Domain and publish the visitor notice; lookups start when both pass. | POST /{namespace}/domains/connect |
domain-authenticated pass | leaseline.app: 3 of 3 records resolve | POST /{namespace}/domains/{domain}/authenticate |
mailbox-warm fail · Not live yet, gaps #1 | Wait for the warm-up to finish, 14 days from day 0; ads-only keeps running. | POST /{namespace}/mailboxes/{mailbox}/warm |
role-unstaffed pass · Not live yet, gaps #8 | SDR and CSM are your agents; AE routes come to you until it is staffed | POST /{namespace}/roles/{role}/staff |
JSON
{
"type": "https://startupbuilder.dev/errors/refused",
"title": "Refused",
"status": 422,
"detail": "account-visits can’t start yet: 2 checks fail and 1 warns.",
"checks": [
{
"check": "owned-by-a-person",
"status": "pass",
"observed": "0 of the 104 fitting visiting companies have a Lead or open Deal a person owns",
"doc": "/plays/account-visits#check-owned-by-a-person",
"then": "Task.assign"
},
{
"check": "use-when",
"status": "pass",
"observed": "3,410 visitors in 30 days, 17 fitting companies at 5 points or more",
"doc": "/plays/account-visits#check-use-when",
"then": "Metric.query"
},
{
"check": "baseline",
"status": "warn",
"observed": "3 hand-raises from 104 fitting visiting companies in 90 days",
"doc": "/plays/account-visits#check-baseline",
"fix": "n 104: it reads before-after, not proof, until a held-back half can read.",
"then": "Metric.query"
},
{
"check": "visit-capture",
"status": "fail",
"doc": "/plays/account-visits#check-visit-capture",
"fix": "Serve your Site from your Domain and publish the visitor notice; lookups start when both pass.",
"then": "Domain.connect"
},
{
"check": "domain-authenticated",
"status": "pass",
"observed": "leaseline.app: 3 of 3 records resolve",
"doc": "/plays/account-visits#check-domain-authenticated",
"then": "Domain.authenticate"
},
{
"check": "mailbox-warm",
"status": "fail",
"doc": "/plays/account-visits#check-mailbox-warm",
"fix": "Wait for the warm-up to finish, 14 days from day 0; ads-only keeps running.",
"then": "Mailbox.warm"
},
{
"check": "role-unstaffed",
"status": "pass",
"observed": "SDR and CSM are your agents; AE routes come to you until it is staffed",
"doc": "/plays/account-visits#check-role-unstaffed",
"then": "Role.staff"
}
]
}It says no, says why, and names the fix.
| Check | Fix (one call) | Your time | Wait |
|---|---|---|---|
owned-by-a-person · use-when · pass: 0 person-owned; 3,410 visitors, 17 fitting companies a month (gaps #8) | POST /{namespace}/metrics/query on visit-reply-lift | 1 min | none |
baseline · warn: 3 of 104 in 90 days, so it reads before-after | POST /{namespace}/metrics/query; it fills Before in Your number | 0 min | none |
visit-capture · fail (Not live yet) | POST /{namespace}/domains/connect so your Site serves from leaseline.app, then POST /{namespace}/sites/{site}/publish the visitor notice | 15 min | the first Visits, under 1 h |
domain-authenticated · mailbox-warm · pass · fail: dana@ and sdr@ warmed 0 of 14 days | POST /{namespace}/mailboxes/{mailbox}/warm; company Audiences fill meanwhile | 2 min | 14 days |
role-unstaffed · pass (gaps #8) | POST /{namespace}/roles/{role}/staff the AE when you hire one | 0 min | none |
Your number · Before 2.9 per 100 · Now 18.2 qualified responses per 100 · Target 20 by Dec 31 · before-after,
Each lookup costs $0.01 per resolved network address, and misses are free; each draft costs $0.04 from your Domain (Worker cost). Contact and company data cost $0.06 per verified address (POST /{namespace}/contacts/{contact}/enrich). That comes to about $4 a month at 17 companies written to.
When it starts
| When | Trigger id | Starts on | Window | How it gets in |
|---|---|---|---|---|
| A fitting company reads your pricing or comparison page | pricing-visit | event on Visit.recorded where a resolved company reads the pricing or comparison Pages | scored the same day; ignored after 72 h | your Site on your Domain records each Visit (POST /{namespace}/events/capture) |
| It returns to a buying page on a second day within 7 | repeat-visit | threshold on Visit.recorded where a buying Page on a second day within 7 days | adds 2 points; the stack decays in 7 days | the same |
| It reads setup docs, an integration or the security page | evaluation-visit | event on Visit.recorded where page in ('setup docs', 'API reference', 'integration', 'case study', 'security') and organization resolved | 7 d | the same |
+ 2 more for company surges and the person rung, off in your file (?lanes=) |
{
"$id": "https://api.sb/leaseline.app/events/event_1412" ,
"$type": "Event",
"name": "Visit.recorded",
"at": "2026-10-19T13:20:05Z" ,
"seq": 1412,
"data": {
"organization": {
"$id": "https://api.sb/leaseline.app/organizations/organization_131" ,
"name": "Fernhill Property Group",
"confidence": "confirmed"
},
"page": "pricing",
"day": 2,
"points": 7
}
}# each delivery is signed with the Watch’s secret
curl -X POST https://api.sb/leaseline.app/watches \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"selector":"Visit.recorded","url":"https://leaseline.app/hooks/sb"}'
npx startup-builder trigger Visit.recorded --like event_1412 # replays the second visit
npx startup-builder clock advance --by 6d # runs touch 2 in the same thread, unless the stack decayed Who runs it
SDR · your agent (role:, worker_2) holds the Key Result. It hands replies to AE · Priya Shah (person) (role:, worker_3, staffed on day 9), top-tier accounts to you (Dana Okafor, person, worker_1), and Customers to CSM · your agent (worker_5). Your number links to Did it work.
| Option | Rung today | Evidence (passed of n) | Moves up at | Drops back when | You can |
|---|---|---|---|---|---|
| Write to them | It does it, you can stop it | 20 of 20 approved unedited, then 7 of 7 | met on day 31 (the first 20 drafts); capped here | any draft fails a Check, or visit-leak fires → You approve each one | POST /{namespace}/actions/{action}/cancel inside the hold |
| Write as you | You approve each one, on every rung | 4 of 4 | never | — | POST /{namespace}/approvals/{approval}/approve · POST /{namespace}/approvals/{approval}/refuse |
| Ads only · To their CSM · Not now · Let it go | It does it | 21 of 21 · 5 of 5 · 6 of 6 · 3 of 3 | — | a held company written to anyway → You approve each one | POST /{namespace}/actions/{action}/redirect |
| Change the scoring or the fit line | You approve each one | 0 merged | never | — | POST /{namespace}/changes/{change}/merge · POST /{namespace}/changes/{change}/revert |
| Never: a Visit, a page read or a time on site in a Message; the person rung | To a person, every time | 1 draft refused, 0 sent | never | — | POST /{namespace}/processes/{process}/pause |
What it reads · What it may call. A Visit picks the company and the role; it never appears in a Message, a subject line or a follow-up. The body comes only from the company’s own public pages and your approved Pages, and every send links a why-you Page with one-press stops. Its tools are POST /{namespace}/messages/{message}/schedule, PATCH /{namespace}/audiences/{id}, POST /{namespace}/tasks/{task}/assign, and any other call becomes an Approval under the Role’s Mandate.
You hold the SDR Key Result: 20 qualified responses per 100 companies written to by 2026-12-31. ← worker, metric
On each scored Visit, choose one option and say why. ← triggers, chooses
Write only to a confirmed fitting company at 5 points or more. ← your file, line 4
Open on a public fact about the company; never on the Visit. ← copyRules 1, visit-in-copy
Companies over 500 units go out as Dana, after her OK. ← your file, line 5
Never write to a Lead or Deal a person owns: draft it into their queue. ← worker.owns, owned-by-a-person The slow loop. The weekly Decisions kept the 5-point line, and no Change was merged in the window, so there is no replay or read-back to print.
Bring your own agent (folded) starts at It does it, you can stop it when it matches or beats the seated agent on the eval suite with 0 Visits named, and at You approve each one when it does not.
npx startup-builder eval --scenario account-visits-36 --agent "claude -p" --seat prospector # seated: 18.2 per 100, 0 Visits named, $0.05 a draft · yours: 15.0, 1 Visit named → You approve each one What it can do next
| Option | Picked when | Decided by | Runs | Hands to | If it’s just you |
|---|---|---|---|---|---|
Write to them write-to-them | confirmed, 5+ points, a role and a fact | Agent’s call, with its reason | visit-two-touch: touch 1 at 08:40 their time, touch 2 in thread at day 6 (POST /{namespace}/messages/{message}/schedule) | on reply, POST /{namespace}/tasks/{task}/assign → AE · Priya Shah, due 60 min · Take this one | you, within 60 min |
Write as you write-as-you | over 500 units, or a senior buyer | Your rule (line 5) | visit-two-touch-founder from dana@, after your OK | you approve each one, due 1 business day · Take this one | you |
Ads only ads-only | 3 or 4 points, or probable | Your rule (line 4) | PATCH /{namespace}/audiences/{id} on the company Audience account-ads reads | nobody | the same |
To their CSM to-their-csm | an active Customer | Your rule (the fit gate) | POST /{namespace}/tasks/{task}/assign with the pages they read, never sent to them | CSM · your agent | you |
Not now not-now | an open Deal or Conversation, a hold, or no verified address | Your rule (rule j) | nothing; names the holder: the AE, the hold, or the missing address | the Deal’s owner · Take this one | you |
Let it go let-it-go | no fact to write from, or under the fit line | Agent’s call, with its reason | nothing; the stack decays in 7 days | nobody | the same |
| When they write back | Fixture reply | What happens | Who answers | By when | Call |
|---|---|---|---|---|---|
| Interested | “Worth a look. Thursday afternoon?” | offers times on the AE’s calendar; the Brief goes to the AE (When it hands to you) | AE · Priya Shah (person) | 60 min | POST /{namespace}/meetings/book |
| Send me pricing | “What does it cost for 180 units?” | answers with the Pricing Page’s list Price and one next step | SDR · your agent | 5 min | POST /{namespace}/conversations/{conversation}/answer |
| Not now | “We’re locked in until spring.” | touch 2 drops; the company stays in the Audience and moves to Nurture | SDR · your agent | at once | POST /{namespace}/leads/{lead}/nurture |
| Wrong person | “Our COO, Ben, owns renewals.” | asks Marta to bring Ben in; it never writes to a name from her text | SDR · your agent | 1 business day | POST /{namespace}/conversations/{conversation}/answer |
| Out of office | an auto-reply until Oct 27 | touch 2 waits for the return date, or drops if the stack decays first | nobody | the return date | POST /{namespace}/messages/{message}/schedule |
| Question it can’t answer | “Do you handle HOA dues as well as leases?” | To the AE, the question in the Brief | AE · Priya Shah (person) | 60 min | POST /{namespace}/conversations/{conversation}/escalate |
| Stop | “Take us off your list.” | nothing else sends to anyone at the company, and its Visits stop being matched | nobody | at once | POST /{namespace}/contacts/{contact}/suppress |
When it hands to you
Replies go to AE · Priya Shah (person); top-tier drafts come to you; Customers go to CSM · your agent. Before day 9 every reply came to you. What lands in each queue, as captured: one Brief per Role, then the Approval (brief, draft, route and checks are Not live yet, gaps #7).
[
{
"$id": "https://api.sb/leaseline.app/tasks/task_244" ,
"$type": "Task",
"assignee": "worker_3",
"due": "2026-09-29T15:12:00Z" ,
"on": "lead_66",
"brief": {
"who": "Ines Duarte, Harbor Row Rentals" ,
"said": "Worth a look. Thursday afternoon?" ,
"chose": "write-to-them",
"why": "pricing twice and a public post about 2 new owners",
"never": [
"mention a Visit or a page they read",
"a price beyond the list",
"a discount"
]
}
},
{
"$id": "https://api.sb/leaseline.app/tasks/task_251" ,
"$type": "Task",
"assignee": "worker_1",
"due": "2026-10-06T17:00:00Z" ,
"on": "organization_102",
"brief": {
"who": "Calloway Residential, 900 units" ,
"chose": "write-as-you",
"why": "over the 500-unit line; read pricing and the security page" ,
"fact": "their news page: 3 new owners this year"
}
},
{
"$id": "https://api.sb/leaseline.app/tasks/task_258" ,
"$type": "Task",
"assignee": "worker_5",
"due": "2026-10-08T17:00:00Z" ,
"on": "customer_9",
"brief": {
"who": "Juniper Homes",
"chose": "to-their-csm",
"why": "an active Subscription on Pro",
"read": "the Team plan page twice"
}
},
{
"$id": "https://api.sb/leaseline.app/approvals/approval_64" ,
"$type": "Approval",
"verdict": null,
"deadline": "2026-10-06T17:00:00Z" ,
"draft": "Hi Grace, Calloway’s news page says you took on 3 new owners this year…" ,
"route": "write-as-you",
"checks": [
{
"check": "visit-in-copy",
"status": "pass"
},
{
"check": "approved-pages",
"status": "pass"
}
]
}
]Each receiver answers with POST /{namespace}/tasks/{task}/claim (Take this one), POST /{namespace}/tasks/{task}/return with a reason, POST /{namespace}/actions/{action}/redirect, POST /{namespace}/approvals/{approval}/approve or POST /{namespace}/approvals/{approval}/refuse, each a one-call window, and an Approval unanswered by its deadline expires and the draft never sends. Once a person claims, touch 2 cancels with claimedByPerson (Not live yet, gaps #7), and every reply goes to the claimer’s queue. A Task nobody claims by its due time goes up a Role through POST /{namespace}/tasks/{task}/escalate (AE to you), and person-promise fires.
On time: AE · Priya Shah, 4 of 4 replies claimed within 60 min, median 21 min, ■□□□ Too early to tell, n 4; you, 4 of 4 approvals by their deadline, n 4. Your queue: counted by kind: in weeks 1 to 4, 20 approvals; since, about 1 top-tier approval and 1 hand-off a week, plus the monthly match check of 50 companies.
The fixed line: POST /{namespace}/deals/{deal}/assign sets owner to the receiving person, the agent is never a Deal’s owner, and Meetings land on the owner’s calendar under the owner’s name.
When it won’t send
| Check (refuses one send) | Refuses | Fix | Then |
|---|---|---|---|
owned-by-a-person (rule j; Not live yet, gaps #8) | a send to a Contact whose Lead or open Deal a person owns, or whose Conversation a person claimed | Leave it to the person who owns it: the draft waits in their queue. | POST /{namespace}/tasks/{task}/assign |
customer-collision (Not live yet, gaps #1) | a first touch to anyone at an active Customer | Send it to their CSM instead: the company leaves the List. | POST /{namespace}/tasks/{task}/assign |
hold | any touch at a company another Play or a person holds | Leave it; the holder writes, or the hold ends. | GET /{namespace}/decisions |
visit-in-copy | a draft that names a Visit, a page read, a time on site or a return | Open on a public fact about the company instead. | POST /{namespace}/messages/{message}/schedule |
approved-pages (Not live yet) | a sentence no approved Page or the company’s own public page backs | Cut the sentence, or publish the Page that says it. | POST /{namespace}/sites/{site}/publish |
notice | a lookup while your visitor notice omits company lookups | Publish the notice Version that names them. | POST /{namespace}/sites/{site}/publish |
market | a lookup for a Visit from a Market your file sets off | Leave it, or turn the Market on in your file. | POST /{namespace}/markets/{market}/enter |
resolution-stop | an Event, Audience row or draft for a network or person who asked to stop being matched | Nothing to fix: it drops before scoring. | GET /{namespace}/suppressions |
decay | a touch whose stack decayed before it sent | It expires; a new visit starts a new stack. | GET /{namespace}/events |
mailbox-warm | a first touch from a Mailbox still warming | Wait for the warm-up to finish, 14 days from day 0; ads-only keeps running. | POST /{namespace}/mailboxes/{mailbox}/warm |
| Stop (pauses the Play) | Pauses when | Then |
|---|---|---|
sending-stop-loss | any sending gate trips: bounces over 3 %, complaints over 0.1 % or a blocklist hit | both touches pause on that Mailbox; POST /{namespace}/mailboxes/{mailbox}/rest |
visit-leak | one delivered Message names a Visit or a page read | every send pauses until the evals re-run and pass |
tracking-complaint | replies that call it tracking pass 1 % of first touches, after 200 | company outreach pauses; Audiences continue |
wrong-company | over 10 % wrong in the monthly check of 50 companies | company outreach stops until the matcher is fixed |
identify-agreement | over 10 % of 30+ matched visitors who sign up disagree with their match | that grain’s outreach halts |
lift-floor | the lift interval sits at or under 0 after 600 companies a half | outreach stops; the Audiences continue |
kill-switch | you press it | every Tactic pauses, lookups stop and Audience adds freeze; POST /{namespace}/processes/{process}/enable after review |
+ 2 more for the person rung, off in your file: wrong-person and its own kill switch (?lanes=) |
The captured refusal, then the Stop that fired as a Campaign.paused reason (Not live yet, gaps #2); the rewrite that sent is the Harbor Row thread in Example.
| Check | Fix | Then |
|---|---|---|
visit-in-copy fail | Open on a public fact about the company instead. | POST /{namespace}/messages/{message}/schedule |
owned-by-a-person pass · Not live yet, gaps #8 | — | POST /{namespace}/tasks/{task}/assign |
JSON
{
"type": "https://startupbuilder.dev/errors/refused",
"title": "Refused",
"status": 422,
"detail": "Touch 1 to Harbor Row Rentals did not send: it names a page they read.",
"checks": [
{
"check": "visit-in-copy",
"status": "fail",
"doc": "/plays/account-visits#check-visit-in-copy",
"fix": "Open on a public fact about the company instead.",
"then": "Message.schedule"
},
{
"check": "owned-by-a-person",
"status": "pass"
}
]
}It says no, says why, and names the fix.
[
{
"reason": {
"limit": "sending-stop-loss",
"observed": "4.1 % bounces on sdr@leaseline.app",
"bound": "3 %",
"window": "7d"
},
"doc": "/plays/account-visits#stop-sending-stop-loss" ,
"mailbox": "sdr@leaseline.app"
}
]Did it work
At about 4 companies a week, no held-back half reads within 90 days, so the basis is before-after (rule o): the columns read Now and Before, and nothing prints as Added. Each figure’s call is POST /{namespace}/metrics/query by node, and every node above the leaf is Not live yet (gaps #4).
18.2 qualified responses per 100 against 2.9 before · before-after,
- Chose
- write-to-them
- Why
- Fernhill Property Group read pricing on 2 days in 4 (7 points, confirmed on 2 networks), and its careers page lists 2 leasing coordinator openings
- From
- write-to-them · write-as-you · ads-only · to-their-csm · not-now · let-it-go
- Expects
- a qualified response from Fernhill within 30d
- Reopens when
- no inbound Message from contact_14 within 30d
- By
- worker_2
JSON
{
"$id": "decisions/decision_455",
"$type": "Decision",
"choice": "write-to-them",
"reason": "Fernhill Property Group read pricing on 2 days in 4 (7 points, confirmed on 2 networks), and its careers page lists 2 leasing coordinator openings",
"stakes": "low",
"options": [
"write-to-them",
"write-as-you",
"ads-only",
"to-their-csm",
"not-now",
"let-it-go"
],
"by": "worker_2",
"on": "organization_131",
"expected": "a qualified response from Fernhill within 30d",
"reopensWhen": "no inbound Message from contact_14 within 30d"
}| Node | Now (56 days) | Before (the 90 days, per 56) | Added | Confidence | Basis |
|---|---|---|---|---|---|
visit-reply-lift | 18.2 qualified responses per 100 written to (4 of 22 matured) · 9 still maturing | 2.9 hand-raises per 100 fitting visiting companies (3 of 104) | — · Worth about +$130 a month | — | before-after, not proof · Other Startups’ results +4.8 per 100, ■■□□ Early sign, n 2,140 |
new-leads | 5 Leads from 31 companies written to | 2 per 56 days | — | — | before-after |
new-arr ↑ | $347, 1 Customer on Pro · Booked: credited, not all caused by this Play | $0 | — | — | before-after · 1 Deal and 1 trial still deciding |
net-new-arr · arr | carried from new-arr | carried | — | — | carried |
Before against after reads one window against the 90 days before it, scaled to 56 days; it is not proof, and Other Startups’ results print beside it. A company counts once 30 days have passed since its first touch.
How it moves it: each fitting company that reads a buying page gets touch 1 a median 19 hours after its scoring visit, opening on a public fact, where before it got nothing unless it wrote first.
Guardrails: before against after: positive-reply-rate on the same Mailboxes, 6.4 % against 5.9 %, and play-sourced-customers, 1 against 0; 0 replies called it tracking.
{
"$id": "https://api.sb/leaseline.app/campaigns/account-visits" ,
"$type": "Campaign",
"play": "play:account-visits@1" ,
"path": [
"visit-reply-lift",
"new-leads",
"new-arr",
"net-new-arr",
"arr"
],
"experiment": {
"metric": "visit-reply-lift",
"unit": "per-100",
"heldBack": 0,
"decision": null
},
"readout": {
"value": null,
"n": 22,
"maturing": 9,
"interval": null,
"readability": "inadmissible",
"basis": "before-after",
"pooled": true
},
"ledger": {
"spend": 4,
"workerCost": 2,
"hours": 2.6,
"wonArr": 347,
"projectedArr": 132,
"paybackDays": 12
}
}Outside benchmark: 2 to 4 verified contacts per resolved company, and about 30 % of business visits resolve to a company (a visitor-identification vendor’s 2026 field data).
Before it · After it. Before it: editorial-seo, paid-search and aeo bring the visits. After it: account-ads reads the company Audience it fills, instant-response answers anyone who writes first, and buying-committee takes a company once 2 or more of its people engage.
Example
Leaseline ran it from 2026-08-24 to 2026-10-19 (56 days), at company grain only. 6,350 visitors came; 1,420 read a buying or evaluation page; 352 network addresses resolved, 241 companies, 84 of them fitting. Read every captured thread at ?threads=; three follow the tables.
| What happened, by option | Companies | Qualified responses | Won (credited, not all caused by this Play) |
|---|---|---|---|
| Write to them · Write as you | 27 · 4 | 4 · 1 | 1 on Leaseline Pro at $29 a month (+$347 ARR); 1 Deal, 1 trial open |
| Ads only · To their CSM | 21 · 5 | — · — | — |
| Not now · Let it go | 6 · 3 | — | — |
| All, against before | 66 decided, 31 written to | 5 (4 of 22 matured) · before: 3 hand-raises in 90 days | $347 · $0 |
| At 4 a week (the Use it when volume) | about 32 in 56 days | before-after, not proof | Other Startups’ results: +4.8 per 100, ■■□□ Early sign, n 2,140 |
| Day | Event | What happened | Your time |
|---|---|---|---|
| 0 | event_1104 Campaign.created | the three steps; the dry run fails visit-capture and mailbox-warm | 25 min |
| 0 | event_1105 Domain.connected | leaseline.app serves the Site; the notice Version named company lookups | 15 min |
| 1–13 | event_1121 Audience.updated, first of 13 | company Audiences filled while the Mailboxes warmed | — |
| 14 | event_1188 Mailbox.warmed | the first touch sent, to Harbor Row Rentals | — |
| 19 | event_1224 Message.created | the first qualified reply, Ines at Harbor Row | — |
| 31 | event_1279 Role.updated | promoted after 20 drafts approved unedited: touches send after a 2-minute hold | — |
| 36 | event_1301 Campaign.paused | sending-stop-loss on sdr@: 4.1 % bounces; POST /{namespace}/mailboxes/{mailbox}/rest for 1 day, resumed | 6 min |
| 1–56 | event_1130 Approval.created, first of 24 | 20 drafts in weeks 1 to 4, then 4 as Dana; the match check from the POST /{namespace}/tasks/next log | 1.6 h · total 2.6 h measured, 3 h asked |
Leaseline → Ines Duarte, Harbor Row Rentals, Sep 7, 08:40 · Subject: renewals across 2 new owners · Your news page says Harbor Row took on 2 new owners this summer.¹ At 2 owners, renewal notices usually slip at the hand-off between leasing and accounting.² Leaseline sends every notice 60 days out, per owner. Worth 20 minutes? *Signed with the Role’s disclosure line, with the why-you link.*³
Ines, Sep 12 · “Worth a look. Thursday afternoon?”
POST /{namespace}/meetings/bookbooked it on Priya’s calendar; Harbor Row took Pro on Oct 2 (+$347 ARR).
- Saw your team checking out our pricing page twice this week, so I thought I’d reach out.
+ Your news page says Harbor Row took on 2 new owners this summer.Calloway Residential, 2026-10-06 09:10 · 900 units, over the 500-unit line, so the draft went to Dana as Write as you (
approval_64, due 17:00). Dana approved it at 11:32, and it sent from dana@ at 08:40 the next morning their time.⁴ The Brief istask_251.
¹ It opens on the company’s own public page. ² Every claim maps to an approved Page. ³ The fixed signature and why-you lines compile from the Role’s Settings. ⁴ Write as you sends only after your OK, on every rung.
Your settings
---
holdout: 0 # at 4 a week, it reads before-after
---
Write only to confirmed property managers at 5 points or more.
Over 500 units, send it as Dana, after her OK.
Never run the person rung.$ npx startup-builder show plays/account-visits
holdout 0 · the 5-point line · the 500-unit line · person rung off your file plays/account-visits.mdx:2,4,5,6 (Ask 2026-08-24)
family demand · process outbound · worker role:prospector, owns never · chooses 6 (4 rule, 2 read) · sequences visit-two-touch, visit-two-touch-founder · approval every-send · checks owned-by-a-person, customer-collision, hold, visit-in-copy, approved-pages, notice, market, resolution-stop, decay, mailbox-warm · stops 7 + 2 for the person rung catalog play:account-visits@1 (Not live yet, gaps #5) Every key from the catalog
Compiled, not written: edit the Play record account-visits and its Leaseline run, 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.