URL: /plays/review-requests · Id: `play:review-requests`

# Review requests

> Asks every Customer for an honest Review when their result lands, on the one Venue you need most, and answers every Review within a day.

Asks every [Customer](https://startupbuilder.dev/business/nouns/customer) for an honest [Review](https://startupbuilder.dev/business/nouns/review) when their result lands, on the one Venue you need most, and answers every Review within a day.

```text title="Set it up · Ask"
Be our CSM: when a Customer’s first lease renews from one of our notices, ask them once for an honest Review on the site we need most that month. Answer every Review within a day, and bring me anything under 4 stars. Show me what’s missing; don’t send.
```

```text title="Run one · Ask"
Hand our latest Customer who reached the outcome to the CSM 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 ("Outcome.delivered", "Review.published", "Monitor.matched", "Change.merged")', 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_752",
    "$type": "Decision",
    "choice": "ask-on-one-venue",
    "reason": "their first lease renewed from a Leaseline notice on Oct 17; Capterra holds 6 Reviews against a threshold of 10, so it is this month’s Venue",
    "stakes": "low",
    "options": [
      "ask-on-one-venue",
      "answer-the-review",
      "low-score-to-a-person",
      "correct-a-fact",
      "show-on-your-pages",
      "not-now"
    ],
    "by": "worker_5",
    "on": "customer_23",
    "expected": "a Review on Capterra within 14d",
    "reopensWhen": "no Review.published matched to customer_23 within 14d"
  },
  "actions": [
    {
      "$id": "https://api.sb/leaseline.app/actions/action_1",
      "$type": "Action",
      "status": "done",
      "mode": "try",
      "by": "worker_5",
      "verb": "Review.list",
      "checks": []
    },
    {
      "$id": "https://api.sb/leaseline.app/actions/action_2",
      "$type": "Action",
      "status": "pending",
      "reason": "awaitingTime",
      "mode": "try",
      "by": "worker_5",
      "verb": "Message.schedule",
      "checks": [
        {
          "check": "owned-by-a-person",
          "status": "pass"
        },
        {
          "check": "keep-first",
          "status": "pass"
        },
        {
          "check": "customer-ask-cap",
          "status": "pass"
        },
        {
          "check": "claims-resolve",
          "status": "pass"
        }
      ]
    }
  ]
}
```

`action_1` reads each Venue’s Review count against its threshold. `action_2` is the ask in Cobalt Street’s own thread, held 2 minutes: the same words every Customer gets, with their outcome and the Capterra link.

`40.9 Reviews per 100 asks against 16.7 before · before-after, not proof · n 22 · path → arr`

| Try another: the Customer | Choice | Reason |
| --- | --- | --- |
| Cobalt Street Rentals: their first lease renewed from a Leaseline notice | `ask-on-one-venue` | the outcome, and Capterra is this month’s Venue |
| Juniper Homes left 5 stars on Capterra | `answer-the-review` | a 4 or 5 gets a reply within a day |
| A 2-star G2 Review: “notices went out in the wrong time zone” | `low-score-to-a-person` | a 1 to 3 never gets an agent reply |
| A 2025 listing says Pro is $19 a month | `correct-a-fact` | the live Price is $29 |
| Harbor Row: first renewal landed, at risk since Oct 2 | `not-now` | `keep-first`: their CSM sees them first |

| Worth | Your time | Spend | First signal | Use it when | Instead |
| --- | --- | --- | --- | --- | --- |
| ≈ +$120 ARR a month at 11 | 20 min week 1, then 10 a week | $0.04 per ask, ~$0.45 a month at 11 | Day 3: the first Review published | 10+ Customers at an outcome, a Venue listing | Under 10 Customers: `written-references` · no outcome Event: declare one (<Op verb="Offer.publish" />) |

**Where it sits:** Stranger → Lead → Trialing → **Activated** → At risk → Churned → Won back · Reviews per 100 asks → People reached → New Customers ↑ → ARR added each week → ARR

Worth’s formula: about +12 Reviews per 100 asks over asking by hand (Other Startups’ results; no lift of your own yet) × 11 asks a month × 21 people reached per Review × 0.012 reached to paid × $347 Pro ARR ≈ +$115. Under First signal: At 11 asks a month, 90 days can tell only +30 per 100 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 Outcome.delivered · first ask 2 min after the outcome · 8 keys Not live yet`

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

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

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

# off: no ask sends; Reviews still get an answer from you
curl -X PATCH https://api.sb/leaseline.app/campaigns/review-requests \
  -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": "review-requests can’t start yet: 1 check fails and 2 warn.",
  "checks": [
    {
      "check": "use-when",
      "status": "pass",
      "observed": "41 Customers; 14 reached a renewed lease in 90 days",
      "doc": "/plays/review-requests#check-use-when",
      "fix": "Keep 10 or more Customers at an outcome, or run written-references.",
      "then": "Metric.query"
    },
    {
      "check": "outcome-event",
      "status": "fail",
      "observed": "Leaseline Pro declares no delivered-outcome Event",
      "doc": "/plays/review-requests#check-outcome-event",
      "fix": "Declare “a lease renewed from a Leaseline notice” as the outcome, then publish.",
      "then": "Offer.publish"
    },
    {
      "check": "baseline",
      "status": "warn",
      "observed": "1 Review from 6 asks by hand in 90 days",
      "doc": "/plays/review-requests#check-baseline",
      "fix": "n 6: it reads before-after, not proof, until volume allows a held-back half.",
      "then": "Metric.query"
    },
    {
      "check": "role-unstaffed",
      "status": "warn",
      "roles": [
        "success-manager",
        "customer-lead"
      ],
      "owned": 4,
      "doc": "/plays/review-requests#check-role-unstaffed",
      "fix": "Staff the CSM, or leave it: low scores come to you, and Priya’s 4 get drafts.",
      "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: 41 Customers, 14 at the outcome; warn: 1 of 6 in 90 days (gaps #8) | <Op verb="Metric.query" /> on `reviews-per-100-asks`, which fills Before in Your number | 1 min | none |
| <a id="check-outcome-event"></a>`outcome-event` · fail: no outcome Event on the Offer | <Op verb="Offer.publish" /> with the outcome Event declared | 5 min | none |
| <a id="check-role-unstaffed"></a>`role-unstaffed` · warn (gaps #8): 4 of your Customers have a person owner, Priya | <Op verb="Role.staff" />, or leave it: low scores come to you, and Priya’s 4 get drafts in her queue | 0 min | none |

`Your number · Before 16.7 · Now 40.9 Reviews per 100 asks · Target 35 by Dec 31 · before-after, not proof · n 22 matured`

Each ask costs $0.04 from your Domain (Worker cost), and reading Reviews and drafting replies about $0.02 each. The Venues charge nothing to be reviewed. That comes to about $0.45 a month at 11 asks.

## When it starts
| When | Trigger id | Starts on | Window | How it gets in |
| --- | --- | --- | --- | --- |
| A Customer’s lease renews from a Leaseline notice | `delivered-outcome` | `event` on `Outcome.delivered` where the Offer’s declared delivered-outcome Event, once per Customer | ask within 7d; once per Venue per 12 months | your app calls <Op verb="Event.capture" /> on each renewal |
| A Review is posted or edited on a Venue where you are listed | `new-venue-review` | `event` on `Review.published` where a Review posted or edited on a Venue where the Startup is listed | answer within 1d | <Op verb="Integration.connect" /> with Capterra and G2 |
| A page outside your Site states an old price or plan | `stale-third-party-fact` | `event` on `Monitor.matched` where page states a fact a fact record contradicts | 30d | <Op verb="Monitor.check" /> on the listings that cite you |
| + 1 more for changed facts (`?lanes=all`) | | | | |

```json title="One signed delivery · StartupBuilder-Signature"
{
  "$id": "https://api.sb/leaseline.app/events/event_9104",
  "$type": "Event",
  "name": "Outcome.delivered",
  "at": "2026-10-17T14:30:00Z",
  "seq": 9104,
  "data": {
    "customer": "customer_23",
    "outcome": "lease renewed from a Leaseline notice",
    "lease": "lease_4471",
    "first": true
  }
}
```

```bash title="Reproduce it in test"
npx startup-builder trigger Outcome.delivered --like event_9104   # replays the outcome
npx startup-builder clock advance --by 14d   # runs the ask’s day-7 reminder and closes it at day 14
npx startup-builder listen Review.published --forward-to http://localhost:3000/hook
```

## Who runs it
**CSM · your agent** (`role:success-manager`, `worker_5`) holds the Key Result. Low scores go to the CSM Role, which has no person, so **you** (Dana Okafor, person, `worker_1`); stale-fact corrections on forums are yours to post; and the 4 Customers **AE · Priya Shah (person)** owns get drafts in her queue. Your number links to Did it work.

| Option | Rung today | Evidence (passed of n) | Moves up at | Drops back when | You can |
| --- | --- | --- | --- | --- | --- |
| Ask on one Venue · Show it on your Pages | It does it, you can stop it | 22 of 22 · 7 of 7 | met on day 14 (the first 10 asks) | any ask fails a Check → You approve each one | <Op verb="Action.cancel" /> inside the hold |
| Answer the Review | You approve each one | 9 of 9 | at 20 passed in a row | — | <Op verb="Approval.approve" /> · <Op verb="Approval.refuse" /> |
| A low score to a person · Correct a stale fact · Not now | It does it | 2 of 2 · 3 of 3 · 2 of 2 | — | — | <Op verb="Action.redirect" /> |
| Change the outcome Event or the Venue rule | You approve each one | 0 merged | never | — | <Op verb="Change.merge" /> · <Op verb="Change.revert" /> |
| Never: asking for stars, a reward for a good score, a reply that offers anything for an edit | Refused, every time | 1 draft refused | never | — | nothing; the Checks hold it |

**What it reads · What it may call.** It reads the outcome Event, each Venue’s Review count and the Reviews themselves. An ask names only the outcome the Customer saw in Leaseline, never anything watched. Its tools are <Op verb="Conversation.answer" />, <Op verb="Issue.triage" />, <Op verb="Task.assign" />, <Op verb="Review.publish" />, and any other call becomes an Approval under the Role’s Mandate.

```md title="The brief (folded): compiled Agent-register Markdown · play:review-requests@1 · each line names its record key"
You hold the CSM Key Result: 35 Reviews per 100 asks by 2026-12-31.              ← worker, metric
At the outcome, ask every Customer the same way, on this month’s one Venue.      ← triggers, chooses, copyRules 1
Ask for an honest Review; never for stars or kind words.                         ← copyRules 2
Answer every Review within a day; a 1 to 3 goes to Dana.                         ← your file, line 4; reply-hold
Show a Review on our Pages only with the reviewer’s yes.                         ← copyRules 10; display-consent
A held Customer gets Not now; their CSM sees them first.                         ← keep-first
```

**The slow loop.** The weekly Decisions kept the outcome Event and the Venue rule, and no Change was merged in the window, so there is no replay or read-back to print. October’s Venue moved from G2 to Capterra by the rule itself, at G2’s 14th Review.

**Bring your own agent** (folded) starts at It does it, you can stop it for asks when it matches the seated agent on the eval suite; replies stay at You approve each one. <Op verb="Role.staff" /> seats it.

```bash
npx startup-builder eval --scenario review-requests-22 --agent "claude -p" --seat success-manager   # seated: 40.9 per 100, every Customer covered, 0 Checks failed sent · yours: 36.4, 1 ask that named a score
```

## What it can do next
| Option | Picked when | Decided by | Runs | Hands to | If it’s just you |
| --- | --- | --- | --- | --- | --- |
| Ask on one Venue `ask-on-one-venue` | a delivered outcome, no ask on that Venue in 12 months | Your rule (line 4) | `review-ask-email` in their own thread: one ask, one reminder | the Customer’s choice | the same |
| Answer the Review `answer-the-review` | a new Review scored 4 or 5 | Agent’s call, with its reason | <Op verb="Conversation.answer" /> on the Venue, within 1 day | you approve it · Take this one | you |
| A low score to a person `low-score-to-a-person` | a 1 to 3, or a draft reply that hits a claim | Your rule (the Reply hold) | <Op verb="Issue.triage" /> on the defect → <Op verb="Task.assign" /> | CSM, so you, due 1 business day · Take this one | you |
| Correct a stale fact `correct-a-fact` | an old price, plan or feature stated about Leaseline | Agent’s call, with its reason | <Op verb="Conversation.answer" /> on a listing; a forum reply drafted → <Op verb="Task.assign" /> | you post forum corrections · Take this one | you |
| Show it on your Pages `show-on-your-pages` | the reviewer said yes to showing it | Your rule (the consent line) | <Op verb="Review.publish" /> on your Reviews Page, every consented Review | nobody | the same |
| Not now `not-now` | another Play holds them, or `keep-first` applies | Your rule (rule j) | nothing; names the holder | their CSM, so you · Take this one | you |

| When they write back | Fixture reply | What happens | Who answers | By when | Call |
| --- | --- | --- | --- | --- | --- |
| Interested | “Happy to. Is G2 all right instead?” | yes: any honest Review counts; the G2 link, and Capterra stays open for next month | CSM · your agent | 1 business day | <Op verb="Conversation.answer" /> |
| Send me pricing | “While I have you, what does Team cost?” | Team’s live Price; the ask stays as it was | CSM · your agent | 5 min | <Op verb="Conversation.answer" /> |
| Not now | “Busy this month, sorry.” | the ask closes, and `customer-ask-cap` frees the Customer | nobody | at once | <Op verb="Decision.list" /> |
| Wrong person | “Our owner should be the one to review it.” | asks them to forward the link; it never writes to an address from their text | CSM · your agent | 1 business day | <Op verb="Conversation.answer" /> |
| Out of office | an auto-reply until Oct 29 | the reminder waits for the return date | nobody | the return date | <Op verb="Message.schedule" /> |
| Question it can’t answer | “Can I leave it without my company’s name?” | To a person: that is the Venue’s own setting | you | 1 business day | <Op verb="Conversation.escalate" /> |
| Stop | “Please don’t ask me for reviews.” | no ask again, on any Venue, from any Play | nobody | at once | <Op verb="Contact.suppress" /> |

## When it hands to you
Low scores, held Customers and forum corrections come to **you**: the CSM Role has no person, and the founder seat posts corrections in your own name. 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_781",
    "$type": "Task",
    "assignee": "worker_1",
    "due": "2026-10-03T16:00:00Z",
    "on": "customer_31",
    "brief": {
      "who": "Tidewater Rentals, on Pro",
      "said": "2 stars on G2: notices went out in the wrong time zone",
      "chose": "low-score-to-a-person",
      "why": "a 1 to 3 never gets an agent reply",
      "issue": "issue_58: the time zone on scheduled notices",
      "never": [
        "anything offered for an edit",
        "their account details in public"
      ]
    }
  },
  {
    "$id": "https://api.sb/leaseline.app/tasks/task_766",
    "$type": "Task",
    "assignee": "worker_1",
    "due": "2026-09-25T17:00:00Z",
    "on": "thread_19",
    "brief": {
      "who": "a 2025 forum thread that cites Leaseline",
      "said": "Leaseline Pro is $19 a month",
      "chose": "correct-a-fact",
      "why": "the live Price is $29, since 2026-03-01",
      "draft": "the dated fact and its source, posted by you, no opinion on the thread"
    }
  }
]
```

You answer 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 you claim a low score, the agent drafts nothing more for that Review. A Task nobody claims by its due time goes up a Role through <Op verb="Task.escalate" />, and `person-promise` fires.

**On time:** you, 5 of 5 within 1 business day, median 3 h, n 5, ■□□□ Too early to tell beside Other Startups’ 95 % on time. **Your queue:** in week 1, 10 asks to approve and 1 weekly review; then about 2 Review replies and 1 low score or correction 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) | an ask to a Customer whose 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-keep-first"></a>`keep-first` (rule j; gaps #8) | a Review ask to a Customer 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 in a window, from any Play | `usage-upsell` holds them until Nov 2. | <Op verb="Decision.list" /> |
| <a id="check-reply-hold"></a>`reply-hold` (the record’s Reply hold) | an agent reply to a 1 to 3 score, or one with a claims-check hit | Have a person answer it, and offer nothing for an edit. | <Op verb="Task.assign" /> |
| <a id="check-display-consent"></a>`display-consent` (marketing) | a Review on your Pages without the reviewer’s yes | Ask the reviewer once; the Review shows when they agree. | <Op verb="Message.schedule" /> |
| <a id="check-claims-resolve"></a>`claims-resolve` (hub) | a reply or correction sentence no approved Page backs | Cut the sentence, or cite the dated fact and its source. | <Op verb="Content.draft" /> |

| Stop (pauses the Play) | Pauses when | Then |
| --- | --- | --- |
| <a id="stop-venue-warning"></a>`venue-warning` | a Venue notice about how Reviews are collected, a hold on new Reviews, or removed Reviews | every ask and reply on that Venue pauses at once |
| <a id="stop-coverage"></a>`coverage` | a Customer reached the outcome with no ask and no listed reason | asks pause in every Play that shares the ask |
| <a id="stop-complaint"></a>`complaint` (email) | complaints, with stop and remove-me replies, near 0.1 % of a day’s email asks | email asks pause on every Mailbox |
| <a id="stop-opt-out"></a>`opt-out` (email) | opt-outs above 2 % of email asks, after 200 | email asks pause; in-product asks continue |
| <a id="stop-bounce"></a>`bounce` (email) | bounces above 2 % of a day’s asks | that Mailbox pauses; the Customer records are flagged |
| <a id="stop-yield-floor"></a>`yield-floor` | under 2 Reviews per 100 asks after 300 asks | the Play run stops; a Decision opens on Venue, moment and copy |
| <a id="stop-kill-switch"></a>`kill-switch` | you press it | every ask and reply pauses |

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

```json title="The captured refusal · sample"
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "The agent’s reply to Tidewater’s 2-star Review did not post: it offers a free month for an update.",
  "checks": [
    {
      "check": "reply-hold",
      "status": "fail",
      "doc": "/plays/review-requests#check-reply-hold",
      "fix": "Have a person answer it, and offer nothing for an edit.",
      "then": "Task.assign"
    },
    {
      "check": "claims-resolve",
      "status": "pass"
    }
  ]
}
```


```json title="The Stops that fired, as Campaign.paused reasons · sample"
[
  {
    "reason": {
      "limit": "coverage",
      "observed": "1 Customer at the outcome with no ask and no reason",
      "bound": "0",
      "window": "the week of Sep 14"
    },
    "doc": "/plays/review-requests#stop-coverage",
    "task": "task_759"
  }
]
```

## Did it work
At 11 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; the leaf and every node above it are Not live yet (gaps #4).

`40.9 Reviews per 100 asks against 16.7 before · before-after, not proof · n 22 · path → arr`

| Node | Now (56 days) | Before (the 90 days, per 56) | Added | Confidence | Basis |
| --- | --- | --- | --- | --- | --- |
| `reviews-per-100-asks` | 40.9 per 100 (9 of 22 matured asks) · 2 still maturing | 16.7 (1 of 6 asks by hand) | — · Worth about +$120 a month | — | before-after, not proof · Other Startups’ results +12 per 100 over asking by hand, ■■■□ Likely, n 6,900 |
| `icp-reached` | 186 people from your Capterra and G2 pages | 41 | — | — | before-after; first visits only |
| `new-arr` ↑ | $694, 2 Customers who came from a Review Venue · Booked: credited, not all caused by this Play | $0 | — | — | before-after · 1 trial still deciding |
| `net-new-arr` · `arr` | carried from `new-arr` | carried | — | — | carried |

Before against after reads one window against the 90 days before it, scaled to 56 days; it is not proof, and Other Startups’ results print beside it. The leaf counts Reviews published on the target Venue within 14 days of an ask, per 100 asks delivered.

**How it moves it:** every Customer at the outcome got the same ask in their own thread within 2 minutes, on the Venue furthest under its threshold.

**Guardrails:** before against after: `logo-churn`, 0 of 22 asked Customers gone against a trailing 2.1 % a month, and opt-outs from asks, 0 of 22.

```json title="The Campaign card · sample"
{
  "$id": "https://api.sb/leaseline.app/campaigns/review-requests",
  "$type": "Campaign",
  "play": "play:review-requests@1",
  "path": [
    "reviews-per-100-asks",
    "icp-reached",
    "new-arr",
    "net-new-arr",
    "arr"
  ],
  "experiment": {
    "metric": "reviews-per-100-asks",
    "unit": "per-100",
    "heldBack": 0,
    "decision": null
  },
  "readout": {
    "value": null,
    "n": 22,
    "maturing": 2,
    "interval": null,
    "readability": "inadmissible",
    "basis": "before-after",
    "pooled": true
  },
  "ledger": {
    "spend": 0,
    "workerCost": 2,
    "hours": 1.1,
    "pipelineArr": 0,
    "wonArr": 694,
    "projectedArr": 115,
    "paybackDays": 1
  }
}
```

Outside benchmark: this Play’s catalog record carries no sourced range yet, so Other Startups’ results stand in: 31 Reviews per 100 asks, ■■■□ Likely, n 6,900.

**Before it · After it.** Before it: `usage-upsell` and `run-beside` bring Customers to the outcome. After it: `directory-listings` reads the Review counts, `aeo` the corrected facts, and `written-references` asks the reviewers who said yes; `keep-first` holds a Customer at risk.

## Example
Leaseline ran it from 2026-08-24 to 2026-10-19 (56 days): 26 Customers reached the outcome, 2 were held by `keep-first` and 2 by another Play’s open ask, and 22 were asked, on G2 in September and Capterra in October. Read every captured thread at `?threads=all`; three follow the tables.

| What happened, by option | Decisions | Reviews | Credited (not all caused by this Play) |
| --- | --- | --- | --- |
| Ask on one Venue | 22 | 9 (5 on G2, 4 on Capterra), 2 still maturing | 2 Customers from Review Venues (+$694 ARR) |
| Answer the Review · A low score to a person | 9 · 2 | every Review answered within 1 day | — |
| Correct a stale fact · Show it on your Pages · Not now | 3 · 7 · 4 | 3 facts fixed · 7 shown · — | — |
| At 10 Customers (the Use it when volume) | about 6 asks in 56 days | before-after, not proof | Other Startups’ results: 31 per 100, ■■■□ Likely, n 6,900 |

| Day | Event | What happened | Your time |
| --- | --- | --- | --- |
| 0 | `event_9002` Campaign.created | the three steps; the dry run fails `outcome-event` | 10 min |
| 0 | `event_9005` Offer.published | Leaseline Pro declares “a lease renewed from a Leaseline notice” | 5 min |
| 3 | `event_9019` Review.published | the first Review: Juniper Homes, 5 stars on G2 | — |
| 14 | `event_9061` Role.updated | promoted: asks send after a 2-minute hold | — |
| 21 | `event_9077` Campaign.paused | `coverage`: one Customer at the outcome with no ask; the gap fixed the same day | 8 min |
| 39 | `event_9090` Task.claimed | you took Tidewater’s 2-star Review; `issue_58` opened on time zones | 20 min |
| 1–56 | `event_9011` Approval.created, first of 19 | you approved 10 asks and 9 replies; `reply-hold` refused 1 draft; weekly reviews | 37 min · **total 1.1 h measured, 1 h asked** |

> **Leaseline → Cobalt Street’s office, Oct 17 14:32** · Your first lease renewed from a Leaseline notice this week.¹ Would you leave an honest Review of Leaseline on Capterra? It takes about 3 minutes: capterra.com/leaseline.² *Signed with the Role’s disclosure line.*³
>
> **Cobalt Street, Oct 18 09:10** · 4 stars on Capterra: “Renewals finally go out on time.” **Leaseline’s reply, Oct 18 11:02** · Thank you; glad the notices land on time.

```diff title="Tidewater Rentals, 2026-10-02 · the reply reply-hold refused, then the reply that posted"
- Sorry about that! We’d love to give you a free month if you’d update your review.
+ Thank you for telling us. Notices now use each property’s own time zone, as of Oct 6. — Dana, founder
```

> **Tidewater Rentals on G2, 2026-10-02 12:00** · 2 stars: “Notices went out in the wrong time zone.” · **The agent, 12:01** · opened `issue_58` and assigned the Review to you (`task_781`), due tomorrow 16:00 · **Dana, 15:20** · posted the reply after the fix shipped.

¹ The outcome the Customer saw in Leaseline, never anything watched. ² An honest Review, on one Venue; no stars asked for and no reward. ³ The fixed signature line, compiled from the Role’s disclosure Setting.

## Your settings
```mdx title="plays/review-requests.mdx"
---
holdout: 0        # 11 asks a month is too few to hold half back; it reads before-after
---
Ask when a Customer’s first lease renews from a Leaseline notice, on the Venue furthest under its threshold.
Answer every Review within a day; anything under 4 stars comes to Dana.
```

```text
$ npx startup-builder show plays/review-requests
holdout 0 · outcome: a lease renewed · one Venue a month · under 4 stars to Dana     your file  plays/review-requests.mdx:2,4,5 (Ask 2026-08-24)
worker role:success-manager, owns never · chooses 6 (4 rule, 2 read) · sequence review-ask-email · approval first-send, every-send · checks owned-by-a-person, keep-first, customer-ask-cap, reply-hold, display-consent, claims-resolve · stops 7 · placements gmail, outlook, yahoo-mail, g2, capterra, trustpilot, google-business-profile, app-store, google-play, shopify-app-store, chrome-web-store, reddit    catalog  play:review-requests@1 (Not live yet, gaps #5)
```

### Every key from the catalog
| Key | Id | What it is |
| --- | --- | --- |
| Tactic | <a id="tactic-review-venue-choice"></a>`review-venue-choice` | Review venue choice picks the one Venue each Customer is asked on each month, by Review count against threshold, recency and answer-engine citations. Starts at You approve the first wave. |
| Tactic | <a id="tactic-review-ask"></a>`review-ask` | Review ask asks every Customer at the declared delivered-outcome Event for an honest Review on one Venue, once per Venue per 12 months. Starts at You approve each one. |
| Tactic | <a id="tactic-review-reply"></a>`review-reply` | Review reply mirrors every new Review, answers each within a day, turns each defect into an Issue, and shows consented Reviews on the Startup’s Pages. Starts at You approve each one. |
| Tactic | <a id="tactic-review-fact-sweep"></a>`review-fact-sweep` | Review fact sweep finds stale prices, plans and features stated about the Startup in Reviews, listings and cited pages, and answers or routes each one. Starts at You approve each one. |
| Tactic | <a id="tactic-ask-coverage-audit"></a>`ask-coverage-audit` | Ask coverage audit checks weekly that every Customer at the outcome Event was asked or holds a listed reason, and pauses asks otherwise. Starts at Sends nothing. |
| Tactic | <a id="tactic-listing-drift-check"></a>`listing-drift-check` | Listing drift check compares every live listing with its manifest version each week, tests every install command it shows, and pushes or pulls each difference. Starts at You approve each one. |
| Tactic | <a id="tactic-third-party-fact-correction"></a>`third-party-fact-correction` | Third-party fact correction finds outside pages that state a stale fact about the Startup and asks each for one sourced correction. Starts at You approve each one. |
| Trigger | <a id="trigger-delivered-outcome"></a>`delivered-outcome` | Delivered outcome: `event` on `Outcome.delivered`, window 7d. |
| Trigger | <a id="trigger-new-venue-review"></a>`new-venue-review` | New venue review: `event` on `Review.published`, window 1d. |
| Trigger | <a id="trigger-stale-third-party-fact"></a>`stale-third-party-fact` | Stale third-party fact: `event` on `Monitor.matched`, window 30d. |
| Trigger | <a id="trigger-fact-changed"></a>`fact-changed` | Fact changed: `event` on `Change.merged`, window 24h. |
| Sequence | <a id="sequence-review-ask-email"></a>`review-ask-email` | Review ask email: 2 steps; it ends on A Review on the target Venue is matched to the Customer.; The Customer replies.; The Customer opts out or is suppressed.; 14 days pass from the ask.. |