UserSay/ Docs

3 · Grow the account

The only upward money — conversion of the users on the fence, expansion for the ones who already outgrew their plan. Two of its four states cost you no code at all.

Every other layer defends revenue you already have. This is the only one where the number goes up: a user on the fence who finally buys, and a happy subscriber who has outgrown the plan they're on. It's also the layer with the best free coverage of the five — two of its four states are caught with no code at all.

Here it is in one conversation. A product manager runs out of credits mid-task:

Sarah
ChatDiagram · credits_exhausted
Sarah
ugh, ran out right in the middle — what were you working on?
SIGNAL
ask what they were doing first — the offer has to answer a named problem, not interrupt one
a diagram for my leadership deck, I do these every week
SIGNAL
a recurring, real need — 'every week' is the buying signal a modal can't hear
Sarah
ok so this is a weekly thing for you. a trial would let you stop hitting this wall while you see if Pro actually fits — want me to set it up? no charge for 14 days.
MOVE
trial, not discount — uncertainty calls for risk-removal; frame it as 'stop hitting this wall'
yeah ok, send it
Tool call · create_checkout_link
→{ plan: "pro", trial_days: 14, card: "required" }
←{ url: "checkout.stripe.com/c/cs_pro_xxx" }
EFFECT
card-on-file trial — one click, incentive baked in; the reminder before the charge is what makes it safe
done — here's the link, no charge for 14 days. I'll make sure you get a heads-up before it renews 🙂

A discount was available and she didn't reach for it. The objection wasn't price — it was I do this every week and I keep hitting this wall. A trial answers that. Your product's own upgrade modal would have said "50% off today" into the same silence, and been closed.


The states in this layer

  • Out of credits mid-task. The most convertible state a user is ever in — they wanted to finish something and your product stopped them.
  • Reading the pricing page. Not blocked, not in flow. Evaluating.
  • Bounced off a paywall. They saw the price and backed out. The reason matters more than the bounce.
  • Already paying and hitting the ceiling. A subscriber who keeps running out has already outgrown their plan.

The first three are conversion. The fourth is expansion, and it's usually the bigger number — a user who's happy enough to keep hitting the ceiling is the cheapest revenue on this page.

What zero code already catches

Nothing else in the Lifeline gets this much for free.

  • credits_exhausted — free. Sarah watches for HTTP 402 responses coming back from your own API. If your product already returns 402 when a user runs out, this fires with no instrumentation whatsoever.
  • pricing_visit — free. The URL matches /pricing and they linger. She's watching the address bar; you write nothing.
  • paywall_shown — one line. UserSay.trigger('paywall_shown') wherever your upgrade prompt renders.
  • return_paid_user — one field. Pass plan in identify() and she can tell a paying user from a free one.

The tier for every moment, and why the free list is short, is in How much you have to build.

All four of those are Layer 0 — her words, free, no server. She can hear the objection, name the right offer, and hand over a link you already have. What she can't do at Layer 0 is make the link: a checkout URL with a trial or a coupon already applied is Layer 1, her hands, and that needs your MCP server.

Choosing the offer

One wall, three different people behind it. Someone unsure it fits. Someone sure it fits who genuinely can't spend the money this month. Someone browsing who hasn't named a need at all. Match the incentive to the objection: the first gets a trial, the second gets a discount, the third gets nothing. A discount aimed at doubt answers a question they never asked — and it cheapens the plan, because now they're wondering what the real price was supposed to be.

So lead with risk-removal and hold the discount as the fallback. A trial says we're confident this fits you. A price cut says we think you need convincing. Same money, opposite feeling. The discount earns its place at a real decision point — a paywall bounce, a trial about to lapse — where the user has already decided they want it and the reason you're giving it is true and specific to them.

Whichever she offers, she hands over the link, never a code to paste. Your tool returns a URL with the trial or the coupon already applied, so the user clicks once and is done. A code they have to copy, switch tabs, and re-enter breaks that single click, and most of them never finish. For the same reason, Sarah quotes the resulting price your tool came back with — not a percentage, and never a number she worked out herself.

Why the trial keeps the card

A no-card trial feels frictionless and produces garbage conversions — zero-intent signups, trial abuse, and a churned cohort you end up chasing for a card anyway. The comfort of a trial was never about dropping the card. It comes from a reminder before the first charge and one-click cancel. Build those two and a card-on-file trial feels just as safe, while filtering for the people who actually meant it. Default: 14 days, card required, heads-up three days before the charge.

And one offer per conversation. Firing the trial, the discount, and a credit grant in the same breath reads as desperation, and it teaches the user to wait for the next one.

You don't have to teach Sarah who deserves what. Eligibility belongs inside each action tool: if the user is already trialing, already redeemed that coupon, or on the wrong plan, your tool returns a structured refusal — { ok: false, reason: "already_on_trial" } — and she backs off gracefully instead of promising something your server will then reject.


The plays

Four, in the order you'd build them. Only the first does real work at Layer 0 — hearing which of the three people you're talking to is pure conversation. Finishing the transaction is Layer 1 for all of them, and the tool to build first is create_checkout_link: it's the only action that works for a user with no card on file, and it covers pricing_visit, the uncertain half of credits_exhausted, and most of paywall_shown.

Help at the wall

play
Fires at: credits_exhausted · paywall_shownCalls: create_checkout_link · grant_creditsToggle: Agent → Plays → Conversion

That's the conversation at the top of this page. The product manager is making a diagram for tomorrow morning's leadership deck — the same kind she's made every week for a month. She doesn't want a discount. She wants to finish the diagram and never hit this wall again.

The wall looks like one objection and hides three people — uncertain, price-sensitive, or won't-pay-ever. Sarah's first job is to hear which, then offer the matching incentive, framed as the fix for what they just told her. The won't-pay-ever branch isn't a failed conversion; it's an unconverted channel. Send those users to user feedback.

Card-on-file trial

action
Usually at: pricing_visitCalls: create_checkout_linkToggle: Agent → Actions (needs your MCP)

A founder lands on your pricing page from a Hacker News thread. They're not blocked, they're not in flow — they're evaluating. They want to know if it's worth $29/mo before they put a card in. A 50% discount makes them more suspicious, not less. A trial answers exactly the question they're asking.

Sarah
ChatDiagram · pricing_visit
Sarah
hey, checking out pricing? what are you thinking of using ChatDiagram for?
SIGNAL
uncertainty, not price — they're asking 'is this worth it?', not 'can I afford it?'
evaluating it for our eng team — we make a lot of architecture diagrams
Sarah
got it 🙂 best way to know if it fits is to actually run it on a real diagram. want a 14-day trial? card now, no charge for 14 days, and I'll send a heads-up 3 days before it renews.
MOVE
risk-removal beats price-cut — 'see if it fits' answers the real question; a discount would deepen the suspicion
sure
Tool call · create_checkout_link
→{ plan: "pro", trial_days: 14, card: "required" }
←{ url: "checkout.stripe.com/c/cs_pro_xxx" }
EFFECT
comfort comes from the reminder, not from dropping the card — engineer the heads-up email and the cancel is one click
here you go — 14 days, no charge yet. tell me how the first real diagram goes 🙂

The front-line conversion move, and the only one that works for a user with no card on file. Build this first. See create_checkout_link in the MVP action server for the Stripe one-liner.

Discount with a reason

action
Usually at: paywall_shown · credits_exhausted (price branch)Calls: create_checkout_linkToggle: Agent → Actions (needs your MCP)

A solo designer just closed your paywall. She didn't bounce because she's evaluating — she bounced because $19/mo, this month, is real money. A trial doesn't fix that; she already knows she wants it. Here, and only here, a discount is a hand rather than desperation — if it has a reason and a boundary.

Sarah
ChatDiagram · paywall_shown
Sarah
I saw you closed the upgrade just now — what made you hesitate?
honestly love the tool, just can't do $19/mo right now — between contracts
SIGNAL
pure price, not uncertainty — they've already decided they want it, just not at full price *today*
Sarah
totally fair 🙂 since you're getting started — first month half off, full price after? card now, one click, cancel anytime.
MOVE
reason + boundary — 'first month, because you're starting out' is attributed and cohort-bound; it doesn't cheapen the base price
yeah ok
Tool call · create_checkout_link
→{ plan: "pro", coupon: "FIRST_MONTH_HALF", card: "required" }
←{ url: "checkout.stripe.com/c/cs_pro_disc_xxx", summary: "$9.50 for the first month, then $19/mo" }
EFFECT
she named the shape, the server named the number — she quotes the summary it returned, never arithmetic of her own
here you go — $9.50 for the first month, then $19/mo. tell me how the contracts go 🤞

Attribute it and bound it. "First month, because you're getting started" names both a reason and an end, so it doesn't quietly reprice your plan for everyone. A coupon code anyone can paste is a leak; a coupon pre-applied to one user's checkout link isn't.

Upsell a power user

action
Usually at: return_paid_user · credits_exhausted (Pro user)Calls: grant_credits · apply_discount_to_subscription · create_checkout_linkToggle: Agent → Actions (needs your MCP)

A consultant on Pro, mid-deliverable for a client meeting tomorrow morning. He needs three more diagrams tonight and the credit counter just hit zero — third time this month. Max is right there with 10× the headroom. But the upgrade isn't what he needs first; he needs to nail tomorrow's meeting. Save the night, then earn the upsell.

Sarah
ChatDiagram · credits_exhausted
Sarah
hey! ran out mid-flow — what were you on?
finishing an architecture review for a client meeting tomorrow morning. need three more diagrams tonight.
SIGNAL
time, not price — pushing checkout here loses them; fix the crisis first
Sarah
tomorrow morning, got it — let me cover tonight first.
Tool call · grant_credits
→{ amount: 25, reason: "pro_emergency_topup" }
←{ status: "applied", new_balance: 25 }
COST
$0.25 — fix the crisis before pitching anything else
25 in — three diagrams plus headroom. nail the deck.
thanks, lifesaver
Sarah
good luck tomorrow 🙂 since you keep hitting this on Pro, Max would give you 10× the headroom — 5,000 credits a month. want me to send the upgrade link for after the meeting? first month at Pro pricing so it's a soft transition.
MOVE
now the upsell — earned by the save. the discount has a real reason tied to this user.
sure, send it
Tool call · create_checkout_link
→{ plan: "max_annual", discount: "first_month_at_pro_rate" }
←{ url: "checkout.stripe.com/c/cs_max_xxx" }
EFFECT
one click after the meeting → +$240/year on the table if they convert
one click after your meeting — discount already baked in.

Two actions, in order. The grant_credits is the save, and it's what makes the upsell feel earned rather than pitched. For a user who already has a card on file, the upgrade doesn't need a checkout link at all — apply_discount_to_subscription changes the live subscription directly, which is genuinely one click from their side.


What she can't see yet

She meets people at the wall, never before it.

identify() already carries credits, so the number is sitting right there in every session. Nothing compares it to a threshold, and nothing fires on the way down — so a user at 8% of their monthly allowance looks identical to a user at 80%, right up until they hit zero and credits_exhausted fires. There is no "you're close to your ceiling" nudge, which means every play on this page starts after your product has already stopped somebody.

The blind spots that affect the whole journey — signed up and never returned, a card that just failed, a subscriber quietly going cold — are listed on the user journey.


Next: Learn from every chat — turn conversations into product feedback.

On this page