URL: /revenue/work-old-leads · Id: `page:/revenue/work-old-leads`

# Work old leads: the run

> Work old leads: the files, the agent’s call, the result, what refused, and the command that grades your own agent.

[Leads](https://startupbuilder.dev/business/nouns/lead) nobody touched in 60 days, and [Deals](https://startupbuilder.dev/business/nouns/deal) lost on price, worked with one fresh reason each. The run holds a quarter back to prove what it added.

| Your time | Spend | First signal | Result |
| --- | --- | --- | --- |
| about 4 h asked, 4.2 h measured | $48 of enrichment · $31 Worker cost | day 3 | day 30 |

```text title="Work old leads · Ask"
Work the 812 leads nobody has touched in 60 days, and the Deals we lost on price.
```

```ts title="Work old leads · SDK"
import { $ } from 'startup-builder'
const sb = $.with({ namespace: 'leaseline.app' })

const { total } = await sb.Lead.list({ where: 'status = "nurture" and touchedAt < "2026-08-20"', total: true })   // 812 Leads, plus Deals lost on price or a missing feature
const { actions: drafts } = await sb.Action.list({ where: 'status = "pending" and reason = "awaitingInput" and by = "prospector"', limit: 20 })   // wave 1: 140 drafts, each with one fresh reason
await sb.Action.redirect({ action: 'action_412', changes: { body: 'Since you asked in May: Leaseline now sends each owner their own renewal notice.' } }, { mode: 'try' })   // Dana reads 20 and redirects 3; each edit becomes a test it must pass
await sb.Message.schedule({ message: 'message_51', at: '2026-10-21T14:00:00Z' }, { mode: 'try' })
// 422 · Harbor Property Co. holds an active Subscription; the Message to its office manager did not send.
// customer-collision: Send it to their CSM instead: the Contact leaves the List. · then Lead.assign
await sb.Lead.assign({ lead: 'lead_88', owner: 'worker_3' }, { mode: 'try' })   // Northgate Rentals wrote "interested, send times": routed to the AE in 40 s
await sb.Approval.approve({ approval: 'approval_9', note: '$48 of enrichment this month' }, { mode: 'try' })
return await sb.Campaign.start({ campaign: 'lead-reactivation', budget: { amount: 48, currency: 'USD', per: 'month' } }, { mode: 'try' })   // the whole run as one record · Not live yet, gaps #2
```

```json title="Work old leads · Response · sample"
{
  "$id": "https://api.sb/leaseline.app/campaigns/lead-reactivation",
  "$type": "Campaign",
  "play": "play:lead-reactivation@3",
  "audience": {
    "selected": 812,
    "afterSuppression": 800
  },
  "experiment": {
    "metric": "reactivated-pipeline",
    "unit": "per-100",
    "heldBack": 200,
    "decision": "ship"
  },
  "readout": {
    "value": 3,
    "n": 600,
    "interval": [
      0.4,
      5.3
    ],
    "readability": "C",
    "pooled": false
  },
  "ledger": {
    "spend": 48,
    "workerCost": 31,
    "hours": 4.2,
    "pipelineArr": 6246,
    "wonArr": 694,
    "projectedArr": 1388
  }
}
```

## The files
```mdx title="audiences/lapsed-leads.mdx"
---
query: leads where status = 'nurture' and touchedAt older than 60d, plus deals lost more than 90d ago with lostReason in [price, missing-capability]
fill: [enrichment]
---
```

```mdx title="plays/lead-reactivation.mdx"
---
audience: lapsed-leads
approval: first-send
---

Every Message cites one fresh reason with its public source. A closed-lost Deal gets a Message only when a shipped Change or Offer term answers its lost reason.
```

```text title="npx startup-builder show plays/lead-reactivation"
audience lapsed-leads · approval first-send (catalog: delay-window; a file only tightens)   from your file   plays/lead-reactivation.mdx:2-3 (Ask, 2026-10-20)
family demand · process lead-nurture · holdout 0.25 · sequence reactivation-round · worker prospector · stops bounce, complaint, reply-floor, unanswered-positive   from catalog   play:lead-reactivation@3
```

## The Worker’s call
| Decision | Value |
| --- | --- |
| Chose | drop the Offer-term reason |
| Why | 0.6 replies per 100 against 3.1 for shipped Changes |
| From | both reasons · shipped Changes only |
| Expects | reactivated-pipeline ≥ 2.5 per 100 |
| Reopens when | a new Offer term ships |
| By | agents/sales/prospector |

```json title="Decision · sample"
{
  "options": [
    "both reasons",
    "shipped Changes only"
  ],
  "choice": "drop the Offer-term reason",
  "reason": "0.6 replies per 100 against 3.1 for shipped Changes",
  "by": "agents/sales/prospector",
  "stakes": "low",
  "on": "schedule: daily",
  "expected": "reactivated-pipeline ≥ 2.5 per 100",
  "reopensWhen": "a new Offer term ships"
}
```

## Result

`+18 Deals · +$1,388 ARR projected · ■■□□ Early sign · n 600 · path → arr · $48 spend · $31 Worker cost · 4.2 h`

| Selected | After suppression | Held back | Extra Deals | Projected | Confidence |
| --- | --- | --- | --- | --- | --- |
| 812 | 800 | 200 | +18 | +$1,388 ARR | ■■□□ Early sign · n 600 |

`sample · leaseline.app · these figures replay from the records until a capture replaces them`

## What refused

```json title="422 · Message.schedule · sample"
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "Harbor Property Co. holds an active Subscription; the Message to its office manager did not send.",
  "checks": [
    {
      "check": "customer-collision",
      "status": "fail",
      "doc": "/code/refusals#check-customer-collision",
      "fix": "Send it to their CSM instead: the Contact leaves the List.",
      "then": "Lead.assign"
    }
  ]
}
```

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

| Check | What it refuses | Fix | Then |
| --- | --- | --- | --- |
| [`customer-collision`](/code/refusals#check-customer-collision) | a new-lead send to a Contact with an active Subscription | Send it to their CSM instead: the Contact leaves the List. | <Op verb="Lead.assign" /> |

A Stop pauses the whole run, where a Check refuses one send: [`unanswered-positive`](/code/refusals#stop-unanswered-positive).

```json title="unanswered-positive · Stop · sample"
{
  "event": "Campaign.paused",
  "reason": {
    "limit": "unanswered-positive",
    "observed": "64m",
    "bound": "60m"
  },
  "task": "tasks/task_88"
}
```

## Run it with your agent
```bash title="The two connect lines"
claude mcp add --transport http salesbuilder-docs https://salesbuilder.dev/mcp
claude mcp add --transport http startup-builder https://api.sb/mcp
```

| Who runs it | Score | Refusals recovered | Checks hit | Model spend |
| --- | --- | --- | --- | --- |
| The built-in Worker · `sample` | 3.0 Deals per 100 | 2 of 2 | 3 | $1.10 |

```bash title="Grade your agent on the same run"
npx startup-builder eval --scenario reactivation-812 --agent "claude -p"
```