Skip to content

LinkedIn outbound

Picks each day’s People on LinkedIn, drafts the connection note and the Messages, and queues them in your send tray; you press every send.

Channel
LinkedIn
Run oneMode try
You, to your agent

Hand our latest announced moment 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(%22Person.announced%22%2C%20%22Person.posted%22%2C%20%22Person.commented%22%2C%20%22Person.reacted%22%2C%20%22Event.captured%22%2C%20%22Contact.imported%22%2C%20%22Person.connected%22%2C%20%22Message.replied%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_1217"}'

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

const { events: [last] } = await $.Event.list({ where: 'name in ("Person.announced", "Person.posted", "Person.commented", "Person.reacted", "Event.captured", "Contact.imported", "Person.connected", "Message.replied")', 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 (\"Person.announced\", \"Person.posted\", \"Person.commented\", \"Person.reacted\", \"Event.captured\", \"Contact.imported\", \"Person.connected\", \"Message.replied\")","limit":1}'
npx startup-builder do Trigger.fire --json '{"like":"event_1217"}' --try   # Not live yet (gaps #7)
npx startup-builder do Decision.list --json '{"where":"on = \"prospect_88\""}'   # Not live yet (gaps #7)
npx startup-builder do Action.list --json '{"where":"on = \"prospect_88\""}'   # Not live yet (gaps #7)
{
  "name": "do",
  "arguments": {
    "code": "const { events: [last] } = await $.Event.list({ where: 'name in (\"Person.announced\", \"Person.posted\", \"Person.commented\", \"Person.reacted\", \"Event.captured\", \"Contact.imported\", \"Person.connected\", \"Message.replied\")', 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_443",
    "$type": "Decision",
    "choice": "request-with-a-note",
    "reason": "Jo Rivera announced on Oct 14 that she started as Head of Operations at Summit Lane Properties (220 units); her post names renewals as her first project",
    "stakes": "low",
    "options": [
      "request-with-a-note",
      "request-no-note",
      "message-a-connection",
      "after-they-accept",
      "to-the-ae",
      "not-now"
  …
sample · 4 Not live yetPreview

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

action_1 checked her identity off LinkedIn. action_3 puts the note in your tray for tomorrow’s 08:30 sitting; you copy it and send it in LinkedIn yourself.

+6.1 Leads per 100 eligible · ■□□□ Too early to tell · n 273 · path → arr

Try another: the ProspectChoiceReason
Jo Rivera, Summit Lane: new Head of Operations, Oct 14request-with-a-notean announced moment and her own stated first project
Ezra Cole commented on a rival’s post about late renewalsrequest-no-notea fit commenter; a note may never name the rival’s post
Wren Holt, a connection since 2022, unmessaged 14 months; her firm grew to 3 ownersmessage-a-connectiona dormant fit with a new public angle
Nico Park: “Sure, show me how the owner batches work.”to-the-aea positive reply goes to the AE

Where it sits: Stranger → Lead → Trialing → Activated → At risk → Churned → Won back · LinkedIn reply rate → New Leads → New Customers ↑ → ARR added each week → ARR

Worth’s formula: about +6.1 Leads per 100 eligible (held back at random, one node above the leaf) × 340 eligible a month × 0.08 Leads to paid × $590 first order ARR ≈ +$979. Under First signal: About 16 weeks to tell a +6.1 lift at 340 eligible a month with 1 in 10 held back; at 56 days it reads Too early to tell.

sample · api.sb/leaseline.app · 41 Customers at 2026-10-19 · on Person.announced · first draft in the tray 3m 30s · 8 keys Not live yet

Start it this week

Terminal
npx startup-builder add play linkedin-outbound   # 1 · writes plays/linkedin-outbound.mdx (Not live yet, gaps #5)

# 2 · dry run; below, its 422 on 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":"linkedin-outbound"}'

# 3 · keep it: you press every send
curl -X POST https://api.sb/leaseline.app/campaigns \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"play":"linkedin-outbound"}'

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

# off: the tray empties; nothing was ever sent without you
curl -X PATCH https://api.sb/leaseline.app/campaigns/linkedin-outbound \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"endsAt":"now"}'
422Refusedlinkedin-outbound can’t start: 2 checks fail and 1 warns.
CheckFixThen
use-when pass · Not live yet, gaps #8Dana, a person, profile 6 years old; 1,180 ICP-fit ProspectsPOST /{namespace}/metrics/query
baseline pass · Not live yet, gaps #89.2 replies with words per 100 of your own requests, n 120 in 90 daysPOST /{namespace}/metrics/query
inbound-source fail · Not live yet, gaps #8Forward LinkedIn notification emails to linkedin@in.leaseline.app so a reply stops the Sequence.POST /{namespace}/integrations/connect
connections-file failUpload your LinkedIn connections export; the monthly sweep finds dormant fits in it.Contact.import
role-unstaffed warn · Not live yet, gaps #8Staff the AE, or leave it: positive replies come to you.POST /{namespace}/roles/{role}/staff
JSON
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "linkedin-outbound can’t start: 2 checks fail and 1 warns.",
  "checks": [
    {
      "check": "use-when",
      "status": "pass",
      "observed": "Dana, a person, profile 6 years old; 1,180 ICP-fit Prospects",
      "doc": "/plays/linkedin-outbound#check-use-when",
      "then": "Metric.query"
    },
    {
      "check": "baseline",
      "status": "pass",
      "observed": "9.2 replies with words per 100 of your own requests, n 120 in 90 days",
      "doc": "/plays/linkedin-outbound#check-baseline",
      "then": "Metric.query"
    },
    {
      "check": "inbound-source",
      "status": "fail",
      "doc": "/plays/linkedin-outbound#check-inbound-source",
      "fix": "Forward LinkedIn notification emails to linkedin@in.leaseline.app so a reply stops the Sequence.",
      "then": "Integration.connect"
    },
    {
      "check": "connections-file",
      "status": "fail",
      "doc": "/plays/linkedin-outbound#check-connections-file",
      "fix": "Upload your LinkedIn connections export; the monthly sweep finds dormant fits in it.",
      "then": "Contact.import"
    },
    {
      "check": "role-unstaffed",
      "status": "warn",
      "roles": [
        "sales-lead"
      ],
      "owned": 9,
      "doc": "/plays/linkedin-outbound#check-role-unstaffed",
      "fix": "Staff the AE, or leave it: positive replies come to you.",
      "then": "Role.staff"
    }
  ]
}

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

CheckFix (one call)Your timeWait
use-when · baseline · pass: a person, 1,180 fits; 9.2 per 100, n 120 (gaps #8)POST /{namespace}/metrics/query on linkedin-reply-rate, which fills Before in Your number1 minnone
inbound-source · fail (gaps #8)a mail rule forwarding LinkedIn notifications, or POST /{namespace}/integrations/connect5 minnone
connections-file · fail (gaps #8)Contact.import (Not live yet, gaps #2) your connections export10 minLinkedIn’s export, often within a day
role-unstaffed · warn (gaps #8): 9 of your open Leads have a person owner, youPOST /{namespace}/roles/{role}/staff, or leave it: positive replies come to you0 minnone

Your number · Before 9.2 per 100 · Now 13.9 per 100 requests · Target 15 by Dec 31 · before-after on the leaf, not proof · n 273 matured

Requests and Messages cost nothing: you send them from your own profile, and a paid Message spends your own LinkedIn credits. Research costs about $0.05 a Person (Worker cost), about $26 a month at 120 a week. Your time is the cost: about 2.5 hours a week in the tray.

When it starts

WhenTrigger idStarts onWindowHow it gets in
A Person or their company announces a new role, a raise, a hire for the role you serve, or a launchannounced-momentevent on Person.announced where kind in ('new-role', 'funding', 'hiring', 'launch')21dread from public posts; POST /{namespace}/events/capture for one you saw
They accept your requestrequest-acceptedevent on Person.connected3dthe forwarded notification email (inbound-source)
They write back on LinkedInlinkedin-replyevent on Message.replied where direction = 'inbound'1hthe forwarded notification email, or share it into the tray
+ 5 more for problem posts, rival-post commenters, reactors to your posts, visiting Organizations and dormant connections (?lanes=all)
One signed delivery · StartupBuilder-Signature
{
  "$id": "https://api.sb/leaseline.app/events/event_1217",
  "$type": "Event",
  "name": "Person.announced",
  "at": "2026-10-19T07:05:44Z",
  "seq": 1217,
  "data": {
    "prospect": "prospects/prospect_88",
    "name": "Jo Rivera",
    "organization": "Summit Lane Properties",
    "units": 220,
    "kind": "new-role",
    "announced": "2026-10-14",
    "source": "a public post"
  }
}
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":"Person.announced","url":"https://leaseline.app/hooks/sb"}'
npx startup-builder trigger Person.announced --like event_1217   # replays the delivery into tomorrow’s tray
npx startup-builder clock advance --by 14d   # withdraws the request if it is still pending

Who runs it

SDR · your agent (role:prospector, worker_2) holds the Key Result: it sets the week’s slots, picks, checks identities and drafts. Every request and Message hands to you (Dana Okafor, person, worker_1), who sends it from your own profile; positive replies go to AE · Priya Shah (person) (role:sales-lead, worker_3, staffed on day 9). On LinkedIn the ladder stops at You approve each one.

OptionRung todayEvidence (passed of n)Moves up atDrops back whenYou can
Request with a note · Request with no noteYou approve each one588 sent by you of 611 queued (21 skipped, 2 wrong person)never: you press every send—skip it in the tray, or POST /{namespace}/actions/{action}/cancel
Message a connection · After they acceptYou approve each one214 sent by younever—POST /{namespace}/tasks/{task}/return with a reason
To the AE · Not nowIt does it12 of 12 · 96 of 96—a hostile-reply Stop → You approve each one, with the brief shown firstPOST /{namespace}/actions/{action}/redirect
Change the week’s slotsYou approve each one1 merged of 1never—POST /{namespace}/changes/{change}/merge · POST /{namespace}/changes/{change}/revert
Never: your password or session, a Visit in a note, a rival namedRefused, every time2 drafted, 2 refusednever—the session-gate and watched-event Checks

What it reads · What it may call. It reads public posts, the lists you paste, your connections export and forwarded notifications, and never your LinkedIn login. A Visit may raise a Prospect’s rank but never appears in a note. It may call POST /{namespace}/contacts/{contact}/enrich, POST /{namespace}/content/draft and POST /{namespace}/tasks/{task}/assign; you send. Its tools are POST /{namespace}/tasks/{task}/assign, POST /{namespace}/meetings/book, and any other call becomes an Approval under the Role’s Mandate.

The brief (folded): compiled Agent-register Markdown · play:linkedin-outbound@1 · each line names its record key
You hold the SDR Key Result: 15 replies with words per 100 requests by 2026-12-31. ← worker, metric
Each morning, choose one option for each candidate and say why; no angle is Not now. ← triggers, chooses
Queue at most 80 requests a week, and 16 a day.                                  ← your file, line 3
A note runs 280 characters or fewer, with no link, price or ask beyond connecting. ← copyRules 1
A first Message runs 80 words or fewer, with one question; a calendar link waits for a yes. ← copyRules 2
Never name a Visit, a rival or the rival’s post.                                 ← copyRules 4, 6, watched-event

The slow loop, as a Change. decision_401 (2026-09-21, weekly) read acceptance at 31 % on announced moments and 18 % on reactors with no note, so change_32 moved 20 weekly slots from reactors to announced moments (your file, line 4), and Dana merged it with POST /{namespace}/changes/{change}/merge. Replayed on past picks, POST /{namespace}/changes/{change}/review put acceptance at 29 %; read back at +21d it is 28 %, n 230, ■■□□ Early sign (Example, day 49).

Bring your own agent (folded) picks and drafts at the same rung when it matches or beats the seated agent on the eval suite; you press every send either way, and POST /{namespace}/roles/{role}/staff seats it.

Terminal
npx startup-builder eval --scenario linkedin-outbound-588 --agent "claude -p" --seat prospector   # seated: 13.9 per 100, 0 watched Events drafted, 2 wrong people · yours: 12.4, 1 Visit drafted, 5 wrong people → fix the identity check first

What it can do next

OptionPicked whenDecided byRunsHands toIf it’s just you
Request with a note request-with-a-notean announced moment, or their own public point, gives the note its angleAgent’s call, with its reasonlinkedin-request with a note → POST /{namespace}/tasks/{task}/assign to your trayyou send it, due your next sitting · Take this oneyou
Request with no note request-no-notea fit engager or a visiting Organization, with no angle a note can carryYour rule (line 4)linkedin-request with no note → POST /{namespace}/tasks/{task}/assignyou send it · Take this oneyou
Message a connection message-a-connectionan ICP-fit connection you have not written to in 180 days, with an angleYour rule (the monthly sweep)linkedin-after-accept from its first Message → POST /{namespace}/tasks/{task}/assignyou send it · Take this oneyou
After they accept after-they-acceptthey accepted your requestYour rule (the Sequence)linkedin-after-accept: 3 Messages over 14 days, ending on any replyyou send each one · Take this oneyou
To the AE to-the-aea positive reply, or a question only a person can answerAgent’s call, with its reasonPOST /{namespace}/meetings/book with the AE’s times → POST /{namespace}/tasks/{task}/assignPOST /{namespace}/tasks/{task}/assign → AE · Priya Shah, due 60 min (response-60m), Brief attached · Take this oneyou, within 60 min
Not now not-nowno angle, a request already pending, or another of your Startups holds themYour rule (the leases)nothing; names the lease, or cold-email when they have an addressnobodythe same
When they write backFixture replyWhat happensWho answersBy whenCall
Interested“Sure, show me how the owner batches work.”To the AE: the Sequence stops, and three times wait in your tray for you to sendAE · Priya Shah (person)60 minPOST /{namespace}/meetings/book
Send me pricing“What do you charge?”drafts your answer with the Pricing Page’s list Price and one questionyou, from a draftyour next sittingPOST /{namespace}/content/draft
Not now“Not this quarter.”the Sequence stops; a date they name goes to lead-nurture as a not-now-dateSDR · your agentat oncePOST /{namespace}/leads/{lead}/nurture
Wrong person“Leasing ops is my colleague Sam.”drafts your thanks asking for an intro; it never writes to someone from their textyou, from a draftyour next sittingPOST /{namespace}/content/draft
Out of officea profile note: away until Oct 28the next Message waits for the return datenobodythe return datePOST /{namespace}/messages/{message}/schedule
Question it can’t answer“Was that note written by a bot?”held for you: you answer truthfully, in your wordsyou (Dana, person)your next sittingPOST /{namespace}/conversations/{conversation}/escalate
Stop“Please don’t message me again.”off every List for every Startup you run; nothing queues for them againnobodyat oncePOST /{namespace}/contacts/{contact}/suppress

When it hands to you

Every request and Message hands to you (Dana Okafor, person) to send; positive replies go to AE · Priya Shah (person), and before day 9 they came to you. As captured, one Brief per Role (brief is Not live yet, gaps #7).

What lands in each queue · sample
[
  {
    "$id": "https://api.sb/leaseline.app/tasks/task_533",
    "$type": "Task",
    "assignee": "worker_1",
    "due": "2026-10-20T08:30:00Z",
    "on": "prospect_88",
    "brief": {
      "who": "Jo Rivera, Head of Operations, Summit Lane Properties (220 units)",
      "reason": "her post, 2026-10-14: started, renewals her first project",
      "note": "Jo, congrats on Summit Lane. Your post says renewals come first; that is all we work on. Happy to connect.",
      "chose": "request-with-a-note",
      "never": [
        "a link",
        "a price",
        "a Visit"
      ]
    }
  },
  {
    "$id": "https://api.sb/leaseline.app/tasks/task_491",
    "$type": "Task",
    "assignee": "worker_3",
    "due": "2026-10-09T11:05:00Z",
    "on": "lead_47",
    "brief": {
      "who": "Nico Park, Ashby Residential, 130 units",
      "said": "Sure, show me how the owner batches work.",
      "chose": "to-the-ae",
      "why": "a positive reply",
      "times": [
        "Tue 10:00",
        "Tue 15:00",
        "Wed 11:00 Central"
      ],
      "never": [
        "a discount",
        "terms"
      ]
    }
  }
]

You answer a tray item by sending it in LinkedIn, with POST /{namespace}/tasks/{task}/return and a reason, or with POST /{namespace}/actions/{action}/redirect. The AE answers with POST /{namespace}/tasks/{task}/claim (Take this one), POST /{namespace}/tasks/{task}/return with a reason, or POST /{namespace}/actions/{action}/redirect; once claimed, the agent’s queued Messages to that Person cancel with claimedByPerson (Not live yet, gaps #7). A Task nobody claims by its due time goes up a Role through POST /{namespace}/tasks/{task}/escalate (AE to you), and 3 business days with no tray action stop picking (tray-silence).

On time: AE · Priya Shah: 11 of 12 claimed within 60 min, median 26 min, n 12. You: 802 of 825 tray items sent by your next sitting, n 825, ■■■□ Likely beside Other Startups’ 88 %. Your queue: counted by kind: about 16 requests and 6 Messages a sitting, 1 sitting a business day, and 1 weekly readout.

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)a request or Message to a Person whose Lead or open Deal someone else owns, or whose Conversation a person claimedLeave it to the person who owns it: the draft waits in their queue.POST /{namespace}/tasks/{task}/assign
fact-check (the fabrication veto)a note or Message with a fact, history or feeling no source or record holdsCut the line, or add the source it came from.POST /{namespace}/content/draft
watched-event (copy rule 4)a Visit, a Page view or a resolved Organization in a note or MessageCut it; lead with what they announced.POST /{namespace}/content/draft
session-gate (LinkedIn)any connector that asks for your LinkedIn password, cookie or sessionNone: send from the tray yourself.POST /{namespace}/tasks/next
pending-ceiling (LinkedIn)a new request while pending requests pass twice the week’s slotsWithdraw requests pending 14 days or more.POST /{namespace}/tasks/next
Stop (pauses the Play)Pauses whenThen
venue-warning (LinkedIn)a restriction notice, identity check, CAPTCHA or invitation warning on your profilethe Mandate pauses at once; you resume it after 7 days at half volume
acceptance-floor (LinkedIn)under 20 accepted of the last 100 requests; under 15the week’s slots halve and the brief shows first; under 15, requests pause
reply-floor (LinkedIn)under 3 replies per 100 requests after 200the Play stops
hostile-reply (LinkedIn)2 hostile replies or removal asks in any 50 sent MessagesYou approve each one, brief first, for 2 weeks
callout (LinkedIn)2 replies in 30 days call a draft machine-writtenYou approve each one, brief first
wrong-person (LinkedIn)3 wrong-person marks in 50 checked itemspicking pauses until the identity check passes its eval set
tray-silence3 business days with no tray actionpicking and drafting stop until you return
kill-switchyou press itevery Tactic and the Mandate pause at once

The captured refusal, then the Stop that fired as a Campaign.paused reason (Not live yet, gaps #2); the rewrite you sent is the Delmar thread in Example.

422RefusedThe note to Delmar Homes did not reach your tray: it names a Visit to the pricing Page.
CheckFixThen
watched-event failCut it; lead with what they announced.POST /{namespace}/content/draft
fact-check pass · Not live yet, gaps #1—POST /{namespace}/content/draft
JSON
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "The note to Delmar Homes did not reach your tray: it names a Visit to the pricing Page.",
  "checks": [
    {
      "check": "watched-event",
      "status": "fail",
      "doc": "/plays/linkedin-outbound#check-watched-event",
      "fix": "Cut it; lead with what they announced.",
      "then": "Content.draft"
    },
    {
      "check": "fact-check",
      "status": "pass"
    }
  ]
}

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

The Stops that fired, as Campaign.paused reasons · sample
[
  {
    "reason": {
      "limit": "tray-silence",
      "observed": "3 business days",
      "bound": "3 business days",
      "window": "the tray"
    },
    "doc": "/plays/linkedin-outbound#stop-tray-silence",
    "task": "task_466"
  }
]

Did it work

The held-back tenth gets no request, so the leaf, replies per 100 requests, reads before-after against your own requests; one node up, Leads per 100 eligible Prospects reads both halves from the same random assignment (rules m–o). Each figure’s call is POST /{namespace}/metrics/query by node and by arm; every node above the leaf is Not live yet (gaps #4).

+6.1 Leads per 100 eligible · ■□□□ Too early to tell · n 273 · path → arr

Chose
request-with-a-note
Why
Jo Rivera announced on Oct 14 that she started as Head of Operations at Summit Lane Properties (220 units); her post names renewals as her first project
From
request-with-a-note · request-no-note · message-a-connection · after-they-accept · to-the-ae · not-now
Expects
accepted and a reply with words within 30d
Reopens when
no Person.connected for prospect_88 within 14d
By
worker_2
JSON
{
  "$id": "decisions/decision_443",
  "$type": "Decision",
  "choice": "request-with-a-note",
  "reason": "Jo Rivera announced on Oct 14 that she started as Head of Operations at Summit Lane Properties (220 units); her post names renewals as her first project",
  "stakes": "low",
  "options": [
    "request-with-a-note",
    "request-no-note",
    "message-a-connection",
    "after-they-accept",
    "to-the-ae",
    "not-now"
  ],
  "by": "worker_2",
  "on": "prospect_88",
  "expected": "accepted and a reply with words within 30d",
  "reopensWhen": "no Person.connected for prospect_88 within 14d"
}
NodeAgent halfHeld backAddedConfidenceBasis
linkedin-reply-rate13.9 per 100 requests (38 of 273 matured)no requests; your own 90 days before: 9.2 per 100 (11 of 120)— on this row—before-after, not proof · Other Startups’ results +4.0 per 100, ■■■□ Likely, n 41,000 requests
new-leads9.8 per 100 eligible (24 of 246 matured)3.7 (1 of 27)+6.1 [−1.9, 14.1] · Worth about +$980 a month■□□□ Too early to tellheld back at random · n 273 · 316 still maturing
new-arr ↑$1,635, 3 Customers (2 on Pro, 1 on Team) · Booked: credited, not all caused by this Play$0+$667 per 100 eligible [−$900, +$2,200]■□□□ Too early to tell (3 vs 0 Customers)the same · 5 trials still deciding
net-new-arr · arr+$1,635$0carried: it moves no other bridge term■□□□ Too early to tellcarried from new-arr

Leads count Persons who accept and reply with words, or reply on any channel, within 30 days, per 100 eligible Prospects assigned once the identity check has run, read to 200 matured in each half; at 246 and 27 the held-back half runs on.

How it moves it: each request carries one reason the Person announced, sent from your own profile, and acceptance on announced moments ran 31 % against 18 % for reactors with no note.

Guardrails: treated against held back: play-sourced-customers, 3 against 0, and removal asks, 1 in 214 Messages; no Venue warning fired.

The Campaign card · sample
{
  "$id": "https://api.sb/leaseline.app/campaigns/linkedin-outbound",
  "$type": "Campaign",
  "play": "play:linkedin-outbound@1",
  "path": [
    "linkedin-reply-rate",
    "new-leads",
    "new-arr",
    "net-new-arr",
    "arr"
  ],
  "experiment": {
    "metric": "new-leads",
    "unit": "per-100",
    "heldBack": 59,
    "decision": null
  },
  "readout": {
    "value": 6.1,
    "n": 273,
    "maturing": 316,
    "interval": [
      -1.9,
      14.1
    ],
    "readability": "inadmissible",
    "basis": "held-back",
    "pooled": false
  },
  "ledger": {
    "spend": 0,
    "workerCost": 48,
    "hours": 21,
    "pipelineArr": 5340,
    "wonArr": 1635,
    "projectedArr": 979,
    "paybackDays": 9
  }
}

Outside benchmark: the record’s agent-profile-accuracy range, from a sales-research firm’s test of agent-built Prospect profiles, finds a share of them wrong or thin, which is why each identity is checked off LinkedIn before it reaches your tray.

Before it · After it. Before it: account-visits, moment-outreach and founder-voice feed it Prospects with a reason. After it: positive replies go to the AE, dated not-now replies to lead-nurture, and wide committees to buying-committee.

Example

Leaseline ran it from 2026-08-24 to 2026-10-19 (56 days): 589 eligible Prospects assigned, 59 held back, 611 requests queued and 588 sent by Dana, and 214 Messages. Read every captured thread at ?threads=all; three follow the tables.

What happened, agent half, by optionDecisionsSent by youAcceptedReplies with words
Request with a note34233010231
Request with no note269258479
Message a connection · After they accept38 · 14938 · 176—6 · 19
To the AE · Not now12 · 96——8 Meetings held, 3 Customers (+$1,635 ARR, credited)
Agent half · held back90680214965 · Leads 24 of 246 matured · 1 of 27
At 340 eligible a month (the Use it when volume)about 520 a monthabout 450held back at randomOther Startups’ results: +4.0 per 100, ■■■□ Likely, n 41,000
DayEventWhat happenedYour time
0event_134 Campaign.createdthe three steps; dry run fails inbound-source and connections-file25 min
1event_152 Contact.importedyour connections export: 2,310 rows, 74 dormant fits10 min
6event_240 Meeting.bookedthe first Meeting from LinkedIn, after a note on a new role—
9event_301 Role.staffedAE · Priya Shah (person) takes role:sales-lead—
28 · 49event_542 Change.merged · event_872 Readout.created20 slots moved to announced moments: replayed at 29 % accepted, read back at +21d at 28 %, n 2305 min
37event_660 Campaign.pausedtray-silence: 3 business days away; picking resumed the morning you returned—
1–56event_136 Task.assigned, first of 825you sent 802 items in 38 sittings, from the POST /{namespace}/tasks/next log19 h · total 21 h measured, 20 h asked

Dana → Nico Park, LinkedIn, Sep 26 · Nico, congrats on the Ashby Residential move. Your post says renewals across 4 owners come first; that is all we work on. Happy to connect.¹

Accepted Sep 27. Dana, Sep 28 · Thanks for connecting. When the 4 owners renew, does each get their own notice batch, or one for all?²

Nico, Oct 8, 10:05 · “Sure, show me how the owner batches work.” To the AE; Priya booked Tue 15:00, and the Brief reached her at 10:06.

Delmar Homes, 2026-09-16 · the note watched-event refused, then the rewrite you sent
- Saw your team checking out our pricing this week; happy to help.
+ Congrats on the third owner group you announced in September; renewals get busy at that size. Happy to connect.

Nico Park, 2026-10-08 10:05 · replied yes. The agent chose To the AE, stopped the Sequence and sent the Brief to Priya, due 11:05. · Priya, 10:31 · claimed it with POST /{namespace}/tasks/{task}/claim and booked Tue 15:00 Central; the Meeting was held Oct 13. The Brief is in When it hands to you (task_491).

¹ 280 characters or fewer, one announced reason, no link and no ask beyond connecting. ² 80 words or fewer and one question; the calendar link waits for a yes.

Your settings

plays/linkedin-outbound.mdx
---
holdout: 0.1        # 1 eligible Prospect in 10 gets no LinkedIn touch for 60 days
slots: 80            # requests a week, at most 16 a day
moments: 60          # of the 80, for announced moments
---
Sitting at 08:30 each business day. Never touch my login.
Text
$ npx startup-builder show plays/linkedin-outbound
holdout 0.1 · 80 slots a week, 60 for announced moments · 08:30 sitting     your file  plays/linkedin-outbound.mdx:2,3,4,6 (Ask 2026-08-24; line 4 by change_32, 2026-09-21)
family demand · worker role:prospector, owns never · chooses 6 (4 rule, 2 read) · sequences linkedin-request, linkedin-after-accept · every send by your hand · checks owned-by-a-person, fact-check, watched-event, session-gate, pending-ceiling · stops 8 · placements linkedin    catalog  play:linkedin-outbound@1 (Not live yet, gaps #5)

Every key from the catalog

Key Id What it is
Tactic invitation-budget Invitation budget sets a Human’s weekly connection request slots, clears them across siblings, and halves or pauses the week on low acceptance. Starts at Sends nothing.
Tactic linkedin-pick LinkedIn pick chooses the day’s People from announced Events, engagers, Visits and dormant connections, and checks each identity off LinkedIn. Starts at Sends nothing.
Tactic engager-capture Engager capture turns a reactor or commenter list the Human pastes from a LinkedIn Post into ICP-fit candidates, and discards the rest. Starts at Sends nothing.
Tactic network-sweep Network sweep reads the Human’s own LinkedIn connections file each month and finds ICP-fit connections the Human has not messaged in 180 days. Starts at Sends nothing.
Tactic linkedin-draft LinkedIn draft writes a connection note and a first Message from the announced reason and the claims source, or exits with no angle. Starts at Sends nothing.
Tactic send-gate-check Send gate check: Checks Suppression, a live reason, the Market, the basis stamp, the Person-month grant, the facts and the labels before any cold Message exists. Starts at Sends nothing.
Tactic fabrication-veto Fabrication veto blocks any draft written in a Human’s name that states a number, name, event, quote or feeling no source supports. Starts at Sends nothing.
Tactic linkedin-send-tray LinkedIn send tray shows each request and Message with its reason, for the Human to check, copy and send in the Venue’s own app. Starts at You approve each one.
Tactic linkedin-inbox-watch LinkedIn inbox watch reads forwarded acceptances and replies, stops the Sequence on any reply, and drafts the answer into the send tray. Starts at Sends nothing.
Tactic reply-matrix Reply matrix: Labels every outreach reply with one of 11 labels, maps it to one of seven reply kinds, and runs that kind’s move by its deadline. Starts at You approve each one.
Tactic linkedin-readout LinkedIn readout computes replies from accepted connections per 100 requests each week, with the note Variant, Trigger kinds, the holdout and the decay check. Starts at Sends nothing.
Trigger announced-moment Announced moment: event on Person.announced, window 21d.
Trigger problem-post Problem post: event on Person.posted, window 7d.
Trigger rival-post-commenter Rival post commenter: event on Person.commented, window 7d.
Trigger own-post-reactor Own post reactor: event on Person.reacted, window 7d.
Trigger org-visit Organization visit: event on Event.captured, window 3d.
Trigger dormant-connection Dormant connection: state on Contact.imported, window 30d.
Trigger request-accepted Request accepted: event on Person.connected, window 3d.
Trigger linkedin-reply LinkedIn reply: event on Message.replied, window 1h.
Sequence linkedin-request LinkedIn request: 4 steps; it ends on The Person accepts; Any reply; Suppression or a removal ask; A Venue warning; The Human kills the item.
Sequence linkedin-after-accept LinkedIn after accept: 3 steps; it ends on Any reply; A booked Meeting; An accepted Offer; Suppression or a removal ask.
Benchmark agent-profile-accuracy Share of agent-built target profiles that are accurate and sufficient: 88 % (A controlled study of automated target research and drafting, journal volume 314, 2026-02-01).
Benchmark agent-profile-thin Share of agent-built target profiles with the right Person and thin facts: 8 % (A controlled study of automated target research and drafting, journal volume 314, 2026-02-01).
Benchmark agent-profile-wrong-person Share of agent-built target profiles that name the wrong Person: 4 % (A controlled study of automated target research and drafting, journal volume 314, 2026-02-01).
Benchmark agent-draft-review-time Human review time per agent-drafted message, in hours (2.7 minutes), of which about 1.1 minutes checking the research: 0.045 hours (A controlled study of automated target research and drafting, journal volume 314, 2026-02-01).

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