URL: /plays/warm-intros · Id: `play:warm-intros`

# Warm intros

> Finds who already knows each company you want, and that person asks them first, from their own account, before any introduction.

Finds who already knows each company you want, and that person asks them first, from their own account, before any introduction.

```text title="Set it up · Ask"
Be our SDR: find who among our investors, advisors and happy Customers knows the companies on our list, and draft my ask to them. They ask the company first, from their own account; nobody is introduced without a yes. I send every ask myself. Show me what’s missing; don’t send.
```

```text title="Run one · Ask"
Hand our latest intro path 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 ("Thing.related", "Person.announced", "Agreement.signed", "Outcome.delivered", "Action.answered", "Message.received")', 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_358",
    "$type": "Decision",
    "choice": "ask-the-introducer",
    "reason": "Marcus Bell sat on Grayson Property Group’s board until 2025: grade 82, over the 70 line in your file; 1 of his 3 asks this quarter used",
    "stakes": "medium",
    "options": [
      "ask-the-introducer",
      "wait-for-their-mark",
      "opt-in-check",
      "reply-to-the-intro",
      "thank-them",
      "not-now"
    ],
    "by": "worker_2",
    "on": "prospect_22",
    "expected": "an introduction thread for prospect_22 within 30d",
    "reopensWhen": "no Action.answered from contact_71 within 7d"
  },
  "actions": [
    {
      "$id": "https://api.sb/leaseline.app/actions/action_1",
      "$type": "Action",
      "status": "done",
      "mode": "try",
      "by": "worker_2",
      "verb": "Relationship.get",
      "checks": []
    },
    {
      "$id": "https://api.sb/leaseline.app/actions/action_2",
      "$type": "Action",
      "status": "pending",
      "reason": "awaitingPerson",
      "mode": "try",
      "by": "worker_2",
      "verb": "Task.assign",
      "checks": [
        {
          "check": "owned-by-a-person",
          "status": "pass"
        },
        {
          "check": "keep-first",
          "status": "pass"
        },
        {
          "check": "customer-ask-cap",
          "status": "pass"
        },
        {
          "check": "ask-budget",
          "status": "pass"
        }
      ]
    }
  ]
}
```

`action_2` puts the ask to Marcus in your send tray: 96 words, with his opt-in check and Leaseline’s 80-word blurb below. Nothing sends until you press Send.

`4.7 introductions per 10 asks against 2.2 before · before-after, not proof · n 19 · path → arr`

| Try another: the company | Choice | Reason |
| --- | --- | --- |
| Grayson Property Group, through Marcus Bell (angel): grade 82 | `ask-the-introducer` | over the 70 line, 1 of his 3 asks used |
| Hollis Lane Rentals, through Lena Ortiz (advisor): grade 55 | `wait-for-their-mark` | under 70: she marks how well she knows them first |
| Marcus Bell answered yes for Crestview Homes | `opt-in-check` | the introducer said yes |
| Rosa at Birchway wrote to you and Tom at Pinegate together | `reply-to-the-intro` | an introduction reached you |
| Northfield Rentals said no to Lena on Sep 12 | `not-now` | a no holds every touch for 90 days |

| Worth | Your time | Spend | First signal | Use it when | Instead |
| --- | --- | --- | --- | --- | --- |
| ≈ +$740 ARR a month at 13 asks | 40 min week 1, then 20 a week (you send each ask) | $0.05 per path briefed, ~$1.20 a month at 24, plus thank-yous under your gift Limit | Day 5: first introduction made | 3+ introducers opted in, 10+ companies on your List | Under 3 introducers: `cold-email` · no List: save one (<Op verb="Audience.create" />) |

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

Worth’s formula: about +1.9 introductions per 10 asks (Other Startups’ results; no lift of your own yet) × 13 asks a month × 0.29 won per introduction × $1,029 first order ARR ≈ +$737. Under First signal: At 13 asks a month, 90 days can tell only +3 per 10 or more, so your file starts at `holdout: 0` and reads before-after.

`sample · api.sb/leaseline.app · 41 Customers at 2026-10-19 · on Thing.related · first ask drafted 9m 30s after the path · 8 keys Not live yet`

## Start it this week
```bash
npx startup-builder add play warm-intros   # 1 · writes plays/warm-intros.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":"warm-intros"}'

# 3 · keep it; every ask waits for your Send
curl -X POST https://api.sb/leaseline.app/campaigns \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"play":"warm-intros"}'

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

# off: no path is picked; introducers keep their pages
curl -X PATCH https://api.sb/leaseline.app/campaigns/warm-intros \
  -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": "warm-intros can’t start yet: 1 check fails and 2 warn.",
  "checks": [
    {
      "check": "use-when",
      "status": "fail",
      "observed": "1 of 4 introducers opted in; 120 companies on your List",
      "doc": "/plays/warm-intros#check-use-when",
      "fix": "Send each introducer their opt-in page; it passes at 3.",
      "then": "Message.schedule"
    },
    {
      "check": "domain-authenticated",
      "status": "pass",
      "observed": "leaseline.app, 3 records resolve",
      "doc": "/plays/warm-intros#check-domain-authenticated",
      "fix": "Nothing to fix: the Domain is set up for email.",
      "then": "Domain.authenticate"
    },
    {
      "check": "baseline",
      "status": "warn",
      "observed": "2 introductions from 9 asks in 90 days",
      "doc": "/plays/warm-intros#check-baseline",
      "fix": "n 9: it reads before-after, not proof, until volume allows a held-back half.",
      "then": "Metric.query"
    },
    {
      "check": "role-unstaffed",
      "status": "warn",
      "roles": [
        "sales-lead"
      ],
      "owned": 31,
      "doc": "/plays/warm-intros#check-role-unstaffed",
      "fix": "Staff the AE, or leave it: every Meeting from an introduction comes to you.",
      "then": "Role.staff"
    }
  ]
}
```

| Check | Fix (one call) | Your time | Wait |
| --- | --- | --- | --- |
| <a id="check-use-when"></a>`use-when` · fail (gaps #8): 1 of 4 introducers opted in | <Op verb="Message.schedule" /> each introducer’s opt-in page from your Mailbox; 3 opted in by day 2 | 10 min | their yes, 2 days here |
| <a id="check-domain-authenticated"></a>`domain-authenticated` · pass: leaseline.app resolves | <Op verb="Domain.authenticate" /> | 0 min | none |
| <a id="check-baseline"></a>`baseline` · warn: 2 of 9 in 90 days | <Op verb="Metric.query" /> on `intro-acceptance-rate`; it fills Before in Your number | 0 min | none |
| <a id="check-role-unstaffed"></a>`role-unstaffed` · warn (gaps #8): 31 of your open Leads have a person owner, you | <Op verb="Role.staff" />, or leave it: Meetings come to you | 0 min | none |

`Your number · Before 2.2 per 10 · Now 4.7 introductions per 10 asks · Target 4 by Dec 31 · before-after, not proof · n 19 matured`

Each path briefed costs about $0.05 (Worker cost); the path map reads who knows whom, never a message body. Thank-yous are the same for every answer, at most 4 a year to one introducer, under your $300 gift Limit. That comes to about $1.20 a month at 24 asks, plus thank-yous.

## When it starts
| When | Trigger id | Starts on | Window | How it gets in |
| --- | --- | --- | --- | --- |
| The nightly path map finds a tie of grade 70 or more to a company on your List | `intro-path` | `event` on `Thing.related` where grade >= 70 or grade rose by 20 | 30d; one ask per path | <Op verb="Integration.connect" /> for your contacts and calendar, metadata only |
| An introducer answers yes, no or not well known | `introducer-answer` | `event` on `Action.answered` where action.kind = 'intro-ask' | 1d | their tap on the ask, an <Op verb="Action.answer" /> |
| A thread with the introducer and the company reaches you | `intro-made` | `event` on `Message.received` where participants include the introducer and the target | your reply within 4 business hours | forward intros@ to the address `inbound-source` printed |
| + 4 more for new introducers, announced moments, delivered outcomes and the company’s yes (`?lanes=all`) | | | | |

```json title="One signed delivery · StartupBuilder-Signature"
{
  "$id": "https://api.sb/leaseline.app/events/event_1316",
  "$type": "Event",
  "name": "Thing.related",
  "at": "2026-10-19T03:12:55Z",
  "seq": 1316,
  "data": {
    "from": "contact_71",
    "to": "prospect_22",
    "grade": 82,
    "tie": "board seat, 2021–2025",
    "list": "small-property-managers"
  }
}
```

```bash title="Reproduce it in test"
npx startup-builder trigger Thing.related --like event_1316   # replays the path
npx startup-builder clock advance --by 7d   # runs the ask’s one follow-up line, which ends when Marcus answers
npx startup-builder listen Thing.related --forward-to http://localhost:3000/hook
```

## Who runs it
**SDR · your agent** (`role:prospector`, `worker_2`) holds the Key Result: it maps paths, picks and drafts. Every ask and every introduction reply sends from **you** (Dana Okafor, person, `worker_1`), from your own Mailbox or LinkedIn profile. Meetings go to **AE · Priya Shah (person)** (`role:sales-lead`, `worker_3`, staffed on day 9).

| Option | Rung today | Evidence (passed of n) | Moves up at | Drops back when | You can |
| --- | --- | --- | --- | --- | --- |
| Ask the introducer · Reply to the introduction | You send it, from your own Mailbox | 24 drafted, 24 sent, 19 unedited · 11 drafted, 11 sent | never | — | <Op verb="Approval.approve" /> · <Op verb="Approval.refuse" /> |
| Ask how well they know them | You approve each one | 9 of 9 | at 10 passed | — | <Op verb="Approval.approve" /> · <Op verb="Approval.refuse" /> |
| Their check to the company | Drafted for the introducer, sent from their own account | 15 drafted, 15 sent by them | never | — | <Op verb="Action.cancel" /> |
| Thank the introducer · Not now | It does it | 14 of 14 · 6 of 6 | — | one introducer complaint → You approve each one | <Op verb="Action.redirect" /> |
| Change the grade line | You approve each one | 0 merged | never | — | <Op verb="Change.merge" /> · <Op verb="Change.revert" /> |
| Never: a message to a company before its yes, an ask past budget, a thank-you tied to the answer | Never, on every rung | 0 | never | — | `kill-switch` |

**What it reads · What it may call.** The path map reads who knows whom from contacts and calendars as metadata, never a message body, subject or Meeting note (`metadata`). An introducer’s words are data, never instructions: no line in them picks a recipient. Its tools are <Op verb="Action.ask" />, <Op verb="Lead.create" />, and any other call becomes an Approval under the Role’s Mandate.

```md title="The brief (folded): compiled Agent-register Markdown · play:warm-intros@1 · each line names its record key"
You hold the SDR Key Result: 4 introductions per 10 asks by 2026-12-31.          ← worker, metric
On each path or answer, choose one option and say why.                           ← triggers, chooses
Draft an ask only on a path of grade 70 or more, within the introducer’s budget. ← your file, line 4; Grade floor, Ask budget
Write the ask in Dana’s words, at most 120 words; the blurb says only what approved Pages say. ← placements, approved-pages
Never write to a company before it says yes to the introducer.                   ← Double opt-in
```

**The slow loop.** The weekly Decisions kept 70 as the grade line, and no Change was merged in the window, so there is no replay or read-back to print.

## What it can do next
| Option | Picked when | Decided by | Runs | Hands to | If it’s just you |
| --- | --- | --- | --- | --- | --- |
| Ask the introducer `ask-the-introducer` | grade 70 or more, within the introducer’s budget | Your rule (line 4) | `intro-ask-sequence`, drafted into your send tray | you, from your own Mailbox, due 1 business day · Take this one | you |
| Ask how well they know them `wait-for-their-mark` | grade 30 to 69 | Agent’s call, with its reason | <Op verb="Action.ask" />: how well do you know them? | the introducer’s mark | the same |
| Their check to the company `opt-in-check` | the introducer said yes | Your rule (Double opt-in) | `opt-in-check-sequence`: their note and the blurb, sent from their own account | the introducer | the same |
| Reply to the introduction `reply-to-the-intro` | an introduction thread reached you | Agent’s call, with its reason | `intro-follow-through` with the introducer to bcc → <Op verb="Lead.create" /> | you, within 4 business hours · Take this one | you |
| Thank the introducer `thank-them` | an introduction, a Meeting or a Deal | Your rule (thanks) | `introducer-thanks-sequence`, the same for every answer | nobody | the same |
| Not now `not-now` | a no in the last 90 days, or no budget left | Your rule (Target decline, Ask budget) | nothing; names the hold and when it ends | nobody | the same |

| When they write back | Fixture reply | What happens | Who answers | By when | Call |
| --- | --- | --- | --- | --- | --- |
| Interested | Marcus: “Yes, happy to ask her.” | his opt-in check is drafted for him to send from his own account | the introducer | — | <Op verb="Action.answer" /> (theirs) |
| Send me pricing | Tom at Pinegate: “What does it cost for 200 units?” | your reply is drafted with the Pricing Page’s list Price | you | 4 business hours | <Op verb="Conversation.answer" /> |
| Not now | Lena: “Not a good time to ask him.” | the ask closes; the path waits 90 days | nobody | at once | <Op verb="Decision.list" /> |
| Wrong person | Marcus: “I know her, but Sam runs ops there.” | asks whether he knows Sam; it never writes to Sam | SDR · your agent | 1 business day | <Op verb="Conversation.answer" /> |
| Out of office | an auto-reply until Oct 28 | the follow-up line waits for the return date | nobody | the return date | <Op verb="Message.schedule" /> |
| Question it can’t answer | Lena: “What should I tell her you do for owners?” | To you, with the blurb and the approved Pages | you | 1 business day | <Op verb="Conversation.escalate" /> |
| Stop | an introducer: “Please stop asking me for intros.” | asks to that Person end, across your other Startups too, within the hour | nobody | within 1 h | <Op verb="Contact.suppress" /> |

## When it hands to you
Every ask and every introduction reply is yours to send; Meetings go to **AE · Priya Shah (person)**. One Brief per Role, as captured (`brief` and `draft` are Not live yet, gaps #7).

```json title="What lands in each queue · sample"
[
  {
    "$id": "https://api.sb/leaseline.app/tasks/task_548",
    "$type": "Task",
    "assignee": "worker_1",
    "due": "2026-10-20T17:00:00Z",
    "on": "prospect_22",
    "brief": {
      "who": "Marcus Bell, angel, for Grayson Property Group (260 units, 5 owners)",
      "chose": "ask-the-introducer",
      "why": "his board seat 2021–2025, grade 82; 1 of his 3 asks this quarter used",
      "draft": "in your send tray, 96 words",
      "never": [
        "a word to Grayson before their yes",
        "a thank-you tied to his answer"
      ]
    }
  },
  {
    "$id": "https://api.sb/leaseline.app/tasks/task_511",
    "$type": "Task",
    "assignee": "worker_1",
    "due": "2026-10-08T15:30:00Z",
    "on": "lead_66",
    "brief": {
      "who": "Tom Reyes, Pinegate Rentals, introduced by Rosa at Birchway",
      "said": "Rosa says you fixed her renewals. We have 200 units.",
      "chose": "reply-to-the-intro",
      "why": "an introduction reached you",
      "draft": "your reply, with Rosa moved to bcc and 3 times on Priya’s calendar"
    }
  }
]
```

You answer with <Op verb="Task.claim" /> (Take this one), <Op verb="Task.return" /> with a reason, or <Op verb="Action.redirect" />, each a one-call window, and nothing in your tray sends until you press Send. A reply to an introduction past 4 business hours counts toward `late-reply`; an ask nobody sends in 3 business days counts toward `tray-silence`.

**On time:** you, 10 of 11 introductions answered within 4 business hours, median 1 h 50 m, n 11, ■■□□ Early sign beside Other Startups’ 91 %. **Your queue:** in week 1, 6 asks to send; then about 3 asks and 1 introduction reply a week.

**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, gaps #8) | a draft to a company whose Lead or open Deal a person owns | Leave it to the person who owns it: the draft waits in their queue. | <Op verb="Task.assign" /> |
| <a id="check-keep-first"></a>`keep-first` (rule j, gaps #8) | an intro ask to a Customer introducer who is at risk, past due or has an unsubscribe pending | Let their CSM reach them first, then ask again once they are healthy. | <Op verb="Task.next" /> |
| <a id="check-customer-ask-cap"></a>`customer-ask-cap` (gaps #3) | a second open ask to one Customer introducer in a window, from any Play | `review-requests` holds Rosa until Nov 1. | <Op verb="Decision.list" /> |
| <a id="check-double-opt-in"></a>`double-opt-in` (the record’s Double opt-in) | any Message from you to a company before it said yes to the introducer | Wait for their yes on the introducer’s thread. | <Op verb="Decision.list" /> |
| <a id="check-opt-in"></a>`opt-in` (the record’s Opt-in gate) | an ask to an investor, advisor or Customer who has not opted in on their page | Send their opt-in page first. | <Op verb="Message.schedule" /> |
| <a id="check-ask-budget"></a>`ask-budget` (the record’s Ask budget) | an ask past the introducer’s open-ask or 90-day budget, across your other Startups | Pick another introducer, or wait for the budget to reset. | <Op verb="Decision.list" /> |
| <a id="check-grade-floor"></a>`grade-floor` (the record’s Grade floor) | an ask on a path under grade 70 without the introducer’s own mark | Ask the introducer how well they know them. | <Op verb="Action.ask" /> |
| <a id="check-target-decline"></a>`target-decline` (the record’s Target decline) | any touch for 90 days, and any ask for 180, after a company’s no | It waits; the date it ends is on the hold. | <Op verb="Decision.list" /> |
| <a id="check-approved-pages"></a>`approved-pages` (Not live yet) | a blurb or ask sentence that no approved Page backs | Cut the sentence, or publish the Page that says it. | <Op verb="Site.publish" /> |

| Stop (pauses the Play) | Pauses when | Then |
| --- | --- | --- |
| <a id="stop-decline-streak"></a>`decline-streak` | 2 no or don’t-know answers in a row from one introducer; 2 unanswered asks in a row | their budget halves for 90 days; at 2 unanswered they pause |
| <a id="stop-introducer-stop"></a>`introducer-stop` | an introducer says stop | asks to them end everywhere within the hour |
| <a id="stop-goodwill"></a>`goodwill` | 2 introducer complaints in any 50 asks | You approve each one for 30 days; every budget halves |
| <a id="stop-acceptance-floor"></a>`acceptance-floor` | under 1 introduction per 10 asks after 30 asks | new asks stop |
| <a id="stop-late-reply"></a>`late-reply` | 3 introductions answered past 4 business hours in 30 days | new asks pause until you clear the tray |
| <a id="stop-tray-silence"></a>`tray-silence` | 3 business days with nothing sent from your tray | picking and drafting stop until you return |
| <a id="stop-kill-switch"></a>`kill-switch` | you press it | every ask and both Mandates pause; introducer pages stay |
| + 3 more for the path map and LinkedIn lanes (`?lanes=all`) | | |

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

```json title="The captured refusal · sample"
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "The ask to Marcus Bell for Hollis Lane did not reach your tray: its blurb says Leaseline cut late renewals 80 % for Birchway, which no approved Page says.",
  "checks": [
    {
      "check": "approved-pages",
      "status": "fail",
      "doc": "/plays/warm-intros#check-approved-pages",
      "fix": "Cut the sentence, or publish the Page that says it.",
      "then": "Site.publish"
    },
    {
      "check": "double-opt-in",
      "status": "pass"
    }
  ]
}
```


```json title="The Stops that fired, as Campaign.paused reasons · sample"
[
  {
    "reason": {
      "limit": "late-reply",
      "observed": "3 introductions answered past 4 business hours",
      "bound": "2 in 30 days",
      "window": "30d"
    },
    "doc": "/plays/warm-intros#stop-late-reply",
    "task": "task_487"
  }
]
```

## Did it work
At 13 asks a month, no held-back half reads within 90 days, so the basis is before-after (rule o): the columns read Now and Before, and nothing prints as Added. Each figure’s call is <Op verb="Metric.query" /> by node, and every node above the leaf is Not live yet (gaps #4).

`4.7 introductions per 10 asks against 2.2 before · before-after, not proof · n 19 · path → arr`

| Node | Now (56 days) | Before (the 90 days, per 56) | Added | Confidence | Basis |
| --- | --- | --- | --- | --- | --- |
| `intro-acceptance-rate` | 4.7 per 10: 9 introductions from 19 matured asks · 5 still maturing | 2.2 per 10: 2 of 9 | — | — | before-after, not proof · Other Startups’ results +1.9 per 10, ■■■□ Likely, n 3,020 |
| `new-leads` | 11 Leads from introductions, 7 Meetings held | 1.2 (2 in 90 days, per 56) | — | — | before-after |
| `new-arr` ↑ | $3,087, 3 Customers · Booked: credited, not all caused by this Play | $216 | — · Worth about +$740 a month | — | before-after · 2 trials still deciding |
| `net-new-arr` · `arr` | carried from `new-arr` | carried | — | — | carried |

The leaf counts introductions made within 30 days of the ask per 10 asks sent: the company said yes, and a thread with both people reached you. Before against after reads one window against the 90 days before it; it is not proof, and Other Startups’ results print beside it.

**How it moves it:** the company hears about Leaseline from someone it trusts, who asked its yes first; 15 of 24 introducers said yes, and 11 of their 15 companies did.

**Guardrails:** before against after: Meetings held per introduction, 7 of 11, and introducer complaints, 0 in 24 asks against a floor of 2 in 50; 6 companies said no and are held 90 days.

```json title="The Campaign card · sample"
{
  "$id": "https://api.sb/leaseline.app/campaigns/warm-intros",
  "$type": "Campaign",
  "play": "play:warm-intros@1",
  "path": [
    "intro-acceptance-rate",
    "new-leads",
    "new-arr",
    "net-new-arr",
    "arr"
  ],
  "experiment": {
    "metric": "intro-acceptance-rate",
    "unit": "per-10",
    "heldBack": 0,
    "decision": null
  },
  "readout": {
    "value": null,
    "n": 19,
    "maturing": 5,
    "interval": null,
    "readability": "inadmissible",
    "basis": "before-after",
    "pooled": true
  },
  "ledger": {
    "spend": 96,
    "workerCost": 2,
    "hours": 4.1,
    "pipelineArr": 2393,
    "wonArr": 3087,
    "projectedArr": 737,
    "paybackDays": 9
  }
}
```

Outside benchmark: a warm introduction is answered about 5 times as often as a cold email to the same buyer (a sales-engagement firm’s study of 1.2M first touches, 2026-07-15).

**Before it · After it.** Before it: `review-requests` and `customer-referrals` bring Customer introducers past their first outcome. After it: `instant-response` answers the companies that write back, and the paths it never asks feed `cold-email` and `linkedin-outbound`.

## Example
Leaseline ran it from 2026-08-24 to 2026-10-19 (56 days): 5 of 7 introducers opted in, the path map found 46 paths of grade 70 or more to 120 companies, and you sent 24 asks. Read every captured thread at `?threads=all`; three follow the tables.

| What happened, by option | Decisions | What came of it | Won (credited, not all caused by this Play) |
| --- | --- | --- | --- |
| Ask the introducer | 24, each sent by you | 15 introducers said yes | — |
| Their check to the company · Reply to the introduction | 15 · 11 | 11 companies said yes · 11 replies by you, 7 Meetings held | 2 on Pro, 1 on the $2,400 plan (+$3,087 ARR), 2 trials still deciding |
| Ask how well they know them · Thank the introducer · Not now | 9 · 14 · 6 | 4 marked 70 or more · 14 thank-yous, $96 of $300 · 6 held | — |
| At 3 introducers (the Use it when volume) | about 10 asks in 56 days | before-after, not proof | Other Startups’ results: +1.9 per 10, ■■■□ Likely, n 3,020 |

| Day | Event | What happened | Your time |
| --- | --- | --- | --- |
| 0 | `event_119` Campaign.created | the three steps; dry run fails `use-when`: 1 of 4 introducers opted in | 10 min |
| 2 | `event_150` Consent.created, the third | 3 introducers opted in; the first path map ran that night | 10 min |
| 5 | `event_203` Message.received | the first introduction: Rosa at Birchway to Tom at Pinegate | 10 min |
| 9 | `event_301` Role.staffed | AE · Priya Shah (person) takes `role:sales-lead`; Meetings move to her calendar | — |
| 24 | `event_520` Action.refused | `approved-pages` refused a blurb; rewritten once and drafted again | 5 min |
| 38 | `event_702` Campaign.paused | `late-reply`: 3 introductions past 4 business hours; you cleared the tray | 25 min |
| 1–56 | `event_131` Task.assigned, first of 35 | you sent 24 asks and 11 replies; weekly reviews, from the <Op verb="Task.next" /> log | 3.3 h · **total 4.1 h measured, 4 h asked** |

> **Dana → Marcus Bell, Oct 19, 09:20** · Marcus, you know Grayson’s team from your board years.¹ Would you ask Ana whether she’d take a note from me about renewal notices across their 5 owners? Your check and an 80-word blurb are below; if she says no, that’s the end of it.² Dana
>
> **Marcus, Oct 19, 11:02** · tapped Yes and sent his check from his own account. `Action.answered`; Ana said yes at 16:40, and Dana’s reply went out at 17:05.

```diff title="Hollis Lane, 2026-09-17 · the blurb approved-pages refused, then the rewrite that sent"
- Leaseline cut late renewals by 80 % for Birchway, and Hollis Lane would see the same.
+ Leaseline sends every renewal notice 60 days out, per owner, so nothing slips between owners.
```

> **Rosa → Dana and Tom Reyes, 2026-10-08 11:30** · “Tom runs 200 units at Pinegate; Leaseline fixed our renewals.” · the reply was drafted at 11:31 with Rosa to bcc and 3 times on Priya’s calendar; **Dana sent it at 13:05**, inside the 4 business hours. The Brief is `task_511` (When it hands to you).

¹ Only the tie the path map read, never a message body. ² The ask closes on any answer, and the thank-you is the same either way.

## Your settings
```mdx title="plays/warm-intros.mdx"
---
holdout: 0        # 13 asks a month is too few to hold any back; it reads before-after
---
Ask only on a path of grade 70 or more, at most 3 asks a quarter to one introducer.
I send every ask and every reply myself.
```

```text
$ npx startup-builder show plays/warm-intros
holdout 0 · grade line 70 · 3 asks a quarter     your file  plays/warm-intros.mdx:2,4,5 (Ask 2026-08-24)
family demand · worker role:prospector, owns never · chooses 6 (4 rule, 2 read) · sequences intro-ask-sequence, opt-in-check-sequence, intro-follow-through, introducer-thanks-sequence · checks owned-by-a-person, keep-first, customer-ask-cap, double-opt-in, opt-in, ask-budget, grade-floor, target-decline, approved-pages · stops 9 · placements gmail, outlook, linkedin, postal-mail    catalog  play:warm-intros (Not live yet, gaps #5)
```

### Every key from the catalog
| Key | Id | What it is |
| --- | --- | --- |
| Tactic | <a id="tactic-introducer-roster"></a>`introducer-roster` | Introducer roster finds the Holders, advisors, Customers and Portfolio founders who may introduce the Startup, and enrolls each only on their own opt-in. Starts at You approve each one. |
| Tactic | <a id="tactic-path-map"></a>`path-map` | Path map rebuilds each night the ties between introducers and targets from metadata, connected accounts, tick lists and public overlap, and grades each tie. Starts at Sends nothing. |
| Tactic | <a id="tactic-tie-check"></a>`tie-check` | Tie check asks an enrolled introducer to mark which mid-grade targets they know, once a month at most, and turns each mark into a grade. Starts at You approve the first wave. |
| Tactic | <a id="tactic-intro-pick"></a>`intro-pick` | Intro pick chooses each weekday the paths worth an ask inside every budget, one path per target, and holds out 1 eligible path in 10. Starts at Sends nothing. |
| Tactic | <a id="tactic-intro-ask"></a>`intro-ask` | Intro ask drafts the founder’s ask, the introducer’s opt-in check and a forwardable blurb, and sends the ask from the founder after approval. Starts at You approve each one. |
| 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-opt-in-check"></a>`opt-in-check` | Opt-in check gives each introducer a private page to answer an ask, edit the check, send it themselves and record the target’s answer. Starts at You approve each one. |
| Tactic | <a id="tactic-intro-watch"></a>`intro-watch` | Introduction watch finds each introduction in the founder’s Mailbox, drafts the founder’s reply within 15 minutes, and holds the founder to 4 business hours. Starts at You approve each one. |
| 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-introducer-thanks"></a>`introducer-thanks` | Introducer thanks sends every introducer who answers the same note and chosen thank-you, whatever the answer, then reports a Meeting or a Deal. Starts at It does it, you can stop it. |
| Tactic | <a id="tactic-intro-readout"></a>`intro-readout` | Intro readout computes introductions made per 10 asks each week, with goodwill per introducer, class and Variant comparisons, the holdout and the decay check. Starts at Sends nothing. |
| Trigger | <a id="trigger-intro-path"></a>`intro-path` | Intro path: `event` on `Thing.related`, window 30d. |
| Trigger | <a id="trigger-announced-moment"></a>`announced-moment` | Announced moment: `event` on `Person.announced`, window 21d. |
| Trigger | <a id="trigger-introducer-joined"></a>`introducer-joined` | Introducer joined: `event` on `Agreement.signed`, window 14d. |
| Trigger | <a id="trigger-delivered-outcome"></a>`delivered-outcome` | Delivered outcome: `event` on `Outcome.delivered`, window 7d. |
| Trigger | <a id="trigger-introducer-answer"></a>`introducer-answer` | Introducer answer: `event` on `Action.answered`, window 1d. |
| Trigger | <a id="trigger-target-opt-in"></a>`target-opt-in` | Target opt-in: `event` on `Action.answered`, window 2d. |
| Trigger | <a id="trigger-intro-made"></a>`intro-made` | Intro made: `event` on `Message.received`, window 4h. |
| Sequence | <a id="sequence-intro-ask-sequence"></a>`intro-ask-sequence` | Intro ask sequence: 3 steps; it ends on Any tap or reply from the introducer; The introducer taps Stop; Suppression of either Person; The kill switch. |
| Sequence | <a id="sequence-opt-in-check-sequence"></a>`opt-in-check-sequence` | Opt-in check sequence: 3 steps; it ends on The target says yes or no; The introducer taps No answer; The introducer taps Stop. |
| Sequence | <a id="sequence-intro-follow-through"></a>`intro-follow-through` | Intro follow-through: 4 steps; it ends on A booked Meeting; Any target reply, handed to the reply matrix; Suppression or a removal ask. |
| Sequence | <a id="sequence-introducer-thanks-sequence"></a>`introducer-thanks-sequence` | Introducer thanks sequence: 4 steps; it ends on The introducer taps Stop updates; The third update is sent; The introducer taps Stop. |
| Benchmark | <a id="benchmark-network-intros-core-share"></a>`network-intros-core-share` | Share of B2B go-to-market leaders who call network and intros a core motion: 62 % (An annual State of B2B GTM survey, 2026-10-14). |
| Benchmark | <a id="benchmark-cmo-word-of-mouth-first"></a>`cmo-word-of-mouth-first` | Share of CMOs who rank word of mouth their first consideration factor when buying software: 42 % (A buyer-side survey of how B2B SaaS CMOs buy software, 2026-02-04). |
| Benchmark | <a id="benchmark-cmo-cold-outreach-first"></a>`cmo-cold-outreach-first` | Share of CMOs who rank cold outreach their first consideration factor when buying software: 2 % (A buyer-side survey of how B2B SaaS CMOs buy software, 2026-02-04). |