2026-05-11
Clerk Billing + force sign-out
- Migration 0004 adds users.plan + users.plan_synced_at; plan policy lives in a single app/services/plans.py module.
- Clerk webhook handles subscription.created / .updated / .active → set plan from items[0].plan.slug; subscriptionItem.canceled / .ended / .expired / .abandoned → revert to starter; subscription.pastDue audit-only.
- Plan gates by capacity (templates, renders/month, storage, requests/min) — every plan can render all four formats. Starter: 5 templates · 100 renders · 100 MB · 60 r/min. Growth: 100 · 500 · 25 GB · 600 r/min. Scale: unlimited · 5 000 · 250 GB · 6 000 r/min. Exceeding any cap returns 402 with error.code=plan_limit_exceeded.
- /v1/me returns the caller's plan + resolved limits — the dashboard's source of truth.
- /admin/users/{id}/force_sign_out now actually revokes Clerk sessions via the Backend API (per the clerk-backend-api skill); returns attempted / revoked / failed counts.
- Clerk's <PricingTable /> renders on /dashboard/settings — checkout runs in Clerk's drawer, your card never touches our servers.
- Admin user detail shows current plan + last billing-sync timestamp.