Skip to content

Account ads

Shows ads to fitting companies on your list a week before the first email, then counts the Deals they open against companies held back.

Channel
Ads and sponsorships
Run oneMode try
You, to your agent

Hand our latest engaged company to the Marketing Lead agent and show me what it chose and why. Don’t spend.

curl "https://api.sb/leaseline.app/events?where=name%20in%20(%22Organization.listed%22%2C%20%22Message.scheduled%22%2C%20%22Visit.recorded%22%2C%20%22Visitor.identified%22%2C%20%22Ad.clicked%22%2C%20%22Lead.qualified%22%2C%20%22Deal.created%22%2C%20%22Organization.delisted%22%2C%20%22Post.published%22%2C%20%22Audience.resized%22%2C%20%22Consent.withdrawn%22%2C%20%22Monitor.checked%22%2C%20%22Experiment.matured%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_1398"}'

# Not live yet (gaps #7)
curl "https://api.sb/leaseline.app/decisions?where=on%20%3D%20%22organization_88%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_88%22" \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
import { $ } from 'startup-builder'

const { events: [last] } = await $.Event.list({ where: 'name in ("Organization.listed", "Message.scheduled", "Visit.recorded", "Visitor.identified", "Ad.clicked", "Lead.qualified", "Deal.created", "Organization.delisted", "Post.published", "Audience.resized", "Consent.withdrawn", "Monitor.checked", "Experiment.matured")', 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 (\"Organization.listed\", \"Message.scheduled\", \"Visit.recorded\", \"Visitor.identified\", \"Ad.clicked\", \"Lead.qualified\", \"Deal.created\", \"Organization.delisted\", \"Post.published\", \"Audience.resized\", \"Consent.withdrawn\", \"Monitor.checked\", \"Experiment.matured\")","limit":1}'
npx startup-builder do Trigger.fire --json '{"like":"event_1398"}' --try   # Not live yet (gaps #7)
npx startup-builder do Decision.list --json '{"where":"on = \"organization_88\""}'   # Not live yet (gaps #7)
npx startup-builder do Action.list --json '{"where":"on = \"organization_88\""}'   # Not live yet (gaps #7)
{
  "name": "do",
  "arguments": {
    "code": "const { events: [last] } = await $.Event.list({ where: 'name in (\"Organization.listed\", \"Message.scheduled\", \"Visit.recorded\", \"Visitor.identified\", \"Ad.clicked\", \"Lead.qualified\", \"Deal.created\", \"Organization.delisted\", \"Post.published\", \"Audience.resized\", \"Consent.withdrawn\", \"Monitor.checked\", \"Experiment.matured\")', 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"
  }
}
Response200application/json
{
  "decision": {
    "$id": "https://api.sb/leaseline.app/decisions/decision_418",
    "$type": "Decision",
    "choice": "proof-ads",
    "reason": "Cedar Row Management clicked the problem ads 4 times in 9 days and opened no lead form",
    "stakes": "low",
    "options": [
      "problem-ads",
      "proof-ads",
      "offer-ads",
      "name-the-company",
      "sponsor-your-post",
      "to-the-ae",
  …
sample · 4 Not live yetPreview

On PATCH /{namespace}/campaigns/{id}Off PATCH /{namespace}/campaigns/{id}Take this one POST /{namespace}/tasks/{task}/claim

action_2 moves Cedar Row from the problem company list to the proof list at the next 06:00 upload. action_3 is the proof change set, which lands paused until you approve it (You approve each one).

+0.32 Deals per 100 listed · ■□□□ Too early to tell · n 743 matured · path → arr

Try another: the listed companyChoiceReason
Cedar Row Management: 4 clicks on the problem ads in 9 daysproof-adsengaged, and no lead-form open yet
Harlow Lettings: listed from the cold-email cell of Oct 20problem-adsnewly listed, no engagement
Birchway Properties: a demo request from Rosa, after the offer adsto-the-aea written request at a listed company
Juniper Homes: took Leaseline Pro on Oct 14off-the-lista Customer leaves every company list

Where it sits: Stranger → Lead → Trialing → Activated → At risk → Churned → Won back · Account Deal rate → Deals and trials opened → New Customers ↑ → ARR added each week → ARR

Worth’s formula: about +0.51 Deals and trials opened per 100 listed companies a month × 751 treated × 0.29 opens to paid × $1,669 first order ARR ≈ +$1,850. Under First signal: About 5 months to tell a +0.4 lift at 750 listed (90 days to read, 2 reads to pool); at 350 listed, 180 days can tell only +1.1 or more, so it reads beside Other Startups’ results.

sample · api.sb/leaseline.app · 41 Customers at 2026-10-19 · on Ad.clicked · first ad live 9 days before the first email · 4 keys Not live yet

Start it this week

Terminal
npx startup-builder add play account-ads   # 1 · writes plays/account-ads.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-ads"}'

# 3 · keep it: every change set lands paused, at You approve each one
curl -X POST https://api.sb/leaseline.app/campaigns \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"play":"account-ads"}'

# on
curl -X PATCH https://api.sb/leaseline.app/campaigns/account-ads \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"endsAt":null}'

# off: every ad set pauses through the ad proxy
curl -X PATCH https://api.sb/leaseline.app/campaigns/account-ads \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"endsAt":"now"}'
422Refusedaccount-ads can’t start yet: 2 checks fail and 1 warns.
CheckFixThen
owned-by-a-person pass · Not live yet, gaps #80 listed companies have an open Deal a person owns; 4 Customers left the ListPOST /{namespace}/tasks/{task}/assign
use-when pass · Not live yet, gaps #81,083 fitting companies; cold-email live; $1,000 a month LimitPOST /{namespace}/metrics/query
baseline pass · Not live yet, gaps #80.31 Deals opened per 100 listed companies in 90 days, n 1,083POST /{namespace}/metrics/query
conversion-send failConnect LinkedIn ads and send one test qualified Lead; that Venue stays off until it lands.POST /{namespace}/integrations/connect
approved-pages fail · Not live yet, gaps #8Publish one Customer result Page; proof ads quote only your own pages.POST /{namespace}/sites/{site}/publish
role-unstaffed warn · Not live yet, gaps #8Staff the AE, or leave it: qualified Leads come to you.POST /{namespace}/roles/{role}/staff
JSON
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "account-ads can’t start yet: 2 checks fail and 1 warns.",
  "checks": [
    {
      "check": "owned-by-a-person",
      "status": "pass",
      "observed": "0 listed companies have an open Deal a person owns; 4 Customers left the List",
      "doc": "/plays/account-ads#check-owned-by-a-person",
      "then": "Task.assign"
    },
    {
      "check": "use-when",
      "status": "pass",
      "observed": "1,083 fitting companies; cold-email live; $1,000 a month Limit",
      "doc": "/plays/account-ads#check-use-when",
      "then": "Metric.query"
    },
    {
      "check": "baseline",
      "status": "pass",
      "observed": "0.31 Deals opened per 100 listed companies in 90 days, n 1,083",
      "doc": "/plays/account-ads#check-baseline",
      "then": "Metric.query"
    },
    {
      "check": "conversion-send",
      "status": "fail",
      "doc": "/plays/account-ads#check-conversion-send",
      "fix": "Connect LinkedIn ads and send one test qualified Lead; that Venue stays off until it lands.",
      "then": "Integration.connect"
    },
    {
      "check": "approved-pages",
      "status": "fail",
      "doc": "/plays/account-ads#check-approved-pages",
      "fix": "Publish one Customer result Page; proof ads quote only your own pages.",
      "then": "Site.publish"
    },
    {
      "check": "role-unstaffed",
      "status": "warn",
      "roles": [
        "sales-lead"
      ],
      "owned": 0,
      "doc": "/plays/account-ads#check-role-unstaffed",
      "fix": "Staff the AE, or leave it: qualified Leads come to you.",
      "then": "Role.staff"
    }
  ]
}

It says no, says why, and names the fix.

CheckFix (one call)Your timeWait
owned-by-a-person · use-when · baseline · pass: 0 person-owned Deals on the List; 1,083 fitting companies; 0.31 Deals per 100, n 1,083 (gaps #8)POST /{namespace}/metrics/query on account-deal-rate, which fills Before in Your number1 minnone
conversion-send · fail (Not live yet)POST /{namespace}/integrations/connect with venue linkedin, then one test qualified Lead from your records10 minthe test conversion, under 24 h
approved-pages · fail: no Customer result Page (Not live yet)POST /{namespace}/sites/{site}/publish the Page; until then problem and offer ads run, proof stays off20 minnone
role-unstaffed · warn (gaps #8): no AE, so qualified Leads come to youPOST /{namespace}/roles/{role}/staff, or leave it0 minnone

Your number · Before 0.31 Deals per 100 listed · Now 0.77 per 100 treated · Target 1.0 by Dec 31 · ■□□□ Too early to tell · n 743 matured · held back at random

Media is your $1,000 a month on LinkedIn, held by a Limit: $800 to the company lists and $200 to sponsoring your own posts. Contact and company data cost $0.02 per listed company (POST /{namespace}/contacts/{contact}/enrich) and each drafted concept costs $0.30 (Worker cost). That comes to about $1,030 a month at 750 listed, $30 of it not media.

When it starts

WhenTrigger idStarts onWindowHow it gets in
A fitting company clears your List ruleorganization-listedstate on Organization.listed where fit = 'strong' and not a Customer, open Deal or Suppressionads start within 1 d; ignored after 14 dPATCH /{namespace}/audiences/{id} on your named List, or a sending Play’s cell
A sending Play schedules a first email 9+ days outair-cover-requestedevent on Message.scheduled where step = 1 and sendAt >= now + 9djoins the company list within 1 hthe sending Play’s POST /{namespace}/messages/{message}/schedule
Someone at a listed company becomes a qualified Leadqualified-lead-recordedevent on Lead.qualified where organization on the named Listreaches the AE within 60 min; the Venue within 24 hPOST /{namespace}/leads/{lead}/qualify in your records
+ 10 more for pricing visits, surges, ad engagement, Deals, exits, your posts, list size, opt-outs, drift and holdout reads (?lanes=all)
One signed delivery · StartupBuilder-Signature
{
  "$id": "https://api.sb/leaseline.app/events/event_1398",
  "$type": "Event",
  "name": "Ad.clicked",
  "at": "2026-10-19T06:00:41Z",
  "seq": 1398,
  "data": {
    "organization": {
      "$id": "https://api.sb/leaseline.app/organizations/organization_88",
      "name": "Cedar Row Management",
      "list": "treated"
    },
    "clicks": 4,
    "leadFormOpens": 0,
    "window": "14d",
    "adSet": "problem"
  }
}
Reproduce it in test
# 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":"Ad.clicked","url":"https://leaseline.app/hooks/sb"}'
npx startup-builder trigger Ad.clicked --like event_1398   # replays the company report row
npx startup-builder clock advance --by 1d   # runs the 06:00 upload that moves Cedar Row (action_2)

Who runs it

Marketing Lead · your agent (role:marketing-lead, worker_6) holds the Key Result and runs the ad sets as Paid media (role:paid-media-manager). It hands qualified Leads to AE · Priya Shah (person) (role:sales-lead, worker_3, staffed on day 9), and every company-name ad and sponsored post to you (Dana Okafor, person, worker_1). Your number links to Did it work.

OptionRung todayEvidence (passed of n)Moves up atDrops back whenYou can
Start on problem ads · Move to proof ads · Show the offerYou approve each one26 of 27 concepts approved unedited, in 3 change setsat 36 approved unedited across 4 change sets and no Stop in 30 days: about day 75any Stop drops one rung; a drift twice in 7 days → every change waits for youPOST /{namespace}/approvals/{approval}/approve · POST /{namespace}/approvals/{approval}/refuse
To the AE · Take them off the ListIt does it14 of 14 · 31 of 31—a person-owned company gets an ad change → You approve each onePOST /{namespace}/actions/{action}/redirect
Name the company · Sponsor your postYou approve each one, on every rung3 of 4 · 2 of 2never—POST /{namespace}/approvals/{approval}/approve · POST /{namespace}/approvals/{approval}/refuse
Change the List rule or the refreshYou approve each one1 merged of 1never—POST /{namespace}/changes/{change}/merge · POST /{namespace}/changes/{change}/revert
Never: a Visit, page or click in an ad; a Price your Pricing Page lacks; a rival named without your OKTo a person, every time1 asked, 1 handed overnever—POST /{namespace}/approvals/{approval}/approve

What it reads · What it may call. A Visit, a page read or an ad click chooses which company is listed and which ad set it sees; none ever appears in an ad, a Page or a Message. It writes only through the ad proxy, and the holdout list is excluded from every Campaign you run. Its tools are PATCH /{namespace}/audiences/{id}, POST /{namespace}/ads, PATCH /{namespace}/ads/{id}, POST /{namespace}/content/draft, POST /{namespace}/leads/{lead}/assign, POST /{namespace}/tasks/{task}/assign, POST /{namespace}/suppressions, and any other call becomes an Approval under the Role’s Mandate.

The brief (folded): compiled Agent-register Markdown · play:account-ads@1 · each line names its record key
You hold the Marketing Lead Key Result: 1.0 Deals opened per 100 listed companies by 2026-12-31. ← worker, metric
On each listing, click report or Lead, choose one option and say why.            ← triggers, chooses
Start ads 9 days before the first cold email to that company.                    ← your file, line 6
Every sentence maps to an approved Page or the company’s own public source.      ← copyRules 1
Never name a Visit, a page, a click or a Person in an ad.                        ← copyRules 3, 5
Never change an ad set for a company whose open Deal a person owns.              ← worker.owns, owned-by-a-person

The slow loop, as a Change. decision_431 (2026-10-05, weekly) read proof concepts falling under their ad set’s median by day 14, so change_22 moved the refresh from 21 to 14 days (your file, line 7) and Dana merged it with POST /{namespace}/changes/{change}/merge. Replayed on past weeks, POST /{namespace}/changes/{change}/review put qualified Leads per 100 treated at 2.1; read back at +14d it is 1.9, n 751, ■□□□ Too early to tell (Example, day 42).

Bring your own agent (folded) starts at You approve each one when it matches the seated agent on the eval suite; ad change sets never pass that rung for any agent. POST /{namespace}/roles/{role}/staff seats it.

Terminal
npx startup-builder eval --scenario account-ads-1073 --agent "claude -p" --seat marketing-lead   # seated: 1.86 qualified Leads per 100, 0 Checks failed served, $131 per qualified Lead · yours: 1.71, 1 failed, $144

What it can do next

OptionPicked whenDecided byRunsHands toIf it’s just you
Start on problem ads problem-adsnewly listed, no engagementYour rule (line 5)PATCH /{namespace}/audiences/{id} adds it to the problem list → POST /{namespace}/ads for the ad set’s conceptsnobodythe same
Move to proof ads proof-ads3+ clicks in 14 days, no lead-form openAgent’s call, with its reasonPATCH /{namespace}/audiences/{id} moves it → PATCH /{namespace}/ads/{id} on the proof setyou approve the change setyou
Show the offer offer-adsengaged with proof, or read pricingAgent’s call, with its reasonPATCH /{namespace}/audiences/{id} → PATCH /{namespace}/ads/{id} with the lead form → answer-inbound answers each formSDR · your agent, for the formyou, within 5 min
Name the company name-the-companyPriority 1, with a public source to citeAgent’s call, with its reasonPOST /{namespace}/content/draft → POST /{namespace}/ads, pausedyou approve each one, due 1 business day · Take this oneyou
Sponsor your post sponsor-your-postyour post in your top quarter at 72 hYour rule (line 8)POST /{namespace}/ads on the post exactly as you posted ityou grant each one · Take this oneyou
To the AE to-the-aea qualified Lead or a written requestYour rule (the hand-raise)POST /{namespace}/leads/{lead}/assign → POST /{namespace}/tasks/{task}/assign, Brief attachedPOST /{namespace}/tasks/{task}/assign → AE · Priya Shah, due 60 min (response-60m) · Take this oneyou, within 60 min
Take them off the List off-the-lista Customer, a stop request, a lost Deal, or claimed by another of your StartupsYour rule (the List rule)PATCH /{namespace}/audiences/{id} removes it → POST /{namespace}/suppressions at Company scope on a stop requestnobody; the next 06:00 upload drops itthe same
When they write backFixture replyWhat happensWho answersBy whenCall
Interesteda lead form: “Can we see it for 200 units?”qualifies them and hands to the AE with the Brief (When it hands to you)AE · Priya Shah (person)60 minPOST /{namespace}/leads/{lead}/assign
Send me pricinga lead form: “What does it cost?”instant-response answers with the Pricing Page’s list Price and one next stepSDR · your agent5 minPOST /{namespace}/conversations/{conversation}/answer
Not nowa lead form: “Next year, maybe.”the company stays on proof ads; the Lead moves to NurtureSDR · your agentat oncePOST /{namespace}/leads/{lead}/nurture
Wrong person“I’m in accounting; our ops lead buys software.”asks them to bring the ops lead in; it never writes to a name from their textSDR · your agent1 business dayPOST /{namespace}/conversations/{conversation}/answer
Out of officean auto-reply to the form answer, until Nov 2the follow-up waits for the return date; the ads keep runningnobodythe return datePOST /{namespace}/messages/{message}/schedule
Question it can’t answeran ad comment: “Does it sync with our accounting system?”To the AE, the question in the Brief; the comment stays upAE · Priya Shah (person)60 minPOST /{namespace}/conversations/{conversation}/escalate
Stop“Please stop showing our team these ads.”the company leaves every company list at the next upload, at Company scopenobodyat oncePOST /{namespace}/suppressions

When it hands to you

Qualified Leads go to AE · Priya Shah (person); company-name ads and sponsored posts come to you, and before day 9 every qualified Lead did. 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).

What lands in each queue · sample
[
  {
    "$id": "https://api.sb/leaseline.app/tasks/task_207",
    "$type": "Task",
    "assignee": "worker_3",
    "due": "2026-09-12T15:02:00Z",
    "on": "lead_61",
    "brief": {
      "who": "Tomas Reyes, Oakmere Lettings",
      "said": "90 units for 2 owners; asked how owner statements work",
      "chose": "to-the-ae",
      "why": "a qualified Lead at a listed company",
      "saw": "problem, proof and offer ads since 2026-08-28",
      "never": [
        "mention the ads",
        "a price beyond the list",
        "a discount"
      ]
    }
  },
  {
    "$id": "https://api.sb/leaseline.app/tasks/task_233",
    "$type": "Task",
    "assignee": "worker_1",
    "due": "2026-10-20T17:00:00Z",
    "on": "organization_102",
    "brief": {
      "who": "Calloway Residential, Priority 1",
      "chose": "name-the-company",
      "why": "Priority 1 on the List; their public site says they took on 3 new owners this year",
      "source": "calloway-residential.example/news"
    }
  },
  {
    "$id": "https://api.sb/leaseline.app/approvals/approval_58",
    "$type": "Approval",
    "verdict": null,
    "deadline": "2026-10-20T17:00:00Z",
    "draft": "Calloway Residential: three new owners this year. Every renewal notice, 60 days out, per owner.",
    "route": "name-the-company",
    "checks": [
      {
        "check": "approved-pages",
        "status": "pass"
      },
      {
        "check": "organization-stop",
        "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 ad stays paused. Once a person claims a Lead, the agent changes nothing for that company but the List, 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, 9 of 9 claimed within 60 min, median 24 min, ■■□□ Early sign, n 9; you, 5 of 5 within 60 min and 5 of 6 approvals by their deadline (1 expired; the ad stayed paused), n 11. Your queue: counted by kind: in week 1, 1 List approval, 1 claims source and 1 change set; since, about 1 change set, 1 hand-off and 1 sponsored post a week.

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)RefusesFixThen
owned-by-a-person (rule j; Not live yet, gaps #8)an ad change for a company whose Lead or open Deal a person ownsLeave 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 company list row for an active CustomerSend it to their CSM instead: the company leaves the List.PATCH /{namespace}/audiences/{id}
audience-guard (on every upload)an upload with one suppressed or opted-out member, or any check that errorsRemove the rows the Guard lists, then upload again; nothing serves until then.PATCH /{namespace}/audiences/{id}
organization-stop (Company scope)any ad to a company that asked to stopLeave them off; the stop holds on every company list.GET /{namespace}/suppressions
approved-pages (Not live yet)an ad sentence no approved Page or the company’s own public source backsCut the sentence, or publish the Page that says it.POST /{namespace}/sites/{site}/publish
Stop (pauses the Play)Pauses whenThen
spend-limitmonth-to-date spend reaches the $1,000 Limitwaits for the month; POST /{namespace}/limits/{limit}/raise with a winning or held read
qualified-lead-floor60 days and $2,000 spent with no qualified Lead from a treated companyPATCH /{namespace}/campaigns/{id} to end it, or review the List rule
cost-ceilinga 30-day cost per qualified Lead over your $250 ceiling, with 5 or morereview the ad sets; resume with PATCH /{namespace}/campaigns/{id}
list-floora company list under 300 matched membersevery ad set on it pauses until it refills
conversion-feedno conversion for 72 h while spend runs; at 7 days the Venue pausesPOST /{namespace}/integrations/{integration}/reconnect
feed-mismatchthe Venue’s qualified Leads differ from yours by over 30 % for 7 daysbudget changes freeze; POST /{namespace}/integrations/{integration}/sync
frequencyan ad set at double its weekly frequency targetthat set pauses until its window refills
drifta live ad or setting differs from its approved sourceresets at once; a second drift in 7 days pauses the set
rejection2 Venue rejections in 7 daysnew uploads pause on that Venue
complaint3 or more comments call one ad creepy, a scam or surveillancethat ad waits for your review
holdout-integrityheld-back companies reached by more than 5 % of the holdoutthe Readout is void and every raise freezes
kill-switchyou press itevery ad set pauses through the proxy and write access is revoked; POST /{namespace}/processes/{process}/enable after review
+ 1 more for the person rung on Facebook, Instagram and LinkedIn, off in your file (?lanes=all)

The captured refusal, then the Stop that fired as a Campaign.paused reason (Not live yet, gaps #2); the upload that went after the fix is day 33 in Example.

422RefusedThe 06:00 upload to the proof company list did not go: 3 rows match a Suppression.
CheckFixThen
audience-guard failRemove the 3 rows the Guard lists, then upload again.PATCH /{namespace}/audiences/{id}
customer-collision pass · Not live yet, gaps #1—POST /{namespace}/leads/{lead}/assign
JSON
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "The 06:00 upload to the proof company list did not go: 3 rows match a Suppression.",
  "checks": [
    {
      "check": "audience-guard",
      "status": "fail",
      "observed": "3 suppressed members of 412 rows",
      "doc": "/plays/account-ads#check-audience-guard",
      "fix": "Remove the 3 rows the Guard lists, then upload again.",
      "then": "Audience.update"
    },
    {
      "check": "customer-collision",
      "status": "pass"
    }
  ]
}

It says no, says why, and names the fix.

The Stops that fired, as Campaign.paused reasons · sample
[
  {
    "reason": {
      "limit": "frequency",
      "observed": "7.8 a week",
      "bound": "7.4 (double the 3.7 target)",
      "window": "7d"
    },
    "doc": "/plays/account-ads#stop-frequency",
    "adSet": "proof"
  }
]

Did it work

Both halves are read from the same salted-hash assignment (rules m–o), and each figure’s call is POST /{namespace}/metrics/query by node and by arm; a company counts once its 30-day window has closed. The parent field and every node above the leaf are Not live yet (gaps #4).

+0.32 Deals per 100 listed · ■□□□ Too early to tell · n 743 matured · path → arr

Chose
proof-ads
Why
Cedar Row Management clicked the problem ads 4 times in 9 days and opened no lead form
From
problem-ads · proof-ads · offer-ads · name-the-company · sponsor-your-post · to-the-ae · off-the-list
Expects
a qualified Lead at Cedar Row within 30d
Reopens when
no Lead.qualified at organization_88 within 30d
By
worker_6
JSON
{
  "$id": "decisions/decision_418",
  "$type": "Decision",
  "choice": "proof-ads",
  "reason": "Cedar Row Management clicked the problem ads 4 times in 9 days and opened no lead form",
  "stakes": "low",
  "options": [
    "problem-ads",
    "proof-ads",
    "offer-ads",
    "name-the-company",
    "sponsor-your-post",
    "to-the-ae",
    "off-the-list"
  ],
  "by": "worker_6",
  "on": "organization_88",
  "expected": "a qualified Lead at Cedar Row within 30d",
  "reopensWhen": "no Lead.qualified at organization_88 within 30d"
}
NodeAgent halfYour halfAddedConfidenceBasis
account-deal-rate0.77 per 100 treated (4 of 520)0.45 held back (1 of 223)+0.32 [−0.62, +1.26]■□□□ Too early to tellheld back at random · n 743 matured · 330 still maturing · Other Startups’ results: +0.4, ■■□□ Early sign, n 6,410
opened-deals-and-trials0.96 per 100 (5 of 520)0.45 (1 of 223)+0.51 [−0.50, +1.52]■□□□ Too early to tellthe same assignment
new-arr ↑$3,338, 2 Customers · Booked: credited, not all caused by this Play$0, 0 Customers+$642 per 100 listed [−$410, +$1,690] · Worth about +$1,800 a month■□□□ Too early to tell (2 vs 0 Customers)the same · 3 Deals still deciding
net-new-arr · arr+$3,338$0+$642 per 100 listed, carried: it moves no other bridge term■□□□ Too early to tellcarried from new-arr

The leaf counts Deals opened within 30 days of listing, after a held Meeting or a written request, per 100 companies assigned, to 300 matured in each half; at 520 and 223 matured the Experiment runs on, and the day-90 read lands 2026-11-22.

How it moves it: the median treated company saw 26 impressions across problem, proof and offer ads in the 9 days before its first cold email, where a held-back company saw none (the Venue’s company report).

Guardrails: treated against held back: cold-email reply rate at listed companies, 4.1 % against 3.6 %, and Meetings held per Deal opened, 1.0 against 1.0.

The Campaign card · sample
{
  "$id": "https://api.sb/leaseline.app/campaigns/account-ads",
  "$type": "Campaign",
  "play": "play:account-ads@1",
  "path": [
    "account-deal-rate",
    "opened-deals-and-trials",
    "new-arr",
    "net-new-arr",
    "arr"
  ],
  "experiment": {
    "metric": "account-deal-rate",
    "unit": "per-100",
    "heldBack": 322,
    "decision": null
  },
  "readout": {
    "value": 0.32,
    "n": 743,
    "maturing": 330,
    "interval": [
      -0.62,
      1.26
    ],
    "readability": "inadmissible",
    "basis": "held-back",
    "pooled": false
  },
  "ledger": {
    "spend": 1862,
    "workerCost": 14,
    "hours": 7.4,
    "wonArr": 3338,
    "projectedArr": 1850,
    "paybackDays": 31
  }
}

Outside benchmark: 0.20 to 0.58 Deals opened per 100 companies on LinkedIn company lists, median, 211 advertisers (an ad-analytics vendor’s 2026 report).

Before it · After it. Before it: cold-email and account-visits nominate the companies, and founder-voice writes the posts it sponsors. After it: instant-response answers the lead forms, and buying-committee takes a company once 2 or more of its people engage. If the List stays under 350, run cold-email alone.

Example

Leaseline ran it from 2026-08-24 to 2026-10-19 (56 days). 1,083 property managers cleared the List rule; Dana struck 6 and pinned 4, which see ads and stay out of the readout. The hash held 322 of the other 1,073 back and treated 751; LinkedIn matched 709 of the 755 rows uploaded. Read every captured run at ?threads=all; three follow the tables.

What happened, treated companies, by optionCompaniesQualified LeadsWon (credited, not all caused by this Play)
Start on problem ads75130
Move to proof ads · Show the offer118 · 374 · 70 · 2: the $2,400 plan (+$2,393 ARR) and Team at $79 a month (+$945 ARR), 3 open
Name the company · Sponsor your post4 (3 approved) · 2 posts00
To the AE · Take them off the List14 · 3114 handed over—
Treated · held back751 · 32214 (1.86 per 100) · 2 (0.62)$3,338 · $0
At 350 listed (holdout: 0.3)about 245 treated, 105 held backheld back at random; too few Deals to read aloneOther Startups’ results: +0.4 Deals per 100 listed, ■■□□ Early sign, n 6,410
DayEventWhat happenedYour time
0event_1102 Campaign.createdthe List rule, 6 strikes and 4 pins; the dry run fails on conversion-send and approved-pages40 min
2event_1117 Integration.connectedLinkedIn ads connected; the test qualified Lead landed in 3 h10 min
4event_1131 Audience.updatedthe hash drew 322 held back; 709 of 755 rows matched after 48 h—
6event_1140 Approval.created, first of 3 change setsyou approved 26 of 27 concepts unedited; problem and offer ads served from day 735 min
9event_1164 Role.staffedAE · Priya Shah (person) takes role:sales-lead—
19event_1243 Lead.qualifiedthe first qualified Lead from a treated company, Oakmere Lettings—
27event_1288 Campaign.pausedfrequency on the proof set; it resumed when the window refilled, 2 days later2 min
33event_1302 Action.refusedaudience-guard held the 06:00 upload for 3 suppressed rows; the fixed upload went at 06:40—
42event_1347 Change.mergedthe 14-day refresh: replayed at 2.1 qualified Leads per 100, read back at +14d at 1.95 min
1–56event_1150 Task.created, first of 23change sets, hand-offs and sponsored posts from the POST /{namespace}/tasks/next log4.8 h · total 7.4 h measured, 8 h asked

Wrenfield Rentals, listed 2026-08-28 · 212 units; the cold-email cell of Sep 6 joined the company list 9 days early. Problem ads from day 7: “Renewals slip when notices go out late.”¹

Sep 9 · 4 clicks · moved to proof ads: “Leaseline sends every renewal notice 60 days out, per owner.”² Sep 14 · a pricing Page read moved it to the offer set and its lead form.³

Sep 15 · Elena Park filled the lead form · POST /{namespace}/leads/{lead}/qualify, then POST /{namespace}/leads/{lead}/assign to AE · Priya Shah, who held the call on Sep 19; Wrenfield took the $2,400 plan on Oct 4 (+$2,393 ARR).

Proof ad, 2026-09-14 · the concept approved-pages refused, then the rewrite that served
- Leaseline cuts renewal work in half for every property manager.
+ Leaseline sends every renewal notice 60 days out, per owner, from your own lease dates.

Oakmere Lettings, 2026-09-12 14:02 · Tomas Reyes became a qualified Lead after the offer ads. POST /{namespace}/leads/{lead}/assign → AE · Priya Shah, due 15:02 (response-60m); she claimed it at 14:26 with the Brief (task_207), which tells her never to mention the ads.⁴ Oakmere took Team on Sep 23 (+$945 ARR).

¹ Problem ads name the problem, never the company, a Visit or a click. ² Every sentence maps to an approved Page. ³ The page read chose the ad set; it never appears in an ad or a Message. ⁴ No Message from a sending Play mentions these ads.

Your settings

plays/account-ads.mdx
---
holdout: 0.3      # 30 % of the List sees no ads from this Play
budget: 1000      # a month, held by a Limit
---
List property managers with 50 to 500 units.
Start ads 9 days before the first cold email.
Refresh each concept every 14 days.
Sponsor my posts that rank in my top quarter.
Text
$ npx startup-builder show plays/account-ads
holdout 0.3 · budget 1000 · List rule · 9-day lead · 14-day refresh · sponsored posts    your file  plays/account-ads.mdx:2,3,5,6,7,8 (Ask 2026-08-24; line 7 by change_22, 2026-10-05)
family demand · worker role:marketing-lead, owns never · chooses 7 (4 rule, 3 read) · approval every-send · person rung off · checks owned-by-a-person, customer-collision, audience-guard, organization-stop, approved-pages · stops 12 + person-rung    catalog  play:account-ads@1 (Not live yet, gaps #5)

Every key from the catalog

Key Id What it is
Tactic named-list-build Named list build names fitting Organizations from the ICP, resolved Visits and sending Plays’ cells, and ranks them by fit times value. Starts at You approve the first wave.
Tactic named-list-holdout Named list holdout puts 30 percent of the list in a holdout by a salted hash, and excludes it from every LinkedIn Campaign. Starts at Sends nothing.
Tactic company-list-upload Company list upload keeps the treated Organizations on LinkedIn as a company list of 300 rows and 300 matched members or more, current daily. Starts at You approve each one.
Tactic creative-ladder Creative ladder runs problem, proof and offer ad sets on one company list, moved by engagement and paced by membership windows. Starts at You approve each one.
Tactic ladder-creative Ladder creative drafts three distinct concepts per ad set from the claims source, retires the tired ones, and refreshes every 21 days. Starts at You approve each one.
Tactic founder-post-sponsoring Founder post sponsoring proposes the founder’s own strongest LinkedIn posts for sponsoring to the company list, and sponsors each only on the founder’s grant. Starts at You approve each one.
Tactic qualified-lead-bidding Qualified lead bidding sends qualified Leads and opened Deals to each Venue within 24 hours, and bids on them, never on form fills. Starts at Sends nothing.
Tactic air-cover-clock Air cover clock lists a sending Play’s Organizations 9 days before its first email, and releases the email after 7 days of Ads. Starts at Sends nothing.
Tactic named-audience-rung Named audience rung adds US people at treated Organizations to LinkedIn and Facebook Audiences, from first-party and person-rung sources only. Starts at You approve the first wave.
Tactic audience-guard-check Audience guard check runs before every Audience upload and refuses the whole upload on any failed or errored check. Starts at Sends nothing.
Tactic account-deal-readout Account deal readout counts qualified Leads and Deals per 100 Organizations by arm, from the CRM, and gates every raise on the result. Starts at You approve each one.
Tactic ad-write-proxy Ad write proxy is the only write path to an ads Venue, holding the token outside the model and forcing every create to paused. Starts at You approve each one.
Tactic ad-drift-monitor Ad drift monitor diffs every live Ad’s rendered creative, text and enhancement settings against the approved source daily, and pauses or resets what drifted. Starts at Sends nothing.
Tactic ad-tag-consent-gate Ad tag consent gate decides on every Visit which Venue tags load, by the Visitor’s Market, Consent and opt-out signal, before any tag fires. Starts at Sends nothing.
Trigger organization-listed Organization listed: state on Organization.listed, window 14d.
Trigger air-cover-requested Air cover requested: event on Message.scheduled, window 14d.
Trigger pricing-visit Pricing visit: event on Visit.recorded, window 72h.
Trigger organization-surge Organization surge: threshold on Visitor.identified, window 7d.
Trigger organization-ad-engagement Organization ad engagement: threshold on Ad.clicked, window 14d.
Trigger qualified-lead-recorded Qualified lead recorded: event on Lead.qualified, window 1d.
Trigger listed-deal-opened Listed deal opened: event on Deal.created, window 1d.
Trigger listed-organization-exit Listed organization exit: state on Organization.delisted, window 1d.
Trigger founder-post-candidate Founder post candidate: threshold on Post.published, window 7d.
Trigger audience-floor-crossed Audience floor crossed: threshold on Audience.resized, window 7d.
Trigger opt-out-received Opt-out received: event on Consent.withdrawn, window 1d.
Trigger creative-drift Creative drift: event on Monitor.checked, window 1d.
Trigger holdout-closed Holdout closed: event on Experiment.matured, window 7d.
Benchmark account-ads-deal-open Deals opened per company targeted with LinkedIn company-list Ads, median: 0.2–0.58 % (An ad-analytics vendor’s 2026 LinkedIn company-list benchmark report, 2026-10-24).
Benchmark account-ads-ctr Click-through rate on LinkedIn company-list Ads, median: 0.69 % (An ad-analytics vendor’s 2026 LinkedIn company-list benchmark report, 2026-10-24).
Benchmark account-ads-impressions-per-company Impressions per targeted company on LinkedIn company-list Ads, median: 27.3 count (An ad-analytics vendor’s 2026 LinkedIn company-list benchmark report, 2026-10-24).

Compiled, not written: edit the Play record account-ads 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.