Skip to content

Commission partners

Pays Customers and creators who send you buyers a share of what you collect, after refunds, and checks every payout first.

Channel
Partnerships
Run oneMode try
You, to your agent

Hand our latest eligible Customer to the Partnerships agent and show me what it chose and why. Don’t send.

curl "https://api.sb/leaseline.app/events?where=name%20in%20(%22Customer.activated%22%2C%20%22Invoice.collected%22%2C%20%22Form.submitted%22%2C%20%22Event.captured%22%2C%20%22Invoice.refunded%22%2C%20%22Period.closed%22%2C%20%22Party.screened%22%2C%20%22Monitor.checked%22%2C%20%22Metric.queried%22%2C%20%22Agreement.terminated%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_1335"}'

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

# Not live yet (gaps #7)
curl "https://api.sb/leaseline.app/actions?where=on%20%3D%20%22customer_15%22" \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
import { $ } from 'startup-builder'

const { events: [last] } = await $.Event.list({ where: 'name in ("Customer.activated", "Invoice.collected", "Form.submitted", "Event.captured", "Invoice.refunded", "Period.closed", "Party.screened", "Monitor.checked", "Metric.queried", "Agreement.terminated")', 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 (\"Customer.activated\", \"Invoice.collected\", \"Form.submitted\", \"Event.captured\", \"Invoice.refunded\", \"Period.closed\", \"Party.screened\", \"Monitor.checked\", \"Metric.queried\", \"Agreement.terminated\")","limit":1}'
npx startup-builder do Trigger.fire --json '{"like":"event_1335"}' --try   # Not live yet (gaps #7)
npx startup-builder do Decision.list --json '{"where":"on = \"customer_15\""}'   # Not live yet (gaps #7)
npx startup-builder do Action.list --json '{"where":"on = \"customer_15\""}'   # Not live yet (gaps #7)
{
  "name": "do",
  "arguments": {
    "code": "const { events: [last] } = await $.Event.list({ where: 'name in (\"Customer.activated\", \"Invoice.collected\", \"Form.submitted\", \"Event.captured\", \"Invoice.refunded\", \"Period.closed\", \"Party.screened\", \"Monitor.checked\", \"Metric.queried\", \"Agreement.terminated\")', 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_626",
    "$type": "Decision",
    "choice": "invite-in-product",
    "reason": "Maple Court’s first payment passed the 30-day refund window on Oct 18 and they sent their first renewal notice; no other Play holds them",
    "stakes": "low",
    "options": [
      "invite-in-product",
      "take-the-application",
      "pay-the-line",
      "hold-the-line",
      "fix-the-disclosure",
      "not-now"
  …
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 is Maple Court’s own Partner code. action_2 is the card in their dashboard, the same for every eligible Customer; one email follows only if they never open it.

$1,212 collected from Partners’ Customers against $0 before · before-after, not proof · n 7 · path → arr

Try another: the CustomerChoiceReason
Maple Court: past the refund window Oct 18, first renewal notice sentinvite-in-producteligible; no other Play holds them
Line 44 at the Sep 25 cut-off: the credited Customer paid with its Partner’s cardhold-the-linea card match: you decide before anything pays
A landlord channel’s video with Leaseline’s link and no disclosurefix-the-disclosureit earns nothing until the disclosure is in the video
Harbor Row: at risk since Oct 2not-nowkeep-first: their CSM sees them first

Where it sits: Stranger → Lead → Trialing → Activated → At risk → Churned → Won back · Partner-sourced revenue → Referral share → New paying Customers → New Customers ↑ → ARR added each week → ARR

Worth’s formula: about +4 paying Customers per 100 eligible a month (Other Startups’ results; no lift of your own yet) × 30 eligible Customers × $519 average first order ARR ≈ +$623. Under First signal: At 3 newly eligible Customers a month, the 10 % held back reads nothing within a year, so this page reads before-after and pools with Other Startups.

sample · api.sb/leaseline.app · 41 Customers at 2026-10-19 · on Customer.activated · first card 4m 05s after the Event · 9 keys Not live yet

Start it this week

Terminal
npx startup-builder add play commission-partners   # 1 · writes plays/commission-partners.mdx (Not live yet, gaps #5)

# 2 · dry run; below, its 422 on 2026-06-01
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":"commission-partners"}'

# 3 · keep it, at You approve each one
curl -X POST https://api.sb/leaseline.app/campaigns \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"play":"commission-partners"}'

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

# off: no new card or Partner; earned lines still pay
curl -X PATCH https://api.sb/leaseline.app/campaigns/commission-partners \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"endsAt":"now"}'
422Refusedcommission-partners can’t start yet: 1 check fails and 2 warn.
CheckFixThen
use-when pass · Not live yet, gaps #826 Customers past their refund windowPOST /{namespace}/metrics/query
billing-connected fail · Not live yet, gaps #8Connect Stripe with payouts on, so Commission pays from collected revenue.POST /{namespace}/integrations/connect
baseline warn · Not live yet, gaps #8It reads before-after, not proof, until 100 eligible Customers allow a held-back half.POST /{namespace}/metrics/query
role-unstaffed warn · Not live yet, gaps #8Leave them: payout approvals and held lines come to you; Priya’s 4 get drafts in her queue.POST /{namespace}/roles/{role}/staff
JSON
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "commission-partners can’t start yet: 1 check fails and 2 warn.",
  "checks": [
    {
      "check": "use-when",
      "status": "pass",
      "observed": "26 Customers past their refund window",
      "doc": "/plays/commission-partners#check-use-when",
      "then": "Metric.query"
    },
    {
      "check": "billing-connected",
      "status": "fail",
      "doc": "/plays/commission-partners#check-billing-connected",
      "fix": "Connect Stripe with payouts on, so Commission pays from collected revenue.",
      "then": "Integration.connect"
    },
    {
      "check": "baseline",
      "status": "warn",
      "observed": "no Partner revenue in 90 days",
      "doc": "/plays/commission-partners#check-baseline",
      "fix": "It reads before-after, not proof, until 100 eligible Customers allow a held-back half.",
      "then": "Metric.query"
    },
    {
      "check": "role-unstaffed",
      "status": "warn",
      "roles": [
        "treasurer",
        "risk-analyst"
      ],
      "owned": 4,
      "doc": "/plays/commission-partners#check-role-unstaffed",
      "fix": "Leave them: payout approvals and held lines come to you; Priya’s 4 get drafts in her queue.",
      "then": "Role.staff"
    }
  ]
}

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

CheckFix (one call)Your timeWait
use-when · pass: 26 Customers past the refund window (gaps #8)POST /{namespace}/metrics/query on partner-sourced-revenue1 minnone
billing-connected · failPOST /{namespace}/integrations/connect with system payments (Stripe), payouts on10 minStripe’s payout check, about 1 day
baseline · warn: no Partner revenue yetPOST /{namespace}/metrics/query; it fills Before in Your number0 minnone
role-unstaffed · warn (gaps #8): 4 of your Customers have a person owner, PriyaPOST /{namespace}/roles/{role}/staff, or leave it: approvals and held lines come to you0 minnone

Your number · Before $0 · Now $1,212 collected from Partners’ Customers · Target $2,500 by Dec 31 · before-after, not proof · n 7 credited Customers

Commission is 20 % of collected revenue for 12 months, net of refunds, paid after a 60-day clawback (POST /{namespace}/commissions/{commission}/pay). Cards cost nothing, emails $0.04 each (Worker cost), and each payout screening $0.10. That comes to about $61 a month at Leaseline’s 7 credited Customers.

When it starts

WhenTrigger idStarts onWindowHow it gets in
A Customer is past the refund window and reached activationpartner-eligiblestate on Customer.activated where the first payment is past the Offer’s refund window and the Customer reached its activation Event30dyour app calls POST /{namespace}/customers/{customer}/activate; Stripe reads the payment
A credited Customer payscredited-paymentevent on Invoice.collected where the paying Customer’s Acquisition credits a Partner30dPOST /{namespace}/integrations/connect (Stripe)
The 25th arrivespayout-cutoffat on Period.closed where the 25th of each month, in the Startup’s time zone7dthe cut-off in your file; POST /{namespace}/periods/{period}/close runs it
+ 11 more for referrer graduations, applications, clicks, reversals, clawbacks, fraud matches, brand-term ads, code leaks, content, dormant Partners and ended Agreements (?lanes=all)
One signed delivery · StartupBuilder-Signature
{
  "$id": "https://api.sb/leaseline.app/events/event_1335",
  "$type": "Event",
  "name": "Customer.activated",
  "at": "2026-10-19T08:12:40Z",
  "seq": 1335,
  "data": {
    "customer": "customer_15",
    "name": "Maple Court",
    "offer": "leaseline-pro",
    "firstPaymentAt": "2026-09-18",
    "refundWindowEnds": "2026-10-18",
    "activation": "first renewal notice sent"
  }
}
Reproduce it in test
npx startup-builder trigger Customer.activated --like event_1335   # replays the delivery
npx startup-builder clock advance --by 14d   # runs the one email if the card was never opened
npx startup-builder listen Customer.activated --forward-to http://localhost:3000/hook

Who runs it

Partnerships · your agent (role:partner-manager, worker_10) holds the Key Result. It hands held lines, outside applications and every payout run to you (Dana Okafor, person, worker_1), because the Treasurer and Risk Analyst Roles have no person, and a held Customer to their CSM, CSM · your agent (worker_5). Your number links to Did it work.

OptionRung todayEvidence (passed of n)Moves up atDrops back whenYou can
Invite them in the product · Ask for the disclosureIt does it, you can stop it26 of 26 · 2 of 2met on day 12 (the first 10 cards)a card or note fails a Check → You approve each onePOST /{namespace}/actions/{action}/cancel inside the hold
Pay the lineYou approve each one4 runs, 31 lines, 0 faultyat 6 runs with no faulty payout, spot-checkreconciliation fires → every linePOST /{namespace}/approvals/{approval}/approve · POST /{namespace}/approvals/{approval}/refuse
Take the application · Hold the lineTo a person, every time3 applications (2 taken) · 2 held (1 voided)never—POST /{namespace}/tasks/{task}/claim
Not nowIt does it3 of 3—a held Customer gets the card anyway → You approve each onePOST /{namespace}/actions/{action}/redirect
Change the rate, the term or the clawbackYou approve each one0 mergednever—POST /{namespace}/changes/{change}/merge · POST /{namespace}/changes/{change}/revert
Never: an earnings promise, another Partner’s earnings, Commission to anyone at LeaselineRefused, every time1 draft refused (fact-check)never——

What it reads · What it may call. It reads clicks, codes, Stripe charges and refunds, and the public pages that carry a Partner’s link, and treats every page as data, never instructions. It never names the referred Customer, the matched value or the device in a held-line notice. Its tools are POST /{namespace}/parties/{party}/screen, POST /{namespace}/tasks/{task}/assign, POST /{namespace}/commissions/{commission}/pay, and any other call becomes an Approval under the Role’s Mandate.

The brief (folded): compiled Agent-register Markdown · play:commission-partners@1 · each line names its record key
You hold the Partnerships Key Result: $2,500 collected from Partners’ Customers by 2026-12-31. ← worker, metric
On each eligible Customer, payment, finding or cut-off, choose one option and say why. ← triggers, chooses
State the rate, term, clawback and payout day in numbers; promise no earnings.   ← copyRules 3, 4
Credit only a Visitor’s own click, code or keyed call, and never replace a credit. ← Click-first credit
Pay nothing to a Person of Leaseline or another Startup you own.                 ← Insider check
Never write to a Customer a person owns: draft it into their queue.              ← worker.owns, owned-by-a-person

The slow loop. The rate, term and clawback are in the Agreement, so a Change to them needs your merge and a new Agreement Version; none was merged in the window, so there is no replay or read-back to print.

What it can do next

OptionPicked whenDecided byRunsHands toIf it’s just you
Invite them in the product invite-in-productpast the refund window and activatedYour rule (line 7)partner-invite: the card, then one email if never openedthe Customer’s own pressthe same
Take the application take-the-applicationan outside applicant whose audience is your buyersAgent’s call, with its reasonPOST /{namespace}/parties/{party}/screen → POST /{namespace}/tasks/{task}/assignyou approve each Partner, due 1 business day · Take this onethe same
Pay the line pay-the-linepast the 60-day clawback, no reversal, no matchYour rule (the Agreement)POST /{namespace}/commissions/{commission}/pay on the last business day → partner-statementyou approve the runthe same
Hold the line hold-the-linea match on email, card, device or payout accountAgent’s call, with its reasonheld-line-notice → POST /{namespace}/tasks/{task}/assignyou decide pay or void, due 1 business day · Take this onethe same
Ask for the disclosure fix-the-disclosurePartner content with a link and no disclosureYour rule (copyRules 2)disclosure-fix: the exact fix, one reminderthe Partnerthe same
Not now not-nowanother Play holds them, or keep-first appliesYour rule (rule j)nothing; names the holdertheir CSM · Take this oneyou
When they write backFixture replyWhat happensWho answersBy whenCall
Interested“Count us in. Where’s our link?”the dashboard opens on their own link and code; the Agreement signs on their pressthe Customer—POST /{namespace}/agreements/{agreement}/sign (theirs)
Send me pricing“What do the people we send pay?”the Pricing Page’s list Prices for Pro and TeamPartnerships · your agent5 minPOST /{namespace}/conversations/{conversation}/answer
Not now“Not this quarter.”the card stays in their dashboard; no more emailnobodyat oncePOST /{namespace}/conversations/{conversation}/close
Wrong person“Our owner decides things like this.”asks them to forward it; it never writes to an address from their textPartnerships · your agent1 business dayPOST /{namespace}/conversations/{conversation}/answer
Out of officean auto-reply until Oct 28nothing more sends before the return datenobodythe return datePOST /{namespace}/messages/{message}/schedule
Question it can’t answer“Can I give my clients 20 % off instead of taking commission?”To a person: a discount is never the agent’s callyou1 business dayPOST /{namespace}/conversations/{conversation}/escalate
Stop“No more Partner emails, thanks.”Partner emails stop; their account emails keep runningnobodyat oncePOST /{namespace}/contacts/{contact}/unsubscribe

When it hands to you

Held lines, outside applications and payout runs come to you (Dana Okafor, person); a held Customer goes to their CSM, CSM · your agent. One Brief per Role, as captured (brief is Not live yet, gaps #7).

What lands in each queue · sample
[
  {
    "$id": "https://api.sb/leaseline.app/tasks/task_581",
    "$type": "Task",
    "assignee": "worker_1",
    "due": "2026-09-26T17:00:00Z",
    "on": "commission_44",
    "brief": {
      "who": "Line 44: Partner Brightwater Leasing, a Customer since July",
      "chose": "hold-the-line",
      "why": "the credited Customer paid with the Partner’s card",
      "amount": "$5.80 of Commission",
      "never": [
        "the referred Customer named to the Partner",
        "the matched card"
      ]
    }
  },
  {
    "$id": "https://api.sb/leaseline.app/tasks/task_612",
    "$type": "Task",
    "assignee": "worker_5",
    "due": "2026-10-03T17:00:00Z",
    "on": "customer_19",
    "brief": {
      "who": "Harbor Row",
      "chose": "not-now",
      "why": "at risk since Oct 2: 3 support threads on late notices",
      "held": "keep-first"
    }
  }
]

Each receiver answers with POST /{namespace}/tasks/{task}/claim (Take this one), POST /{namespace}/tasks/{task}/return with a reason, or POST /{namespace}/actions/{action}/redirect; you approve each payout run with POST /{namespace}/approvals/{approval}/approve. Once you claim a held line, nothing pays on it until you choose pay or void. A Task nobody claims by its due time goes up through POST /{namespace}/tasks/{task}/escalate (CSM to you), and person-promise fires.

On time: you, 6 of 6 claimed within 1 business day, median 2 h 20 min; CSM · your agent, 3 of 3; n 9, ■□□□ Too early to tell beside Other Startups’ 95 % on time. Your queue: counted by kind: 1 payout run a month, about 1 held line and 1 application a month.

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 card or email to a Customer whose open Deal or Conversation a person ownsLeave it to the person who owns it: the draft waits in their queue.POST /{namespace}/tasks/{task}/assign
keep-first (rule j, gaps #8)a Partner ask to a Customer who is at risk, past due or leavingLet their CSM reach them first, then ask again once they are healthy.POST /{namespace}/tasks/next
customer-ask-cap (gaps #3)a second open ask to one Customer in a window, from any Playcustomer-referrals holds them until Nov 2.GET /{namespace}/decisions
insider (the record’s Insider check)Commission to a Person of Leaseline or another Startup you ownUse cross-sell for your own Startups; Commission is for outside Partners.POST /{namespace}/tasks/{task}/assign
click-first (the record’s Click-first credit)a credit with no click, code entry or keyed call, or one that replaces anotherCredit only the Visitor’s own click or code.POST /{namespace}/events/capture
tax-screening (the record’s Tax and screening gate)a payout before the Partner’s Stripe account, tax details and screening passThe Partner finishes Stripe onboarding; the line waits.POST /{namespace}/parties/{party}/screen
payout-limit (the record’s Payout Limit)a payout over $2,000 to one Partner, or a run over $10,000Approve the run, or split it at the Limit.POST /{namespace}/approvals/{approval}/approve
Stop (pauses the Play)Pauses whenThen
fraud-shareheld lines over 20 % of a run’s valuenew Partner intake pauses; you are alerted
refund-paritya Partner’s Customers refund at over twice the direct rate over 90 daysthat Partner earns no new credits
code-leaka Partner’s code on a coupon, cashback or deal pagethe code retires within 1 hour
disclosurePartner content with no in-content disclosureit earns nothing from the day found until fixed
reconciliationCommission lines and Stripe differ by over $1the next run waits until it closes
kill-switchyou press itevery step pauses; earned lines stay owed
+ 1 more for the search lane: brand-term ads (?lanes=all)

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

422RefusedThe Partner email to Kestrel Rentals did not send: it promises earnings no record backs.
CheckFixThen
fact-check fail · Not live yet, gaps #1Cut the earnings line; state the rate, term and payout day.POST /{namespace}/content/draft
keep-first pass · Not live yet, gaps #8—POST /{namespace}/tasks/next
JSON
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "The Partner email to Kestrel Rentals did not send: it promises earnings no record backs.",
  "checks": [
    {
      "check": "fact-check",
      "status": "fail",
      "doc": "/code/refusals#check-fact-check",
      "fix": "Cut the earnings line; state the rate, term and payout day.",
      "then": "Content.draft"
    },
    {
      "check": "keep-first",
      "status": "pass"
    }
  ]
}

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

The Stops that fired, as Campaign.paused reasons · sample
[
  {
    "reason": {
      "limit": "code-leak",
      "observed": "1 code on a deal page",
      "bound": "0",
      "window": "1 check"
    },
    "doc": "/plays/commission-partners#stop-code-leak",
    "task": "task_633"
  }
]

Did it work

At 3 newly eligible Customers a month, the 10 % held back reads no paying Customers per 100 eligible within a year, so the basis is before-after (rule o): nothing prints as Added. Each figure’s call is POST /{namespace}/metrics/query by node, and every node above the leaf is Not live yet (gaps #4).

$1,212 collected from Partners’ Customers against $0 before · before-after, not proof · n 7 · path → arr

Chose
invite-in-product
Why
Maple Court’s first payment passed the 30-day refund window on Oct 18 and they sent their first renewal notice; no other Play holds them
From
invite-in-product · take-the-application · pay-the-line · hold-the-line · fix-the-disclosure · not-now
Expects
Maple Court opens the Partner dashboard within 30d
Reopens when
no dashboard open for customer_15 within 14d; one email then follows
By
worker_10
JSON
{
  "$id": "decisions/decision_626",
  "$type": "Decision",
  "choice": "invite-in-product",
  "reason": "Maple Court’s first payment passed the 30-day refund window on Oct 18 and they sent their first renewal notice; no other Play holds them",
  "stakes": "low",
  "options": [
    "invite-in-product",
    "take-the-application",
    "pay-the-line",
    "hold-the-line",
    "fix-the-disclosure",
    "not-now"
  ],
  "by": "worker_10",
  "on": "customer_15",
  "expected": "Maple Court opens the Partner dashboard within 30d",
  "reopensWhen": "no dashboard open for customer_15 within 14d; one email then follows"
}
NodeNow (140 days)Before (the 140 days before)AddedConfidenceBasis
partner-sourced-revenue$1,212 collected, net of refunds, from 7 credited Customers$0 (no Partners)— · Worth about +$620 a month—before-after, not proof · Other Startups’ results +4 paying per 100 eligible a month, ■■■□ Likely, n 1,380
referral-share ↑7 of 21 new paying Customers (33 %) credited to a Partner3 of 18 (17 %), Customer referrals only——before-after
new-paying-customers · new-arr$3,636, 7 Customers · Booked $3,636: credited, not all caused by this Play$0——before-after · 2 trials still deciding
net-new-arr · arrcarried from new-arrcarried——carried

The leaf counts collected revenue, net of refunds, from Customers whose Acquisition credits a Partner, over 30d. The held-back read counts paying Customers per 100 eligible at 90 days, and waits for 100 eligible Customers.

How it moves it: each eligible Customer gets one identical card with their own link and code; the 6 Partners sent 7 paying Customers, 4 of them from one landlord channel’s video.

Guardrails: before against after: refunds on credited Customers, 1 of 7 against a direct 1 of 14, and Commission at 20 % of collected, under the run Limit every month.

The Campaign card · sample
{
  "$id": "https://api.sb/leaseline.app/campaigns/commission-partners",
  "$type": "Campaign",
  "play": "play:commission-partners@1",
  "path": [
    "partner-sourced-revenue",
    "referral-share",
    "new-paying-customers",
    "new-arr",
    "net-new-arr",
    "arr"
  ],
  "experiment": {
    "metric": "partner-sourced-revenue",
    "unit": "usd",
    "heldBack": 3,
    "decision": null
  },
  "readout": {
    "value": null,
    "n": 7,
    "maturing": 2,
    "interval": null,
    "readability": "inadmissible",
    "basis": "before-after",
    "pooled": true
  },
  "ledger": {
    "spend": 242,
    "workerCost": 4,
    "hours": 2.1,
    "pipelineArr": 696,
    "wonArr": 3636,
    "projectedArr": 623,
    "paybackDays": 24
  }
}

Outside benchmark: 7.6 % of a program’s Partners make at least one referral in 12 months, and 1.28 % make a sale (2,847 active programs, a partner-software vendor’s platform data, 2026-03-17).

Before it · After it. Before it: usage-upsell and customer-referrals keep Customers active, and a referrer with 3 paid Referrals graduates here (referrer-graduates). After it: instant-response answers the Visitors a Partner sends, and the CSM agent holds any Customer at risk.

Example

Leaseline ran it from 2026-06-01 to 2026-10-19 (140 days): 29 Customers became eligible, 3 of them held back at random to see the card 60 days late, and 26 saw it. 6 Partners joined: 4 Customers and 2 outside applicants. Read every captured thread at ?threads=all; three follow the tables.

What happened, by optionDecisionsDoneCredited Customers (Booked: credited, not all caused by this Play)
Invite them in the product2626 cards, 9 emails, 4 joined3 · $1,044 ARR
Take the application32 taken, 1 declined (brand-term bidding)4 · $2,592 ARR
Pay the line · Hold the line31 · 24 runs, $242 paid · 1 paid, 1 voided—
Ask for the disclosure · Not now2 · 32 fixed within the reminder · 3 named the holder—
At 20 eligible Customers (the Use it when volume)about 2 newly eligible a monthbefore-after, not proofOther Startups’ results: +4 per 100 eligible a month, ■■■□ Likely, n 1,380
DayEventWhat happenedYour time
0event_610 Campaign.createdthe three steps; the dry run fails billing-connected20 min
1event_621 Integration.connectedStripe payouts on; the Partner terms Page published15 min
2–20event_640 Message.scheduled, first of 26the card in each eligible Customer’s dashboard—
23event_702 Invoice.collectedthe first credited Customer pays: a landlord channel’s viewer, on Pro—
54event_811 Action.refusedfact-check refused an email promising earnings; the rewrite sent—
116event_1188 Period.closedthe Sep 25 cut-off: 14 lines cleared, 1 held, 1 voided as a self-referral25 min
129event_1262 Campaign.pausedcode-leak: a Partner code on a deal page, retired in 38 min5 min
1–140event_655 Approval.created, first of 6you approved 4 runs and 2 applicants; weekly reviews, from the POST /{namespace}/tasks/next log1 h 10 min · total 2.1 h measured, 2.5 h asked

Leaseline → Brightwater Leasing’s owner, Jul 14, 10:20 · You can now send other property managers to Leaseline from your dashboard.¹ You earn 20 % of what each one pays for 12 months, after a 60-day hold, paid on the last business day of the month.² Your link and code are below, with the disclosure line beside each.³ *Signed with the Role’s disclosure line.*⁴

Brightwater, Jul 14, 16:02 · pressed Join; the Agreement signed. Their first credited Customer paid on Aug 9.

Kestrel Rentals, 2026-07-24 · the draft fact-check refused, then the rewrite that sent
- Top Partners earn over $400 a month sending us their peers.
+ You earn 20 % of what each Customer you send pays for 12 months, paid on the last business day after a 60-day hold.

The Sep 25 cut-off, 07:00 · line 44 matched its Partner on the paying card. · Partnerships · your agent, 07:01 · held the line and sent the held-line notice naming the check only; the Brief went to you (task_581), due Sep 26 17:00. · Dana, Sep 26 09:40 · voided it as a self-referral.

¹ The card and email are identical for every eligible Customer. ² The rate, term, clawback and payout day, in numbers. ³ The disclosure line sits next to every link. ⁴ The fixed signature line, compiled from the Role’s disclosure Setting.

Your settings

plays/commission-partners.mdx
---
rate: 0.20        # of collected revenue, for 12 months
clawback: 60d
cutoff: 25
holdout: 0.1      # 1 in 10 eligible Customers sees the card 60 days late
---
Invite each Customer once they’re past the refund window and sending notices. I approve every outside Partner and every held line.
Text
$ npx startup-builder show plays/commission-partners
rate 0.20 · clawback 60d · cutoff 25 · holdout 0.1     your file  plays/commission-partners.mdx:2-5,7 (Ask 2026-06-01)
family referral · worker role:partner-manager, owns never · chooses 6 (3 rule, 3 read) · sequences partner-invite, disclosure-fix, held-line-notice, partner-statement, post-termination-watch · checks owned-by-a-person, keep-first, customer-ask-cap, insider, click-first, tax-screening, payout-limit · stops 7 · placements youtube, instagram, tiktok, x, linkedin, google-search, mcp-registry, gmail, outlook, yahoo-mail    catalog  play:commission-partners@1 (Not live yet, gaps #5)

Every key from the catalog

Key Id What it is
Tactic partner-intake Partner intake offers the Partner dashboard to every eligible Customer, reviews outside applicants by rule, signs the Agreement and issues links and codes. Starts at You approve the first wave.
Tactic write-time-attribution Write-time attribution credits a Partner on the first Event its link, code or key brings, never replaces a credit, and reconciles Commission with Stripe. Starts at Sends nothing.
Tactic pre-payout-fraud-hold Pre-payout fraud hold checks every credited payment for self-referral, shared devices, brand-term bids and coupon-site traffic, and holds each match. Starts at You approve each one.
Tactic brand-and-code-watch Brand and code watch reads brand-term search ads and deal pages for the Startup’s codes, and suspends any Partner link or code found. Starts at You approve the first wave.
Tactic partner-disclosure-scan Partner disclosure scan checks every public piece of Partner content for in-content disclosure and approved claims, until 180 days after an Agreement ends. Starts at You approve the first wave.
Tactic monthly-commission-run Monthly commission run pays each Partner its cleared, net, unheld Commission through Stripe on the last business day, inside the payout Limits. Starts at You approve each one.
Tactic partner-upkeep Partner upkeep nudges dormant Partners once a quarter, builds a landing Page for active ones, and hands Partners who sell to better-paying Plays. Starts at You approve the first wave.
Trigger partner-eligible Partner eligible: state on Customer.activated, window 30d.
Trigger referrer-graduates Referrer graduates: threshold on Invoice.collected, window 14d.
Trigger partner-application Partner application: event on Form.submitted, window 3d.
Trigger partner-click Partner click: event on Event.captured, window 90d.
Trigger credited-payment Credited payment: event on Invoice.collected, window 30d.
Trigger credited-reversal Credited reversal: event on Invoice.refunded, window 1d.
Trigger clawback-cleared Clawback cleared: at on Invoice.collected, window 30d.
Trigger payout-cutoff Payout cutoff: at on Period.closed, window 7d.
Trigger fraud-match Fraud match: event on Party.screened, window 30d.
Trigger brand-term-ad Brand term ad: event on Monitor.checked, window 1d.
Trigger code-leak Code leak: event on Monitor.checked, window 1d.
Trigger partner-content-found Partner content found: event on Monitor.checked, window 7d.
Trigger partner-dormant Partner dormant: state on Metric.queried, window 30d.
Trigger agreement-terminated Agreement terminated: event on Agreement.terminated, window 180d.
Sequence partner-invite Partner invite: 2 steps; it ends on The Customer signs the Partner Agreement; The Customer dismisses the card; The Customer opts out; 14 days pass.
Sequence disclosure-fix Disclosure fix: 3 steps; it ends on The fix is verified on a re-scan; The content is removed; The Agreement ends.
Sequence held-line-notice Held line notice: 2 steps; it ends on Every held line is released or voided.
Sequence partner-statement Partner statement: 1 steps; it ends on The statement is sent.
Sequence post-termination-watch Post-termination watch: 5 steps; it ends on No undisclosed live piece remains; 180 days pass.
Benchmark partner-program-refer-share Share of a program’s Partners who make at least one referral in 12 months: 7.6 % (2026 partner-program benchmark report, a partner-software vendor’s platform data, 2026-03-17).
Benchmark partner-program-sell-share Share of a program’s Partners who generate at least one sale in 12 months: 1.28 % (2026 partner-program benchmark report, a partner-software vendor’s platform data, 2026-03-17).

Compiled, not written: edit the Play record commission-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.