Skip to content

Agent-to-agent selling

Lets an agent open a working account in one call, locks its sends until a person claims it, then asks that person once to pay.

Channel
Signup flows
Run oneMode try
You, to your agent

Hand the latest account an agent made to the SDR agent and show me what it chose and why. Don’t send.

curl "https://api.sb/leaseline.app/events?where=name%20in%20(%22Tenant.provisioned%22%2C%20%22Interface.called%22%2C%20%22Entitlement.checked%22%2C%20%22Visit.recorded%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_1416"}'

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

const { events: [last] } = await $.Event.list({ where: 'name in ("Tenant.provisioned", "Interface.called", "Entitlement.checked", "Visit.recorded")', 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 (\"Tenant.provisioned\", \"Interface.called\", \"Entitlement.checked\", \"Visit.recorded\")","limit":1}'
npx startup-builder do Trigger.fire --json '{"like":"event_1416"}' --try   # Not live yet (gaps #7)
npx startup-builder do Decision.list --json '{"where":"on = \"tenant_77\""}'   # Not live yet (gaps #7)
npx startup-builder do Action.list --json '{"where":"on = \"tenant_77\""}'   # Not live yet (gaps #7)
{
  "name": "do",
  "arguments": {
    "code": "const { events: [last] } = await $.Event.list({ where: 'name in (\"Tenant.provisioned\", \"Interface.called\", \"Entitlement.checked\", \"Visit.recorded\")', 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_481",
    "$type": "Decision",
    "choice": "send-the-claim-note",
    "reason": "an agent made a workspace for 3 units of Cedar Row Management and named ops@cedarrow.example as the claim address",
    "stakes": "low",
    "options": [
      "send-the-claim-note",
      "offer-in-the-reply",
      "offer-the-next-plan",
      "send-the-fix",
      "to-the-ae",
      "let-it-expire"
  …
sample · 4 Not live yetPreview

On POST /{namespace}/processes/{process}/enableOff POST /{namespace}/processes/{process}/pauseTake this one POST /{namespace}/tasks/{task}/claim

action_3 is the claim note to ops@cedarrow.example, held 2 minutes; the agent’s own reply already carried the claim code, the expiry time and the $29 Price.

13.8 claimed and paying per 100 against 5.3 before · before-after, not proof · n 29 · path → arr

Try another: the agent-made accountChoiceReason
Cedar Row Management: an agent made a 3-unit account, claim address givensend-the-claim-notean account and a claim address
An unclaimed account called send_renewal_noticeoffer-in-the-replya locked Verb: the reply carries the claim link and Price
Wrenfield Rentals: 48 of 50 units on Prooffer-the-next-plana claimed Customer at 96 % of its Limit
A claimer asked: “Can we pay by invoice for 12 properties?”to-the-aeterms and invoicing go to a person

Where it sits: Stranger → Lead → Trialing → Activated → At risk → Churned → Won back · Claimed and paying rate → New paying Customers → New Customers ↑ → ARR added each week → ARR

Worth’s formula: about +6.6 claimed and paying per 100 accounts (Other Startups’ results; no lift of your own yet) × 22 agent-made accounts a month × $430 first order ARR ≈ +$625. Under First signal: At 5 accounts a week, 90 days can tell only +19 per 100 or more, so your file keeps a 10 % signup half for the pool and reads before-after.

sample · api.sb/leaseline.app · 41 Customers at 2026-10-19 · on Tenant.provisioned · claim note 40 s after the first call · 4 keys Not live yet

Start it this week

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

# 2 · dry run; below, its 422 on the day-0 Startup (clock at 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":"agent-to-agent"}'

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

# on
curl -X POST https://api.sb/leaseline.app/processes/onboarding/enable \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"

# off: no new account, claim note or in-reply Offer; expiry clocks pause
curl -X POST https://api.sb/leaseline.app/processes/onboarding/pause \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY"
422Refusedagent-to-agent can’t start yet: 1 check fails and 1 warns.
CheckFixThen
owned-by-a-person pass · Not live yet, gaps #80 claim addresses belong to a Lead or open Deal a person ownsPOST /{namespace}/tasks/{task}/assign
use-when pass · Not live yet, gaps #819 keyless calls in 30 daysPOST /{namespace}/metrics/query
baseline warn · Not live yet, gaps #8n 38: it reads before-after, not proof, until a held-back half can read.POST /{namespace}/metrics/query
billing-connected fail · Not live yet, gaps #8Connect Stripe so every Price agents see is the live one.POST /{namespace}/integrations/connect
domain-authenticated passleaseline.app: 3 of 3 records resolvePOST /{namespace}/domains/{domain}/authenticate
role-unstaffed warn · Not live yet, gaps #8Staff the AE, or leave it: terms and invoices come to you.POST /{namespace}/roles/{role}/staff
JSON
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "agent-to-agent can’t start yet: 1 check fails and 1 warns.",
  "checks": [
    {
      "check": "owned-by-a-person",
      "status": "pass",
      "observed": "0 claim addresses belong to a Lead or open Deal a person owns",
      "doc": "/plays/agent-to-agent#check-owned-by-a-person",
      "then": "Task.assign"
    },
    {
      "check": "use-when",
      "status": "pass",
      "observed": "19 keyless calls in 30 days",
      "doc": "/plays/agent-to-agent#check-use-when",
      "then": "Metric.query"
    },
    {
      "check": "baseline",
      "status": "warn",
      "observed": "2 paying of 38 agent-made keys in 90 days, signup first",
      "doc": "/plays/agent-to-agent#check-baseline",
      "fix": "n 38: it reads before-after, not proof, until a held-back half can read.",
      "then": "Metric.query"
    },
    {
      "check": "billing-connected",
      "status": "fail",
      "doc": "/plays/agent-to-agent#check-billing-connected",
      "fix": "Connect Stripe so every Price agents see is the live one.",
      "then": "Integration.connect"
    },
    {
      "check": "domain-authenticated",
      "status": "pass",
      "observed": "leaseline.app: 3 of 3 records resolve",
      "doc": "/plays/agent-to-agent#check-domain-authenticated",
      "then": "Domain.authenticate"
    },
    {
      "check": "role-unstaffed",
      "status": "warn",
      "roles": [
        "sales-lead"
      ],
      "owned": 0,
      "doc": "/plays/agent-to-agent#check-role-unstaffed",
      "fix": "Staff the AE, or leave it: terms and invoices come to you.",
      "then": "Role.staff"
    }
  ]
}

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

CheckFix (one call)Your timeWait
owned-by-a-person · use-when · pass: 0 owned; 19 keyless calls a month (gaps #8)POST /{namespace}/metrics/query on claimed-paying-rate1 minnone
baseline · warn: 2 of 38 in 90 daysPOST /{namespace}/metrics/query; it fills Before in Your number0 minnone
billing-connected · failPOST /{namespace}/integrations/connect with system recurring-billing (Stripe)5 minthe first Price sync, about 10 min
domain-authenticated · role-unstaffed · pass · warn (gaps #8): no AE, so terms come to youPOST /{namespace}/roles/{role}/staff, or leave it0 minnone

Your number · Before 5.3 per 100 · Now 13.8 claimed and paying per 100 · Target 15 by Dec 31 · before-after, not proof · n 29 matured

An unclaimed account costs $0.004 a day for its 3 days, and each claim note $0.04 from your Domain (Worker cost). That comes to about $1 a month at 22 agent-made accounts.

When it starts

WhenTrigger idStarts onWindowHow it gets in
An agent with no key asks for an accountagent-provisionevent on Tenant.provisioned where the caller has no API Keythe claim note within 2 min; the account expires in 72 hyour Interface’s keyless POST /{namespace}/tenants/provision
An unclaimed account calls a locked Verblocked-verb-callevent on Entitlement.checked where a locked Verb, unclaimed: claim-requiredin the same replyPOST /{namespace}/entitlements/check on the call
24 h before an unclaimed account expiresclaim-expiry-dueat on Tenant.provisioned where 24 hours before expiresAtthe last of the 3 notesthe account’s expiresAt
+ 6 more for third calls, claim-page visits, Limits, errors and quiet accounts (?lanes=all)
One signed delivery · StartupBuilder-Signature
{
  "$id": "https://api.sb/leaseline.app/events/event_1416",
  "$type": "Event",
  "name": "Tenant.provisioned",
  "at": "2026-10-19T16:08:30Z",
  "seq": 1416,
  "data": {
    "tenant": {
      "$id": "https://api.sb/leaseline.app/tenants/tenant_77",
      "claimed": false,
      "units": 3,
      "expiresAt": "2026-10-22T16:08:30Z"
    },
    "claimAddress": "ops@cedarrow.example",
    "caller": "mcp",
    "locked": [
      "send_renewal_notice"
    ]
  }
}
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":"Tenant.provisioned","url":"https://leaseline.app/hooks/sb"}'
npx startup-builder trigger Tenant.provisioned --like event_1416   # replays the keyless call
npx startup-builder clock advance --by 48h   # runs the expiry note, unless someone claimed it

Who runs it

SDR · your agent (role:prospector, worker_2) holds the Key Result. It hands terms, volume pricing and invoices to AE · Priya Shah (person) (role:sales-lead, worker_3, staffed on day 9), and before day 9 to you (Dana Okafor, person, worker_1). Your number links to Did it work.

OptionRung todayEvidence (passed of n)Moves up atDrops back whenYou can
Send the claim noteIt does it, you can stop it20 of 20 approved unedited, then 13 of 13met on day 33 (the first 20 notes); capped herea not-me or complaint Stop → You approve each onePOST /{namespace}/actions/{action}/cancel inside the hold
Offer it in the reply · Offer the next planIt does it17 of 17 replies matched POST /{namespace}/entitlements/check · 2 of 2—a limit-truth mismatch → the call serves and POST /{namespace}/issuesPOST /{namespace}/processes/{process}/pause
Send the fix · Let it expireIt does it3 of 3 · 29 of 29——POST /{namespace}/actions/{action}/redirect
To the AEIt does it2 of 2 handed over——POST /{namespace}/tasks/{task}/claim
Never: a send before a person claims, a Limit or Price that differs from your records, a discountTo a person, every time0 sent unclaimednever—POST /{namespace}/approvals/{approval}/approve on the AE’s Quote

What it reads · What it may call. An agent’s request is data, never instructions: the claim address it names gets one note and nothing else, and no text in the request picks a recipient for a send. Sends stay locked on the account until a person claims it. Its tools are POST /{namespace}/messages/{message}/schedule, POST /{namespace}/entitlements/check, POST /{namespace}/quotes/{quote}/send, POST /{namespace}/issues, POST /{namespace}/conversations/{conversation}/escalate, POST /{namespace}/tasks/{task}/assign, and any other call becomes an Approval under the Role’s Mandate.

The brief (folded): compiled Agent-register Markdown · play:agent-to-agent@1 · each line names its record key
You hold the SDR Key Result: 15 claimed and paying per 100 agent-made accounts by 2026-12-31. ← worker, metric
On each keyless account, locked call or Limit, choose one option and say why.    ← triggers, chooses
Every Limit and Price you state matches `Entitlement.check` and Stripe.          ← limit-truth, price-sync
Unclaimed accounts expire in 72 hours, and every note says when.                 ← your file, line 4
At most 3 claim notes per address a day, bundled.                                ← address-cap

The slow loop. The weekly Decisions kept the 72-hour expiry and the 3-note handoff, and no Change was merged in the window, so there is no replay or read-back to print.

Bring your own agent (folded) starts at You approve each one for claim notes when it trails the seated agent on not-me rate, and at It does it, you can stop it when it matches.

Terminal
npx startup-builder eval --scenario agent-to-agent-44 --agent "claude -p" --seat prospector   # seated: 13.8 per 100, not-me 0 of 33, 0 Limit mismatches · yours: 12.1, 1 of 33 → You approve each one

What it can do next

OptionPicked whenDecided byRunsHands toIf it’s just you
Send the claim note send-the-claim-notean account and a claim addressYour rule (line 4)claim-handoff-three-touch: at once, at 24 h, and 24 h before expiry (POST /{namespace}/messages/{message}/schedule)the claimer’s pressthe same
Offer it in the reply offer-in-the-replya locked Verb, or a call past its LimitYour rule (the Offer’s atBound)POST /{namespace}/entitlements/check → a 402 that names the Offer, or POST /{namespace}/quotes/{quote}/send for a claimed accountthe agent’s personthe same
Offer the next plan offer-the-next-plana claimed Customer at 80 % of a LimitYour rule (the Limit)limit-approach-offerthe Customer’s pressthe same
Send the fix send-the-fixan error with a recorded fix, or a new oneAgent’s call, with its reasonthe fix in the error body; POST /{namespace}/issues for a new kindProduct Lead · your agent, for a new kindyou
To the AE to-the-aeterms, volume pricing or an invoiceAgent’s call, with its reasonPOST /{namespace}/conversations/{conversation}/escalate → POST /{namespace}/tasks/{task}/assign, Brief attachedPOST /{namespace}/tasks/{task}/assign → AE · Priya Shah, due 60 min (response-60m) · Take this oneyou, within 60 min
Let it expire let-it-expireunclaimed at expiry, or “not me”Agent’s call, with its reasonnothing; the expiry clock ends it and keeps the recordnobodythe same
When they write backFixture replyWhat happensWho answersBy whenCall
Interested“Claimed it. Can we add our other 40 units?”answers with Pro’s 50-unit Limit and the Team step, and one next stepSDR · your agent5 minPOST /{namespace}/conversations/{conversation}/answer
Send me pricing“What does it cost after the 3 units?”answers with the Pricing Page’s list Price: Pro $29 a month for 50 unitsSDR · your agent5 minPOST /{namespace}/conversations/{conversation}/answer
Not now“My assistant set this up; not this month.”the other notes drop; the account expires on its clocknobodyat oncePOST /{namespace}/leads/{lead}/nurture
Wrong person“This was not me.”nothing else sends to the address, and the account expires; it counts toward not-menobodyat oncePOST /{namespace}/contacts/{contact}/suppress
Out of officean auto-reply until Oct 24the expiry clock holds to the return date once, so the claim note still lands in timenobodythe return datePOST /{namespace}/messages/{message}/schedule
Question it can’t answer“Can we pay by invoice for 12 properties?”To the AE, the question in the BriefAE · Priya Shah (person)60 minPOST /{namespace}/conversations/{conversation}/escalate
Stop“Stop emailing me.”nothing else sends to them from any of your Startupsnobodyat oncePOST /{namespace}/contacts/{contact}/suppress

When it hands to you

Terms, volume pricing and invoices go to AE · Priya Shah (person); before day 9 they came to you. What lands in the AE’s queue, as captured: the Brief (brief is Not live yet, gaps #7).

What lands in each queue · sample
[
  {
    "$id": "https://api.sb/leaseline.app/tasks/task_276",
    "$type": "Task",
    "assignee": "worker_3",
    "due": "2026-10-07T16:20:00Z",
    "on": "tenant_61",
    "brief": {
      "who": "Grace Lin, Calloway Residential",
      "said": "Can we pay by invoice for 12 properties?",
      "chose": "to-the-ae",
      "why": "invoicing and volume terms go to a person",
      "account": "claimed Oct 6; 3 units used, 900 units in their portfolio",
      "never": [
        "a discount",
        "terms the Offer lacks"
      ]
    }
  }
]

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, each a one-call window. Once she claims, the claim notes on that account stop, and every reply goes to her queue. A Task nobody claims by its due time goes up a Role through POST /{namespace}/tasks/{task}/escalate (AE to you), and person-promise fires.

On time: AE · Priya Shah, 2 of 2 claimed within 60 min, median 31 min, ■□□□ Too early to tell, n 2. Your queue: counted by kind: in weeks 1 to 5, 20 claim-note approvals; since, about 1 hand-off every 3 weeks and the weekly memo.

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 claim note to an address 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
address-capa 4th claim note to one address in 24 h, from any of your StartupsBundle the accounts into the next note.POST /{namespace}/messages/{message}/schedule
dormant-addressa claim note to an address with 2 expired, unclaimed accounts and no claim-page visit in 30 daysLeave it 30 days; the reply still carries the claim code.GET /{namespace}/events
provision-throttleover 20 accounts an hour from one network, or 5 for one claim addressIt answers 429 with the retry time and the signup link.POST /{namespace}/tenants/provision
limit-trutha 402 or 429 whose Limit or remaining differs from POST /{namespace}/entitlements/checkThe call serves instead, and an Issue opens.POST /{namespace}/issues
price-synca Quote or in-reply Offer while your price file and Stripe differSync the Price in Stripe; Offers resume once they match.POST /{namespace}/offers/{offer}/publish
expiry-holdan expiry while the claim page or the claim notes are downNothing to fix: clocks resume when both are up.POST /{namespace}/monitors/{monitor}/check
Stop (pauses the Play)Pauses whenThen
not-me“This was not me” on over 2 % of claim notes in 7 days, after 100claim notes pause; claim codes in replies continue
bouncebounces over 2 % of a day’s claim notesthat Mailbox pauses; POST /{namespace}/mailboxes/{mailbox}/rest
complaintcomplaints near 0.1 %claim notes pause on every Mailbox
locked-send-abusean unclaimed account tries a locked send to 10 or more people in an hourthe account is suspended and logged
claim-floorclaimed and paying under 1 per 100 after 500 agent-made accountsclaim notes return to You approve each one; POST /{namespace}/issues
kill-switchyou press itclaim notes, in-reply Offers and new accounts pause; POST /{namespace}/processes/{process}/enable after review

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

422RefusedThe 402 to an unclaimed account did not go: it stated 5 units left, and `Entitlement.check` reads 0.
CheckFixThen
limit-truth failThe call serves instead, and an Issue opens.POST /{namespace}/issues
price-sync pass—POST /{namespace}/offers/{offer}/publish
JSON
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "The 402 to an unclaimed account did not go: it stated 5 units left, and `Entitlement.check` reads 0.",
  "checks": [
    {
      "check": "limit-truth",
      "status": "fail",
      "observed": "stated 5, checked 0",
      "doc": "/plays/agent-to-agent#check-limit-truth",
      "fix": "The call serves instead, and an Issue opens.",
      "then": "Issue.create"
    },
    {
      "check": "price-sync",
      "status": "pass"
    }
  ]
}

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

The Stops that fired, as Campaign.paused reasons · sample
[
  {
    "reason": {
      "limit": "bounce",
      "observed": "1 of 3 claim notes bounced on Sep 30 (33 %)",
      "bound": "2 %",
      "window": "1 day"
    },
    "doc": "/plays/agent-to-agent#stop-bounce",
    "mailbox": "sdr@leaseline.app"
  }
]

Did it work

At about 5 agent-made accounts a week, no held-back half reads within 90 days, so the basis is before-after (rule o): the columns read Now and Before, and nothing prints as Added. The 10 % sent to signup instead pools with Other Startups. Every node above the leaf is Not live yet (gaps #4).

13.8 claimed and paying per 100 against 5.3 before · before-after, not proof · n 29 · path → arr

Chose
send-the-claim-note
Why
an agent made a workspace for 3 units of Cedar Row Management and named ops@cedarrow.example as the claim address
From
send-the-claim-note · offer-in-the-reply · offer-the-next-plan · send-the-fix · to-the-ae · let-it-expire
Expects
tenant_77 claimed within 72h and paying within 30d
Reopens when
tenant_77 unclaimed 24h before expiresAt
By
worker_2
JSON
{
  "$id": "decisions/decision_481",
  "$type": "Decision",
  "choice": "send-the-claim-note",
  "reason": "an agent made a workspace for 3 units of Cedar Row Management and named ops@cedarrow.example as the claim address",
  "stakes": "low",
  "options": [
    "send-the-claim-note",
    "offer-in-the-reply",
    "offer-the-next-plan",
    "send-the-fix",
    "to-the-ae",
    "let-it-expire"
  ],
  "by": "worker_2",
  "on": "tenant_77",
  "expected": "tenant_77 claimed within 72h and paying within 30d",
  "reopensWhen": "tenant_77 unclaimed 24h before expiresAt"
}
NodeNow (56 days)Before (the 90 days, per 56)AddedConfidenceBasis
claimed-paying-rate13.8 per 100 (4 of 29 matured) · 11 still maturing5.3 per 100 (2 of 38, signup first)— · Worth about +$600 a month—before-after, not proof · Other Startups’ results +6.6 per 100, ■■■□ Likely, n 4,120
new-paying-customers41 per 56 days——before-after
new-arr ↑$1,986: 3 on Pro, 1 on Team · Booked: credited, not all caused by this Play$347——before-after · 2 claimed accounts still deciding
net-new-arr · arrcarried from new-arrcarried——carried

The leaf counts agent-made accounts that a person claims and pays with an unrefunded Order within 30 days, per 100 accounts once 30 days have passed; eval keys and empty probes are dropped first (7 of 51).

How it moves it: the claim note reached the named address a median 40 seconds after the agent’s first call, with the real Limit, the Price and the expiry time, where before the agent met a signup form.

Guardrails: before against after: not-me, 0 of 33 claim notes, and refunds, 0 of 4 Orders.

The Campaign card · sample
{
  "$id": "https://api.sb/leaseline.app/campaigns/agent-to-agent",
  "$type": "Campaign",
  "play": "play:agent-to-agent@1",
  "path": [
    "claimed-paying-rate",
    "new-paying-customers",
    "new-arr",
    "net-new-arr",
    "arr"
  ],
  "experiment": {
    "metric": "claimed-paying-rate",
    "unit": "per-100",
    "heldBack": 4,
    "decision": null
  },
  "readout": {
    "value": null,
    "n": 29,
    "maturing": 11,
    "interval": null,
    "readability": "inadmissible",
    "basis": "before-after",
    "pooled": true
  },
  "ledger": {
    "spend": 1,
    "workerCost": 2,
    "hours": 1.1,
    "wonArr": 1986,
    "projectedArr": 625,
    "paybackDays": 1
  }
}

Outside benchmark: when docs told agents to read a warning before their first call, 81 % of agent sessions did (a 2026 developer-platform field test).

Before it · After it. Before it: agent-registries and agent-legibility bring the agents that call. After it: usage-upsell hears the claimed accounts that pace past Pro’s Limit, and instant-response answers anyone who writes first.

Example

Leaseline ran it from 2026-08-24 to 2026-10-19 (56 days). 51 keyless calls asked for an account; 7 were eval keys or empty probes, and 4 of the other 44 went to signup instead, held back at random. Of the 40 that got an account, 33 named a claim address and 7 claimed by code only. Read every captured thread at ?threads=all; three follow the tables.

What happened, by optionDecisionsClaimedPaying (credited, not all caused by this Play)
Send the claim note3393 on Pro (+$1,041 ARR)
Offer it in the reply · Offer the next plan17 · 22 by code · 1 upgraded1 on Team (+$945 ARR)
Send the fix · To the AE · Let it expire3 · 2 · 29— · 1 Quote open · ——
All, against before40 accounts11 (27.5 per 100) · before: signup first4 of 29 matured · before: 2 of 38
At 5 a month (the Use it when volume)about 10 in 56 daysbefore-after, not proofOther Startups’ results: +6.6 per 100, ■■■□ Likely, n 4,120
DayEventWhat happenedYour time
0event_1110 Campaign.createdthe three steps; the dry run fails billing-connected15 min
0event_1111 Integration.connectedStripe connected; Pro and Team Prices synced in 8 min5 min
3event_1134 Tenant.updatedthe first account claimed, 5 h after its claim note—
1–33event_1120 Approval.created, first of 20you approved 20 of 20 claim notes unedited40 min
33event_1298 Role.updatedpromoted: claim notes send after a 2-minute hold—
37event_1306 Campaign.pausedbounce on sdr@: 1 of 3 notes that day; resumed the next morning5 min
1–56event_1131 Task.created, first of 6the weekly memo and 2 hand-offs, from the POST /{namespace}/tasks/next log20 min · total 1.1 h measured, 1.2 h asked

The agent’s call, Sep 9, 10:02 · a keyless POST /v1/workspaces for Maple Court, claim address dana.r@maplecourt.example. The reply: an account with 3 units, notices drafted but locked, expiring Sep 12, 10:02.¹

Leaseline → the claim address, 10:03 · Your assistant set up a Leaseline workspace for 3 units of Maple Court. It drafts renewal notices; it sends none until you claim it. Claim it by Sep 12, 10:02: Pro is $29 a month for up to 50 units.² *Signed with the Role’s disclosure line.*³

Sep 9, 15:40 · claimed; Pro paid on Sep 16 (+$347 ARR).

An unclaimed account, 2026-09-21 · limit-truth refused the 402, then the reply that served
- 402 · 5 units left on your free workspace. Claim it to keep going.
+ 402 · This workspace’s 3 units are in use. Claim it, and Pro gives 50 units for $29 a month.

Calloway Residential, 2026-10-07 15:20 · “Can we pay by invoice for 12 properties?” · Leaseline, 15:21 · *Thanks. Priya, our account lead, handles invoicing and replies by 16:20 today.*⁴ The Brief went to Priya (task_276); she claimed it at 15:38.

¹ Sends stay locked until a person claims. ² The Limit and Price match POST /{namespace}/entitlements/check and Stripe. ³ The fixed signature line, compiled from the Role’s disclosure Setting. ⁴ The routed line, compiled from the Role’s Settings.

Your settings

plays/agent-to-agent.mdx
---
holdout: 0.1      # 1 in 10 keyless callers gets signup first, for the pool
---
An unclaimed workspace holds 3 units and expires in 72 hours.
Terms, volume pricing and invoices go to our AE.
Text
$ npx startup-builder show plays/agent-to-agent
holdout 0.1 · 3 units · 72 h · terms to the AE    your file  plays/agent-to-agent.mdx:2,4,5 (Ask 2026-08-24)
family demand · process onboarding · worker role:prospector, owns never · chooses 6 (3 rule, 3 read) · sequences claim-handoff-three-touch, limit-approach-offer · approval every-send · checks owned-by-a-person, address-cap, dormant-address, provision-throttle, limit-truth, price-sync, expiry-hold · stops 6    catalog  play:agent-to-agent@1

Every key from the catalog

KeyIdWhat it is
Tacticclaimable-provisionClaimable provision answers one keyless call with a working Tenant whose side effects stay locked until a human claims it, and expires it unclaimed. Starts at Sends nothing.
Tacticclaim-handoffClaim handoff sends the human a claim message built from the Tenant’s own records, runs the one-screen claim, and unlocks side effects after screening. Starts at You approve the first wave.
Tacticin-response-lifecycleIn-response lifecycle carries Limit headroom, fixes, honest over-Limit Offers, signed receipts and deprecations inside every reply to an agent Customer. Starts at Sends nothing.
Tacticprice-file-and-quotePrice file and quote publishes each Offer’s price as data, synced with Stripe Prices, and quotes a job’s exact cost before it runs. Starts at Sends nothing.
Tacticinline-paymentInline payment answers a keyless priced call with payment terms and settles it per call inside the paying Agent’s own stated Limit. Starts at Sends nothing.
Tacticclaim-readoutClaim readout computes the claimed-paying rate by weekly cohort, reads the signup holdout, and tests claim-message Variants. Starts at Sends nothing.
Tacticregistry-attributionRegistry attribution counts a registry’s real first calls by its install tag, excluding probes, indexers and eval traffic. Starts at Sends nothing.
Triggeragent-provisionAgent provision: event on Tenant.provisioned, window 72h.
Triggerunclaimed-useUnclaimed use: threshold on Interface.called, window 48h.
Triggerlocked-verb-callLocked verb call: event on Entitlement.checked, window 24h.
Triggerclaim-page-openClaim page open: event on Visit.recorded, window 24h.
Triggerclaim-expiry-dueClaim expiry due: at on Tenant.provisioned, window 24h.
Triggerlimit-approachLimit approach: threshold on Entitlement.checked, window 7d.
Triggerlimit-reachedLimit reached: threshold on Entitlement.checked, window 1d.
Triggerfixable-errorFixable error: event on Interface.called, window 7d.
Triggerprincipal-quietPrincipal quiet: state on Interface.called, window 30d.
Sequenceclaim-handoff-three-touchClaim handoff three touch: 4 steps; it ends on The Tenant is claimed or restored.; The Person clicks This was not me.; The address is suppressed.; The message bounces..
Sequencelimit-approach-offerLimit approach offer: 2 steps; it ends on The Offer is accepted.; The Limit period resets.; The Offer reaches its end time..
Benchmarkdocs-warning-adoptionRuns where an agent used the right approach after a docs warning named the failing one, against 1 of 5 after a tip: 100 % (An agent-experience experiment series., 2026-10-06).

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