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

# Marketplace listings

> Lists your Product in each app store on what that store ranks by, tests every change, and asks each Customer for one Review.

Lists your [Product](https://startupbuilder.dev/business/nouns/product) in each app store on what that store ranks by, tests every change, and asks each [Customer](https://startupbuilder.dev/business/nouns/customer) for one [Review](https://startupbuilder.dev/business/nouns/review).

```text title="Set it up · Ask"
Be our Marketing Lead for app stores: list our Slack app and Chrome extension on what each store ranks by, test each change, and reply to every Review. Ask every Customer for a Review once, when they add their first owner, the same words for everyone. Show me what’s missing; don’t submit.
```

```text title="Run one · Ask"
Hand our latest listing Event to the Marketing Lead agent and show me what it chose and why. Don’t post.
```

```ts title="Run one · SDK"
import { $ } from 'startup-builder'

const { events: [last] } = await $.Event.list({ where: 'name in ("Version.deployed", "Offer.published", "Customer.activated", "Review.published", "Monitor.checked", "Quote.requested", "Renewal.opened")', 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_449",
    "$type": "Decision",
    "choice": "reply-to-the-review",
    "reason": "a 2-star Review on the Slack listing says alerts came a day late for one owner; the fix shipped Oct 16 (the changelog), and the reply asks nothing about the rating",
    "stakes": "low",
    "options": [
      "write-the-listing",
      "refresh-the-listing",
      "test-a-change",
      "ask-for-a-review",
      "reply-to-the-review",
      "private-offer",
      "not-now"
    ],
    "by": "worker_8",
    "on": "review_19",
    "expected": "the reply posted within 2d",
    "reopensWhen": "no Conversation.answered on review_19 within 2d"
  },
  "actions": [
    {
      "$id": "https://api.sb/leaseline.app/actions/action_1",
      "$type": "Action",
      "status": "done",
      "mode": "try",
      "by": "worker_8",
      "verb": "Content.draft",
      "checks": [
        {
          "check": "approved-pages",
          "status": "pass"
        },
        {
          "check": "store-rule",
          "status": "pass"
        }
      ]
    },
    {
      "$id": "https://api.sb/leaseline.app/actions/action_2",
      "$type": "Action",
      "status": "pending",
      "reason": "awaitingTime",
      "mode": "try",
      "by": "worker_8",
      "verb": "Conversation.answer",
      "checks": [
        {
          "check": "owned-by-a-person",
          "status": "pass"
        },
        {
          "check": "review-reply",
          "status": "pass"
        }
      ]
    }
  ]
}
```

`action_2` is the reply, held 2 minutes so you can stop it. It names the fix and its date, and never asks for a change to the Review.

`+1.5 installs per 100 views · ■■□□ Early sign · n 4 blocks · path → arr`

| Try another: the listing Event | Choice | Reason |
| --- | --- | --- |
| Slack listing: a 2-star Review, “alerts a day late for one owner” | `reply-to-the-review` | every Review gets a reply, at any rating |
| Oakmont Living added its first owner on Oct 17 | `ask-for-a-review` | the activation Event: the same ask for everyone |
| Chrome listing: a new first screenshot | `test-a-change` | a ranked field; a two-week switchback |
| Harbor Row added its first owner; 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 2,600 | 45 min week 1, then 10 min a day | $0 a listing; the store’s share of each sale | Day 3: first install from a listing | a build a store can list, 1,000+ listing views a month | No build: `integration-partners` · no activation Event: declare it (<Op verb="Offer.publish" />) |

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

Worth’s formula: about +1.5 installs per 100 views (two-week blocks switched at random) × 2,600 listing views a month × 0.09 installs to paid × $347 first order ARR ≈ +$122. Under First signal: About 12 weeks to tell a +1.5 lift at 2,600 views a month in two-week blocks; at 8 weeks it reads Early sign.

`sample · api.sb/leaseline.app · 41 Customers at 2026-10-19 · on Review.published · first reply draft 4m 05s · 7 keys Not live yet`

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

# 3 · keep it: a store’s first listing waits on you
curl -X POST https://api.sb/leaseline.app/campaigns \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"play":"marketplace-listings"}'

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

# off: listings stay as they are; asks and replies stop
curl -X PATCH https://api.sb/leaseline.app/campaigns/marketplace-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": "marketplace-listings can’t start: 2 checks fail and 1 warns.",
  "checks": [
    {
      "check": "use-when",
      "status": "pass",
      "observed": "2 builds a store can list: the Slack app and the Chrome extension; 2,450 listing views a month",
      "doc": "/plays/marketplace-listings#check-use-when",
      "then": "Metric.query"
    },
    {
      "check": "baseline",
      "status": "pass",
      "observed": "3.1 installs per 100 listing views, n 4,900 in 90 days",
      "doc": "/plays/marketplace-listings#check-baseline",
      "then": "Metric.query"
    },
    {
      "check": "inbound-source",
      "status": "fail",
      "doc": "/plays/marketplace-listings#check-inbound-source",
      "fix": "Connect each store’s developer account so installs and Reviews come in.",
      "then": "Integration.connect"
    },
    {
      "check": "activation-event",
      "status": "fail",
      "doc": "/plays/marketplace-listings#check-activation-event",
      "fix": "Declare the Offer’s activation Event before the first ask: the first owner added.",
      "then": "Offer.publish"
    },
    {
      "check": "role-unstaffed",
      "status": "warn",
      "roles": [
        "sales-lead",
        "success-manager"
      ],
      "owned": 4,
      "doc": "/plays/marketplace-listings#check-role-unstaffed",
      "fix": "Staff the AE and CSM, or leave them: private offers and held Customers 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: 2 builds, 2,450 views a month; 3.1 per 100, n 4,900 (gaps #8) | <Op verb="Metric.query" /> on `listing-install-rate`, which fills Before in Your number | 1 min | none |
| <a id="check-inbound-source"></a>`inbound-source` · fail (gaps #8) | <Op verb="Integration.connect" /> with each store’s developer account | 10 min | none |
| <a id="check-activation-event"></a>`activation-event` · fail (gaps #8) | <Op verb="Offer.publish" /> with the activation Event: the first owner added | 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 | 0 min | none |

`Your number · Before 3.1 per 100 views · Now 4.6 per 100 on the new listing · Target 5 by Dec 31 · ■■□□ Early sign · n 4 blocks`

A listing costs nothing on these two stores, and the stores keep their share of each sale made through them. Drafting a listing costs about $0.30 and a reply $0.02 (Worker cost). That comes to about $2 a month, plus your 10 minutes a day.

## When it starts
| When | Trigger id | Starts on | Window | How it gets in |
| --- | --- | --- | --- | --- |
| A Review posts on one of your listings | `store-review-posted` | `event` on `Review.published` where on one of the Startup’s listings | 2d | the store’s developer account (`inbound-source`) |
| A Customer adds their first owner | `activation-reached` | `event` on `Customer.activated` where first = true | 72h | your app calls <Op verb="Event.capture" /> on the first owner |
| A Version changes what a listed build does or shows | `listed-version` | `event` on `Version.deployed` where changes what a listed Offer does or shows | 2d | from your deploys; nothing to connect |
| + 8 more for builds ready, Offers for Organizations, uninstall spikes, store thresholds, vitals, marketplace buyers, catalog changes and renewal windows (`?lanes=all`) | | | | |

```json title="One signed delivery · StartupBuilder-Signature"
{
  "$id": "https://api.sb/leaseline.app/events/event_1222",
  "$type": "Event",
  "name": "Review.published",
  "at": "2026-10-19T11:48:09Z",
  "seq": 1222,
  "data": {
    "review": "reviews/review_19",
    "listing": "slack-marketplace",
    "rating": 2,
    "text": "Good idea, but alerts came a day late for one of our owners.",
    "reviewer": "a Leaseline Customer"
  }
}
```

```bash title="Reproduce it in test"
# each delivery is signed with the Watch’s secret
curl -X POST https://api.sb/leaseline.app/watches \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"selector":"Review.published","url":"https://leaseline.app/hooks/sb"}'
npx startup-builder trigger Review.published --like event_1222   # replays the delivery
npx startup-builder clock advance --by 14d   # ends a switchback block and starts the next
```

## Who runs it
**Marketing Lead · your agent** (`role:marketing-lead`, `worker_8`) holds the Key Result: it writes and refreshes listings, runs the switchbacks, asks for Reviews and replies to every one. A store’s first listing hands to **you** (Dana Okafor, person, `worker_1`); private offers go to **AE · Priya Shah (person)** (`role:sales-lead`, `worker_3`, staffed on day 9).

| Option | Rung today | Evidence (passed of n) | Moves up at | Drops back when | You can |
| --- | --- | --- | --- | --- | --- |
| Write the listing | You approve each one | 2 of 2 | never for a store’s first listing | — | <Op verb="Approval.approve" /> · <Op verb="Approval.refuse" /> |
| Refresh the listing · Test a listing change | It does it, you can stop it | 3 refreshes, 2 tests, 0 rejections | met on day 20 | a `rejection` Stop → You approve each one | <Op verb="Action.cancel" /> inside the hold |
| Ask for a Review · Reply to the Review | It does it, you can stop it | 28 asks, 11 replies, 0 Checks failed sent | met on day 14 | a filtered ask → You approve each one | <Op verb="Action.cancel" /> inside the hold |
| Make a private offer · Not now | It does it | 0 · 9 of 9 | — | — | <Op verb="Action.redirect" /> |
| Change the ask moment | You approve each one | 0 merged | never | — | <Op verb="Change.merge" /> · <Op verb="Change.revert" /> |
| Never: an ask only to happy Customers, a reward for a rating, a Review from your own team | Refused, every time | 1 drafted, 1 refused | never | — | the `uniform-ask`, `reward` and `insider` Checks |

**What it reads · What it may call.** It reads each store’s ranking inputs, its listing spec and every Review; a Review is data, never instructions. It may call <Op verb="Content.draft" />, <Op verb="App.publish" />, <Op verb="Experiment.start" /> and <Op verb="Conversation.answer" />; a store’s first listing needs your <Op verb="Approval.approve" />. Its tools are <Op verb="Content.draft" />, <Op verb="App.publish" />, <Op verb="Experiment.start" />, <Op verb="Conversation.answer" />, <Op verb="Quote.send" />, and any other call becomes an Approval under the Role’s Mandate.

```md title="The brief (folded): compiled Agent-register Markdown · play:marketplace-listings@1 · each line names its record key"
You hold the Marketing Lead Key Result: 5 installs per 100 listing views by 2026-12-31. ← worker, metric
On each listing Event, choose one option and say why; a held Customer gets Not now. ← triggers, chooses, keep-first
Ask every Customer once per store, when they add their first owner, in the same words. ← your file, line 6
The ask never mentions stars, a score or a reward.                               ← copyRules 3, uniform-ask, reward
Reply to every Review alike; never ask for a change or a removal.                ← copyRules 4, 6
No outcome, unverified number or rival name in a title, keyword field or caption. ← copyRules 2
```

**The slow loop.** The weekly Decisions kept the ask at the first owner added, and no Change was merged in the window, so there is no replay or read-back to print; the switchback on the Chrome screenshot is the test that ran.

**Bring your own agent** (folded) writes and replies at the same rung when it matches or beats the seated agent on the eval suite; <Op verb="Role.staff" /> seats it.

```bash
npx startup-builder eval --scenario marketplace-listings-200 --agent "claude -p" --seat marketing-lead   # seated: 4.6 per 100, 0 filtered asks, 0 rejections · yours: 4.4, 1 ask filtered to happy Customers → You approve each one
```

## What it can do next
| Option | Picked when | Decided by | Runs | Hands to | If it’s just you |
| --- | --- | --- | --- | --- | --- |
| Write the listing `write-the-listing` | a build deploys to all traffic, or an Offer for Organizations publishes | Your rule (the store list, line 4) | <Op verb="Content.draft" /> from the claims source and the store’s listing spec → <Op verb="App.publish" /> | you approve a store’s first listing, due 1 business day · Take this one | you |
| Refresh the listing `refresh-the-listing` | a Version changes what a listed build does or shows | Your rule (line 4) | <Op verb="Content.draft" /> → <Op verb="App.publish" /> | nobody | the same |
| Test a listing change `test-a-change` | a field the store ranks by, with a store experiment or a two-week switchback | Agent’s call, with its reason | <Op verb="Experiment.start" />, two-week blocks at random → <Op verb="Content.draft" /> the variant | nobody; the result reads in Did it work | the same |
| Ask for a Review `ask-for-a-review` | a Customer adds their first owner | Your rule (line 6) | `review-ask-once`: one ask per store, at the next session | the Customer, who reviews or not | the same |
| Reply to the Review `reply-to-the-review` | a Review posts, at any rating | Agent’s call, with its reason | <Op verb="Content.draft" /> → <Op verb="Conversation.answer" /> on the store | nobody | the same |
| Make a private offer `private-offer` | an open Deal’s buyer names a cloud marketplace as its buying path | Agent’s call, with its reason | <Op verb="Quote.send" /> as a private offer → `private-offer-follow-up` | <Op verb="Task.assign" /> → AE · Priya Shah, due 1 business day · Take this one | you |
| Not now `not-now` | a store threshold unmet, a listing spec fails, or the Customer was asked on this store already | Your rule (the store list and rule j) | nothing; names the threshold, the rule or the earlier ask | their owner, for `keep-first` · Take this one | you |

| When they write back | Fixture reply | What happens | Who answers | By when | Call |
| --- | --- | --- | --- | --- | --- |
| Interested | a reviewer: “Love it. Does it do owner reports too?” | answers from the claims source and links the owner-report Page | Marketing Lead · your agent | 2d | <Op verb="Conversation.answer" /> |
| Send me pricing | a listing question: “Is the Slack app free?” | answers as the Pricing Page states it: free with every plan | Marketing Lead · your agent | 2d | <Op verb="Conversation.answer" /> |
| Not now | a Customer dismisses the ask | no second ask on that store, ever; `customer-ask-cap` frees them | nobody | at once | <Op verb="Decision.list" /> |
| Wrong person | “I’m not the admin; ask our office manager.” | a thank-you; it never writes to someone from their text | Marketing Lead · your agent | 2d | <Op verb="Conversation.answer" /> |
| Out of office | no session after the activation Event for 30 days | the ask waits for their next session | nobody | their next session | <Op verb="Decision.list" /> |
| Question it can’t answer | a Review: “Do you support Teams?” | To you: a date for an unshipped Change is never the agent’s to give | you (Dana, person) | 2d | <Op verb="Conversation.escalate" /> |
| Stop | a Customer turns off in-app asks | no ask shows to them again, on any store | nobody | at once | <Op verb="Contact.unsubscribe" /> |

## When it hands to you
A store’s first listing hands to **you** (Dana Okafor, person); private offers go to **AE · Priya Shah (person)**. As captured, the Approval for the Chrome listing (`draft`, `route` and `checks` are Not live yet, gaps #7); no private offer arose in the window.

```json title="What lands in each queue · sample"
[
  {
    "$id": "https://api.sb/leaseline.app/approvals/approval_83",
    "$type": "Approval",
    "verdict": "approved",
    "deadline": "2026-08-27T17:00:00Z",
    "draft": "Leaseline: save a unit from any listing site to your renewal calendar in one click.",
    "route": "write-the-listing",
    "checks": [
      {
        "check": "store-rule",
        "status": "pass"
      },
      {
        "check": "approved-pages",
        "status": "pass"
      }
    ]
  }
]
```

You answer a first listing with <Op verb="Approval.approve" />, an edit, or <Op verb="Approval.refuse" /> with a reason; one unanswered by its deadline expires, and nothing submits. The AE answers a private offer with <Op verb="Task.claim" /> (Take this one), <Op verb="Task.return" /> with a reason, or <Op verb="Action.redirect" />. A Task nobody claims by its due time goes up a Role through <Op verb="Task.escalate" /> (AE to you).

**On time:** You: 2 of 2 first listings approved within 1 business day, median 1h 50m, n 2, ■□□□ Too early to tell beside Other Startups’ 93 % on time. **Your queue:** counted by kind: in week 1, 2 listing Approvals; after that, about 1 Review flagged for you a month and the weekly quality read.

**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 reply or ask to a Customer whose open Deal or claimed 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, gaps #8) | a Review ask to a Customer who is at risk, past due or leaving | 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-approved-pages"></a>`approved-pages` (Not live yet) | a listing line, caption or reply the claims source does not back | Cut the line, or publish the Page that says it. | <Op verb="Site.publish" /> |
| <a id="check-uniform-ask"></a>`uniform-ask` | an ask audience filtered by anything beyond the Event, the store, Suppression, opt-outs, once per store, insiders and the prompt cap | Ask everyone who reached the Event, the same way. | <Op verb="Decision.list" /> |
| <a id="check-reward"></a>`reward` | an ask with a reward on a store your list sets to no rewards, or a reward that varies with the rating | Remove the reward. | <Op verb="Content.draft" /> |
| <a id="check-insider"></a>`insider` | an ask, Review or rating from your team, your other Startups or the Studio | None: insiders never review. | <Op verb="Decision.list" /> |
| <a id="check-store-rule"></a>`store-rule` (listings) | a listing field, screenshot or feed row that fails the store’s listing spec | Fix the field to the listing spec; it submits once it passes. | <Op verb="Content.draft" /> |
| <a id="check-prompt-cap"></a>`prompt-cap` | a second ask in one session, or past the store’s own prompt cap | None: it waits for the next allowed session. | <Op verb="Decision.list" /> |
| <a id="check-review-reply"></a>`review-reply` (copy rules 4 and 6) | a reply asking a reviewer to change or remove a Review, or offering anything for it | Thank them and name the fix; ask nothing about the rating. | <Op verb="Content.draft" /> |

| Stop (pauses the Play) | Pauses when | Then |
| --- | --- | --- |
| <a id="stop-rejection"></a>`rejection` (listings) | 2 store rejections in 30 days | listing writes to that store stop until the Marketing Lead, so you, clears it |
| <a id="stop-uninstall"></a>`uninstall` (listings) | a 7-day uninstall rate over twice the listing’s baseline | paid and Portfolio install sources to it pause |
| <a id="stop-vitals"></a>`vitals` (listings) | a store performance limit failed in 2 weekly reads running | new install sources pause until a fix deploys |
| <a id="stop-kill-switch"></a>`kill-switch` | you press it | every Tactic pauses at once; listings stay up |
| + 1 more for the feed lane: a Price that differs from the live Site leaves the feed within 1 hour (`?lanes=all`) | | |

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

```json title="The captured refusal · sample"
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "The Review ask did not show: its audience was filtered to Customers with a high health score.",
  "checks": [
    {
      "check": "uniform-ask",
      "status": "fail",
      "doc": "/plays/marketplace-listings#check-uniform-ask",
      "fix": "Ask everyone who reached the Event, the same way.",
      "then": "Decision.list"
    },
    {
      "check": "reward",
      "status": "pass"
    }
  ]
}
```


```json title="The Stops that fired, as Campaign.paused reasons · sample"
[
  {
    "reason": {
      "limit": "uninstall",
      "observed": "9.4 % in 7 days",
      "bound": "2 × 4.1 %",
      "window": "7d, Chrome listing"
    },
    "doc": "/plays/marketplace-listings#stop-uninstall",
    "task": "task_452"
  }
]
```

## Did it work
Neither store runs listing experiments, so the new and old listings alternate in two-week blocks switched at random (rule o, alternating weeks at 10 or more a week). Each figure’s call is <Op verb="Metric.query" /> by node and by block; every node above the leaf is Not live yet (gaps #4).

`+1.5 installs per 100 views · ■■□□ Early sign · n 4 blocks · path → arr`

| Node | New listing blocks | Old listing blocks | Added | Confidence | Basis |
| --- | --- | --- | --- | --- | --- |
| `listing-install-rate` | 4.6 per 100 views (121 of 2,640) | 3.1 (79 of 2,560) | +1.5 [0.1, 2.9] · Worth about +$120 a month | ■■□□ Early sign | two-week blocks switched at random · n 4 blocks, 5,200 views |
| `new-leads` | 30 signups a week | 20 | +10 a week [−1, +21] · Not carrying up yet | ■□□□ Too early to tell | the same assignment |
| `new-arr` ↑ | $2,429, 7 Customers on Pro · Booked: credited, not all caused by this Play | $1,041, 3 Customers on Pro | +$347 a week [−$350, +$1,050] | ■□□□ Too early to tell (7 vs 3 Customers) | the same · 14 trials still deciding |
| `net-new-arr` · `arr` | +$2,429 | +$1,041 | carried: it moves no other bridge term | ■□□□ Too early to tell | carried from `new-arr` |

The leaf counts installs or signups per 100 listing views, each store read on its own and summed here, across two-week blocks set at random; a block’s installs count only from views inside it.

**How it moves it:** the new Chrome listing leads with one screenshot of the renewal calendar and a title that names what it does, where the old one led with the logo.

**Guardrails:** new blocks against old: paid conversion from installs, 7 of 121 against 3 of 79, and the 7-day uninstall rate, 4.4 % against 4.1 %, under the Stop’s 2 times.

```json title="The Campaign card · sample"
{
  "$id": "https://api.sb/leaseline.app/campaigns/marketplace-listings",
  "$type": "Campaign",
  "play": "play:marketplace-listings@1",
  "path": [
    "listing-install-rate",
    "new-leads",
    "new-arr",
    "net-new-arr",
    "arr"
  ],
  "experiment": {
    "metric": "listing-install-rate",
    "unit": "per-100",
    "heldBack": 2,
    "decision": null
  },
  "readout": {
    "value": 1.5,
    "n": 4,
    "maturing": 0,
    "interval": [
      0.1,
      2.9
    ],
    "readability": "C",
    "basis": "alternating-weeks",
    "pooled": false
  },
  "ledger": {
    "spend": 0,
    "workerCost": 4,
    "hours": 9.8,
    "pipelineArr": 0,
    "wonArr": 3470,
    "projectedArr": 122,
    "paybackDays": 0
  }
}
```

Outside benchmark: 2.0 to 2.4 % of subscription app downloads convert to paid (the record’s `subscription-app-download-to-paid`, a subscription-analytics firm’s report, 2026-03-19), so paid conversion reads beside the install rate, never in its place.

**Before it · After it.** Before it: `integration-partners` and `launch-week` hand it listed builds and refreshes. After it: Reviews go to `review-requests`, installs to `product-led`, and a marketplace buyer to the AE.

## Example
Leaseline ran it from 2026-08-24 to 2026-10-19 (56 days) on 2 listings: 5,200 listing views, 200 installs, 28 Review asks and 11 Reviews, each answered. Read every captured thread at `?threads=all`; three follow the tables.

| What happened, by option | Decisions | Published or sent | Installs or Reviews | Paid (credited, not all caused by this Play) |
| --- | --- | --- | --- | --- |
| Write the listing · Refresh the listing · Test a listing change | 2 · 3 · 2 | 2 listings, 3 refreshes, 2 switchbacks | 200 installs | 10 on Pro (+$3,470 ARR) |
| Ask for a Review · Reply to the Review | 28 · 11 | 28 asks · 11 replies | 11 Reviews: 8 five-star, 2 four, 1 two | — |
| Make a private offer · Not now | 0 · 9 | — | — | — |
| **All** · old listing blocks | **55** | — | **121** of 2,640 views · 79 of 2,560 | **$3,470** |
| At 2,600 views a month (the Use it when volume) | about 30 a month | — | two-week blocks at random | Other Startups’ results: +1.1 per 100, ■■■□ Likely, n 3,100 blocks |

| Day | Event | What happened | Your time |
| --- | --- | --- | --- |
| 0 | `event_137` Campaign.created | the three steps; dry run fails `inbound-source` and `activation-event` | 25 min |
| 3 | `event_190` Approval.approved | you approved the Chrome listing; the first install came at 16:20 | 5 min |
| 14 | `event_346` Role.updated | promoted: asks and replies send after a 2-minute hold | — |
| 24 | `event_450` Campaign.paused | `uninstall`: 9.4 % in 7 days on Chrome after a broken permission; fixed and resumed in a day | 20 min |
| 28–56 | `event_540` Experiment.started | the screenshot switchback: 4 two-week blocks, 2 new and 2 old | — |
| 1–56 | `event_180` Approval.created, first of 2 | you approved 2 listings and read 8 weekly quality reads, from the <Op verb="Task.next" /> log | 9.3 h · **total 9.8 h measured, 9.5 h asked** |

> **Slack Marketplace, Oct 19, 11:48, a 2-star Review** · “Good idea, but alerts came a day late for one of our owners.”
>
> **Leaseline’s reply, 11:52** · Thanks for writing this up. A time-zone bug delayed alerts for owners outside your own zone; the fix shipped on Oct 16, and the changelog lists it.¹ *Signed with the Role’s disclosure line.*²

```diff title="The Review ask, 2026-09-05 · the audience uniform-ask refused, then the ask that showed"
- Show the ask to Customers with a health score over 80.
+ Show the same ask to every Customer who added their first owner, once per store.
```

> **A Review, 2026-10-09** · “Do you support Teams?” A date for an unshipped Change is never the agent’s, so the reply waited on Dana, due in 2 days. · **Dana, Oct 10** · wrote it herself: Teams is on the list, with no date yet to give.

¹ The fix and its date, from the changelog; nothing about the rating. ² The fixed signature line, compiled from the Role’s Settings.

## Your settings
```mdx title="plays/marketplace-listings.mdx"
---
switchback: 14d      # neither store runs listing experiments
reviewHoldout: 0.1   # 1 in 10 activated Customers is asked 14 days later
stores: [slack-marketplace, chrome-web-store]
---
Ask every Customer once per store, when they add their first owner.
```

```text
$ npx startup-builder show plays/marketplace-listings
switchback 14d · review holdout 0.1 · stores slack-marketplace, chrome-web-store · ask at the first owner     your file  plays/marketplace-listings.mdx:2,3,4,6 (Ask 2026-08-24)
family demand · worker role:marketing-lead, owns never · chooses 7 (4 rule, 3 read) · sequences review-ask-once, private-offer-follow-up · checks owned-by-a-person, keep-first, customer-ask-cap, approved-pages + 6 · stops 4 + 1 on the feed lane · placements 8 stores and gmail    catalog  play:marketplace-listings@1 (Not live yet, gaps #5)
```

### Every key from the catalog
| Key | Id | What it is |
| --- | --- | --- |
| Tactic | <a id="tactic-store-listing-upkeep"></a>`store-listing-upkeep` | Store listing upkeep writes, submits and refreshes one listing per store and job from the claims source, and replies to every Review. Starts at You approve the first wave. |
| Tactic | <a id="tactic-store-quality-levers"></a>`store-quality-levers` | Store quality levers read each store’s ranking inputs weekly, fix install quality and performance, and apply for each badge the listing earns. Starts at You approve each one. |
| Tactic | <a id="tactic-fixed-event-review-ask"></a>`fixed-event-review-ask` | Fixed-event review ask asks every Customer once per store at the activation Event, with no sentiment filter and no reward tied to the rating. Starts at You approve the first wave. |
| Tactic | <a id="tactic-private-offer-desk"></a>`private-offer-desk` | Private offer desk turns an open Deal’s Quote into a cloud marketplace private offer, and counts the marketplace as the buying path, never the source. Starts at You approve each one. |
| Tactic | <a id="tactic-product-feed-sync"></a>`product-feed-sync` | Product feed sync publishes every commerce Offer variant to shopping and answer surfaces as a discovery asset, linking back to the Startup’s own checkout. Starts at You approve the first wave. |
| Trigger | <a id="trigger-host-build-ready"></a>`host-build-ready` | Host build ready: `event` on `Version.deployed`, window 30d. |
| Trigger | <a id="trigger-org-offer-published"></a>`org-offer-published` | Organization Offer published: `event` on `Offer.published`, window 30d. |
| Trigger | <a id="trigger-listed-version"></a>`listed-version` | Listed version: `event` on `Version.deployed`, window 2d. |
| Trigger | <a id="trigger-activation-reached"></a>`activation-reached` | Activation reached: `event` on `Customer.activated`, window 72h. |
| Trigger | <a id="trigger-store-review-posted"></a>`store-review-posted` | Store review posted: `event` on `Review.published`, window 2d. |
| Trigger | <a id="trigger-uninstall-spike"></a>`uninstall-spike` | Uninstall spike: `threshold` on `Monitor.checked`, window 7d. |
| Trigger | <a id="trigger-store-threshold-met"></a>`store-threshold-met` | Store threshold met: `threshold` on `Monitor.checked`, window 30d. |
| Trigger | <a id="trigger-vitals-breach"></a>`vitals-breach` | Vitals breach: `threshold` on `Monitor.checked`, window 7d. |
| Trigger | <a id="trigger-marketplace-buy-path"></a>`marketplace-buy-path` | Marketplace buy path: `event` on `Quote.requested`, window 3d. |
| Trigger | <a id="trigger-catalog-change"></a>`catalog-change` | Catalog change: `event` on `Offer.published`, window 1h. |
| Trigger | <a id="trigger-marketplace-renewal-window"></a>`marketplace-renewal-window` | Marketplace renewal window: `at` on `Renewal.opened`, window 30d. |
| Sequence | <a id="sequence-review-ask-once"></a>`review-ask-once` | Review ask once: 3 steps; it ends on A Review posts on that store; The Customer opts out; The store’s prompt cap is reached; 14 days pass. |
| Sequence | <a id="sequence-private-offer-follow-up"></a>`private-offer-follow-up` | Private offer follow-up: 2 steps; it ends on The buyer accepts the offer; The buyer declines; The Quote expires; The Deal is lost. |
| Benchmark | <a id="benchmark-subscription-app-download-to-paid"></a>`subscription-app-download-to-paid` | Median download-to-paid conversion for subscription apps, non-AI against AI apps: 2–2.4 % (State of Subscription Apps 2026, 2026-03-19). |
| Benchmark | <a id="benchmark-subscription-app-day35-by-paywall"></a>`subscription-app-day35-by-paywall` | Median day-35 conversion, freemium apps against hard-paywall apps: 2.1–10.7 % (State of Subscription Apps 2026, 2026-03-19). |