> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chatblocks.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Customer Portal

> Hosted Stripe Customer Portal for plan switching, cancel, and card update. Card charged immediately at Checkout — no free trial.

Builder ($9/mo or $89/yr) is billed through Stripe Checkout and the Customer Portal. There is **no free trial** — your card is charged when you complete Checkout.

This page covers upgrade mechanics, how the Stripe Customer Portal works, and what happens on payment failure.

## Upgrading to Builder

<Steps>
  <Step title="Upgrade from /settings/billing">
    Workspace **owner only** can upgrade. Pick monthly or annual; both run through Stripe Checkout.
  </Step>

  <Step title="Provide a card">
    Card is required. Stripe verifies and charges immediately — there is no trial period.
  </Step>

  <Step title="Workspace flips to Builder immediately">
    On `checkout.session.completed` (the webhook lands within seconds), `workspaces.plan` becomes `builder`. All Builder caps unlock instantly.
  </Step>
</Steps>

## Refund policy

**No published refund policy.** If something went wrong, reach out via the support address shown on `/settings/billing`. Refunds are handled case-by-case in the Stripe Dashboard.

If you cancel an active subscription, you keep Builder until the end of the current billing period (no proration, no partial refund), then drop to Free.

## The Customer Portal

The portal is hosted by Stripe — zero billing UI on our side. From `/settings/billing` → "Manage subscription," workspace owners get redirected to a Stripe-hosted page that handles:

* **Plan switching** — monthly ↔ annual. Stripe computes the proration.
* **Cancellation** — schedules the subscription to end at the period boundary, or cancels immediately (your choice).
* **Card update** — replace the saved payment method.
* **Invoice history** — every charge and receipt.

<Note>
  The portal session is minted via `createPortalSession` on the Convex side, which calls `stripe.billingPortal.sessions.create`. The return URL defaults to `https://chatblocks.ai/settings/billing`. Owner-only — co-members can't open the portal.
</Note>

## What customers see after subscribing

| Status                | Builder caps               | Card charged?                       |                                         |
| --------------------- | -------------------------- | ----------------------------------- | --------------------------------------- |
| `active`              | Active                     | Yes — at Checkout and each renewal  |                                         |
| Cancelled             | `canceled` at period end   | Builder until period end, then Free | Already charged for current period      |
| `past_due` / `unpaid` | Active during retry window | Retry in progress                   | See [Payment failure](#payment-failure) |

The plan literal flips back to `"free"` when Stripe's `customer.subscription.deleted` webhook arrives — usually at the period boundary.

## Payment failure

If a renewal charge fails:

1. Stripe sends `invoice.payment_failed`.
2. Our receiver flips `stripeSubscriptionStatus` to `past_due` or `unpaid` (whatever Stripe set).
3. Stripe's **smart retry** schedule attempts the card again over the next few days.
4. The workspace stays on Builder during this window — we don't immediately yank access.
5. After Stripe gives up (typically \~3 weeks), the subscription cancels and the workspace flips to Free.

To resolve, open the portal and update your card. Stripe retries automatically after a card update.

## Webhook events the receiver handles

The Convex webhook endpoint is at `/webhooks/stripe-billing` and dedupes events via `billingWebhookEvents.externalEventId`. It listens for 6 event types:

| Event                                  | What it does                                                 |
| -------------------------------------- | ------------------------------------------------------------ |
| `checkout.session.completed`           | Stamps `stripeSubscriptionId`, flips `plan` to `builder`.    |
| `customer.subscription.updated`        | Syncs `stripeSubscriptionStatus` + `stripeCurrentPeriodEnd`. |
| `customer.subscription.deleted`        | Flips `plan` back to `free`.                                 |
| `invoice.payment_failed`               | Updates `stripeSubscriptionStatus` to past\_due / unpaid.    |
| `invoice.payment_succeeded`            | Confirms `active` + updates period end.                      |
| `customer.subscription.trial_will_end` | No-op kept for any legacy trialing subs in Stripe.           |

Receiver runs idempotently — replays from Stripe's dashboard are safe.

## Frequently asked

<AccordionGroup>
  <Accordion title="Can I get a refund?">
    No published policy. Reach out via the support address on `/settings/billing` if you need help.
  </Accordion>

  <Accordion title="What happens if I downgrade with over-cap resources?">
    Existing resources are preserved. New creates fail with `LIMIT_REACHED` until you delete enough to get under the Free caps. The data isn't lost — it's just frozen at over-cap.
  </Accordion>

  <Accordion title="Can I switch monthly → annual mid-month?">
    Yes — open the Customer Portal, pick annual. Stripe prorates the difference and bills the rest immediately.
  </Accordion>

  <Accordion title="Do I get my BYOK keys back when I re-upgrade?">
    Yes. Downgrading doesn't delete BYOK rows — they just can't be used while you're on Free (the panel is gated). Re-upgrading restores access.
  </Accordion>

  <Accordion title="Can a co-member upgrade for me?">
    No. Upgrade is owner-only. The owner is the immutable `ownerAuthUserId` set on workspace creation — the paying operator.
  </Accordion>
</AccordionGroup>

## What's next

<CardGroup cols={2}>
  <Card title="Plans" icon="layer-group" href="/billing/plans">
    Pricing and the full cap table.
  </Card>

  <Card title="Workspaces" icon="users" href="/concepts/workspaces">
    Owner vs admin vs member — who can do what on a Builder workspace.
  </Card>
</CardGroup>
