URL: /plays/product-signals · Id: `play:product-signals`

# Product signals

> Finds teams getting value on a free account and writes to the right person, citing only their own usage, after you approve the first send.

Finds teams getting value on a free account and writes to the right person, citing only their own usage, after you approve the first send.

```text title="Set it up · Ask"
Be our SDR: when 3 or more people at one free account are using Leaseline, write to them about Team with their own counts only, never a name. A director who signs up gets a note from me. Keep half with no touch, as today. Show me what’s missing; don’t send.
```

```text title="Run one · Ask"
Hand our latest free account that crossed a usage line 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 ("Event.captured", "Entitlement.checked", "Customer.activated", "Page.viewed", "Customer.stalled", "Prospect.engaged")', 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_402",
    "$type": "Decision",
    "choice": "team-plan-note",
    "reason": "4 people at Brightwater Property Group drafted 31 notices this week on a free account; Team covers 5 members, and the count is 3 or more",
    "stakes": "low",
    "options": [
      "team-plan-note",
      "shared-limit-note",
      "senior-welcome",
      "reach-who-pays",
      "rescue",
      "open-a-deal",
      "to-the-csm"
    ],
    "by": "worker_2",
    "on": "lead_63",
    "expected": "a Deal or a Team trial opened by day 21",
    "reopensWhen": "no Deal opened and no trial started for lead_63 within 21d"
  },
  "actions": [
    {
      "$id": "https://api.sb/leaseline.app/actions/action_1",
      "$type": "Action",
      "status": "done",
      "mode": "try",
      "by": "worker_2",
      "verb": "Lead.qualify",
      "checks": []
    },
    {
      "$id": "https://api.sb/leaseline.app/actions/action_2",
      "$type": "Action",
      "status": "done",
      "mode": "try",
      "by": "worker_2",
      "verb": "Flag.evaluate",
      "checks": []
    },
    {
      "$id": "https://api.sb/leaseline.app/actions/action_3",
      "$type": "Action",
      "status": "pending",
      "reason": "awaitingApproval",
      "mode": "try",
      "by": "worker_2",
      "verb": "Message.schedule",
      "checks": [
        {
          "check": "owned-by-a-person",
          "status": "pass"
        },
        {
          "check": "customer-collision",
          "status": "pass"
        },
        {
          "check": "mailbox-warm",
          "status": "pass"
        },
        {
          "check": "count-floor",
          "status": "pass"
        }
      ]
    }
  ]
}
```

`action_2` puts Brightwater in the agent half. `action_3` is the team plan note to `contact_14`, the member who signed up first, waiting for your approval because it is this account’s first send.

`+18.5 Deals per 100 · ■■□□ Early sign · n 108 · path → arr`

| Try another: the free account | Choice | Reason |
| --- | --- | --- |
| Brightwater Property Group: 4 active people this week on a free account | `team-plan-note` | 3 or more active people |
| Redfern Homes: 2 people refused at the 5-unit Limit on Tuesday | `shared-limit-note` | two people met the same Limit |
| Marlow Estates: the Director of Operations activated yesterday | `senior-welcome` | a director, by their own stated role, at a strong fit |
| Harbor Row: a paying Customer read the cancel page twice | `to-the-csm` | a paying Customer never gets a sales touch |

| Worth | Your time | Spend | First signal | Use it when | Instead |
| --- | --- | --- | --- | --- | --- |
| ≈ +$2,400 ARR a month at 69 | 45 min week 1, then 15 a week | $0.05 per fired account, ~$3.50 a month at 69 | Day 2: the first Meeting booked | 30+ free or trial accounts a month, a team plan | Under 30: `signup-interviews` · no team plan: publish one (<Op verb="Offer.publish" />) |

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

Worth’s formula: about +18.5 Deals per 100 fired accounts × 69 a month × 0.35 Deals to paid (your last 90 days) × $547 first order ARR ≈ +$2,444. Under First signal: About 8 weeks to tell a +12 lift at 69 a month (5 to read, 3 to mature); at 30 a month, 90 days can tell only +22 or more, so your half starts at 0.

`sample · api.sb/leaseline.app · 41 Customers at 2026-10-19 · on Event.captured · first card 11 min after the crossing · 9 keys Not live yet`

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

# 3 · keep it, at You approve the first wave
curl -X POST https://api.sb/leaseline.app/campaigns \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"play":"product-signals"}'

# on
curl -X POST https://api.sb/leaseline.app/processes/outbound/enable \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"

# off: no touch sends; churn alerts still reach the CSM
curl -X POST https://api.sb/leaseline.app/processes/outbound/pause \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
```


```json title="The dry run’s 422 · 2026-08-24 · sample"
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "product-signals can’t start yet: 1 check fails and 1 warns.",
  "checks": [
    {
      "check": "use-when",
      "status": "pass",
      "observed": "99 free accounts in 30 days; Team at $79 for 5 members",
      "doc": "/plays/product-signals#check-use-when",
      "fix": "Keep 30 or more free accounts a month, or run signup-interviews.",
      "then": "Metric.query"
    },
    {
      "check": "baseline",
      "status": "pass",
      "observed": "13.0 Deals per 100 accounts that would have fired, n 140 in 90 days",
      "doc": "/plays/product-signals#check-baseline",
      "fix": "Read it once; it fills Before in Your number.",
      "then": "Metric.query"
    },
    {
      "check": "domain-authenticated",
      "status": "pass",
      "observed": "leaseline.app",
      "doc": "/plays/product-signals#check-domain-authenticated",
      "fix": "Add the 3 DNS records shown; it passes once they resolve.",
      "then": "Domain.authenticate"
    },
    {
      "check": "mailbox-warm",
      "status": "fail",
      "observed": "agent@leaseline.app, 3 days into warm-up",
      "doc": "/plays/product-signals#check-mailbox-warm",
      "fix": "Warm the agent Mailbox; it passes after 14 days at rising volume.",
      "then": "Mailbox.warm"
    },
    {
      "check": "role-unstaffed",
      "status": "warn",
      "roles": [
        "sales-lead",
        "success-manager"
      ],
      "owned": 3,
      "doc": "/plays/product-signals#check-role-unstaffed",
      "fix": "Staff the AE and CSM, or leave them: those routes 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: 99 free accounts a month, a team plan; 13.0 Deals per 100, n 140 (gaps #8) | <Op verb="Metric.query" /> on `pql-deal-rate`, which fills Before in Your number | 1 min | none |
| <a id="check-domain-authenticated"></a>`domain-authenticated` · pass | <Op verb="Domain.authenticate" /> | 0 min | none |
| <a id="check-mailbox-warm"></a>`mailbox-warm` · fail (gaps #1): 3 days into warm-up | <Op verb="Mailbox.warm" /> on agent@leaseline.app | 2 min | 14 days; in-product cards show meanwhile |
| <a id="check-role-unstaffed"></a>`role-unstaffed` · warn (gaps #8): 3 free accounts have a person owner, you | <Op verb="Role.staff" />, or leave it: AE and CSM routes come to you, and your 3 get drafts in your queue | 0 min | none |

`Your number · Before 13.0 · Now 31.5 Deals per 100 · Target 30 by Dec 31 · ■■□□ Early sign · n 108 matured`

Each usage card costs $0.01 to compute and each touch $0.04 from your Domain (Worker cost); first-party matching from your own sign-ins costs nothing. That comes to about $3.50 a month at 69 fired accounts and $1.50 at 30, under the $20 model spend Limit.

## When it starts
| When | Trigger id | Starts on | Window | How it gets in |
| --- | --- | --- | --- | --- |
| 3 or more people at one free account are active in 14 days | `team-cluster` | `threshold` on `Event.captured` where 3 or more active People at one free or trial Organization within 14 days | card within 1 h; touch within 1d | your app calls <Op verb="Event.capture" /> on each notice drafted |
| 2 or more people at one account are refused at the same Limit | `limit-pair` | `threshold` on `Entitlement.checked` where allowed = false for 2 or more People at one Organization on the same Limit | 7d | your app calls <Op verb="Entitlement.check" /> before each unit is added |
| Someone answers a touch with interest | `product-hand-raise` | `event` on `Prospect.engaged` where the answer is to a touch of this Play, read as interested | a Deal within 1 h | replies arrive in the agent Mailbox; <Op verb="Conversation.answer" /> reads them |
| + 4 more for senior signups, pricing views, stalled signups and paying Customers on pricing (`?lanes=all`) | | | | |

```json title="One signed delivery · StartupBuilder-Signature"
{
  "$id": "https://api.sb/leaseline.app/events/event_2211",
  "$type": "Event",
  "name": "Event.captured",
  "at": "2026-10-19T08:47:31Z",
  "seq": 2211,
  "data": {
    "lead": "lead_63",
    "organization": "organization_29",
    "event": "notice.drafted",
    "people": 4,
    "count": 31,
    "window": "7d",
    "offer": "leaseline-free"
  }
}
```

```bash title="Reproduce it in test"
npx startup-builder trigger Event.captured --like event_2211   # replays the delivery
npx startup-builder clock advance --by 3d   # runs the team plan note’s day-3 step, which ends on any reply
npx startup-builder listen Event.captured --forward-to http://localhost:3000/hook
```

## Who runs it
**SDR · your agent** (`role:prospector`, `worker_2`) holds the Key Result. It hands hand-raises and director signups to **AE · Priya Shah (person)** (`role:sales-lead`, `worker_3`, staffed on day 9), the who-pays note to **you** (Dana Okafor, person, `worker_1`) to send from your own Mailbox, and paying Customers on pricing to the CSM Role, which is unstaffed, so **you**. Your number links to Did it work.

| Option | Rung today | Evidence (passed of n) | Moves up at | Drops back when | You can |
| --- | --- | --- | --- | --- | --- |
| Team plan note · Shared limit note | It does it, you can stop it | 22 of 22 · 9 of 9 | met on day 16 (the first wave of 20) | any touch fails a Check, or `count-floor` fires → You approve the first wave | <Op verb="Action.cancel" /> inside the hold |
| Rescue a stalled signup · Open a Deal · To the CSM | It does it, you can stop it | 7 of 8 (1 redirected) · 14 of 14 · 5 of 5 | the same | the same | the same |
| Founder welcome · Reach who pays | You approve each one | 4 of 4 · 3 of 3 | never: they go out under your name | — | <Op verb="Approval.approve" /> · <Op verb="Approval.refuse" /> |
| Change a Trigger’s threshold | You approve each one | 1 merged of 1 | never | — | <Op verb="Change.merge" /> · <Op verb="Change.revert" /> |
| Never: a colleague’s name, a page view, a count under 3 | Refused, every time | 2 drafts refused | never | — | nothing; `count-floor` holds it |

**What it reads · What it may call.** It reads counts from your own product Events, matched to a company by sign-in domain only. A page view, a session length or a stalled state chooses who and when, and never appears in a Message. Its tools are <Op verb="Entitlement.check" />, <Op verb="Meeting.book" />, <Op verb="Task.assign" />, <Op verb="Conversation.answer" />, <Op verb="Lead.qualify" />, <Op verb="Deal.assign" />, and any other call becomes an Approval under the Role’s Mandate.

```md title="The brief (folded): compiled Agent-register Markdown · play:product-signals@1 · each line names its record key"
You hold the SDR Key Result: 30 Deals per 100 fired accounts by 2026-12-31.      ← worker, metric
On each Trigger, choose one option and say why; a paying Customer goes To the CSM. ← triggers, chooses
A count shown to anyone covers 3 or more people and names none of them.          ← copyRules 2, 5
State only numbers a live usage card line holds; never a page view.              ← copyRules 1, 3
Team note at 3 active people; founder note for a director (your file, lines 4–5). ← your file
Never write to a Lead or Deal a person owns: draft it into their queue.          ← worker.owns, owned-by-a-person
```

**The slow loop, as a Change.** `decision_311` (2026-09-21, weekly) read that reviewers rejected 13 of the last 30 `team-cluster` fires at 2 active people, so `change_23` raised the line to 3 (your file, line 4) and Dana merged it with <Op verb="Change.merge" />. **Replayed on past fires**, <Op verb="Change.review" /> put the agent half at 29.8 per 100; read back at +21d it is 31.5, n 54, ■■□□ Early sign (Example, day 49).

**Bring your own agent** (folded) starts at It does it, you can stop it when it matches or beats the seated agent on the eval suite, and at You approve the first wave when it does not; <Op verb="Role.staff" /> seats it.

```bash
npx startup-builder eval --scenario product-signals-120 --agent "claude -p" --seat prospector   # seated: 31.5 per 100, 0 Checks failed sent, $0.05 a fire · yours: 27.2, 1 count-floor refusal, $0.07 → You approve the first wave
```

## What it can do next
| Option | Picked when | Decided by | Runs | Hands to | If it’s just you |
| --- | --- | --- | --- | --- | --- |
| Team plan note `team-plan-note` | 3 or more active people at one free account | Your rule (line 4) | `team-plan-note`, in the product and from the agent Mailbox | the reader’s press, or Open a Deal on a reply | the same |
| Shared limit note `shared-limit-note` | 2 or more people refused at one Limit | Your rule (line 4) | <Op verb="Entitlement.check" /> → `shared-limit-note` | the same | the same |
| Founder welcome `senior-welcome` | a director or above activates at a strong fit | Agent’s call, with its reason | `senior-welcome` from your Mailbox → on reply <Op verb="Meeting.book" /> | <Op verb="Task.assign" /> → AE · Priya Shah, due 60 min · Take this one | you, within 60 min |
| Reach who pays `reach-who-pays` | an active team and no known Contact who pays | Agent’s call, with its reason | `buyer-note`, drafted for you → <Op verb="Task.assign" /> | you, to send from your own Mailbox · Take this one | you |
| Rescue a stalled signup `rescue` | a fit signup stopped short of first value | Agent’s call, with its reason | `dormant-rescue`: their own last failed step, no plan | nobody | the same |
| Open a Deal `open-a-deal` | someone answers a touch with interest | Your rule (the hand-raise) | <Op verb="Lead.qualify" /> → <Op verb="Deal.assign" /> | <Op verb="Task.assign" /> → AE · Priya Shah, due 60 min (`response-60m`), Brief attached · Take this one | you, within 60 min |
| To the CSM `to-the-csm` | a paying Customer reads pricing, billing or cancel | Your rule (the Customer guard) | <Op verb="Task.assign" /> with a health brief; no Message | CSM, so you · Take this one | you |

| When they write back | Fixture reply | What happens | Who answers | By when | Call |
| --- | --- | --- | --- | --- | --- |
| Interested | “Yes, set us up on Team.” | opens a Deal and books it; the Brief goes to the AE (When it hands to you) | AE · Priya Shah (person) | 60 min | <Op verb="Meeting.book" /> |
| Send me pricing | “What would Team cost for the four of us?” | Team’s live Price for the active count, never more seats | SDR · your agent | 5 min | <Op verb="Conversation.answer" /> |
| Not now | “We’re fine on free for now.” | the note stops; the account rests 30 days before any new fire | nobody | at once | <Op verb="Lead.nurture" /> |
| Wrong person | “Our owner signs off on software.” | asks them to forward the team-totals link; it never writes to an address from their text | SDR · your agent | 1 business day | <Op verb="Conversation.answer" /> |
| Out of office | an auto-reply until Oct 30 | the follow-up waits for the return date | nobody | the return date | <Op verb="Message.schedule" /> |
| Question it can’t answer | “Can Team sync with our accounting system?” | To a person, the question in the Brief | AE · Priya Shah | 60 min | <Op verb="Conversation.escalate" /> |
| Stop | “How do you know how many of us use it? Stop.” | every touch to that company stops for 90 days, and the reply goes to you (`privacy`) | you | at once | <Op verb="Contact.suppress" /> |

## When it hands to you
Hand-raises and director signups go to **AE · Priya Shah (person)**; the who-pays note and paying Customers on pricing come to **you**, and before day 9 every hand-off did. One Brief per Role, as captured (`brief` is Not live yet, gaps #7).

```json title="What lands in each queue · sample"
[
  {
    "$id": "https://api.sb/leaseline.app/tasks/task_509",
    "$type": "Task",
    "assignee": "worker_3",
    "due": "2026-10-19T10:58:00Z",
    "on": "lead_63",
    "brief": {
      "who": "Brightwater Property Group, free account",
      "said": "Yes, set us up on Team. We’re 4 now, maybe 6 by spring.",
      "chose": "open-a-deal",
      "why": "answered the team plan note with interest",
      "counted": "4 active people, 31 notices in 7 days",
      "never": [
        "a colleague’s name",
        "a page view",
        "a discount"
      ]
    }
  },
  {
    "$id": "https://api.sb/leaseline.app/tasks/task_477",
    "$type": "Task",
    "assignee": "worker_1",
    "due": "2026-10-06T17:00:00Z",
    "on": "lead_58",
    "brief": {
      "who": "Tidewater Rentals, free account",
      "chose": "reach-who-pays",
      "why": "5 active people, and none of them is the owner who pays",
      "draft": "a 3-line note from you, citing the team’s own totals"
    }
  },
  {
    "$id": "https://api.sb/leaseline.app/approvals/approval_61",
    "$type": "Approval",
    "verdict": null,
    "deadline": "2026-09-03T15:00:00Z",
    "draft": "Hi Sam, your team of 4 drafted 31 renewal notices on Leaseline this week…",
    "route": "team-plan-note",
    "checks": [
      {
        "check": "count-floor",
        "status": "pass"
      },
      {
        "check": "customer-collision",
        "status": "pass"
      }
    ]
  }
]
```

Each receiver answers with <Op verb="Task.claim" /> (Take this one), <Op verb="Task.return" /> with a reason, <Op verb="Action.redirect" />, <Op verb="Approval.approve" /> or <Op verb="Approval.refuse" />, each a one-call window. Once a person claims, the agent’s pending touches to that company cancel with `claimedByPerson` (Not live yet, gaps #7), and every reply goes to the claimer. A Task nobody claims by its due time goes up a Role through <Op verb="Task.escalate" /> (AE to you), and `person-promise` fires.

**On time:** AE · Priya Shah, 13 of 14 claimed within 60 min, median 21 min, ■■□□ Early sign, n 14; you, 8 of 8, median 3 h on who-pays notes, n 8. **Your queue:** in week 1, 20 approvals and 1 weekly review; after the promotion on day 16, 1 weekly review, about 2 hand-offs and 1 who-pays note 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 touch to a Contact whose Lead or open Deal a person 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-customer-collision"></a>`customer-collision` (the record’s Paying Customer guard; gaps #1) | a sales touch to a paying Customer | Send it to their CSM instead. | <Op verb="Lead.assign" /> |
| `mailbox-warm` (gaps #1) | a touch from a Mailbox still in warm-up | Warm the Mailbox; it passes after 14 days at rising volume. | <Op verb="Mailbox.warm" /> |
| <a id="check-count-floor"></a>`count-floor` (the record’s Count-floor stop, held at the send) | a count under 3 people, a colleague’s name, or someone who left the totals | Say “2 people on your team”, never a name. | <Op verb="Message.schedule" /> |

| Stop (pauses the Play) | Pauses when | Then |
| --- | --- | --- |
| <a id="stop-privacy"></a>`privacy` | one reply objects to being watched; 2 in 30 days | that company rests 90 days; 2 pause the Play |
| <a id="stop-reply-floor"></a>`reply-floor` | interested replies plus checkouts under 1 % after 300 first touches | the Play run stops |
| <a id="stop-unanswered-positive"></a>`unanswered-positive` (the record’s Owner stop; standard `response-60m`) | a hand-raise waits past 1 business hour, or 30 % of a week’s approvals expire | new fires pause; <Op verb="Approval.approve" /> resumes it |
| <a id="stop-false-fire"></a>`false-fire` | reviewers reject over 40 % of a Trigger’s last 30 fires | its threshold rises one notch |
| <a id="stop-holdout-integrity"></a>`holdout-integrity` | a touch reaches a held-back account; over 5 % voids the readout | <Op verb="Task.assign" /> names the Play that sent it |
| <a id="stop-spend-limit"></a>`spend-limit` | month-to-date model spend reaches its Limit | that part pauses until the month turns (<Op verb="Limit.raise" />) |
| <a id="stop-bounce"></a>`bounce` (email) | bounces above 2 % of a day’s sends | that Mailbox pauses |
| <a id="stop-complaint"></a>`complaint` (email) | complaints near 0.1 % | the Play’s email pauses on every Mailbox |
| <a id="stop-stop-line"></a>`stop-line` (email) | stop clicks above 2 % of a template’s last 100 delivered | that template pauses and returns to You approve each one |
| <a id="stop-sync-lag"></a>`sync-lag` (in-product) | Events landing over 60 min late, or a fire over 24 h after its crossing | every route with a window under 7 days pauses |
| <a id="stop-kill-switch"></a>`kill-switch` | you press it | <Op verb="Process.pause" /> on `outbound`; churn alerts still reach the CSM |

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

```json title="The captured refusal · sample"
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "The note to Redfern Homes did not send: it names a colleague.",
  "checks": [
    {
      "check": "count-floor",
      "status": "fail",
      "doc": "/plays/product-signals#check-count-floor",
      "fix": "Say “2 people on your team”, never a name.",
      "then": "Message.schedule"
    },
    {
      "check": "customer-collision",
      "status": "pass"
    }
  ]
}
```


```json title="The Stops that fired, as Campaign.paused reasons · sample"
[
  {
    "reason": {
      "limit": "unanswered-positive",
      "observed": "1h 22m",
      "bound": "1 business hour",
      "window": "1 hand-raise"
    },
    "doc": "/plays/product-signals#stop-unanswered-positive",
    "task": "task_488"
  }
]
```

## Did it work
Both halves are read from the same random assignment at the fire (rules m–o), and each figure’s call is <Op verb="Metric.query" /> by node and by arm; the leaf and every node above it are Not live yet (gaps #4).

`+18.5 Deals per 100 · ■■□□ Early sign · n 108 · path → arr`

| Node | Agent half | Your half | Added | Confidence | Basis |
| --- | --- | --- | --- | --- | --- |
| `pql-deal-rate` | 31.5 per 100 (17 of 54) | 13.0 (7 of 54) | +18.5 [3.2, 33.8] | ■■□□ Early sign | held back at random · n 108 · 12 still maturing |
| `opened-deals-and-trials` | 38.9 per 100 (21 of 54) | 22.2 (12 of 54) | +16.7 [−0.4, 33.8] · Not carrying up yet | ■□□□ Too early to tell | the same assignment; your half’s trials started on their own |
| `new-arr` ↑ | $3,284, 6 Customers · Booked: credited, not all caused by this Play | $694, 2 Customers | +$2,590 [−$701, +$5,077] · Worth about +$2,400 a month | ■□□□ Too early to tell (6 vs 2 Customers) | the same · 7 Deals still deciding |
| `net-new-arr` · `arr` | +$3,284 | +$694 | +$2,590, carried: it moves no other bridge term | ■□□□ Too early to tell | carried from `new-arr` |

The leaf counts Deals or Team trials opened within 21 days per 100 free accounts assigned at their first fire, once paying Customers and held accounts are left out (rule m), to 100 matured in each half; at 54 and 54 so far the Experiment runs on.

**How it moves it:** a touch on a median of day 1 after the crossing, citing the team’s own counts, where your half heard nothing until they asked.

**Guardrails:** treated against held back: paid per Deal opened, 0.29 against 0.29 (`new-paying-customers` per Deal), and stop clicks, 0.9 % against no sends.

```json title="The Campaign card · sample"
{
  "$id": "https://api.sb/leaseline.app/campaigns/product-signals",
  "$type": "Campaign",
  "play": "play:product-signals@1",
  "path": [
    "pql-deal-rate",
    "opened-deals-and-trials",
    "new-arr",
    "net-new-arr",
    "arr"
  ],
  "experiment": {
    "metric": "pql-deal-rate",
    "unit": "per-100",
    "heldBack": 60,
    "decision": null
  },
  "readout": {
    "value": 18.5,
    "n": 108,
    "maturing": 12,
    "interval": [
      3.2,
      33.8
    ],
    "readability": "C",
    "basis": "held-back",
    "pooled": false
  },
  "ledger": {
    "spend": 2,
    "workerCost": 4,
    "hours": 2.4,
    "pipelineArr": 5688,
    "wonArr": 3284,
    "projectedArr": 2444,
    "paybackDays": 2
  }
}
```

Outside benchmark: a median 8 % of free accounts convert to paid within six months, across self-serve B2B products (a growth analyst’s survey with two vendors, 2026-02-04).

**Before it · After it.** Before it: `product-led` and `programmatic-seo` bring the free accounts. After it: `closer-handoff` hears each Deal it opens, and `usage-upsell` hears the Team accounts that pace past 150 units; churn alerts it raises feed the CSM’s queue, never a sales touch.

## Example
Leaseline ran it from 2026-08-24 to 2026-10-19 (56 days). 128 free accounts fired a Trigger, 8 were left out as paying Customers or held by a person, and 120 were assigned at random, 60 to each half; 108 have matured and 12 are still maturing. Read every captured thread at `?threads=all`; three follow the tables.

| What happened, agent half, by option | Decisions | Deals or trials opened | Won (credited, not all caused by this Play) |
| --- | --- | --- | --- |
| Team plan note · Shared limit note | 22 · 9 | 9 · 4 | 3 on Team (+$2,844 ARR) |
| Founder welcome · Reach who pays | 4 · 3 | 2 · 1 | 1 on Pro (+$347 ARR) · 0, 1 open |
| Rescue a stalled signup · Open a Deal · To the CSM | 8 · 14 · 5 | 1 · 14 (the Deals above) · 0 | 1 on Pro · counted above · 0 |
| **Agent half** · your half | **60** · 60 assigned | **21** of 54 matured · 12 of 54 | **$3,284** · $694 |
| At 30 a month (the Use it when volume) | about 56 in 56 days | held back at random, half the fires | Other Startups’ results: +12.0 per 100, ■■■□ Likely, n 2,860 |

| Day | Event | What happened | Your time |
| --- | --- | --- | --- |
| 0 | `event_2003` Campaign.created | the three steps; the dry run fails `mailbox-warm` | 15 min |
| 0–14 | `event_2006` Mailbox.warmed | agent@leaseline.app warms; in-product cards show meanwhile | 2 min |
| 2 | `event_2031` Meeting.booked | the first Meeting: Marlow Estates’ director, from your welcome note | — |
| 9 | `event_2089` Role.staffed | AE · Priya Shah (person) takes `role:sales-lead` | — |
| 16 | `event_2120` Role.updated | promoted: team and limit notes send after a 2-minute hold | — |
| 28 · 49 | `event_2144` Change.merged · `event_2195` Readout.created | the team line from 2 to 3 people: replayed at 29.8 per 100, read back at +21d at 31.5, n 54 | 5 min |
| 33 | `event_2160` Campaign.paused | `unanswered-positive`: a hand-raise waited 1 h 22 min; resumed in 9 min | 6 min |
| 1–56 | `event_2012` Approval.created, first of 27 | you approved the first wave and 7 founder notes; `count-floor` refused 2 drafts, each rewritten once and sent; weekly reviews | 1 h 12 min · **total 2.4 h measured, 2.5 h asked** |

> **Leaseline → Sam at Brightwater, Oct 19 09:02** · Your team of 4 drafted 31 renewal notices in Leaseline this week.¹ Team is $79 a month for up to 5 members and 150 units, and keeps everyone’s notices in one place.² Want me to set it up for the 4 of you? *Signed with the Role’s disclosure line.*³
>
> **Sam, 09:57** · “Yes, set us up on Team. We’re 4 now, maybe 6 by spring.” A Deal opened, and the Brief reached Priya at 09:58 (When it hands to you).

```diff title="Redfern Homes, 2026-09-11 · the draft count-floor refused, then the rewrite that sent"
- Hi Lee, Maria on your team hit the 5-unit limit on Tuesday too.
+ Hi Lee, 2 people on your team hit the 5-unit Limit this week. Pro covers 50 units for $29 a month.
```

> **Marlow Estates, 2026-08-26 10:14** · the Director of Operations activated · **Dana, from her own Mailbox, 10:40** · a 3-line welcome, approved by Dana · **Reply, 13:05** · “Could we talk Thursday?” · *Priya, our account lead, has Thursday at 14:00 or 16:00.*⁴ The Brief went to Priya (`task_451`).

¹ A count of 3 or more people, from a live usage card line, naming none of them. ² The live Offer’s Price, for the active count only. ³ The fixed signature line, compiled from the Role’s disclosure Setting. ⁴ The routed line, compiled from the Role’s Settings.

## Your settings
```mdx title="plays/product-signals.mdx"
---
holdout: 0.5      # half the fired accounts get no touch for 60 days, as today
---
Write about Team when 3 or more people at one free account are active.
A director who signs up gets a note from Dana, approved each time.
Hand-raises go to the AE.
```

```text
$ npx startup-builder show plays/product-signals
holdout 0.5 · team line 3 people · founder note for a director · hand-raises to the AE     your file  plays/product-signals.mdx:2,4,5,6 (Ask 2026-08-24; line 4 by change_23, 2026-09-21)
worker role:prospector, owns never · chooses 7 (4 rule, 3 read) · sequences team-plan-note, shared-limit-note, senior-welcome, dormant-rescue, buyer-note · approval first-send, every-send · checks owned-by-a-person, customer-collision, mailbox-warm · stops 11 + count-floor held at the send · placements gmail, outlook, yahoo-mail, linkedin + in-product card    catalog  play:product-signals@1 (Not live yet, gaps #5)
```

### Every key from the catalog
| Key | Id | What it is |
| --- | --- | --- |
| Tactic | <a id="tactic-usage-identity-resolve"></a>`usage-identity-resolve` | Usage identity resolve: Resolves each Person in the Product to an Organization from first-party evidence only, with a confidence, and marks each Organization’s lifecycle. Starts at Sends nothing. |
| Tactic | <a id="tactic-usage-trigger-score"></a>`usage-trigger-score` | Usage trigger score: Grades fit first, fires a Trigger when a usage threshold crosses, stacks live Triggers by half-life, draws the holdout and routes each Organization. Starts at Sends nothing. |
| Tactic | <a id="tactic-usage-card-build"></a>`usage-card-build` | Usage card build computes the only numbers a touch may state about an Organization: its People’s counted use, its Limits reached and its plan math. Starts at Sends nothing. |
| Tactic | <a id="tactic-usage-touch-compose"></a>`usage-touch-compose` | Usage touch compose: Fills at most three slots of the route’s template from the usage card, checks facts and the count floor, and queues an approval card. Starts at You approve the first wave. |
| Tactic | <a id="tactic-usage-touch-send"></a>`usage-touch-send` | Usage touch send: Delivers an approved touch in the Product and from the home-Domain Mailbox, records its trigger, and runs the route’s one follow-up. Starts at You approve the first wave. |
| Tactic | <a id="tactic-buyer-route"></a>`buyer-route` | Approver route reaches the Person who pays for a triggered team through a known Contact, the champion’s own forward, or an opt-in cold route. Starts at You approve each one. |
| Tactic | <a id="tactic-churn-alert"></a>`churn-alert` | Churn alert: Turns a paying Customer’s visit to pricing, billing or cancel Pages into a Success Manager Task with a health brief, and sends no Message. Starts at Sends nothing. |
| Tactic | <a id="tactic-trigger-deal-log"></a>`trigger-deal-log` | Trigger deal log opens a Deal on each hand-raise, counts yield weekly, reads the holdout at day 60 and recalibrates every 30 days. Starts at You approve the first wave. |
| 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-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. |
| Trigger | <a id="trigger-team-cluster"></a>`team-cluster` | Team cluster: `threshold` on `Event.captured`, window 14d. |
| Trigger | <a id="trigger-limit-pair"></a>`limit-pair` | Limit pair: `threshold` on `Entitlement.checked`, window 7d. |
| Trigger | <a id="trigger-senior-signup"></a>`senior-signup` | Senior signup: `event` on `Customer.activated`, window 7d. |
| Trigger | <a id="trigger-active-pricing"></a>`active-pricing` | Active pricing: `event` on `Page.viewed`, window 3d. |
| Trigger | <a id="trigger-dormant-signup"></a>`dormant-signup` | Dormant signup: `state` on `Customer.stalled`, window 14d. |
| Trigger | <a id="trigger-customer-pricing-visit"></a>`customer-pricing-visit` | Customer pricing visit: `event` on `Page.viewed`, window 7d. |
| Trigger | <a id="trigger-product-hand-raise"></a>`product-hand-raise` | Product hand raise: `event` on `Prospect.engaged`, window 1d. |
| Sequence | <a id="sequence-team-plan-note"></a>`team-plan-note` | Team plan note: 4 steps; it ends on A reply; A checkout started; A Deal opened; The card dismissed; An Organization-scope stop; The Organization becomes a paying Customer; 14 days. |
| Sequence | <a id="sequence-shared-limit-note"></a>`shared-limit-note` | Shared limit note: 4 steps; it ends on A reply; A checkout started; A Deal opened; An Organization-scope stop; The Organization becomes a paying Customer; 14 days. |
| Sequence | <a id="sequence-senior-welcome"></a>`senior-welcome` | Senior welcome: 2 steps; it ends on A reply; A booked Meeting; A checkout started; A stop; 14 days. |
| Sequence | <a id="sequence-dormant-rescue"></a>`dormant-rescue` | Dormant rescue: 4 steps; it ends on Any activation Event; A reply; A stop; A send gate refusal for the colleague; 21 days. |
| Sequence | <a id="sequence-buyer-note"></a>`buyer-note` | Approver note: 2 steps; it ends on A reply; A Quote request; A checkout started; A stop; 14 days. |
| Benchmark | <a id="benchmark-product-signals-plan-upgrade-timing"></a>`product-signals-plan-upgrade-timing` | Monthly plan upgrade rate after a purchase, from month 4 (low) to month 1 (high).: 0.16–0.75 % (A subscription-analytics vendor’s upgrade-timing study, published 2026-09-30., 2026-10-24). |
| Benchmark | <a id="benchmark-product-signals-seat-upgrade-timing"></a>`product-signals-seat-upgrade-timing` | Monthly seat upgrade rate after a purchase, from month 4 (low) to month 1 (high).: 0.19–0.83 % (A subscription-analytics vendor’s upgrade-timing study, published 2026-09-30., 2026-10-24). |
| Benchmark | <a id="benchmark-product-signals-free-to-paid-median"></a>`product-signals-free-to-paid-median` | Median free-to-paid conversion within six months of signup, across self-serve B2B products.: 8 % (A self-serve benchmark survey by a growth analyst and two vendors, published 2026-02-04., 2026-10-24). |