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
- Kind
- Outreach
- Summary
- startupbuilder.dev
Be our SDR on LinkedIn: each morning, pick the property managers with a real reason to hear from me, draft the connection note and the messages, and queue them in my tray. I press every send from my own profile. Never touch my login. Hold 1 in 10 back. 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":"linkedin-outbound"}'import { $ } from 'startup-builder'
return await $.Campaign.create({ play: 'linkedin-outbound' }, { 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":"linkedin-outbound"}' --try # Not live yet (gaps #2){
"name": "do",
"arguments": {
"code": "return await $.Campaign.create({ play: 'linkedin-outbound' }, { 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": "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"
},
{
…
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"
}
}application/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 yetPreviewOn 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 Prospect | Choice | Reason |
|---|---|---|
| Jo Rivera, Summit Lane: new Head of Operations, Oct 14 | request-with-a-note | an announced moment and her own stated first project |
| Ezra Cole commented on a rival’s post about late renewals | request-no-note | a 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 owners | message-a-connection | a dormant fit with a new public angle |
| Nico Park: “Sure, show me how the owner batches work.” | to-the-ae | a positive reply goes to the AE |
- 3 h week 1, then 2.5 h a week
- Your time
- Day 6: first Meeting booked from LinkedIn
- First signal
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/
Start it this week
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"}' | Check | Fix | Then |
|---|---|---|
use-when pass · Not live yet, gaps #8 | Dana, a person, profile 6 years old; 1,180 ICP-fit Prospects | POST /{namespace}/metrics/query |
baseline pass · Not live yet, gaps #8 | 9.2 replies with words per 100 of your own requests, n 120 in 90 days | POST /{namespace}/metrics/query |
inbound-source fail · Not live yet, gaps #8 | Forward LinkedIn notification emails to linkedin@in.leaseline.app so a reply stops the Sequence. | POST /{namespace}/integrations/connect |
connections-file fail | Upload your LinkedIn connections export; the monthly sweep finds dormant fits in it. | Contact.import |
role-unstaffed warn · Not live yet, gaps #8 | Staff 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.
| Check | Fix (one call) | Your time | Wait |
|---|---|---|---|
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 number | 1 min | none |
inbound-source · fail (gaps #8) | a mail rule forwarding LinkedIn notifications, or POST /{namespace}/integrations/connect | 5 min | none |
connections-file · fail (gaps #8) | Contact.import (Not live yet, gaps #2) your connections export | 10 min | LinkedIn’s export, often within a day |
role-unstaffed · warn (gaps #8): 9 of your open Leads have a person owner, you | POST /{namespace}/roles/{role}/staff, or leave it: positive replies come to you | 0 min | none |
Your number · Before 9.2 per 100 · Now 13.9 per 100 requests · Target 15 by Dec 31 · before-after on the leaf,
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
| When | Trigger id | Starts on | Window | How it gets in |
|---|---|---|---|---|
| A Person or their company announces a new role, a raise, a hire for the role you serve, or a launch | announced-moment | event on Person.announced where kind in ('new-role', 'funding', 'hiring', 'launch') | 21d | read from public posts; POST /{namespace}/events/capture for one you saw |
| They accept your request | request-accepted | event on Person.connected | 3d | the forwarded notification email (inbound-source) |
| They write back on LinkedIn | linkedin-reply | event on Message.replied where direction = 'inbound' | 1h | the 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=) |
{
"$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"
}
}# 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 pendingWho runs it
SDR · your agent (role:, 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:, worker_3, staffed on day 9). On LinkedIn the ladder stops at You approve each one.
| Option | Rung today | Evidence (passed of n) | Moves up at | Drops back when | You can |
|---|---|---|---|---|---|
| Request with a note · Request with no note | You approve each one | 588 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 accept | You approve each one | 214 sent by you | never | — | POST /{namespace}/tasks/{task}/return with a reason |
| To the AE · Not now | It does it | 12 of 12 · 96 of 96 | — | a hostile-reply Stop → You approve each one, with the brief shown first | POST /{namespace}/actions/{action}/redirect |
| Change the week’s slots | You approve each one | 1 merged of 1 | never | — | POST /{namespace}/changes/{change}/merge · POST /{namespace}/changes/{change}/revert |
| Never: your password or session, a Visit in a note, a rival named | Refused, every time | 2 drafted, 2 refused | never | — | 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.
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.
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
| Option | Picked when | Decided by | Runs | Hands to | If it’s just you |
|---|---|---|---|---|---|
Request with a note request-with-a-note | an announced moment, or their own public point, gives the note its angle | Agent’s call, with its reason | linkedin-request with a note → POST /{namespace}/tasks/{task}/assign to your tray | you send it, due your next sitting · Take this one | you |
Request with no note request-no-note | a fit engager or a visiting Organization, with no angle a note can carry | Your rule (line 4) | linkedin-request with no note → POST /{namespace}/tasks/{task}/assign | you send it · Take this one | you |
Message a connection message-a-connection | an ICP-fit connection you have not written to in 180 days, with an angle | Your rule (the monthly sweep) | linkedin-after-accept from its first Message → POST /{namespace}/tasks/{task}/assign | you send it · Take this one | you |
After they accept after-they-accept | they accepted your request | Your rule (the Sequence) | linkedin-after-accept: 3 Messages over 14 days, ending on any reply | you send each one · Take this one | you |
To the AE to-the-ae | a positive reply, or a question only a person can answer | Agent’s call, with its reason | POST /{namespace}/meetings/book with the AE’s times → POST /{namespace}/tasks/{task}/assign | POST /{namespace}/tasks/{task}/assign → AE · Priya Shah, due 60 min (response-60m), Brief attached · Take this one | you, within 60 min |
Not now not-now | no angle, a request already pending, or another of your Startups holds them | Your rule (the leases) | nothing; names the lease, or cold-email when they have an address | nobody | the same |
| When they write back | Fixture reply | What happens | Who answers | By when | Call |
|---|---|---|---|---|---|
| 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 send | AE · Priya Shah (person) | 60 min | POST /{namespace}/meetings/book |
| Send me pricing | “What do you charge?” | drafts your answer with the Pricing Page’s list Price and one question | you, from a draft | your next sitting | POST /{namespace}/content/draft |
| Not now | “Not this quarter.” | the Sequence stops; a date they name goes to lead-nurture as a not-now-date | SDR · your agent | at once | POST /{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 text | you, from a draft | your next sitting | POST /{namespace}/content/draft |
| Out of office | a profile note: away until Oct 28 | the next Message waits for the return date | nobody | the return date | POST /{namespace}/messages/{message}/schedule |
| Question it can’t answer | “Was that note written by a bot?” | held for you: you answer truthfully, in your words | you (Dana, person) | your next sitting | POST /{namespace}/conversations/{conversation}/escalate |
| Stop | “Please don’t message me again.” | off every List for every Startup you run; nothing queues for them again | nobody | at once | POST /{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).
[
{
"$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) | Refuses | Fix | Then |
|---|---|---|---|
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 claimed | Leave 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 holds | Cut 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 Message | Cut it; lead with what they announced. | POST /{namespace}/content/draft |
session-gate (LinkedIn) | any connector that asks for your LinkedIn password, cookie or session | None: send from the tray yourself. | POST /{namespace}/tasks/next |
pending-ceiling (LinkedIn) | a new request while pending requests pass twice the week’s slots | Withdraw requests pending 14 days or more. | POST /{namespace}/tasks/next |
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.
| Check | Fix | Then |
|---|---|---|
watched-event fail | Cut 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.
[
{
"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"
}| Node | Agent half | Held back | Added | Confidence | Basis |
|---|---|---|---|---|---|
linkedin-reply-rate | 13.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-leads | 9.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 tell | held 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 | $0 | carried: it moves no other bridge term | ■□□□ Too early to tell | carried 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.
{
"$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=; three follow the tables.
| What happened, agent half, by option | Decisions | Sent by you | Accepted | Replies with words |
|---|---|---|---|---|
| Request with a note | 342 | 330 | 102 | 31 |
| Request with no note | 269 | 258 | 47 | 9 |
| Message a connection · After they accept | 38 · 149 | 38 · 176 | — | 6 · 19 |
| To the AE · Not now | 12 · 96 | — | — | 8 Meetings held, 3 Customers (+$1,635 ARR, credited) |
| Agent half · held back | 906 | 802 | 149 | 65 · Leads 24 of 246 matured · 1 of 27 |
| At 340 eligible a month (the Use it when volume) | about 520 a month | about 450 | held back at random | Other Startups’ results: +4.0 per 100, ■■■□ Likely, n 41,000 |
| Day | Event | What happened | Your time |
|---|---|---|---|
| 0 | event_134 Campaign.created | the three steps; dry run fails inbound-source and connections-file | 25 min |
| 1 | event_152 Contact.imported | your connections export: 2,310 rows, 74 dormant fits | 10 min |
| 6 | event_240 Meeting.booked | the first Meeting from LinkedIn, after a note on a new role | — |
| 9 | event_301 Role.staffed | AE · Priya Shah (person) takes role: | — |
| 28 · 49 | event_542 Change.merged · event_872 Readout.created | 20 slots moved to announced moments: replayed at 29 % accepted, read back at +21d at 28 %, n 230 | 5 min |
| 37 | event_660 Campaign.paused | tray-silence: 3 business days away; picking resumed the morning you returned | — |
| 1–56 | event_136 Task.assigned, first of 825 | you sent 802 items in 38 sittings, from the POST /{namespace}/tasks/next log | 19 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.
- 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}/claimand 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
---
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. $ 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
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.