URL: /plays/linkedin-outbound · Id: `play:linkedin-outbound`

# 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.

Picks each day’s [People](https://startupbuilder.dev/business/nouns/person) on LinkedIn, drafts the connection note and the [Messages](https://startupbuilder.dev/business/nouns/message), and queues them in your send tray; you press every send.

```text title="Set it up · Ask"
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.
```

```text title="Run one · Ask"
Hand our latest announced moment to the SDR agent and show me what it chose and why. Don’t send.
```

```ts title="Run one · SDK"
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 }
```

```json title="Run one · Response · sample"
{
  "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"
    ],
    "by": "worker_2",
    "on": "prospect_88",
    "expected": "accepted and a reply with words within 30d",
    "reopensWhen": "no Person.connected for prospect_88 within 14d"
  },
  "actions": [
    {
      "$id": "https://api.sb/leaseline.app/actions/action_1",
      "$type": "Action",
      "status": "done",
      "mode": "try",
      "by": "worker_2",
      "verb": "Contact.enrich",
      "checks": [
        {
          "check": "wrong-person",
          "status": "pass"
        }
      ]
    },
    {
      "$id": "https://api.sb/leaseline.app/actions/action_2",
      "$type": "Action",
      "status": "done",
      "mode": "try",
      "by": "worker_2",
      "verb": "Content.draft",
      "checks": [
        {
          "check": "fact-check",
          "status": "pass"
        },
        {
          "check": "watched-event",
          "status": "pass"
        }
      ]
    },
    {
      "$id": "https://api.sb/leaseline.app/actions/action_3",
      "$type": "Action",
      "status": "pending",
      "reason": "awaitingFounder",
      "mode": "try",
      "by": "worker_2",
      "verb": "Task.assign",
      "checks": [
        {
          "check": "owned-by-a-person",
          "status": "pass"
        },
        {
          "check": "session-gate",
          "status": "pass"
        },
        {
          "check": "pending-ceiling",
          "status": "pass"
        }
      ]
    }
  ]
}
```

`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 |

| Worth | Your time | Spend | First signal | Use it when | Instead |
| --- | --- | --- | --- | --- | --- |
| ≈ +$980 ARR a month at 340 | 3 h week 1, then 2.5 h a week | $0.05 per Person researched, ~$26 a month | Day 6: first Meeting booked from LinkedIn | a person who sends from their own profile, 200+ fits | No person to press send: `cold-email` · under 200 fits: `founder-voice` |

**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
```bash
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"}'
```


```json title="The dry run’s 422 · 2026-08-24 · sample"
{
  "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"
    }
  ]
}
```

| Check | Fix (one call) | Your time | Wait |
| --- | --- | --- | --- |
| <a id="check-use-when"></a>`use-when` · <a id="check-baseline"></a>`baseline` · pass: a person, 1,180 fits; 9.2 per 100, n 120 (gaps #8) | <Op verb="Metric.query" /> on `linkedin-reply-rate`, which fills Before in Your number | 1 min | none |
| <a id="check-inbound-source"></a>`inbound-source` · fail (gaps #8) | a mail rule forwarding LinkedIn notifications, or <Op verb="Integration.connect" /> | 5 min | none |
| <a id="check-connections-file"></a>`connections-file` · fail (gaps #8) | `Contact.import` (Not live yet, gaps #2) your connections export | 10 min | LinkedIn’s export, often within a day |
| <a id="check-role-unstaffed"></a>`role-unstaffed` · warn (gaps #8): 9 of your open Leads have a person owner, you | <Op verb="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, 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
| 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; <Op verb="Event.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=all`) | | | | |

```json title="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"
  }
}
```

```bash title="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.

| 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 <Op verb="Action.cancel" /> |
| Message a connection · After they accept | You approve each one | 214 sent by you | never | — | <Op verb="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 | <Op verb="Action.redirect" /> |
| Change the week’s slots | You approve each one | 1 merged of 1 | never | — | <Op verb="Change.merge" /> · <Op verb="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 <Op verb="Contact.enrich" />, <Op verb="Content.draft" /> and <Op verb="Task.assign" />; you send. Its tools are <Op verb="Task.assign" />, <Op verb="Meeting.book" />, and any other call becomes an Approval under the Role’s Mandate.

```md title="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 <Op verb="Change.merge" />. **Replayed on past picks**, <Op verb="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 <Op verb="Role.staff" /> seats it.

```bash
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 → <Op verb="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 → <Op verb="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 → <Op verb="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 | <Op verb="Meeting.book" /> with the AE’s times → <Op verb="Task.assign" /> | <Op verb="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 | <Op verb="Meeting.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 | <Op verb="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 | <Op verb="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 | <Op verb="Content.draft" /> |
| Out of office | a profile note: away until Oct 28 | the next Message waits for the return date | nobody | the return date | <Op verb="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 | <Op verb="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 | <Op verb="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).

```json title="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 <Op verb="Task.return" /> and a reason, or with <Op verb="Action.redirect" />. The AE answers with <Op verb="Task.claim" /> (Take this one), <Op verb="Task.return" /> with a reason, or <Op verb="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 <Op verb="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:** <Op verb="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 |
| --- | --- | --- | --- |
| <a id="check-owned-by-a-person"></a>`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. | <Op verb="Task.assign" /> |
| <a id="check-fact-check"></a>`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. | <Op verb="Content.draft" /> |
| <a id="check-watched-event"></a>`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. | <Op verb="Content.draft" /> |
| <a id="check-session-gate"></a>`session-gate` (LinkedIn) | any connector that asks for your LinkedIn password, cookie or session | None: send from the tray yourself. | <Op verb="Task.next" /> |
| <a id="check-pending-ceiling"></a>`pending-ceiling` (LinkedIn) | a new request while pending requests pass twice the week’s slots | Withdraw requests pending 14 days or more. | <Op verb="Task.next" /> |

| Stop (pauses the Play) | Pauses when | Then |
| --- | --- | --- |
| <a id="stop-venue-warning"></a>`venue-warning` (LinkedIn) | a restriction notice, identity check, CAPTCHA or invitation warning on your profile | the Mandate pauses at once; you resume it after 7 days at half volume |
| <a id="stop-acceptance-floor"></a>`acceptance-floor` (LinkedIn) | under 20 accepted of the last 100 requests; under 15 | the week’s slots halve and the brief shows first; under 15, requests pause |
| <a id="stop-reply-floor"></a>`reply-floor` (LinkedIn) | under 3 replies per 100 requests after 200 | the Play stops |
| <a id="stop-hostile-reply"></a>`hostile-reply` (LinkedIn) | 2 hostile replies or removal asks in any 50 sent Messages | You approve each one, brief first, for 2 weeks |
| <a id="stop-callout"></a>`callout` (LinkedIn) | 2 replies in 30 days call a draft machine-written | You approve each one, brief first |
| <a id="stop-wrong-person"></a>`wrong-person` (LinkedIn) | 3 wrong-person marks in 50 checked items | picking pauses until the identity check passes its eval set |
| <a id="stop-tray-silence"></a>`tray-silence` | 3 business days with no tray action | picking and drafting stop until you return |
| <a id="stop-kill-switch"></a>`kill-switch` | you press it | every 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.

```json title="The captured refusal · sample"
{
  "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"
    }
  ]
}
```


```json title="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 <Op verb="Metric.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`

| 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.

```json title="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 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:sales-lead` | — |
| 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 <Op verb="Task.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.

```diff title="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 <Op verb="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
```mdx title="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 | <a id="tactic-invitation-budget"></a>`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 | <a id="tactic-linkedin-pick"></a>`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 | <a id="tactic-engager-capture"></a>`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 | <a id="tactic-network-sweep"></a>`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 | <a id="tactic-linkedin-draft"></a>`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 | <a id="tactic-send-gate-check"></a>`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 | <a id="tactic-fabrication-veto"></a>`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 | <a id="tactic-linkedin-send-tray"></a>`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 | <a id="tactic-linkedin-inbox-watch"></a>`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 | <a id="tactic-reply-matrix"></a>`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 | <a id="tactic-linkedin-readout"></a>`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 | <a id="trigger-announced-moment"></a>`announced-moment` | Announced moment: `event` on `Person.announced`, window 21d. |
| Trigger | <a id="trigger-problem-post"></a>`problem-post` | Problem post: `event` on `Person.posted`, window 7d. |
| Trigger | <a id="trigger-rival-post-commenter"></a>`rival-post-commenter` | Rival post commenter: `event` on `Person.commented`, window 7d. |
| Trigger | <a id="trigger-own-post-reactor"></a>`own-post-reactor` | Own post reactor: `event` on `Person.reacted`, window 7d. |
| Trigger | <a id="trigger-org-visit"></a>`org-visit` | Organization visit: `event` on `Event.captured`, window 3d. |
| Trigger | <a id="trigger-dormant-connection"></a>`dormant-connection` | Dormant connection: `state` on `Contact.imported`, window 30d. |
| Trigger | <a id="trigger-request-accepted"></a>`request-accepted` | Request accepted: `event` on `Person.connected`, window 3d. |
| Trigger | <a id="trigger-linkedin-reply"></a>`linkedin-reply` | LinkedIn reply: `event` on `Message.replied`, window 1h. |
| Sequence | <a id="sequence-linkedin-request"></a>`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 | <a id="sequence-linkedin-after-accept"></a>`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 | <a id="benchmark-agent-profile-accuracy"></a>`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 | <a id="benchmark-agent-profile-thin"></a>`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 | <a id="benchmark-agent-profile-wrong-person"></a>`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 | <a id="benchmark-agent-draft-review-time"></a>`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). |