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.
- Step
- Get found
- Channel
- Reviews and referrals
- Kind
- Search
- Summary
- startupbuilder.dev
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.
# Not live yet (gaps #2)
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"}'import { $ } from 'startup-builder'
return await $.Campaign.create({ play: 'review-requests' }, { mode: 'try' }) // dry run on the day-0 Startup, 2026-08-24 · Not live yet, gaps #2
npx startup-builder do Campaign.create --json '{"play":"review-requests"}' --try # Not live yet (gaps #2){
"name": "do",
"arguments": {
"code": "return await $.Campaign.create({ play: 'review-requests' }, { mode: 'try' }) // dry run on the day-0 Startup, 2026-08-24 · Not live yet, gaps #2",
"mode": "try"
}
}application/json{
"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"
},
…
Hand our latest Customer who reached the outcome to the CSM agent and show me what it chose and why. Don’t send.
curl "https://api.sb/leaseline.app/events?where=name%20in%20(%22Outcome.delivered%22%2C%20%22Review.published%22%2C%20%22Monitor.matched%22%2C%20%22Change.merged%22)&limit=1" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
# Not live yet (gaps #7)
curl -X POST "https://api.sb/leaseline.app/triggers/fire?mode=try" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"like":"event_9104"}'
# Not live yet (gaps #7)
curl "https://api.sb/leaseline.app/decisions?where=on%20%3D%20%22customer_23%22" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
# Not live yet (gaps #7)
curl "https://api.sb/leaseline.app/actions?where=on%20%3D%20%22customer_23%22" \
-H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"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 }
npx startup-builder do Event.list --json '{"where":"name in (\"Outcome.delivered\", \"Review.published\", \"Monitor.matched\", \"Change.merged\")","limit":1}'
npx startup-builder do Trigger.fire --json '{"like":"event_9104"}' --try # Not live yet (gaps #7)
npx startup-builder do Decision.list --json '{"where":"on = \"customer_23\""}' # Not live yet (gaps #7)
npx startup-builder do Action.list --json '{"where":"on = \"customer_23\""}' # Not live yet (gaps #7){
"name": "do",
"arguments": {
"code": "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\nconst { subject } = await $.Trigger.fire({ like: last.$id }, { mode: 'try' }) // the same Event again, in try mode · Not live yet, gaps #7\nconst { decisions: [decision] } = await $.Decision.list({ where: `on = \"${subject}\"` }) // what it chose, and why · Not live yet, gaps #7\nconst { actions } = await $.Action.list({ where: `on = \"${subject}\"` }) // what it did, with its checks · Not live yet, gaps #7\nreturn { decision, actions }",
"mode": "try"
}
}application/json{
"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"
…
sample · 4 Not live yetPreviewOn PATCH /{namespace}/campaigns/{id}Off PATCH /{namespace}/campaigns/{id}Take this one POST /{namespace}/tasks/{task}/claim
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,
| 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 |
- 20 min week 1, then 10 a week
- Your time
- Day 3: the first Review published
- First signal
- 10+ Customers at an outcome, a Venue listing
- Use it when
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: and reads before-after.
sample · api.sb/
Start it this week
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"}' | Check | Fix | Then |
|---|---|---|
use-when pass · Not live yet, gaps #8 | Keep 10 or more Customers at an outcome, or run written-references. | POST /{namespace}/metrics/query |
outcome-event fail | Declare “a lease renewed from a Leaseline notice” as the outcome, then publish. | POST /{namespace}/offers/{offer}/publish |
baseline warn · Not live yet, gaps #8 | n 6: it reads before-after, not proof, until volume allows a held-back half. | POST /{namespace}/metrics/query |
role-unstaffed warn · Not live yet, gaps #8 | Staff the CSM, or leave it: low scores come to you, and Priya’s 4 get drafts. | POST /{namespace}/roles/{role}/staff |
JSON
{
"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"
}
]
}It says no, says why, and names the fix.
| Check | Fix (one call) | Your time | Wait |
|---|---|---|---|
use-when · baseline · pass: 41 Customers, 14 at the outcome; warn: 1 of 6 in 90 days (gaps #8) | POST /{namespace}/metrics/query on reviews-per-100-asks, which fills Before in Your number | 1 min | none |
outcome-event · fail: no outcome Event on the Offer | POST /{namespace}/offers/{offer}/publish with the outcome Event declared | 5 min | none |
role-unstaffed · warn (gaps #8): 4 of your Customers have a person owner, Priya | POST /{namespace}/roles/{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,
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 POST /{namespace}/events/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 | POST /{namespace}/integrations/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 | POST /{namespace}/monitors/{monitor}/check on the listings that cite you |
+ 1 more for changed facts (?lanes=) |
{
"$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
}
}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:, 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 | POST /{namespace}/actions/{action}/cancel inside the hold |
| Answer the Review | You approve each one | 9 of 9 | at 20 passed in a row | — | POST /{namespace}/approvals/{approval}/approve · POST /{namespace}/approvals/{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 | — | — | POST /{namespace}/actions/{action}/redirect |
| Change the outcome Event or the Venue rule | You approve each one | 0 merged | never | — | POST /{namespace}/changes/{change}/merge · POST /{namespace}/changes/{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 POST /{namespace}/conversations/{conversation}/answer, POST /{namespace}/issues/{issue}/triage, POST /{namespace}/tasks/{task}/assign, POST /{namespace}/reviews/{review}/publish, and any other call becomes an Approval under the Role’s Mandate.
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. POST /{namespace}/roles/{role}/staff seats it.
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 | POST /{namespace}/conversations/{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) | POST /{namespace}/issues/{issue}/triage on the defect → POST /{namespace}/tasks/{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 | POST /{namespace}/conversations/{conversation}/answer on a listing; a forum reply drafted → POST /{namespace}/tasks/{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) | POST /{namespace}/reviews/{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 | POST /{namespace}/conversations/{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 | POST /{namespace}/conversations/{conversation}/answer |
| Not now | “Busy this month, sorry.” | the ask closes, and customer-ask-cap frees the Customer | nobody | at once | GET /{namespace}/decisions |
| 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 | POST /{namespace}/conversations/{conversation}/answer |
| Out of office | an auto-reply until Oct 29 | the reminder waits for the return date | nobody | the return date | POST /{namespace}/messages/{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 | POST /{namespace}/conversations/{conversation}/escalate |
| Stop | “Please don’t ask me for reviews.” | no ask again, on any Venue, from any Play | nobody | at once | POST /{namespace}/contacts/{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).
[
{
"$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 POST /{namespace}/tasks/{task}/claim (Take this one), POST /{namespace}/tasks/{task}/return with a reason, POST /{namespace}/actions/{action}/redirect, POST /{namespace}/approvals/{approval}/approve or POST /{namespace}/approvals/{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 POST /{namespace}/tasks/{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: POST /{namespace}/deals/{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 |
|---|---|---|---|
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. | POST /{namespace}/tasks/{task}/assign |
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. | POST /{namespace}/tasks/next |
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. | GET /{namespace}/decisions |
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. | POST /{namespace}/tasks/{task}/assign |
display-consent (marketing) | a Review on your Pages without the reviewer’s yes | Ask the reviewer once; the Review shows when they agree. | POST /{namespace}/messages/{message}/schedule |
claims-resolve (hub) | a reply or correction sentence no approved Page backs | Cut the sentence, or cite the dated fact and its source. | POST /{namespace}/content/draft |
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.
| Check | Fix | Then |
|---|---|---|
reply-hold fail | Have a person answer it, and offer nothing for an edit. | POST /{namespace}/tasks/{task}/assign |
claims-resolve pass | — | POST /{namespace}/content/draft |
JSON
{
"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"
}
]
}It says no, says why, and names the fix.
[
{
"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 POST /{namespace}/metrics/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,
- Chose
- ask-on-one-venue
- Why
- 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
- From
- ask-on-one-venue · answer-the-review · low-score-to-a-person · correct-a-fact · show-on-your-pages · not-now
- Expects
- a Review on Capterra within 14d
- Reopens when
- no Review.published matched to customer_23 within 14d
- By
- worker_5
JSON
{
"$id": "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"
}| 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.
{
"$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=; 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.
- 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_58and 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
---
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. $ 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
Compiled, not written: edit the Play record review-requests and its Leaseline run, then regenerate. Every step runs from a sentence or a line of code. In your words: the CRM words you know, and where each lives here.