URL: /plays/integration-partners · Id: `play:integration-partners`

# Integration partners

> Builds integrations with the tools your Customers already use, sends each partner a working build, and lists one only after a Customer pays through it.

Builds integrations with the tools your [Customers](https://startupbuilder.dev/business/nouns/customer) already use, sends each partner a working build, and lists one only after a Customer pays through it.

```text title="Set it up · Ask"
Be our Partner Manager: build integrations with the tools our Customers already use, privately, against each partner’s sandbox. Send the partner a working build. List one only after a paying Customer uses it, and I sign every Agreement. Show me what’s missing; don’t send.
```

```text title="Run one · Ask"
Hand the latest tool that crossed the build line to the Partner Manager 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 ("Partner.ranked", "App.passed", "Message.replied", "Integration.connected", "Agreement.signed", "Monitor.checked", "Referral.registered", "App.failed", "Organization.announced", "Customer.activated")', 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_429",
    "$type": "Decision",
    "choice": "propose-to-the-partner",
    "reason": "the Porchlight build passed its sandbox suite on Oct 17; 14 of your Customers use Porchlight, it runs a partner program, and you hold no Agreement",
    "stakes": "medium",
    "options": [
      "build-it-privately",
      "propose-to-the-partner",
      "list-it",
      "adoption-email",
      "intro-a-lead",
      "park-it"
    ],
    "by": "worker_9",
    "on": "partner_9",
    "expected": "a partner yes within 14d",
    "reopensWhen": "no Message.replied from partner_9 within 14d"
  },
  "actions": [
    {
      "$id": "https://api.sb/leaseline.app/actions/action_1",
      "$type": "Action",
      "status": "done",
      "mode": "try",
      "by": "worker_9",
      "verb": "Integration.call",
      "checks": [
        {
          "check": "sandbox-only",
          "status": "pass"
        }
      ]
    },
    {
      "$id": "https://api.sb/leaseline.app/actions/action_2",
      "$type": "Action",
      "status": "done",
      "mode": "try",
      "by": "worker_9",
      "verb": "Content.draft",
      "checks": [
        {
          "check": "announced-only",
          "status": "pass"
        }
      ]
    },
    {
      "$id": "https://api.sb/leaseline.app/actions/action_3",
      "$type": "Action",
      "status": "pending",
      "reason": "awaitingApproval",
      "mode": "try",
      "by": "worker_9",
      "verb": "Message.schedule",
      "checks": [
        {
          "check": "owned-by-a-person",
          "status": "pass"
        },
        {
          "check": "terms-hold",
          "status": "pass"
        },
        {
          "check": "no-contact",
          "status": "pass"
        }
      ]
    }
  ]
}
```

`action_1` ran the suite against Porchlight’s sandbox, never a production account. `action_3` is the proposal with the sandbox link, waiting on your approval as the first one to this partner.

`41 ICP Organizations through 2 builds so far, against 9 per build before · before-after, not proof · n 0 matured · path → arr`

| Try another: the partner tool | Choice | Reason |
| --- | --- | --- |
| Porchlight, a resident portal: build green Oct 17; 14 Customers use it | `propose-to-the-partner` | a green build, a partner program, no Agreement |
| Ledgerly, landlord accounting: Maple Court connected through the private build | `list-it` | a paying Customer outside your team |
| A maintenance-ticket tool: 4 Customers use it | `park-it` | under the 8-Customer build line |
| Ledgerly registered Hilltop Rentals as a Lead, Consent recorded | `intro-a-lead` | a partner Lead with the Person’s Consent |

| Worth | Your time | Spend | First signal | Use it when | Instead |
| --- | --- | --- | --- | --- | --- |
| ≈ +$290 ARR a month at 2 builds | 45 min week 1, then 10 a day | $0 cash; ~$6 a build in sandbox and model minutes | Day 15: first paying Customer through a build | 10+ paying Customers or 50 Prospects with a known stack | Under 10 and 50: `marketplace-listings` · no tool 5+ use: `cold-email` |

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

Worth’s formula: about +14 ICP Organizations per build in 90 days (Other Startups’ results; no lift of your own to read) × 2 live builds ÷ 3 months × 0.09 ICP Organizations to paid × $347 first order ARR ≈ +$291. Under First signal: Each build reads at day 90; at 2 builds, 90 days can tell only +30 per build or more, so your file reads before-after.

`sample · api.sb/leaseline.app · 41 Customers at 2026-10-19 · on Partner.ranked · first build green 3h 12m · 8 keys Not live yet`

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

# 3 · keep it: first listings and every Agreement wait 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":"integration-partners"}'

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

# off: proposals stop; live builds stay listed and keep their nightly suite
curl -X PATCH https://api.sb/leaseline.app/campaigns/integration-partners \
  -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": "integration-partners can’t start: 2 checks fail and 1 warns.",
  "checks": [
    {
      "check": "use-when",
      "status": "pass",
      "observed": "41 paying Customers; 23 use Ledgerly, 14 use Porchlight",
      "doc": "/plays/integration-partners#check-use-when",
      "then": "Metric.query"
    },
    {
      "check": "baseline",
      "status": "pass",
      "observed": "9 ICP Organizations in 90 days through 1 listed build (the CSV import)",
      "doc": "/plays/integration-partners#check-baseline",
      "then": "Metric.query"
    },
    {
      "check": "sandbox-connected",
      "status": "fail",
      "doc": "/plays/integration-partners#check-sandbox-connected",
      "fix": "Connect each partner’s sandbox account; no suite touches a production account.",
      "then": "Integration.connect"
    },
    {
      "check": "domain-authenticated",
      "status": "fail",
      "doc": "/plays/integration-partners#check-domain-authenticated",
      "fix": "Add the 3 DNS records shown; it passes once they resolve, often within an hour.",
      "then": "Domain.authenticate"
    },
    {
      "check": "role-unstaffed",
      "status": "warn",
      "roles": [
        "sales-lead"
      ],
      "owned": 0,
      "doc": "/plays/integration-partners#check-role-unstaffed",
      "fix": "Staff the AE, or leave it: partner Leads 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: 41 Customers with a known stack; 9 per build, n 1 (gaps #8) | <Op verb="Metric.query" /> on `integration-icp-accounts`, which fills Before in Your number | 1 min | none |
| <a id="check-sandbox-connected"></a>`sandbox-connected` · fail (gaps #8) | <Op verb="Integration.connect" /> with each partner’s sandbox | 10 min | a partner’s sandbox approval, often 1 to 3 days |
| <a id="check-domain-authenticated"></a>`domain-authenticated` · fail | <Op verb="Domain.authenticate" /> | 10 min | until DNS resolves, often under 1 h |
| <a id="check-role-unstaffed"></a>`role-unstaffed` · warn (gaps #8): no AE, so partner Leads come to you | <Op verb="Role.staff" />, or leave it | 0 min | none |

`Your number · Before 9 ICP Organizations per build · Now 41 through 2 builds so far · Target 30 per build by Dec 31 · before-after, not proof · n 0 matured, 2 builds still maturing`

A private build costs no cash: about $6 of sandbox and model minutes to build and $0.30 a month for its nightly suite (Worker cost). Store listings cost nothing, and a partner reward is 15 % of collected first-year revenue, paid after the refund window. That comes to about $14 a month at 2 live builds.

## When it starts
| When | Trigger id | Starts on | Window | How it gets in |
| --- | --- | --- | --- | --- |
| A tool crosses the build line in the weekly rank of your Customers’ stacks | `partner-demand` | `threshold` on `Partner.ranked` where customersUsing >= 8 or prospectsUsing >= 20 | 30d | read from your Customers’ connected tools; <Op verb="Event.capture" /> adds one |
| A paying Customer outside your team connects through a private build | `first-paying-integration` | `event` on `Integration.connected` where customer.paying = true and customer.insider = false and build.listed = false | 7d | <Op verb="Integration.connect" />, from the Customer’s own credentials |
| A partner answers a proposal or asks for a change | `partner-reply` | `event` on `Message.replied` where direction = 'inbound' and on a proposal | 2d | the reply lands in the proposal’s thread |
| + 8 more for green and red builds, signed Agreements, partner Leads, API changes, idle listings, Customer outcomes and tool changes (`?lanes=all`) | | | | |

```json title="One signed delivery · StartupBuilder-Signature"
{
  "$id": "https://api.sb/leaseline.app/events/event_1203",
  "$type": "Event",
  "name": "Partner.ranked",
  "at": "2026-10-19T06:00:11Z",
  "seq": 1203,
  "data": {
    "partner": "partners/partner_9",
    "name": "Porchlight",
    "customersUsing": 14,
    "prospectsUsing": 31,
    "build": "green",
    "program": true,
    "agreement": null
  }
}
```

```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":"Partner.ranked","url":"https://leaseline.app/hooks/sb"}'
npx startup-builder trigger Partner.ranked --like event_1203   # replays the delivery
npx startup-builder clock advance --by 7d   # runs the one follow-up with one new fact
```

## Who runs it
**Partner Manager · your agent** (`role:partner-manager`, `worker_9`) holds the Key Result: it ranks the stacks, builds privately, runs the suites and drafts proposals. First proposals, first listings per Venue and every Agreement hand to **you** (Dana Okafor, person, `worker_1`); partner Leads 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 |
| --- | --- | --- | --- | --- | --- |
| Build it privately · Park it | It does it | 3 builds, 2 parked | — | a `terms-hold` → You approve each one | <Op verb="Action.cancel" /> |
| Propose to the partner | You approve each one | 3 of 3 | at 10 proposals with 0 Checks failed | — | <Op verb="Approval.approve" /> · <Op verb="Approval.refuse" /> |
| List it | You approve each one | 1 of 1 | never for a Venue’s first listing | — | <Op verb="Approval.approve" /> |
| Move shared Customers onto it · Introduce a Lead | It does it, you can stop it | 17 emails, 0 failed · 2 of 2 | met on day 33 | any Check fails a send → You approve each one | <Op verb="Action.cancel" /> inside the hold |
| Change the build line | You approve each one | 1 merged of 1 | never | — | <Op verb="Change.merge" /> · <Op verb="Change.revert" /> |
| Never: “official” or “certified” before the Agreement; a private overlap match in copy | Refused, every time | 1 drafted, 1 refused | never | — | the `announced-only` Check |

**What it reads · What it may call.** It reads the tools your Customers connect and each partner’s public docs and terms; an overlap list is matched on domains only and never appears in copy. It may call <Op verb="Integration.create" /> and <Op verb="Integration.call" /> against sandboxes only; <Op verb="App.publish" /> needs your Approval for a Venue’s first listing. Its tools are <Op verb="Integration.create" />, <Op verb="Integration.call" />, <Op verb="Approval.approve" />, <Op verb="App.publish" />, <Op verb="Site.publish" />, <Op verb="Lead.create" />, <Op verb="Lead.assign" />, and any other call becomes an Approval under the Role’s Mandate.

```md title="The brief (folded): compiled Agent-register Markdown · play:integration-partners@1 · each line names its record key"
You hold the Partner Manager Key Result: 30 ICP Organizations per live build by 2026-12-31. ← worker, metric
On each ranked tool, green build or partner reply, choose one option and say why. ← triggers, chooses
Build privately once 8 Customers or 20 Prospects use a tool.                     ← your file, line 5
Say “works with” and the tool’s name until an Agreement exists; never “official”. ← copyRules 2, 3
List a build only after a paying Customer outside the team connects.             ← chooses.list-it, paying-user
Pass a Lead to a partner only with the Person’s recorded Consent.                ← consent-stop
```

**The slow loop, as a Change.** `decision_389` (2026-09-21, weekly) read 2 green builds out of 2 tools at 8 or more Customers against 0 of 3 at 5 to 7, so `change_28` raised the build line from 5 to 8 Customers (your file, line 5), and Dana merged it with <Op verb="Change.merge" />. **Replayed on past ranks**, <Op verb="Change.review" /> cut parked builds from 3 to 1 with no live build lost; read back at +28d, 0 builds parked, n 1 rank, ■□□□ Too early to tell (Example, day 56).

**Bring your own agent** (folded) builds at the same rung when it matches or beats the seated agent on the eval suite; first listings and Agreements wait on you either way, and <Op verb="Role.staff" /> seats it.

```bash
npx startup-builder eval --scenario integration-partners-6 --agent "claude -p" --seat partner-manager   # seated: 0 premature listings, 0 partner claims sent, 41 sandbox min a build · yours: 0, 1 “official” drafted, 63 min → You approve each one
```

## What it can do next
| Option | Picked when | Decided by | Runs | Hands to | If it’s just you |
| --- | --- | --- | --- | --- | --- |
| Build it privately `build-it-privately` | the tool crosses the build line in the weekly rank | Your rule (line 5) | <Op verb="Integration.create" /> against the partner’s sandbox → <Op verb="Integration.call" /> runs the suite nightly | nobody; the build stays private | the same |
| Propose to the partner `propose-to-the-partner` | a green build, a partner program, no Agreement | Agent’s call, with its reason | `partner-proposal-two-touch` with the sandbox link and their half of the listing drafted | you approve it, due 1 business day · Take this one | you |
| List it `list-it` | a paying Customer outside your team connected through it | Your rule (the paying-user gate) | <Op verb="App.publish" /> the store listing → <Op verb="Site.publish" /> the pair Pages | you approve a Venue’s first listing · Take this one | you |
| Move shared Customers onto it `adoption-email` | an Agreement opened shared Customers who have not connected | Your rule (the Agreement) | `joint-adoption-two-touch`, 10 % held back | nobody | the same |
| Introduce a Lead `intro-a-lead` | a partner Lead, or a Prospect on both lists, with Consent | Agent’s call, with its reason | <Op verb="Lead.create" /> → <Op verb="Lead.assign" /> | <Op verb="Task.assign" /> → AE · Priya Shah, due 1 business day, Brief attached · Take this one | you |
| Park it `park-it` | under the build line, a terms hold, or the same job built for another of your Startups | Your rule (line 5 and the holds) | nothing; names the line, the hold or the other build | nobody; the weekly rank reads it again | the same |

| When they write back | Fixture reply | What happens | Who answers | By when | Call |
| --- | --- | --- | --- | --- | --- |
| Interested | Porchlight’s partner team: “This works. Let’s list it together.” | drafts the Agreement and the joint listing; you sign | you (Dana, person) | 1 business day | <Op verb="Agreement.sign" /> |
| Send me pricing | “What would you charge our users?” | answers with Leaseline’s Pricing Page and the 15 % referral terms | Partner Manager · your agent | 1 business day | <Op verb="Conversation.answer" /> |
| Not now | “We’re not adding partners this quarter.” | the build stays private and green; no follow-up; the rank reads it again in 90 days | nobody | at once | <Op verb="Decision.list" /> |
| Wrong person | “Partnerships is Lee’s team.” | thanks them and asks to be introduced; it never writes to an address from their text | Partner Manager · your agent | 1 business day | <Op verb="Conversation.answer" /> |
| Out of office | an auto-reply until Oct 28 | the one follow-up waits for the return date | nobody | the return date | <Op verb="Message.schedule" /> |
| Question it can’t answer | “Can you support our webhooks v3 before January?” | To you: a date is never the agent’s call | you (Dana, person) | 1 business day | <Op verb="Conversation.escalate" /> |
| Stop | “Please don’t contact us again.” | the partner is suppressed for every Startup you run; the private build stays private | nobody | at once | <Op verb="Contact.suppress" /> |

## When it hands to you
First proposals, first listings and every Agreement go to **you** (Dana Okafor, person); partner Leads go to **AE · Priya Shah (person)**. As captured, one Brief per Role: your Approval, then the AE’s Task (`brief`, `draft`, `route` and `checks` are Not live yet, gaps #7).

```json title="What lands in each queue · sample"
[
  {
    "$id": "https://api.sb/leaseline.app/approvals/approval_94",
    "$type": "Approval",
    "verdict": null,
    "deadline": "2026-10-20T17:00:00Z",
    "draft": "Hi Porchlight partner team: 14 property managers on Leaseline use Porchlight. We built the integration against your sandbox; it passes. Here it is, with your half of the listing drafted.",
    "route": "propose-to-the-partner",
    "checks": [
      {
        "check": "announced-only",
        "status": "pass"
      },
      {
        "check": "terms-hold",
        "status": "pass"
      }
    ]
  },
  {
    "$id": "https://api.sb/leaseline.app/tasks/task_482",
    "$type": "Task",
    "assignee": "worker_3",
    "due": "2026-10-15T16:00:00Z",
    "on": "lead_47",
    "brief": {
      "who": "Hilltop Rentals, 95 units, registered by Ledgerly",
      "consent": "recorded on the partner’s form, 2026-10-14",
      "chose": "intro-a-lead",
      "why": "a partner Lead with the Person’s Consent; they keep their books in Ledgerly",
      "never": [
        "a discount",
        "a promise of a Ledgerly feature"
      ]
    }
  }
]
```

You answer the Approval with <Op verb="Approval.approve" />, an edit, or <Op verb="Approval.refuse" /> with a reason; one unanswered by its deadline expires, and the proposal never sends. The AE answers with <Op verb="Task.claim" /> (Take this one), <Op verb="Task.return" /> with a reason, or <Op verb="Action.redirect" />; once claimed, the agent’s pending sends to that Lead cancel with `claimedByPerson` (Not live yet, gaps #7). A Task nobody claims by its due time goes up a Role through <Op verb="Task.escalate" /> (AE to you).

**On time:** You: 4 of 4 Approvals answered within 1 business day, median 2h 40m, n 4. AE · Priya Shah: 2 of 2 partner Leads claimed within 1 business day, median 50 min, n 2, ■□□□ Too early to tell beside Other Startups’ 90 % on time. **Your queue:** counted by kind: in week 1, 2 sandbox connections and 1 proposal Approval; after that, about 1 Approval and 1 partner Lead every 2 weeks.

**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 proposal or adoption email to a Contact whose Lead or open Deal a person owns | Leave it to the person who owns it: the draft waits in their queue. | <Op verb="Task.assign" /> |
| <a id="check-consent-stop"></a>`consent-stop` (gaps #3) | a Lead passed to a partner with no recorded Consent | Ask the Person first; the intro waits for their yes. | <Op verb="Conversation.answer" /> |
| <a id="check-partner-claim"></a>`partner-claim` (copy rules 2 and 3) | “partner”, “official”, “certified” or a partner logo before the Agreement or certification exists | Say “works with” and the tool’s name until the Agreement is signed. | <Op verb="Content.draft" /> |
| <a id="check-terms-hold"></a>`terms-hold` | a build whose partner terms returned a limit | The Partner Manager, so you, clears the terms; it stays private until then. | <Op verb="Task.next" /> |
| <a id="check-sandbox-only"></a>`sandbox-only` | a suite run against a partner’s production account | Run it in the partner’s sandbox. | <Op verb="Integration.connect" /> |
| <a id="check-paying-user"></a>`paying-user` (listings) | a public listing before a paying Customer outside your team connects | Wait for the first paying connection; send the private link to one. | <Op verb="Message.schedule" /> |
| <a id="check-duplicate"></a>`duplicate` (listings) | a second listing for the same host and job across your Startups | Keep one listing; the second build stays private. | <Op verb="Decision.list" /> |
| <a id="check-overlap-terms"></a>`overlap-terms` (email) | an overlap match with no signed data-sharing terms | Sign the terms; each list carries its allowed use. | <Op verb="Agreement.sign" /> |
| <a id="check-announced-only"></a>`announced-only` (email) | a line saying a Prospect uses a tool, from any source but a public one | Cut it; name the integration as a product fact. | <Op verb="Content.draft" /> |
| <a id="check-no-contact"></a>`no-contact` (email) | a send to a partner that said no, from any of your Startups | None: their no holds for every Startup you run. | <Op verb="Decision.list" /> |
| <a id="check-reward-hold"></a>`reward-hold` | a partner reward before the refund window closes, or on uncollected revenue | It settles after the window, on collected revenue. | <Op verb="Commission.pay" /> |

| Stop (pauses the Play) | Pauses when | Then |
| --- | --- | --- |
| <a id="stop-red-build"></a>`red-build` (listings) | 3 red nights on a build’s suite; 7 unlist it | every promotion of that build pauses until it runs green |
| <a id="stop-idle-unlist"></a>`idle-unlist` (listings) | 90 days with no active Integration through a listed build | it unlists and its pair Pages unpublish, as Changes you can revert |
| <a id="stop-proposal-floor"></a>`proposal-floor` (email) | no yes after 30 proposals | proposals pause, and the rank runs again with the replies as inputs |
| <a id="stop-kill-switch"></a>`kill-switch` | you press it | every Tactic pauses at once; live builds keep running for Customers |

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

```json title="The captured refusal · sample"
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "The Ledgerly listing copy did not publish: it says “official partner” before an Agreement exists.",
  "checks": [
    {
      "check": "announced-only",
      "status": "pass"
    },
    {
      "check": "paying-user",
      "status": "pass"
    },
    {
      "check": "partner-claim",
      "status": "fail",
      "doc": "/plays/integration-partners#check-partner-claim",
      "fix": "Say “works with Ledgerly” until the Agreement is signed.",
      "then": "Content.draft"
    }
  ]
}
```


```json title="The Stops that fired, as Campaign.paused reasons · sample"
[
  {
    "reason": {
      "limit": "red-build",
      "observed": "3 red nights",
      "bound": "3",
      "window": "the nightly suite"
    },
    "doc": "/plays/integration-partners#stop-red-build",
    "task": "task_461"
  }
]
```

## Did it work
Each build reads at day 90, and at 2 builds no held-back half reads, so the basis is before-after (rule o): the columns read Now and Before, nothing prints as Added, and Too early to tell prints with its counts. Each figure’s call is <Op verb="Metric.query" /> by node, and every node above the leaf is Not live yet (gaps #4).

`41 ICP Organizations through 2 builds so far, against 9 per build before · before-after, not proof · n 0 matured · path → arr`

| Node | Now (56 days) | Before (the 90 days, per 56) | Added | Confidence | Basis |
| --- | --- | --- | --- | --- | --- |
| `integration-icp-accounts` | 41 ICP Organizations so far through 2 live builds (day 41 and day 18 of 90) · 2 builds still maturing | 9 per build: the CSV import, listed, matured | — · Worth about +$290 a month | — · Too early to tell, n 0 matured | before-after, not proof · Other Startups’ results +14 per build, ■■□□ Early sign, n 96 builds |
| `icp-reached` | 41 ICP Organizations from the listings, pair Pages and partner intros | 6 | — | — | before-after |
| `new-arr` ↑ | $1,735, 5 Customers on Pro · Booked: credited, not all caused by this Play | $347 | — · 2 trials still deciding | — | before-after, not proof |
| `net-new-arr` · `arr` | carried from `new-arr` | carried | — | — | carried |

Before against after reads ICP Organizations reached through live builds against the 90 days before, when one CSV import was the only listing, scaled to 56 days. It is not proof; Other Startups’ results, read with half of gated builds listed at once and half 30 days later at random, print beside it.

**How it moves it:** the Ledgerly build listed on day 18 after Maple Court paid through it, and its store listing and 2 pair Pages reached 34 ICP Organizations in its first 38 days.

**Guardrails:** before against after: `partner-sourced-revenue`, $1,388 against $0, and refunds within 30 days on build-sourced Customers, 0 of 5 against Leaseline’s 4 %.

```json title="The Campaign card · sample"
{
  "$id": "https://api.sb/leaseline.app/campaigns/integration-partners",
  "$type": "Campaign",
  "play": "play:integration-partners@1",
  "path": [
    "integration-icp-accounts",
    "icp-reached",
    "new-arr",
    "net-new-arr",
    "arr"
  ],
  "experiment": {
    "metric": "integration-icp-accounts",
    "unit": "per-build",
    "heldBack": 0,
    "decision": null
  },
  "readout": {
    "value": null,
    "n": 0,
    "maturing": 2,
    "interval": null,
    "readability": "inadmissible",
    "basis": "before-after",
    "pooled": true
  },
  "ledger": {
    "spend": 0,
    "workerCost": 21,
    "hours": 6.5,
    "pipelineArr": 2400,
    "wonArr": 1735,
    "projectedArr": 291,
    "paybackDays": 4
  }
}
```

Outside benchmark: none measures ICP Organizations per partner build; Other Startups’ results, +14 per build, ■■□□ Early sign, n 96 builds, stand in for one.

**Before it · After it.** Before it: the weekly rank reads the tools your Customers connect. After it: listed builds go to `marketplace-listings`, pair Pages to `programmatic-seo`, and the integration as a product fact to `cold-email`.

## Example
Leaseline ran it from 2026-08-24 to 2026-10-19 (56 days). The weekly rank read 41 Customers’ and 118 Prospects’ stacks; 3 builds went green, 1 listed, 1 is proposed and 1 parked. Read every captured thread at `?threads=all`; three follow the tables.

| What happened, by option | Decisions | Sent or published | ICP Organizations reached | Won (credited, not all caused by this Play) |
| --- | --- | --- | --- | --- |
| Build it privately · Park it | 3 · 2 | 3 builds, all private | — | — |
| Propose to the partner | 3 | 3 proposals, 1 yes | — | — |
| List it | 1 | Ledgerly’s store listing + 2 pair Pages | 34 | 4 on Pro (+$1,388 ARR) |
| Move shared Customers onto it · Introduce a Lead | 1 · 2 | 17 emails (2 held back) · 2 Leads | 7 | 1 on Pro (+$347), 2 trials still deciding |
| **All** · Before (90 days, per 56) | **12** | — | **41** · 6 | **$1,735** · $347 |
| At 2 live builds (the Use it when volume) | about 12 in 56 days | — | before-after, not proof | Other Startups’ results: +14 per build, ■■□□ Early sign, n 96 |

| Day | Event | What happened | Your time |
| --- | --- | --- | --- |
| 0 | `event_128` Campaign.created | the three steps; dry run fails `sandbox-connected` and `domain-authenticated` | 25 min |
| 3 | `event_170` App.passed | the Ledgerly build passes its sandbox suite, privately | — |
| 15 | `event_330` Integration.connected | Maple Court, a paying Customer, connects through the private link: the gate opens | — |
| 18 | `event_371` App.published | you approved Ledgerly’s first store listing | 10 min |
| 28 · 56 | `event_534` Change.merged · `event_908` Readout.created | the build line at 8 Customers: replayed at 1 parked build, read back at +28d at 0 | 5 min |
| 40 | `event_690` Campaign.paused | `red-build`: Porchlight’s sandbox changed an endpoint; green again in 26 h | — |
| 1–56 | `event_151` Approval.created, first of 4 | you approved 3 proposals and 1 listing, and signed 1 Agreement | 1.2 h · **total 6.5 h measured, 7 h asked** |

> **Leaseline → Ledgerly partner team, Aug 28, 10:05** · 23 property managers on Leaseline keep their books in Ledgerly.¹ We built the integration against your sandbox, and it passes end to end: renewals post as receivables the day they sign.² Here it is, with your half of the listing drafted.³
>
> **Ledgerly, Sep 1, 14:30** · “Looks good. List it once a customer is live.” Maple Court connected on Sep 8, and the listing went up Sep 11 with “works with Ledgerly”.

```diff title="Ledgerly listing copy, 2026-09-10 · the draft refused, then the rewrite that listed"
- Leaseline is an official Ledgerly partner.
+ Leaseline works with Ledgerly: renewals post as receivables the day they sign.
```

> **Ledgerly, 2026-10-14 15:12** · registered Hilltop Rentals (95 units) as a Lead, with Consent on their form. The agent chose Introduce a Lead and sent the Brief to Priya, due 16:00 the next business day. · **Priya, 2026-10-15 09:40** · claimed it with <Op verb="Task.claim" /> and booked a call for Oct 17. The Brief is in When it hands to you (`task_482`).

¹ A count from your own Customers’ connected tools, never a private overlap match. ² A result the suite measured in the partner’s sandbox. ³ Their half of the listing and docs, drafted.

## Your settings
```mdx title="plays/integration-partners.mdx"
---
holdout: 0          # 2 builds are too few to stagger listings; it reads before-after
adoptionHoldout: 0.1
---
Build once 8 Customers or 20 Prospects use a tool. I sign every Agreement.
```

```text
$ npx startup-builder show plays/integration-partners
holdout 0 · adoption holdout 0.1 · build line 8 Customers or 20 Prospects     your file  plays/integration-partners.mdx:2,3,5 (Ask 2026-08-24; line 5 by change_28, 2026-09-21)
family demand · worker role:partner-manager, owns never · chooses 6 (4 rule, 2 read) · sequences partner-proposal-two-touch, joint-adoption-two-touch, overlap-brief-three-touch, idle-unlist-notice · checks owned-by-a-person, consent-stop + 8 · stops 4 · placements 13 stores and registries, google-search, linkedin, gmail, outlook, yahoo-mail    catalog  play:integration-partners@1 (Not live yet, gaps #5)
```

### Every key from the catalog
| Key | Id | What it is |
| --- | --- | --- |
| Tactic | <a id="tactic-host-build-queue"></a>`host-build-queue` | Host build queue ranks partner products by the Startup’s own Customer and Prospect stacks and builds the top ones privately against each partner’s sandbox. Starts at Sends nothing. |
| Tactic | <a id="tactic-partner-proposal"></a>`partner-proposal` | Partner proposal sends each partner a working build in its own sandbox, with its half of the listing, docs and announcement already drafted. Starts at It does it, you can stop it. |
| Tactic | <a id="tactic-list-when-used"></a>`list-when-used` | List when used keeps a host build private until a paying Customer connects, lists it then, and unlists it after 90 idle days. Starts at You approve the first wave. |
| Tactic | <a id="tactic-host-build-upkeep"></a>`host-build-upkeep` | Host build upkeep runs every maintained build’s suite nightly, fixes breaks before partners’ deadlines, and publishes each build’s status. Starts at Sends nothing. |
| Tactic | <a id="tactic-partner-overlap-map"></a>`partner-overlap-map` | Partner overlap map matches two parties’ opened account lists on domains only and routes each cell to adoption, intros, outbound or a joint asset. Starts at You approve the first wave. |
| Tactic | <a id="tactic-joint-adoption-run"></a>`joint-adoption-run` | Joint adoption run moves shared Customers onto the integration with one email and one reminder against a holdout, then turns outcomes into case studies. Starts at You approve the first wave. |
| Tactic | <a id="tactic-overlap-outbound-brief"></a>`overlap-outbound-brief` | Overlap outbound brief names the integration as a product fact in outbound and tests it against an enrichment-only brief in randomized arms. Starts at You approve each one. |
| Tactic | <a id="tactic-partner-lead-exchange"></a>`partner-lead-exchange` | Partner lead exchange passes Leads both ways under the referral Agreement, each with the Person’s Consent, and rewards on collected revenue. Starts at You approve the first wave. |
| Tactic | <a id="tactic-send-gate-check"></a>`send-gate-check` | Send gate check: Checks Suppression, a live reason, the Market, the basis stamp, the Person-month grant, the facts and the labels before any cold Message exists. Starts at Sends nothing. |
| Tactic | <a id="tactic-reply-matrix"></a>`reply-matrix` | Reply matrix: Labels every outreach reply with one of 11 labels, maps it to one of seven reply kinds, and runs that kind’s move by its deadline. Starts at You approve each one. |
| 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-tool-description-lint"></a>`tool-description-lint` | Tool description lint blocks any description that holds hidden text, addresses the reading agent, names a rival, or states a fact the claims source lacks. Starts at Sends nothing. |
| Trigger | <a id="trigger-partner-demand"></a>`partner-demand` | Partner demand: `threshold` on `Partner.ranked`, window 30d. |
| Trigger | <a id="trigger-host-build-green"></a>`host-build-green` | Host build green: `event` on `App.passed`, window 14d. |
| Trigger | <a id="trigger-partner-reply"></a>`partner-reply` | Partner reply: `event` on `Message.replied`, window 2d. |
| Trigger | <a id="trigger-first-paying-integration"></a>`first-paying-integration` | First paying integration: `event` on `Integration.connected`, window 7d. |
| Trigger | <a id="trigger-partner-agreement-signed"></a>`partner-agreement-signed` | Partner agreement signed: `event` on `Agreement.signed`, window 30d. |
| Trigger | <a id="trigger-technology-change"></a>`technology-change` | Technology change: `event` on `Monitor.checked`, window 30d. |
| Trigger | <a id="trigger-partner-lead-registered"></a>`partner-lead-registered` | Partner lead registered: `event` on `Referral.registered`, window 2d. |
| Trigger | <a id="trigger-host-build-red"></a>`host-build-red` | Host build red: `event` on `App.failed`, window 1d. |
| Trigger | <a id="trigger-partner-api-change"></a>`partner-api-change` | Partner API change: `event` on `Organization.announced`, window 30d. |
| Trigger | <a id="trigger-integration-idle"></a>`integration-idle` | Integration idle: `state` on `Monitor.checked`, window 30d. |
| Trigger | <a id="trigger-mutual-customer-outcome"></a>`mutual-customer-outcome` | Mutual customer outcome: `threshold` on `Customer.activated`, window 14d. |
| Sequence | <a id="sequence-partner-proposal-two-touch"></a>`partner-proposal-two-touch` | Partner proposal two touch: 2 steps; it ends on The partner replies; The partner program answers; The partner opts out; 14 days pass. |
| Sequence | <a id="sequence-joint-adoption-two-touch"></a>`joint-adoption-two-touch` | Joint adoption two touch: 3 steps; it ends on Integration.connected fires for the Customer; The Customer opts out; 14 days pass. |
| Sequence | <a id="sequence-overlap-brief-three-touch"></a>`overlap-brief-three-touch` | Overlap brief three touch: 3 steps; it ends on The Contact replies; A Meeting is booked; The Contact opts out; The address bounces. |
| Sequence | <a id="sequence-idle-unlist-notice"></a>`idle-unlist-notice` | Idle unlist notice: 2 steps; it ends on A new active Integration appears; The Partner Manager cancels the unlisting. |