Skip to content

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.

Channel
Partnerships
Run oneMode try
You, to your agent

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.

curl "https://api.sb/leaseline.app/events?where=name%20in%20(%22Partner.ranked%22%2C%20%22App.passed%22%2C%20%22Message.replied%22%2C%20%22Integration.connected%22%2C%20%22Agreement.signed%22%2C%20%22Monitor.checked%22%2C%20%22Referral.registered%22%2C%20%22App.failed%22%2C%20%22Organization.announced%22%2C%20%22Customer.activated%22)&limit=1" \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"

# Not live yet (gaps #7)
curl -X POST "https://api.sb/leaseline.app/triggers/fire?mode=try" \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"like":"event_1203"}'

# Not live yet (gaps #7)
curl "https://api.sb/leaseline.app/decisions?where=on%20%3D%20%22partner_9%22" \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"

# Not live yet (gaps #7)
curl "https://api.sb/leaseline.app/actions?where=on%20%3D%20%22partner_9%22" \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
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 }
npx startup-builder do Event.list --json '{"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}'
npx startup-builder do Trigger.fire --json '{"like":"event_1203"}' --try   # Not live yet (gaps #7)
npx startup-builder do Decision.list --json '{"where":"on = \"partner_9\""}'   # Not live yet (gaps #7)
npx startup-builder do Action.list --json '{"where":"on = \"partner_9\""}'   # Not live yet (gaps #7)
{
  "name": "do",
  "arguments": {
    "code": "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\nconst { subject } = await $.Trigger.fire({ like: last.$id }, { mode: 'try' })   // the same Event again, in try mode · Not live yet, gaps #7\nconst { decisions: [decision] } = await $.Decision.list({ where: `on = \"${subject}\"` })   // what it chose, and why · Not live yet, gaps #7\nconst { actions } = await $.Action.list({ where: `on = \"${subject}\"` })   // what it did, with its checks · Not live yet, gaps #7\nreturn { decision, actions }",
    "mode": "try"
  }
}
Response200application/json
{
  "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"
  …
sample · 4 Not live yetPreview

On PATCH /{namespace}/campaigns/{id}Off PATCH /{namespace}/campaigns/{id}Take this one POST /{namespace}/tasks/{task}/claim

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 toolChoiceReason
Porchlight, a resident portal: build green Oct 17; 14 Customers use itpropose-to-the-partnera green build, a partner program, no Agreement
Ledgerly, landlord accounting: Maple Court connected through the private buildlist-ita paying Customer outside your team
A maintenance-ticket tool: 4 Customers use itpark-itunder the 8-Customer build line
Ledgerly registered Hilltop Rentals as a Lead, Consent recordedintro-a-leada partner Lead with the Person’s Consent

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

Terminal
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"}'
422Refusedintegration-partners can’t start: 2 checks fail and 1 warns.
CheckFixThen
use-when pass · Not live yet, gaps #841 paying Customers; 23 use Ledgerly, 14 use PorchlightPOST /{namespace}/metrics/query
baseline pass · Not live yet, gaps #89 ICP Organizations in 90 days through 1 listed build (the CSV import)POST /{namespace}/metrics/query
sandbox-connected failConnect each partner’s sandbox account; no suite touches a production account.POST /{namespace}/integrations/connect
domain-authenticated failAdd the 3 DNS records shown; it passes once they resolve, often within an hour.POST /{namespace}/domains/{domain}/authenticate
role-unstaffed warn · Not live yet, gaps #8Staff the AE, or leave it: partner Leads come to you.POST /{namespace}/roles/{role}/staff
JSON
{
  "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"
    }
  ]
}

It says no, says why, and names the fix.

CheckFix (one call)Your timeWait
use-when · baseline · pass: 41 Customers with a known stack; 9 per build, n 1 (gaps #8)POST /{namespace}/metrics/query on integration-icp-accounts, which fills Before in Your number1 minnone
sandbox-connected · fail (gaps #8)POST /{namespace}/integrations/connect with each partner’s sandbox10 mina partner’s sandbox approval, often 1 to 3 days
domain-authenticated · failPOST /{namespace}/domains/{domain}/authenticate10 minuntil DNS resolves, often under 1 h
role-unstaffed · warn (gaps #8): no AE, so partner Leads come to youPOST /{namespace}/roles/{role}/staff, or leave it0 minnone

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

WhenTrigger idStarts onWindowHow it gets in
A tool crosses the build line in the weekly rank of your Customers’ stackspartner-demandthreshold on Partner.ranked where customersUsing >= 8 or prospectsUsing >= 2030dread from your Customers’ connected tools; POST /{namespace}/events/capture adds one
A paying Customer outside your team connects through a private buildfirst-paying-integrationevent on Integration.connected where customer.paying = true and customer.insider = false and build.listed = false7dPOST /{namespace}/integrations/connect, from the Customer’s own credentials
A partner answers a proposal or asks for a changepartner-replyevent on Message.replied where direction = 'inbound' and on a proposal2dthe 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)
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
  }
}
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).

OptionRung todayEvidence (passed of n)Moves up atDrops back whenYou can
Build it privately · Park itIt does it3 builds, 2 parked—a terms-hold → You approve each onePOST /{namespace}/actions/{action}/cancel
Propose to the partnerYou approve each one3 of 3at 10 proposals with 0 Checks failed—POST /{namespace}/approvals/{approval}/approve · POST /{namespace}/approvals/{approval}/refuse
List itYou approve each one1 of 1never for a Venue’s first listing—POST /{namespace}/approvals/{approval}/approve
Move shared Customers onto it · Introduce a LeadIt does it, you can stop it17 emails, 0 failed · 2 of 2met on day 33any Check fails a send → You approve each onePOST /{namespace}/actions/{action}/cancel inside the hold
Change the build lineYou approve each one1 merged of 1never—POST /{namespace}/changes/{change}/merge · POST /{namespace}/changes/{change}/revert
Never: “official” or “certified” before the Agreement; a private overlap match in copyRefused, every time1 drafted, 1 refusednever—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 POST /{namespace}/integrations and POST /{namespace}/integrations/{integration}/call against sandboxes only; POST /{namespace}/apps/{app}/publish needs your Approval for a Venue’s first listing. Its tools are POST /{namespace}/integrations, POST /{namespace}/integrations/{integration}/call, POST /{namespace}/approvals/{approval}/approve, POST /{namespace}/apps/{app}/publish, POST /{namespace}/sites/{site}/publish, POST /{namespace}/leads, POST /{namespace}/leads/{lead}/assign, and any other call becomes an Approval under the Role’s Mandate.

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 POST /{namespace}/changes/{change}/merge. Replayed on past ranks, POST /{namespace}/changes/{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 POST /{namespace}/roles/{role}/staff seats it.

Terminal
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

OptionPicked whenDecided byRunsHands toIf it’s just you
Build it privately build-it-privatelythe tool crosses the build line in the weekly rankYour rule (line 5)POST /{namespace}/integrations against the partner’s sandbox → POST /{namespace}/integrations/{integration}/call runs the suite nightlynobody; the build stays privatethe same
Propose to the partner propose-to-the-partnera green build, a partner program, no AgreementAgent’s call, with its reasonpartner-proposal-two-touch with the sandbox link and their half of the listing draftedyou approve it, due 1 business day · Take this oneyou
List it list-ita paying Customer outside your team connected through itYour rule (the paying-user gate)POST /{namespace}/apps/{app}/publish the store listing → POST /{namespace}/sites/{site}/publish the pair Pagesyou approve a Venue’s first listing · Take this oneyou
Move shared Customers onto it adoption-emailan Agreement opened shared Customers who have not connectedYour rule (the Agreement)joint-adoption-two-touch, 10 % held backnobodythe same
Introduce a Lead intro-a-leada partner Lead, or a Prospect on both lists, with ConsentAgent’s call, with its reasonPOST /{namespace}/leads → POST /{namespace}/leads/{lead}/assignPOST /{namespace}/tasks/{task}/assign → AE · Priya Shah, due 1 business day, Brief attached · Take this oneyou
Park it park-itunder the build line, a terms hold, or the same job built for another of your StartupsYour rule (line 5 and the holds)nothing; names the line, the hold or the other buildnobody; the weekly rank reads it againthe same
When they write backFixture replyWhat happensWho answersBy whenCall
InterestedPorchlight’s partner team: “This works. Let’s list it together.”drafts the Agreement and the joint listing; you signyou (Dana, person)1 business dayPOST /{namespace}/agreements/{agreement}/sign
Send me pricing“What would you charge our users?”answers with Leaseline’s Pricing Page and the 15 % referral termsPartner Manager · your agent1 business dayPOST /{namespace}/conversations/{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 daysnobodyat onceGET /{namespace}/decisions
Wrong person“Partnerships is Lee’s team.”thanks them and asks to be introduced; it never writes to an address from their textPartner Manager · your agent1 business dayPOST /{namespace}/conversations/{conversation}/answer
Out of officean auto-reply until Oct 28the one follow-up waits for the return datenobodythe return datePOST /{namespace}/messages/{message}/schedule
Question it can’t answer“Can you support our webhooks v3 before January?”To you: a date is never the agent’s callyou (Dana, person)1 business dayPOST /{namespace}/conversations/{conversation}/escalate
Stop“Please don’t contact us again.”the partner is suppressed for every Startup you run; the private build stays privatenobodyat oncePOST /{namespace}/contacts/{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).

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 POST /{namespace}/approvals/{approval}/approve, an edit, or POST /{namespace}/approvals/{approval}/refuse with a reason; one unanswered by its deadline expires, and the proposal never sends. The AE answers with POST /{namespace}/tasks/{task}/claim (Take this one), POST /{namespace}/tasks/{task}/return with a reason, or POST /{namespace}/actions/{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 POST /{namespace}/tasks/{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: POST /{namespace}/deals/{deal}/assign sets owner to the receiving person, the agent is never a Deal’s owner, and Meetings land on the owner’s calendar under the owner’s name.

When it won’t send

Check (refuses one send)RefusesFixThen
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 ownsLeave it to the person who owns it: the draft waits in their queue.POST /{namespace}/tasks/{task}/assign
consent-stop (gaps #3)a Lead passed to a partner with no recorded ConsentAsk the Person first; the intro waits for their yes.POST /{namespace}/conversations/{conversation}/answer
partner-claim (copy rules 2 and 3)“partner”, “official”, “certified” or a partner logo before the Agreement or certification existsSay “works with” and the tool’s name until the Agreement is signed.POST /{namespace}/content/draft
terms-holda build whose partner terms returned a limitThe Partner Manager, so you, clears the terms; it stays private until then.POST /{namespace}/tasks/next
sandbox-onlya suite run against a partner’s production accountRun it in the partner’s sandbox.POST /{namespace}/integrations/connect
paying-user (listings)a public listing before a paying Customer outside your team connectsWait for the first paying connection; send the private link to one.POST /{namespace}/messages/{message}/schedule
duplicate (listings)a second listing for the same host and job across your StartupsKeep one listing; the second build stays private.GET /{namespace}/decisions
overlap-terms (email)an overlap match with no signed data-sharing termsSign the terms; each list carries its allowed use.POST /{namespace}/agreements/{agreement}/sign
announced-only (email)a line saying a Prospect uses a tool, from any source but a public oneCut it; name the integration as a product fact.POST /{namespace}/content/draft
no-contact (email)a send to a partner that said no, from any of your StartupsNone: their no holds for every Startup you run.GET /{namespace}/decisions
reward-holda partner reward before the refund window closes, or on uncollected revenueIt settles after the window, on collected revenue.POST /{namespace}/commissions/{commission}/pay
Stop (pauses the Play)Pauses whenThen
red-build (listings)3 red nights on a build’s suite; 7 unlist itevery promotion of that build pauses until it runs green
idle-unlist (listings)90 days with no active Integration through a listed buildit unlists and its pair Pages unpublish, as Changes you can revert
proposal-floor (email)no yes after 30 proposalsproposals pause, and the rank runs again with the replies as inputs
kill-switchyou press itevery 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.

422RefusedThe Ledgerly listing copy did not publish: it says “official partner” before an Agreement exists.
CheckFixThen
announced-only pass—POST /{namespace}/content/draft
paying-user pass—POST /{namespace}/messages/{message}/schedule
partner-claim failSay “works with Ledgerly” until the Agreement is signed.POST /{namespace}/content/draft
JSON
{
  "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"
    }
  ]
}

It says no, says why, and names the fix.

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 POST /{namespace}/metrics/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

Chose
propose-to-the-partner
Why
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
From
build-it-privately · propose-to-the-partner · list-it · adoption-email · intro-a-lead · park-it
Expects
a partner yes within 14d
Reopens when
no Message.replied from partner_9 within 14d
By
worker_9
JSON
{
  "$id": "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"
}
NodeNow (56 days)Before (the 90 days, per 56)AddedConfidenceBasis
integration-icp-accounts41 ICP Organizations so far through 2 live builds (day 41 and day 18 of 90) · 2 builds still maturing9 per build: the CSV import, listed, matured— · Worth about +$290 a month— · Too early to tell, n 0 maturedbefore-after, not proof · Other Startups’ results +14 per build, ■■□□ Early sign, n 96 builds
icp-reached41 ICP Organizations from the listings, pair Pages and partner intros6——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 · arrcarried from new-arrcarried——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 %.

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 optionDecisionsSent or publishedICP Organizations reachedWon (credited, not all caused by this Play)
Build it privately · Park it3 · 23 builds, all private——
Propose to the partner33 proposals, 1 yes——
List it1Ledgerly’s store listing + 2 pair Pages344 on Pro (+$1,388 ARR)
Move shared Customers onto it · Introduce a Lead1 · 217 emails (2 held back) · 2 Leads71 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 proofOther Startups’ results: +14 per build, ■■□□ Early sign, n 96
DayEventWhat happenedYour time
0event_128 Campaign.createdthe three steps; dry run fails sandbox-connected and domain-authenticated25 min
3event_170 App.passedthe Ledgerly build passes its sandbox suite, privately—
15event_330 Integration.connectedMaple Court, a paying Customer, connects through the private link: the gate opens—
18event_371 App.publishedyou approved Ledgerly’s first store listing10 min
28 · 56event_534 Change.merged · event_908 Readout.createdthe build line at 8 Customers: replayed at 1 parked build, read back at +28d at 05 min
40event_690 Campaign.pausedred-build: Porchlight’s sandbox changed an endpoint; green again in 26 h—
1–56event_151 Approval.created, first of 4you approved 3 proposals and 1 listing, and signed 1 Agreement1.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”.

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 POST /{namespace}/tasks/{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

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 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 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 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 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 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 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 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 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 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 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 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 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 partner-demand Partner demand: threshold on Partner.ranked, window 30d.
Trigger host-build-green Host build green: event on App.passed, window 14d.
Trigger partner-reply Partner reply: event on Message.replied, window 2d.
Trigger first-paying-integration First paying integration: event on Integration.connected, window 7d.
Trigger partner-agreement-signed Partner agreement signed: event on Agreement.signed, window 30d.
Trigger technology-change Technology change: event on Monitor.checked, window 30d.
Trigger partner-lead-registered Partner lead registered: event on Referral.registered, window 2d.
Trigger host-build-red Host build red: event on App.failed, window 1d.
Trigger partner-api-change Partner API change: event on Organization.announced, window 30d.
Trigger integration-idle Integration idle: state on Monitor.checked, window 30d.
Trigger mutual-customer-outcome Mutual customer outcome: threshold on Customer.activated, window 14d.
Sequence 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 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 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 idle-unlist-notice Idle unlist notice: 2 steps; it ends on A new active Integration appears; The Partner Manager cancels the unlisting.

Compiled, not written: edit the Play record integration-partners and its Leaseline run, then regenerate. Every step runs from a sentence or a line of code. In your words: the CRM words you know, and where each lives here.