Billing · Subscription
account · billing · subscription

Manage subscription

Change the plan, end it, and see who is on it — the three things a member cannot do inside Relay today.

Stub

Low-fidelity on purpose. This poses the manage half's three questions; it does not answer them. The purchase half is drawn properly at Checkout & billing profile, and the post-purchase Billing page at Billing. Do not cite this page as a design — cite the program map.

Seat model
Subscription
Instrumentation
Stub — inert. Nothing is charged, changed or cancelled.
Plan
Team Active
5 seats · renews 11 October 2026 · billed monthly
$75.00
USD / month · $15 per seat
    view billing.subscription.viewed {tier, seats, status} · cancel opens a confirm step, never cancels on click
    Change plan
    What each plan enforces, next to what this workspace is using now.
    Free
    $0
    USD / month
    • 90 relays a month
    • 1 connection
    • 4,096-token context
    • Kept for 30 days
    Pro
    $15
    USD / month
    • 500 relays a month
    • 2 connections
    • 8,192-token context
    • Kept for 6 months
    TeamCurrent
    $15
    USD / seat / month · 3-seat minimum
    • 1,000 relays a month per seat
    • 1 connection per seat
    • 16,384-token context
    • Kept for 6 months
    select → billing.plan_change.selected {from, to, blocked} · a blocked tier still emits, with the reason — a refusal nobody can see is a refusal nobody can fix
    Seats
    Workspace members and their seat state
    MemberRoleSeat 

    What this stub is for. Three things a Relay member cannot do in the app today, each one already half-built underneath:
    • Cancel. cancel_at_period_end is a real org_billing column, webhook-fed, and already returned by /billing/status — the app reads it only to tell a trial from a subscription. The ending state above is that column, drawn. Cancelling should set it, not delete anything; resuming clears it.
    • Change plan. Upgrade free→pro is live. Downgrade does not exist anywhere, and a downgrade that would land under current usage has no drawn behaviour — hence the guard, which states the excess rather than greying a button.
    • Seats — the decision. Relay seats are derived: sync_subscription_seats trues the quantity to the org's member count, so a seat is a consequence of membership. The reference console assigns users to seats, which implies a member can exist without one. That is a quota-and-true-up change, not a UI preference. Switch the model above to see both.
    Instrumentation is part of the design, not a follow-up. Every action names the analytics event and the audit row it writes. The platform already has integrations/ga4.py with ga4_event() — and zero callers; the app has no tracking helper at all. The contract both ends must satisfy is relay-platform/docs/SPEC.billing-surface-requirements.md (audit logging, the edge-case register, the UI event taxonomy).
    Record. Program map relay-board/docs/product/subscription-management.md · purchase half /concepts/checkout · post-purchase /concepts/app-v35/billing.html. Deep links: ?seat=assigned · ?state=ending.