URL: /plays/sso-rung · Id: `play:sso-rung`

# Single sign-on upgrade

> Answers each single sign-on ask within one business hour with the plan that includes it, and the admin tests sign-in before paying.

Answers each single sign-on ask within one business hour with the plan that includes it, and the admin tests sign-in before paying.

```text title="Set it up · Ask"
Be our CSM: when a Customer asks for single sign-on, answer within the hour with the plan that includes it, its price and today’s charge, and let their admin test sign-in before paying. At 50 seats or a security questionnaire, bring in our AE. Show me what’s missing; don’t send.
```

```text title="Run one · Ask"
Hand our latest single sign-on ask to the CSM agent and show me what it chose and why. Don’t send.
```

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

const { events: [last] } = await $.Event.list({ where: 'name in ("Message.received", "Entitlement.checked", "Quote.requested", "Thread.joined", "Tenant.thresholdCrossed", "Domain.verified", "Renewal.opened", "Deal.lost", "SSOConnection.failed")', limit: 1 })   // { play } compiled to this Play’s Trigger Events
const { subject } = await $.Trigger.fire({ like: last.$id }, { mode: 'try' })   // the same Event again, in try mode · Not live yet, gaps #7
const { decisions: [decision] } = await $.Decision.list({ where: `on = "${subject}"` })   // what it chose, and why · Not live yet, gaps #7
const { actions } = await $.Action.list({ where: `on = "${subject}"` })   // what it did, with its checks · Not live yet, gaps #7
return { decision, actions }
```

```json title="Run one · Response · sample"
{
  "decision": {
    "$id": "https://api.sb/leaseline.app/decisions/decision_347",
    "$type": "Decision",
    "choice": "set-up-first",
    "reason": "Pinecrest’s admin opened single sign-on setup on Team; 22 active members is under the 50-seat line, so they test sign-in before paying",
    "stakes": "low",
    "options": [
      "answer-the-ask",
      "set-up-first",
      "to-the-ae",
      "note-the-plan",
      "not-now"
    ],
    "by": "worker_5",
    "on": "customer_33",
    "expected": "Subscription.switched to the annual plan within 60d",
    "reopensWhen": "no Subscription.switched for customer_33 within 60d"
  },
  "actions": [
    {
      "$id": "https://api.sb/leaseline.app/actions/action_1",
      "$type": "Action",
      "status": "done",
      "mode": "try",
      "by": "worker_5",
      "verb": "Entitlement.check",
      "checks": []
    },
    {
      "$id": "https://api.sb/leaseline.app/actions/action_2",
      "$type": "Action",
      "status": "done",
      "mode": "try",
      "by": "worker_5",
      "verb": "SSOConnection.create",
      "checks": []
    },
    {
      "$id": "https://api.sb/leaseline.app/actions/action_3",
      "$type": "Action",
      "status": "pending",
      "reason": "awaitingTime",
      "mode": "try",
      "by": "worker_5",
      "verb": "Conversation.answer",
      "checks": [
        {
          "check": "owned-by-a-person",
          "status": "pass"
        },
        {
          "check": "keep-first",
          "status": "pass"
        },
        {
          "check": "customer-ask-cap",
          "status": "pass"
        },
        {
          "check": "reconcile",
          "status": "pass"
        }
      ]
    }
  ]
}
```

`action_2` opens a test connection that signs in nobody until the admin presses Test; `action_3` is the answer, held 2 minutes.

`63 per 100 asks upgraded against 17 before · before-after, not proof · n 8 · path → arr`

| Try another: the Customer | Choice | Reason |
| --- | --- | --- |
| Pinecrest Management: their admin opened single sign-on setup on Team, 22 members | `set-up-first` | an admin at setup, under the 50-seat line |
| Quarry Lane Homes: “Do you support SSO with our Google accounts?” | `answer-the-ask` | a written ask on Team, under the line |
| Northgate Rentals: the open Deal’s security questionnaire asks for SAML | `to-the-ae` | a questionnaire on an open Deal is the AE’s |
| Ridgeway Rentals: 21 active members, a sign-in Domain verified, no ask | `note-the-plan` | past 20 members with no ask; one note in 90 days |
| Harbor Row: asked about SSO, at risk since Oct 2 | `not-now` | `keep-first`: their CSM sees them first |

| Worth | Your time | Spend | First signal | Use it when | Instead |
| --- | --- | --- | --- | --- | --- |
| ≈ +$1,200 ARR a month at 4 | 20 min week 1, then 5 a week | $0.04 per answer, ~$0.16 a month at 4 | Day 9: first upgrade paid | 2+ single sign-on asks a month, and a plan that has it | No plan has it: add it (<Op verb="Offer.publish" />) · under 2 asks a month: `usage-upsell` |

**Where it sits:** Stranger → Lead → Trialing → **Activated** → At risk → Churned → Won back · Tier upgrade rate → Upgrades ↑ → ARR added each week → ARR

Worth’s formula: about +21 upgrades per 100 asks (Other Startups’ results; no lift of your own yet) × 4 asks a month × $1,448 per Team-to-annual upgrade ≈ +$1,216. Under First signal: At 4 asks a month, 90 days can tell only +45 per 100 or more, so your file starts at `holdout: 0` and reads before-after.

`sample · api.sb/leaseline.app · 41 Customers at 2026-10-19 · on Entitlement.checked · first answer 6m 40s · 8 keys Not live yet`

## Start it this week
```bash
npx startup-builder add play sso-rung   # 1 · writes plays/sso-rung.mdx (Not live yet, gaps #5)

# 2 · dry run; below, its 422 on 2026-07-20
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":"sso-rung"}'

# 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":"sso-rung"}'

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

# off: asks come to you; active connections keep working
curl -X PATCH https://api.sb/leaseline.app/campaigns/sso-rung \
  -H "Authorization: Bearer $STARTUP_BUILDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"endsAt":"now"}'
```


```json title="The dry run’s 422 · 2026-07-20 · sample"
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "sso-rung can’t start yet: 1 check fails and 2 warn.",
  "checks": [
    {
      "check": "use-when",
      "status": "pass",
      "observed": "6 single sign-on asks in 90 days; the $2,400 plan includes it",
      "doc": "/plays/sso-rung#check-use-when",
      "fix": "Nothing to fix: 2 or more asks a month, and a plan that has it.",
      "then": "Metric.query"
    },
    {
      "check": "billing-connected",
      "status": "pass",
      "observed": "Stripe, connected",
      "doc": "/plays/sso-rung#check-billing-connected",
      "fix": "Nothing to fix: every charge reads from Stripe.",
      "then": "Integration.connect"
    },
    {
      "check": "approved-pages",
      "status": "fail",
      "observed": "no Page lists what the $2,400 plan includes",
      "doc": "/plays/sso-rung#check-approved-pages",
      "fix": "Publish the pricing Page row for the $2,400 plan; answers quote only your own pages.",
      "then": "Site.publish"
    },
    {
      "check": "baseline",
      "status": "warn",
      "observed": "1 upgrade of 6 asks in 90 days",
      "doc": "/plays/sso-rung#check-baseline",
      "fix": "n 6: it reads before-after, not proof, until volume allows a held-back half.",
      "then": "Metric.query"
    },
    {
      "check": "role-unstaffed",
      "status": "warn",
      "roles": [
        "success-manager",
        "sales-engineer"
      ],
      "owned": 4,
      "doc": "/plays/sso-rung#check-role-unstaffed",
      "fix": "Staff the CSM, or leave it: 4 Customers Priya owns get drafts in her queue.",
      "then": "Role.staff"
    }
  ]
}
```

| Check | Fix (one call) | Your time | Wait |
| --- | --- | --- | --- |
| <a id="check-use-when"></a>`use-when` · <a id="check-billing-connected"></a>`billing-connected` · pass: 6 asks in 90 days; Stripe connected (gaps #8) | <Op verb="Metric.query" /> on `tier-upgrade-rate`; <Op verb="Integration.connect" /> for Stripe | 1 min | none |
| <a id="check-approved-pages"></a>`approved-pages` · fail (Not live yet): no Page lists the $2,400 plan’s single sign-on | <Op verb="Site.publish" /> the pricing Page row | 10 min | none |
| <a id="check-baseline"></a>`baseline` · warn: 1 of 6 in 90 days | <Op verb="Metric.query" />; it fills Before in Your number | 0 min | none |
| <a id="check-role-unstaffed"></a>`role-unstaffed` · warn: 4 of your Customers have a person owner, Priya | <Op verb="Role.staff" />, or leave it: CSM routes come to you, and Priya’s 4 get drafts in her queue | 0 min | none |

`Your number · Before 17 per 100 · Now 63 per 100 asks upgraded · Target 50 by Dec 31 · before-after, not proof · n 8 matured`

Each answer costs $0.04 from your Domain (Worker cost); <Op verb="Entitlement.check" /> and a test connection cost nothing. That comes to about $0.16 a month at 4 asks and $0.08 at 2.

## When it starts
| When | Trigger id | Starts on | Window | How it gets in |
| --- | --- | --- | --- | --- |
| A Customer or a Deal asks for single sign-on in writing | `sso-written-ask` | `event` on `Message.received` where intent = 'single-sign-on' | answer within 1 business hour; 3d | forward support@ to the address `inbound-source` printed, or <Op verb="Integration.connect" /> your inbox |
| An admin opens single sign-on setup on a plan without it | `sso-setup-refused` | `event` on `Entitlement.checked` where entitlement = 'single-sign-on' and allowed = false | 3d | your app calls <Op verb="Entitlement.check" /> |
| A team on Team reaches 20 active members | `seat-threshold-crossed` | `threshold` on `Tenant.thresholdCrossed` where activeMembers >= 20 within 30d | a threshold; one note per 90 days | <Op verb="Metric.watch" /> on active members |
| + 7 more for questionnaires, Quote requests, IT contacts, sign-in Domains, renewals, lost Deals and failing connections (`?lanes=all`) | | | | |

```json title="One signed delivery · StartupBuilder-Signature"
{
  "$id": "https://api.sb/leaseline.app/events/event_1288",
  "$type": "Event",
  "name": "Entitlement.checked",
  "at": "2026-10-19T10:22:31Z",
  "seq": 1288,
  "data": {
    "customer": "customer_33",
    "entitlement": "single-sign-on",
    "allowed": false,
    "offer": "team",
    "activeMembers": 22
  }
}
```

```bash title="Reproduce it in test"
npx startup-builder trigger Entitlement.checked --like event_1288   # replays the delivery
npx startup-builder clock advance --by 2d   # then runs the follow-up’s tested check
npx startup-builder listen Entitlement.checked --forward-to http://localhost:3000/hook
```

## Who runs it
**CSM · your agent** (`role:success-manager`, `worker_5`) holds the Key Result. It hands questionnaires and Quotes to **AE · Priya Shah (person)** (`role:sales-lead`, `worker_3`), and held Customers to **you** (Dana Okafor, person, `worker_1`), because the CSM and Sales Engineer Roles have no person. Your number links to Did it work.

| Option | Rung today | Evidence (passed of n) | Moves up at | Drops back when | You can |
| --- | --- | --- | --- | --- | --- |
| Answer with the plan · Set it up before they pay | It does it, you can stop it | 6 of 6 · 3 of 3 | met on day 16 (the first 5 answers) | any answer fails a Check, or the `misquote` Stop fires → You approve each one | <Op verb="Action.cancel" /> inside the hold |
| Note the plan at 20 members | You approve each one | 1 of 1 | at 10 passed | — | <Op verb="Approval.approve" /> · <Op verb="Approval.refuse" /> |
| To the AE · Not now | It does it | 1 of 1 · 1 of 1 | — | a held Customer is asked anyway → You approve each one | <Op verb="Action.redirect" /> |
| Change the AE line | You approve each one | 0 merged | never | — | <Op verb="Change.merge" /> · <Op verb="Change.revert" /> |
| Never: single sign-on turned on for them, a discount, a plan the Pricing Page lacks | To a person, every time | 1 asked, 1 handed over | never | — | <Op verb="Approval.approve" /> on the AE’s Quote |

**What it reads · What it may call.** It reads the ask, the Entitlement, the live Price and the active-member count, and quotes only the pricing Page. It opens a test connection; turning single sign-on on for members and <Op verb="Subscription.switch" /> are the admin’s own presses, never the agent’s. Its tools are <Op verb="Entitlement.check" />, <Op verb="Conversation.answer" />, <Op verb="SSOConnection.create" />, <Op verb="Conversation.escalate" />, <Op verb="Quote.send" />, <Op verb="Task.assign" />, and any other call becomes an Approval under the Role’s Mandate.

```md title="The brief (folded): compiled Agent-register Markdown · play:sso-rung@1 · each line names its record key"
You hold the CSM Key Result: 50 upgrades per 100 single sign-on asks by 2026-12-31. ← worker, metric
Answer each ask within 1 business hour with the plan that includes it, its live Price and today’s charge. ← chooses.answer-the-ask, Answer-time stop
At 50 seats, a questionnaire or a Quote on an open Deal, choose To the AE.       ← your file, line 4
Offer a test sign-in before any charge.                                          ← chooses.set-up-first, your file line 5
Never turn single sign-on on for a team; the admin does.                         ← Lockout stop
```

**The slow loop.** The weekly Decisions kept 50 seats as the AE line, and no Change was merged in the window, so there is no replay or read-back to print.

## What it can do next
| Option | Picked when | Decided by | Runs | Hands to | If it’s just you |
| --- | --- | --- | --- | --- | --- |
| Answer with the plan `answer-the-ask` | a written ask from a Customer under 50 seats | Your rule (line 4) | <Op verb="Entitlement.check" /> → <Op verb="Conversation.answer" /> → `sso-ask-follow-up` | the admin’s press | the same |
| Set it up before they pay `set-up-first` | an admin opened setup, or asks to try it first | Agent’s call, with its reason | <Op verb="SSOConnection.create" /> (test only) → on press `change-plan` → `sso-go-live` | the admin’s test, then their press | the same |
| To the AE `to-the-ae` | a questionnaire or Quote on an open Deal, 50 seats or more, or terms | Agent’s call, with its reason | <Op verb="Conversation.escalate" /> → <Op verb="Quote.send" /> | <Op verb="Task.assign" /> → AE · Priya Shah, due 60 min · Take this one | you |
| Note the plan at 20 members `note-the-plan` | 20 active members or a verified sign-in Domain, with no ask | Agent’s call, with its reason | `sso-proactive-note`, one note in 90 days | nobody | the same |
| Not now `not-now` | another Play holds them, or `keep-first` applies | Your rule (rule j) | nothing; names the holder | their CSM, so you · Take this one | you |

| When they write back | Fixture reply | What happens | Who answers | By when | Call |
| --- | --- | --- | --- | --- | --- |
| Interested | “Test passed. Move us to the annual plan.” | the one-press link; nothing is charged until they press | the Customer | — | <Op verb="Subscription.switch" /> (theirs) |
| Send me pricing | “What would the SSO plan cost us?” | the live $2,400 Price, today’s $2,366.87 charge after the Team credit | CSM · your agent | 5 min | <Op verb="Conversation.answer" /> |
| Not now | “Not until next budget year.” | the ask closes, and `customer-ask-cap` frees the Customer | nobody | at once | <Op verb="Decision.list" /> |
| Wrong person | “Our IT lead, Sam, sets this up.” | asks them to bring Sam in; it never writes to an address from their text | CSM · your agent | 1 business day | <Op verb="Conversation.answer" /> |
| Out of office | an auto-reply until Oct 28 | the follow-up waits for the return date | nobody | the return date | <Op verb="Message.schedule" /> |
| Question it can’t answer | “Can we add SSO to Team for a fee?” | To a person: a plan the Pricing Page lacks is never the agent’s call | AE · Priya Shah, or you | 60 min | <Op verb="Conversation.escalate" /> |
| Stop | “Stop emailing us about plans.” | notes stop; the test connection keeps working | nobody | at once | <Op verb="Contact.suppress" /> |

## When it hands to you
Questionnaires and Quotes go to **AE · Priya Shah (person)**; held Customers come to **you**. One Brief per Role, as captured (`brief` is Not live yet, gaps #7).

```json title="What lands in each queue · sample"
[
  {
    "$id": "https://api.sb/leaseline.app/tasks/task_506",
    "$type": "Task",
    "assignee": "worker_3",
    "due": "2026-10-16T16:05:00Z",
    "on": "contact_14",
    "brief": {
      "who": "Northgate Rentals, open Deal on the $2,400 plan",
      "said": "Item 14 of the security questionnaire: SAML single sign-on required.",
      "chose": "to-the-ae",
      "why": "a questionnaire on an open Deal is the AE’s",
      "answer": "the $2,400 plan includes SAML; the trust page link is attached",
      "never": [
        "a discount",
        "single sign-on on Team"
      ]
    }
  },
  {
    "$id": "https://api.sb/leaseline.app/tasks/task_489",
    "$type": "Task",
    "assignee": "worker_1",
    "due": "2026-10-09T17:00:00Z",
    "on": "customer_19",
    "brief": {
      "who": "Harbor Row",
      "said": "Do you have SSO?",
      "chose": "not-now",
      "why": "at risk since Oct 2: 3 support threads on late notices",
      "held": "keep-first"
    }
  }
]
```

The owner answers with <Op verb="Task.claim" /> (Take this one), <Op verb="Task.return" /> with a reason, or <Op verb="Action.redirect" />, each a one-call window. Once a person claims, the agent’s pending answers to that Customer cancel with `claimedByPerson` (Not live yet, gaps #7), and every reply goes to the owner. A Task nobody claims by its due time goes up a Role through <Op verb="Task.escalate" />, and `person-promise` fires.

**On time:** 2 of 2 claimed within their due time, median 25 min, n 2, ■□□□ Too early to tell beside Other Startups’ 95 % on time. **Your queue:** in week 1, 4 approvals; after the promotion, about 1 hand-off every 2 weeks.

**The fixed line:** <Op verb="Deal.assign" /> sets `owner` to the receiving person, the agent is never a Deal’s owner, and Meetings land on the owner’s calendar under the owner’s name.

## When it won’t send
| Check (refuses one send) | Refuses | Fix | Then |
| --- | --- | --- | --- |
| <a id="check-owned-by-a-person"></a>`owned-by-a-person` (rule j, gaps #8) | an answer to a Customer whose open Deal a person owns, or whose Conversation a person claimed | Leave it to the person who owns it: the draft waits in their queue. | <Op verb="Task.assign" /> |
| <a id="check-keep-first"></a>`keep-first` (rule j, gaps #8) | an upgrade answer to a Customer who is at risk, past due or has an unsubscribe pending | Let their CSM reach them first, then ask again once they are healthy. | <Op verb="Task.next" /> |
| <a id="check-customer-ask-cap"></a>`customer-ask-cap` (gaps #3) | a second open ask to one Customer in a window, from any Play | `signal-expansion` holds them until its seat note closes. | <Op verb="Decision.list" /> |
| <a id="check-reconcile"></a>`reconcile` (the record’s Reconcile stop; Not live yet, gaps #8) | a Customer whose Subscription and Stripe disagree | Clear the Stripe mismatch first. | <Op verb="Task.next" /> |
| <a id="check-price"></a>`price` (hub guard on <Op verb="Quote.send" />, gaps #1) | a plan, Price or discount the pricing Page does not show | Quote the plan that includes it, or get the discount approved. | <Op verb="Approval.approve" /> |

| Stop (pauses the Play) | Pauses when | Then |
| --- | --- | --- |
| <a id="stop-misquote"></a>`misquote` | a Price, charge or Quote total differs from the live Price | You approve each one |
| <a id="stop-lockout"></a>`lockout` | members cannot sign in after a switch, a switch down or a failed renewal | setups pause; <Op verb="Task.assign" /> to you within 5 min |
| <a id="stop-answer-time"></a>`answer-time` | an ask waits past 1 business hour; 3 late in a week drop the answer lane one rung | the proactive lane pauses |
| <a id="stop-test-failure"></a>`test-failure` | under half of setups pass a test sign-in within 7 days, after 20 | set-up-first pauses; failing identity systems are reported to you |
| <a id="stop-spend-limit"></a>`spend-limit` | month-to-date model spend reaches its Limit | asks get the plan answer by rule; <Op verb="Limit.raise" /> |
| <a id="stop-kill-switch"></a>`kill-switch` | you press it | every answer stops; active connections keep working |
| + 6 more for the proactive lane (`?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 Quarry Lane thread in Example.

```json title="The captured refusal · sample"
{
  "type": "https://startupbuilder.dev/errors/refused",
  "title": "Refused",
  "status": 422,
  "detail": "The answer to Quarry Lane Homes did not send: it offers single sign-on on Team, which no live Offer shows.",
  "checks": [
    {
      "check": "price",
      "status": "fail",
      "doc": "/plays/sso-rung#check-price",
      "fix": "Quote the plan that includes it, or get the discount approved.",
      "then": "Approval.approve"
    },
    {
      "check": "keep-first",
      "status": "pass"
    }
  ]
}
```


```json title="The Stops that fired, as Campaign.paused reasons · sample"
[
  {
    "reason": {
      "limit": "answer-time",
      "observed": "1h 25m",
      "bound": "1 business hour",
      "window": "1 ask"
    },
    "doc": "/plays/sso-rung#stop-answer-time",
    "task": "task_377"
  }
]
```

## Did it work
At 4 asks a month, 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. Each figure’s call is <Op verb="Metric.query" /> by node, and every node above the leaf is Not live yet (gaps #4).

`63 per 100 asks upgraded against 17 before · before-after, not proof · n 8 · path → arr`

| Node | Now (91 days) | Before (the 90 days before) | Added | Confidence | Basis |
| --- | --- | --- | --- | --- | --- |
| `tier-upgrade-rate` | 63 per 100: 5 of 8 matured asks upgraded within 60 days · 3 still maturing | 17 per 100: 1 of 6 | — | — | before-after, not proof · Other Startups’ results +21 per 100, ■■■□ Likely, n 1,140 |
| `expansion-arr` ↑ | $12,947, of it $8,688 from 6 upgrades · Booked: credited, not all caused by this Play | $2,644 | — · Worth about +$1,200 a month | — | before-after; includes usage-upsell’s $2,392 and signal-expansion’s $1,867 |
| `net-new-arr` · `arr` | carried from `expansion-arr` | carried | — | — | carried |

Before against after reads the 91 days against the 90 before them; an ask counts once its 60 days have passed. It is not proof, and Other Startups’ results print beside it.

**How it moves it:** a median first answer of 6m 40s against 2 days 4 hours before, each naming the plan, its live Price and today’s charge, with a test sign-in before any charge.

**Guardrails:** before against after: `contraction-arr`, $0 from switches back to Team against $0 before, and `logo-churn`, 0 of 6 upgraded Customers gone; 6 of 6 upgraded teams had an active connection by day 14.

```json title="The Campaign card · sample"
{
  "$id": "https://api.sb/leaseline.app/campaigns/sso-rung",
  "$type": "Campaign",
  "play": "play:sso-rung@1",
  "path": [
    "tier-upgrade-rate",
    "expansion-arr",
    "net-new-arr",
    "arr"
  ],
  "experiment": {
    "metric": "tier-upgrade-rate",
    "unit": "per-100",
    "heldBack": 0,
    "decision": null
  },
  "readout": {
    "value": null,
    "n": 8,
    "maturing": 3,
    "interval": null,
    "readability": "inadmissible",
    "basis": "before-after",
    "pooled": true
  },
  "ledger": {
    "spend": 1,
    "workerCost": 1,
    "hours": 0.9,
    "pipelineArr": 2393,
    "wonArr": 8688,
    "projectedArr": 1216,
    "paybackDays": 1
  }
}
```

Outside benchmark: 3.2 % of Subscriptions on a plan without single sign-on move to one with it in their fourth month (a subscription-analytics firm’s billing data, 2026-09-30).

**Before it · After it.** Before it: `signal-expansion` hands it a team that reaches 20 active members, and `trust-pack` hands it questionnaire items on open Deals. After it: `written-references` hears the Customers it upgraded, and `annual-prepay` already holds them on the annual plan; `keep-first` holds both while a Customer is at risk.

## Example
Leaseline ran it from 2026-07-20 to 2026-10-19 (91 days): 11 asks and setups on 10 Customers and 1 Deal, 8 of them matured past their 60-day window. Read every captured thread at `?threads=all`; three follow the tables.

| What happened, by option | Decisions | Upgraded | Booked (credited, not all caused by this Play) |
| --- | --- | --- | --- |
| Answer with the plan · Set it up before they pay | 6 · 3 | 3 · 3, with 3 test sign-ins passed | $8,688 |
| To the AE · Note the plan · Not now | 1 · 1 · 1 | 1 Quote open · 0 · 0 | $2,393 in an open Quote |
| At 2 a month (the Use it when volume) | about 6 in 91 days | before-after, not proof | Other Startups’ results: +21 per 100, ■■■□ Likely, n 1,140 |

| Day | Event | What happened | Your time |
| --- | --- | --- | --- |
| 0 | `event_088` Campaign.created | the three steps; dry run fails `approved-pages` | 10 min |
| 0 | `event_091` Site.published | the pricing Page row for the $2,400 plan | 10 min |
| 9 | `event_203` Subscription.switched | the first upgrade paid: Juniper Homes, Team to the annual plan, +$1,448 ARR | — |
| 16 | `event_301` Role.updated | promoted: answers send after a 2-minute hold | — |
| 44 | `event_655` Campaign.paused | `answer-time`: an ask waited 1h 25m; you answered it and resumed | 10 min |
| 58 | `event_902` Action.refused | `price` refused the Quarry Lane draft; rewritten once and sent | 5 min |
| 1–91 | `event_097` Approval.created, first of 5 | you approved 5 answers; weekly reviews, from the <Op verb="Task.next" /> log | 20 min · **total 55 min measured, 1 h asked** |

> **Leaseline → Pinecrest’s admin, Oct 19, 10:29** · Single sign-on comes with the $2,400-a-year plan, which also adds directory sync.¹ Switching today costs $2,366.87, after the $33.13 left on Team this month.² Test sign-in with your own accounts first; nothing is charged until you press Switch.³ *Signed with the Role’s disclosure line.*⁴
>
> **Pinecrest, Oct 19, 11:52** · test sign-in passed, then pressed Switch. `Subscription.switched`, +$1,448 ARR.

```diff title="Quarry Lane Homes, 2026-09-16 · the draft price refused, then the rewrite that sent"
- We can turn on SSO for you on Team at no extra cost for the first 3 months.
+ Single sign-on with your Google accounts comes with the $2,400-a-year plan; you can test sign-in before you pay.
```

> **Northgate Rentals, 2026-10-16 15:05** · questionnaire item 14: “SAML single sign-on required.” · **Leaseline, 15:06** · *Thanks. Priya, your account lead, replies by 16:05 with the answer and the trust page.*⁵ The Brief went to Priya (`task_506`); she answered at 15:41.

¹ Only what the pricing Page lists for the plan. ² The live Price and today’s charge, after the Team credit. ³ The test sign-in before any charge. ⁴ The fixed signature line, compiled from the Role’s disclosure Setting. ⁵ The routed line, compiled from the Role’s Settings.

## Your settings
```mdx title="plays/sso-rung.mdx"
---
holdout: 0        # 4 asks a month is too few to hold any back; it reads before-after
---
Answer every single sign-on ask within the hour with the $2,400 plan. At 50 seats, a questionnaire or a Quote, bring in the AE.
Let the admin test sign-in before any charge.
```

```text
$ npx startup-builder show plays/sso-rung
holdout 0 · AE line 50 seats · test before charge     your file  plays/sso-rung.mdx:2,4,5 (Ask 2026-07-20)
family expansion · worker role:success-manager, owns never · chooses 5 (2 rule, 3 read) · sequences sso-ask-follow-up, sso-proactive-note, sso-go-live · checks owned-by-a-person, keep-first, customer-ask-cap, reconcile, price · stops 12 · placements gmail, outlook    catalog  play:sso-rung@1 (Not live yet, gaps #5)
```

### Every key from the catalog
| Key | Id | What it is |
| --- | --- | --- |
| Tactic | <a id="tactic-deal-requirement-intake"></a>`deal-requirement-intake` | Deal requirement intake extracts each stated requirement, with its source, from every buyer record on an open Deal for the Play that answers it. Starts at Sends nothing. |
| Tactic | <a id="tactic-sso-ask-detect"></a>`sso-ask-detect` | Single sign-on ask detect finds each request in Messages, transcripts, refused setups and Deal requirement rows, and keeps one ask record per Tenant or Deal. Starts at Sends nothing. |
| Tactic | <a id="tactic-sso-offer-answer"></a>`sso-offer-answer` | Single sign-on offer answer replies within one business hour with the SSO Offer, its live Price, today’s charge or a Quote, and a setup link. Starts at You approve each one. |
| Tactic | <a id="tactic-sso-configure-first"></a>`sso-configure-first` | Single sign-on configure first lets an admin connect and test sign-in on a draft SSO Connection before paying, and activates it when the Entitlement arrives. Starts at Sends nothing. |
| Tactic | <a id="tactic-sso-tier-change"></a>`sso-tier-change` | Single sign-on offer change moves a Subscription to the SSO Offer with proration, only on the Customer’s own press or signature. Starts at Sends nothing. |
| Tactic | <a id="tactic-sso-proactive-rung"></a>`sso-proactive-rung` | Single sign-on proactive rung shows the SSO Offer to admins whose Tenant records show the need: one note in 90 days, with a holdout. Starts at You approve each one. |
| Tactic | <a id="tactic-sso-rung-readout"></a>`sso-rung-readout` | Single sign-on rung readout counts upgrades per ask over 60 days, reads the proactive holdout at 90 days, and recalibrates the seat threshold each quarter. Starts at You approve the first wave. |
| Tactic | <a id="tactic-send-gate-check"></a>`send-gate-check` | Send gate check: Checks Suppression, a live reason, the Market, the basis stamp, the Person-month grant, the facts and the labels before any cold Message exists. Starts at Sends nothing. |
| Tactic | <a id="tactic-reply-matrix"></a>`reply-matrix` | Reply matrix: Labels every outreach reply with one of 11 labels, maps it to one of seven reply kinds, and runs that kind’s move by its deadline. Starts at You approve each one. |
| Trigger | <a id="trigger-sso-written-ask"></a>`sso-written-ask` | Single sign-on written ask: `event` on `Message.received`, window 3d. |
| Trigger | <a id="trigger-sso-setup-refused"></a>`sso-setup-refused` | Single sign-on setup refused: `event` on `Entitlement.checked`, window 3d. |
| Trigger | <a id="trigger-sso-questionnaire-item"></a>`sso-questionnaire-item` | Single sign-on questionnaire item: `event` on `Message.received`, window 14d. |
| Trigger | <a id="trigger-sso-quote-request"></a>`sso-quote-request` | Single sign-on quote request: `event` on `Quote.requested`, window 24h. |
| Trigger | <a id="trigger-it-contact-joined"></a>`it-contact-joined` | IT contact joined: `event` on `Thread.joined`, window 14d. |
| Trigger | <a id="trigger-seat-threshold-crossed"></a>`seat-threshold-crossed` | Seat threshold crossed: `threshold` on `Tenant.thresholdCrossed`, window 30d. |
| Trigger | <a id="trigger-sign-in-domain-verified"></a>`sign-in-domain-verified` | Sign-in domain verified: `event` on `Domain.verified`, window 14d. |
| Trigger | <a id="trigger-sso-renewal-window"></a>`sso-renewal-window` | Single sign-on renewal window: `at` on `Renewal.opened`, window 30d. |
| Trigger | <a id="trigger-sso-objection-lost"></a>`sso-objection-lost` | Single sign-on objection lost: `event` on `Deal.lost`, window 180d. |
| Trigger | <a id="trigger-sso-connection-failing"></a>`sso-connection-failing` | Single sign-on connection failing: `state` on `SSOConnection.failed`, window 24h. |
| Sequence | <a id="sequence-sso-ask-follow-up"></a>`sso-ask-follow-up` | Single sign-on ask follow-up: 5 steps; it ends on An upgrade to the SSO Offer; A signed Quote; A reply; A not-now; An opt-out; A lost Deal; The last note or the tested nudge sent; 14 days from the answer. |
| Sequence | <a id="sequence-sso-proactive-note"></a>`sso-proactive-note` | Single sign-on proactive note: 2 steps; it ends on An upgrade to the SSO Offer; An ask, which moves the Tenant to the ask follow-up; An opt-out; 90 days. |
| Sequence | <a id="sequence-sso-go-live"></a>`sso-go-live` | Single sign-on go-live: 3 steps; it ends on 3 people signed in through the connection; The Success Manager’s Task closed. |
| Benchmark | <a id="benchmark-sso-rung-month-one-upgrade"></a>`sso-rung-month-one-upgrade` | Share of subscriptions moving to a higher Offer in their first month: 0.75 % (A subscription-analytics firm’s billing data, published in a pricing newsletter, 2026-09-30). |
| Benchmark | <a id="benchmark-sso-rung-month-four-upgrade"></a>`sso-rung-month-four-upgrade` | Share of subscriptions moving to a higher Offer in their fourth month: 0.16 % (A subscription-analytics firm’s billing data, published in a pricing newsletter, 2026-09-30). |
| Benchmark | <a id="benchmark-sso-rung-renewal-upgrade-share"></a>`sso-rung-renewal-upgrade-share` | Share of first-year Offer upgrades that land at the month-12 renewal: 40 % (A subscription-analytics firm’s billing data, published in a pricing newsletter, 2026-09-30). |