URL: /plays/directory-listings · Id: `play:directory-listings`

# Directory listings

> Directory listings keeps one sourced listing on every directory and review site your buyers read, and fixes any field that drifts within a week.

Directory listings keeps one sourced listing on every directory and review site your buyers read, and fixes any field that drifts within a week.

```text title="Set it up · Ask"
Be our Content Lead: list Leaseline everywhere small property managers compare software, from our own records only, and fix any listing that drifts. Program applications and Review replies come to me. Show me what’s missing; don’t send.
```

```text title="Run one · Ask"
Hand our latest listing check to the Content Lead 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 ("Startup.advanced", "Startup.launched", "Fact.changed", "Monitor.checked", "Monitor.matched", "Review.published")', 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_455",
    "$type": "Decision",
    "choice": "fix-drift",
    "reason": "the Capterra listing shows $19 a month; your Pro Offer’s Price has been $29 since 2026-07-01",
    "stakes": "low",
    "options": [
      "push-the-listing",
      "fix-drift",
      "ask-for-a-correction",
      "apply-to-a-program",
      "reply-to-a-review",
      "ask-for-reviews"
    ],
    "by": "worker_7",
    "on": "listing_4",
    "expected": "the next weekly check reads $29 on listing_4",
    "reopensWhen": "Monitor.checked on listing_4 still differs within 7d"
  },
  "actions": [
    {
      "$id": "https://api.sb/leaseline.app/actions/action_1",
      "$type": "Action",
      "status": "done",
      "mode": "try",
      "by": "worker_7",
      "verb": "Monitor.check",
      "checks": []
    },
    {
      "$id": "https://api.sb/leaseline.app/actions/action_2",
      "$type": "Action",
      "status": "done",
      "mode": "try",
      "by": "worker_7",
      "verb": "Document.render",
      "checks": [
        {
          "check": "blank-over-guess",
          "status": "pass"
        },
        {
          "check": "claims-resolve",
          "status": "pass"
        }
      ]
    },
    {
      "$id": "https://api.sb/leaseline.app/actions/action_3",
      "$type": "Action",
      "status": "pending",
      "reason": "awaitingTime",
      "mode": "try",
      "by": "worker_7",
      "verb": "Integration.call",
      "checks": [
        {
          "check": "invented-value",
          "status": "pass"
        },
        {
          "check": "blank-over-guess",
          "status": "pass"
        },
        {
          "check": "claims-resolve",
          "status": "pass"
        }
      ]
    }
  ]
}
```

`action_3` pushes the price field to Capterra after a 1-hour hold, which you can stop; the other 43 fields read the same as your records.

`8 listing signups a month against 3 before · before-after, not proof · n 15 · path → arr`

| Try another: the listing | Choice | Reason |
| --- | --- | --- |
| Capterra listing: “$19 a month” | `fix-drift` | your Pro Price is $29 since Jul 1 |
| A property-management software directory, 8,000 visits a month, no Leaseline listing | `push-the-listing` | qualified: the buyers your file names read it |
| A comparison blog: “Leaseline has no e-signature.” | `ask-for-a-correction` | your records show e-signature since March |
| A startup perk portal closes applications Oct 31 | `apply-to-a-program` | you qualify; its terms bind, so you sign |
| A 2-star G2 Review: “Setup took a week.” | `reply-to-a-review` | a Review reply goes out in your name, so you approve it |

| Worth | Your time | Spend | First signal | Use it when | Instead |
| --- | --- | --- | --- | --- | --- |
| ≈ +$520 ARR a month at 9 listings | 80 min week 1, then 15 a week | $0.05 per listing check, ~$2 a month at 9 | Day 6: first listing signup | A published Price, 5+ places your buyers read | No published Price: publish the Offer (<Op verb="Offer.publish" />) · under 5 places: `editorial-seo` |

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

Worth’s formula: about +6 listing signups a month (Other Startups’ results; no lift of your own yet) × 0.25 paid per signup × $347 first order ARR ≈ +$520. Under First signal: At 9 listings and about 8 listing signups a month, 90 days can tell only +12 a month or more, so it reads before-after.

`sample · api.sb/leaseline.app · 41 Customers at 2026-10-19 · on Monitor.checked · first drift fixed 3h 12m · 8 keys Not live yet`

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

# 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":"directory-listings"}'

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

# off: no push; live listings stay as they are
curl -X PATCH https://api.sb/leaseline.app/campaigns/directory-listings \
  -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": "directory-listings can’t start yet: 1 check fails and 1 warns.",
  "checks": [
    {
      "check": "use-when",
      "status": "pass",
      "observed": "Pricing Page live; 11 qualified places, 2 listing Leaseline",
      "doc": "/plays/directory-listings#check-use-when",
      "fix": "None needed: 5 places are enough.",
      "then": "Metric.query"
    },
    {
      "check": "baseline",
      "status": "pass",
      "observed": "5 listing signups in 90 days",
      "doc": "/plays/directory-listings#check-baseline",
      "fix": "None needed: 5 in 90 days is your Before.",
      "then": "Metric.query"
    },
    {
      "check": "approved-pages",
      "status": "fail",
      "doc": "/plays/directory-listings#check-approved-pages",
      "fix": "Publish the facts Page; 5 manifest fields stay blank until a record states them.",
      "then": "Site.publish"
    },
    {
      "check": "role-unstaffed",
      "status": "warn",
      "roles": [
        "content-lead",
        "partner-manager"
      ],
      "owned": 0,
      "doc": "/plays/directory-listings#check-role-unstaffed",
      "fix": "Staff the Content Lead, or leave it: applications and replies 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: 11 qualified places, 2 listed; 5 listing signups in 90 days (gaps #8) | <Op verb="Metric.query" /> on `listing-signups`, which fills Before in Your number | 1 min | none |
| <a id="check-approved-pages"></a>`approved-pages` · fail (Not live yet) | <Op verb="Site.publish" /> the facts Page | 20 min | none |
| <a id="check-role-unstaffed"></a>`role-unstaffed` · warn (gaps #8): no Content Lead or Partnerships seat | <Op verb="Role.staff" />, or leave it: applications and Review replies come to you | 0 min | none |

`Your number · Before 3 a month · Now 8 listing signups a month · Target 12 a month by Dec 31 · before-after, not proof · n 15 signups`

Each weekly listing check costs about $0.05 (Worker cost), and every push is free on the places Leaseline uses; perks cost forgone revenue you set, never cash. That comes to about $2 a month at 9 listings.

## When it starts
| When | Trigger id | Starts on | Window | How it gets in |
| --- | --- | --- | --- | --- |
| The weekly check finds a field that differs from your records | `listing-drift` | `event` on `Monitor.checked` where a listing differs from the manifest, or fails its install test | 7d | <Op verb="Monitor.create" /> on each listing; it runs weekly |
| A Price, Offer or fact a listing shows changes | `manifest-record-changed` | `event` on `Fact.changed` where record in manifest.sources | every listing within 24 h | <Op verb="Offer.update" /> or <Op verb="Page.update" />; the change is the Event |
| A new Review or question lands on a listing | `listing-review-posted` | `event` on `Review.published` where listing in startup.listings | 2d | <Op verb="Integration.connect" /> the review sites |
| + 6 more for status changes, install failures, new places, program deadlines and stale outside facts (`?lanes=all`) | | | | |

```json title="One signed delivery · StartupBuilder-Signature"
{
  "$id": "https://api.sb/leaseline.app/events/event_1460",
  "$type": "Event",
  "name": "Monitor.checked",
  "at": "2026-10-19T06:00:12Z",
  "seq": 1460,
  "data": {
    "monitor": "monitor_9",
    "listing": {
      "$id": "https://api.sb/leaseline.app/listings/listing_4",
      "venue": "capterra",
      "field": "price",
      "shows": "$19 a month",
      "records": "$29 a month",
      "since": "2026-07-01"
    }
  }
}
```

```bash title="Reproduce it in test"
npx startup-builder trigger Monitor.checked --like event_1460   # replays the delivery
npx startup-builder clock advance --by 7d   # then runs the next weekly check
npx startup-builder listen Monitor.checked --forward-to http://localhost:3000/hook
```

## Who runs it
**Content Lead · your agent** (`role:content-lead`, `worker_7`) holds the Key Result. It hands program applications, whose terms bind, and every Review reply to **you** (Dana Okafor, person, `worker_1`), because the Partnerships Role has no person. Review asks go only to Customers no other Play is asking. Your number links to Did it work.

| Option | Rung today | Evidence (passed of n) | Moves up at | Drops back when | You can |
| --- | --- | --- | --- | --- | --- |
| Fix drift · Push the listing | It does it, you can stop it | 7 of 7 · 9 of 9 | met on day 14 (the first 10 pushes) | a field shows a value no record held → You approve each one | <Op verb="Action.cancel" /> inside the hold |
| Ask a publisher to correct | You approve each one | 2 of 2 | at 5 approved | — | <Op verb="Approval.approve" /> · <Op verb="Approval.refuse" /> |
| Apply to a program · Reply to a Review | To a person, every time | 1 · 3 | never: terms bind, and a reply speaks for you | — | <Op verb="Approval.approve" /> · <Op verb="Task.claim" /> |
| Ask for Reviews | You approve the first wave | 1 wave, 18 Customers | at 2 waves with no audit gap | the asked set misses an eligible Customer → paused | <Op verb="Approval.approve" /> |
| Change the listing rule | You approve each one | 0 merged | never | — | <Op verb="Change.merge" /> · <Op verb="Change.revert" /> |
| Never: a Review it writes, a rating it suggests, a rival named, a superlative no record states | To a person, every time | 0 asked | never | — | <Op verb="Task.claim" /> |

**What it reads · What it may call.** It reads your Offers, Prices, Pages and fact records, and each listing as the place shows it. A Visit from a listing chooses nothing and never appears in a Message. Its tools are <Op verb="Document.render" />, <Op verb="Integration.call" />, <Op verb="Monitor.create" />, <Op verb="Monitor.check" />, <Op verb="Message.schedule" />, <Op verb="Document.create" />, <Op verb="Approval.create" />, <Op verb="Comment.create" />, <Op verb="Message.broadcast" />, and any other call becomes an Approval under the Role’s Mandate.

```md title="The brief (folded): compiled Agent-register Markdown · play:directory-listings@1 · each line names its record key"
You hold the Content Lead Key Result: 12 listing signups a month by 2026-12-31.  ← worker, metric
On each check, record change, new place or Review, choose one option and say why. ← triggers, chooses
Fill every field from a sourced record; a field with no source stays blank.      ← copyRules 1; sendGates Blank over guess
No rank, superlative or rival’s name appears on any listing.                     ← copyRules 2, 3
Ask for Reviews only when the ask covers every eligible Customer; suggest no text or rating. ← copyRules 7; sendGates Review ask audit
Program terms and Review replies go to Dana.                                     ← your file, line 4
```

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

**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 each one when it does not; <Op verb="Role.staff" /> seats it.

```bash
npx startup-builder eval --scenario directory-listings-11 --agent "claude -p" --seat content-lead   # seated: 44 of 44 fields sourced, drift fixed in 0.4 days, 0 Checks failed sent · yours: 43 of 44, 1 failed `blank-over-guess` → You approve each one
```

## What it can do next
| Option | Picked when | Decided by | Runs | Hands to | If it’s just you |
| --- | --- | --- | --- | --- | --- |
| Push the listing `push-the-listing` | a qualified place has no listing, or a shown record changed | Your rule (line 4) | <Op verb="Document.render" /> the manifest → <Op verb="Integration.call" /> → <Op verb="Monitor.create" /> | nobody; a captcha or signed form goes to you | the same |
| Fix drift `fix-drift` | a live field differs from your records | Your rule (line 4) | <Op verb="Monitor.check" /> → <Op verb="Integration.call" /> the one field | nobody | the same |
| Ask a publisher to correct `ask-for-a-correction` | an outside page states a stale fact | Agent’s call, with its reason | `publisher-correction` → <Op verb="Message.schedule" /> | the publisher’s listed contact | the same |
| Apply to a program `apply-to-a-program` | a program you qualify for closes within 14 days | Agent’s call, with its reason | <Op verb="Document.create" /> → <Op verb="Approval.create" /> → `program-status-check` at 10 days | <Op verb="Task.assign" /> → you sign, due 1 business day · Take this one | you |
| Reply to a Review `reply-to-a-review` | a new Review or question on a listing | Agent’s call, with its reason | <Op verb="Comment.create" /> drafted → <Op verb="Approval.create" /> | you approve it, due 1 business day · Take this one | you |
| Ask for Reviews `ask-for-reviews` | every eligible Customer is in the asked set | Your rule (the audit) | <Op verb="Message.broadcast" /> to the whole eligible set | nobody; held Customers go to their CSM first | the same |

| When they write back | Fixture reply | What happens | Who answers | By when | Call |
| --- | --- | --- | --- | --- | --- |
| Interested | “Happy to fix it. What’s the right figure?” | answers with the fact and its source link, quoting only the stale claim | Content Lead · your agent | 1 business day | <Op verb="Conversation.answer" /> |
| Send me pricing | “What does Leaseline cost now?” | answers with the Pricing Page’s list Prices, $29 and $79 a month | Content Lead · your agent | 1 business day | <Op verb="Conversation.answer" /> |
| Not now | “We update comparisons each quarter.” | the follow-up stops; the Decision reopens at their next update | nobody | at once | <Op verb="Enrollment.cancel" /> |
| Wrong person | “Our editor, Sam, handles corrections.” | asks them to forward it; it never writes to an address from their text | Content Lead · your agent | 1 business day | <Op verb="Conversation.answer" /> |
| Out of office | an auto-reply until Oct 28 | the follow-up waits for the return date | nobody | the return date | <Op verb="Message.schedule" /> |
| Question it can’t answer | “Want a featured slot? It’s $400 a month.” | To a person: paid placement is your call | you | 1 business day | <Op verb="Conversation.escalate" /> |
| Stop | “Don’t contact us again.” | that publisher is suppressed on every lane | nobody | at once | <Op verb="Contact.suppress" /> |

## When it hands to you
Program applications and Review replies come to **you**: terms bind, and a reply speaks in your 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_563",
    "$type": "Task",
    "assignee": "worker_1",
    "due": "2026-10-09T16:00:00Z",
    "on": "review_12",
    "brief": {
      "who": "a G2 reviewer, a 30-unit property manager",
      "said": "Setup took a week.",
      "chose": "reply-to-a-review",
      "why": "a reply speaks in your name",
      "draft": "Thanks for the honest Review. Setup now imports units from a spreadsheet in one step; if you want, Dana can walk you through it.",
      "never": [
        "a suggested rating",
        "an offer in exchange for an edit"
      ]
    }
  },
  {
    "$id": "https://api.sb/leaseline.app/approvals/approval_67",
    "$type": "Approval",
    "verdict": null,
    "deadline": "2026-10-24T17:00:00Z",
    "draft": "Perk portal application: first month free on Leaseline Pro, 50 codes",
    "route": "apply-to-a-program",
    "checks": [
      {
        "check": "binding-terms",
        "status": "held"
      },
      {
        "check": "claims-resolve",
        "status": "pass"
      }
    ]
  }
]
```

You answer with <Op verb="Task.claim" /> (Take this one), <Op verb="Task.return" /> with a reason, <Op verb="Approval.approve" /> or <Op verb="Approval.refuse" />, each a one-call window; an Approval unanswered by its deadline expires and nothing is signed. Once you claim, the agent’s pending draft cancels with `claimedByPerson` (Not live yet, gaps #7). A Task nobody claims by its due time goes up through <Op verb="Task.escalate" />, and `person-promise` fires.

**On time:** you, 4 of 4 claimed within their due time, median 5 h, n 4, ■□□□ Too early to tell beside Other Startups’ 90 % on time. **Your queue:** counted by kind: in week 1, 9 listing approvals and 1 application; since then about 1 Review reply a week and the weekly review.

**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; Not live yet, gaps #8) | a Review ask to a Customer whose open Deal or Conversation a person holds | 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; Not live yet, gaps #8) | a Review ask to a Customer at risk, past due or with 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 3. | <Op verb="Decision.list" /> |
| <a id="check-claims-resolve"></a>`claims-resolve` (shared) | a listing field, rank or superlative that resolves to no record | Cut the claim, or name the record that backs it. | <Op verb="Content.draft" /> |
| <a id="check-blank-over-guess"></a>`blank-over-guess` (the record’s Blank over guess) | a field filled with no sourced record behind it | Leave it blank, or publish the record that states it. | <Op verb="Page.update" /> |
| <a id="check-review-ask-audit"></a>`review-ask-audit` (the record’s Review ask audit) | a Review ask that leaves out an eligible Customer | Widen the ask to every eligible Customer, then send. | <Op verb="Audience.update" /> |
| <a id="check-binding-terms"></a>`binding-terms` (the record’s Binding terms hold) | an Agreement or binding term with no person’s signature | You sign it, or the application stays in draft. | <Op verb="Agreement.sign" /> |

| Stop (pauses the Play) | Pauses when | Then |
| --- | --- | --- |
| <a id="stop-invented-value"></a>`invented-value` | a listing shows a value no manifest version held | every push pauses; You approve each one |
| <a id="stop-policy-rejection"></a>`policy-rejection` | 3 rejections on one place in 30 days, or 5 across all | submissions there pause; the sweep drops to You approve each one |
| <a id="stop-accuracy-floor"></a>`accuracy-floor` | weekly accuracy under 98 % of checked fields, 2 weeks running | discovery pauses until it recovers |
| <a id="stop-install-command"></a>`install-command` | an install command a listing shows fails in a clean sandbox | it comes off every listing within 24 h |
| <a id="stop-venue-warning"></a>`venue-warning` | a place warns, removes or suspends a listing | every Tactic on that place pauses; the Marketing Lead is told |
| <a id="stop-correction-complaint"></a>`correction-complaint` | 2 publisher complaints in 30 days | outside corrections pause; that publisher is suppressed |
| <a id="stop-yield-floor"></a>`yield-floor` | 90 days at 20+ verified listings with no listing signup | it drops to drift checks and pushes only |
| <a id="stop-kill-switch"></a>`kill-switch` | you press it | every Tactic pauses; live listings stay as they are |

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

```json title="The captured refusal · sample"
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "The G2 description did not push: “the #1 lease renewal app” resolves to no record.",
  "checks": [
    {
      "check": "claims-resolve",
      "status": "fail",
      "doc": "/plays/directory-listings#check-claims-resolve",
      "fix": "Cut the claim, or name the record that backs it.",
      "then": "Content.draft"
    },
    {
      "check": "blank-over-guess",
      "status": "pass"
    }
  ]
}
```


```json title="The Stops that fired, as Campaign.paused reasons · sample"
[
  {
    "reason": {
      "limit": "policy-rejection",
      "observed": "3 rejections on one directory",
      "bound": "3",
      "window": "30d"
    },
    "doc": "/plays/directory-listings#stop-policy-rejection",
    "task": "task_559"
  }
]
```

## Did it work
Listings can’t be held back from a buyer at random, and at about 8 signups a month no alternating weeks read in 90 days, so the basis is before-after (rule o): the columns read Now and Before, and nothing prints as Added. Every node above the leaf is Not live yet (gaps #4).

`8 listing signups a month against 3 before · before-after, not proof · n 15 · path → arr`

| Node | Now (56 days) | Before (the 90 days, per 56) | Added | Confidence | Basis |
| --- | --- | --- | --- | --- | --- |
| `listing-signups` | 15 signups from 1,120 listing visits | 3 from 410 | — | — | before-after, not proof · Other Startups’ results +6 a month, ■■□□ Early sign, n 640 listings |
| `new-leads` | 15 of 212 new Leads came through a listing | 3 of 190 | — | — | before-after |
| `new-arr` ↑ | $1,388, 4 Customers on Pro · Booked: credited, not all caused by this Play | $347, 1 Customer | — · Worth about +$520 a month | — | before-after · 5 trials still deciding |
| `net-new-arr` · `arr` | carried from `new-arr` | carried | — | — | carried |

Before against after reads 56 days against the 90 days before, scaled to 56; it is not proof, and Other Startups’ results print beside it. The leaf counts signups whose first Visit came from a listing, over 30 days.

**How it moves it:** every listing shows the $29 and $79 Prices and 39 sourced facts, where 2 old listings showed $19 and no facts.

**Guardrails:** before against after: paid per listing signup, 0.27 against 0.33, and weekly field accuracy, 100 % against 61 %; 5 trials are still deciding.

```json title="The Campaign card · sample"
{
  "$id": "https://api.sb/leaseline.app/campaigns/directory-listings",
  "$type": "Campaign",
  "play": "play:directory-listings@1",
  "path": [
    "listing-signups",
    "new-leads",
    "new-arr",
    "net-new-arr",
    "arr"
  ],
  "experiment": {
    "metric": "listing-signups",
    "unit": "count",
    "heldBack": 0,
    "decision": null
  },
  "readout": {
    "value": null,
    "n": 15,
    "maturing": 5,
    "interval": null,
    "readability": "inadmissible",
    "basis": "before-after",
    "pooled": true
  },
  "ledger": {
    "spend": 0,
    "workerCost": 3,
    "hours": 2.3,
    "openArr": 1735,
    "wonArr": 1388,
    "projectedArr": 520,
    "paybackDays": 1
  }
}
```

Outside benchmark: G2 was 15.9 % of the outside sources answer engines cited for pricing questions (a pricing-answer study across 6 engines, its data partner a disclosed sponsor).

**Before it · After it.** Before it: `editorial-seo` and `aeo` bring the buyers who compare on these places. After it: `instant-response` answers the listing signups who write in, and `review-requests` hears the Customers who left a Review.

## Example
Leaseline ran it from 2026-08-24 to 2026-10-19 (56 days): 11 qualified places, 9 verified listings, 7 drift fixes, 2 outside corrections, 1 program application and 1 Review wave. Read every captured thread at `?threads=all`; three follow the tables.

| What happened, by option | Decisions | Done | Listing signups after |
| --- | --- | --- | --- |
| Push the listing · Fix drift | 9 · 7 | 9 live · 7 fixed, median 3 h | 15 in all |
| Ask a publisher to correct · Apply to a program | 2 · 1 | 1 corrected, 1 still deciding · 1 awaiting your signature | — |
| Reply to a Review · Ask for Reviews | 3 · 1 wave | 3 replies you approved · 18 Customers asked, 6 Reviews | — |
| **All** · the 90 days before, per 56 | **23** | **9 listings, 100 % of fields sourced** | **15** · 3 |

| Day | Event | What happened | Your time |
| --- | --- | --- | --- |
| 0 | `event_1421` Campaign.created | the three steps; the dry run fails `approved-pages` | 20 min |
| 0 | `event_1422` Site.published | the facts Page goes live; 39 of 44 fields sourced, 5 blank | 20 min |
| 1–7 | `event_1425` Approval.created, first of 9 | you approved the first 9 pushes; 2 captchas and 1 signed form | 40 min |
| 6 | `event_1430` Lead.created | the first listing signup, from a property-management directory | — |
| 14 | `event_1437` Role.updated | promoted: pushes and drift fixes send after a 1-hour hold | — |
| 33 | `event_1449` Campaign.paused | `policy-rejection`: one directory rejected 3 screenshots; you swapped them | 15 min |
| 56 | `event_1460` Monitor.checked | Capterra shows $19; the fix waits its hold | — |
| 1–56 | `event_1428` Task.assigned, first of 4 | 3 Review replies and 1 application; weekly reviews, from the <Op verb="Task.next" /> log | 45 min · **total 2.3 h measured, 2.5 h asked** |

> **Leaseline → a comparison blog’s listed contact, Sep 12, 10:02** · Hi, your Sep 2 comparison says “Leaseline has no e-signature.”¹ Leaseline has offered e-signature on every plan since March 2026: leaseline.app/features/e-signature.² Could you update the line?
>
> **Editor, Sep 13** · “Fixed, thanks.” The next check read the corrected line.

```diff title="G2 description, 2026-09-03 · the draft claims-resolve refused, then the rewrite that pushed"
- The #1 lease renewal app for small property managers.
+ Leaseline sends every renewal notice 60 days out, per owner, for property managers with 10 to 500 units.
```

> **G2 Review, 2026-10-08 16:00** · 2 stars: “Setup took a week.” · The draft reply went to you (`task_563`), due 16:00 on Oct 9; **Dana, Oct 9, 11:20** · *approved the reply with one edit and posted it in Leaseline’s name.*³

¹ A correction quotes nothing but the stale claim. ² It states the fact and its source. ³ No reply offers anything for an edit or a rating.

## Your settings
```mdx title="plays/directory-listings.mdx"
---
holdout: 0        # a listing can’t be held back from a buyer; it reads before-after
---
List Leaseline wherever small property managers compare software, from our records only. Program applications and Review replies come to Dana.
```

```text
$ npx startup-builder show plays/directory-listings
holdout 0 · applications and replies to Dana     your file  plays/directory-listings.mdx:2,4 (Ask 2026-08-24)
family demand · process launches · worker role:content-lead, owns never · chooses 6 (3 rule, 3 read) · sequences publisher-correction, program-status-check · checks owned-by-a-person, keep-first, customer-ask-cap, claims-resolve, blank-over-guess, review-ask-audit, binding-terms · stops 8 · placements g2, capterra, trustpilot, google-business-profile, yelp, github    catalog  play:directory-listings@1 (Not live yet, gaps #5)
```

### Every key from the catalog
| Key | Id | What it is |
| --- | --- | --- |
| Tactic | <a id="tactic-listing-manifest"></a>`listing-manifest` | Listing manifest builds one versioned set of sourced listing fields from the Startup's records and renders it for each Venue's own fields. Starts at You approve the first wave. |
| Tactic | <a id="tactic-listing-sweep"></a>`listing-sweep` | Listing sweep finds every qualified place the ICP looks, claims or submits the Startup there as itself, verifies it and tags its link. Starts at You approve each one. |
| 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. |
| Tactic | <a id="tactic-program-applications"></a>`program-applications` | Program applications list the Startup on perk portals, startup programs and member-benefit programs, each with one coded perk and a human signing binding terms. Starts at You approve each one. |
| 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-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. |
| Trigger | <a id="trigger-startup-entered-building"></a>`startup-entered-building` | Startup entered Building: `state` on `Startup.advanced`, window 7d. |
| Trigger | <a id="trigger-startup-entered-live"></a>`startup-entered-live` | Startup entered Live: `state` on `Startup.launched`, window 7d. |
| Trigger | <a id="trigger-manifest-record-changed"></a>`manifest-record-changed` | Manifest record changed: `event` on `Fact.changed`, window 7d. |
| Trigger | <a id="trigger-listing-drift"></a>`listing-drift` | Listing drift: `event` on `Monitor.checked`, window 7d. |
| Trigger | <a id="trigger-install-command-failed"></a>`install-command-failed` | Install command failed: `event` on `Monitor.matched`, window 1d. |
| Trigger | <a id="trigger-listing-venue-found"></a>`listing-venue-found` | Listing venue found: `event` on `Monitor.matched`, window 30d. |
| Trigger | <a id="trigger-program-deadline"></a>`program-deadline` | Program deadline: `at` on `Monitor.matched`, window 14d. |
| 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-listing-review-posted"></a>`listing-review-posted` | Listing review posted: `event` on `Review.published`, window 2d. |
| Sequence | <a id="sequence-publisher-correction"></a>`publisher-correction` | Publisher correction: 2 steps; it ends on A reply; The page is corrected; An opt-out; Day 28 after the first Message. |
| Sequence | <a id="sequence-program-status-check"></a>`program-status-check` | Program status check: 1 steps; it ends on A decision; A reply; An opt-out; The deadline passes. |
| Benchmark | <a id="benchmark-pricing-answers-g2-share"></a>`pricing-answers-g2-share` | G2's share of the outside sources cited in answer-engine pricing answers.: 15.9 % (Pricing-answer study across 6 answer engines; the data partner is a disclosed sponsor., 2026-09-02). |