Billing
STOP, prerequisite. Billing must be enabled before any
<PricingTable />,<CheckoutButton />,has({ plan }), orhas({ feature })usage works. Two paths: (1) Dashboard → Billing → Settings, or (2)clerk enable billing(see "Agent-first: Programmatic billing config" below). Enabling auto-creates defaultfree_user/free_orgplans. Dev instances can use the shared Clerk development gateway (no Stripe account needed); production requires a Stripe account for payment processing only.Note: Billing APIs are still experimental. Pin your
@clerk/nextjsandclerk-jspackage versions. Seeclerkskill for the supported version table.
Quick Start
-
Enable Billing, via Dashboard → Billing → Settings or
clerk enable billing(see Agent-first section). Skipping this throwscannot_render_billing_disabledin dev and renders empty in prod. -
Create plans in the matching tab, Dashboard → Billing → Plans. Two tabs, slugs scoped per tab, not movable after creation:
- User Plans →
<PricingTable />(defaultfor="user") - Organization Plans →
<PricingTable for="organization" />
Wrong-tab is the #1 cause of an empty
<PricingTable />. Plans live in Clerk; not synced to Stripe. - User Plans →
-
Add features inside a plan, open the plan in Dashboard → Billing → Plans, use its Features section. Features are scoped per plan, not global. The same slug can attach to multiple plans;
has({ feature: 'export' })matches if the active plan contains that slug. -
Render
<PricingTable />(passfor="organization"for B2B). -
Gate access with
has({ plan })orhas({ feature })fromauth(). -
Handle billing webhooks for subscription lifecycle.
Dashboard shortcuts
Agent-first: Programmatic billing config
The full billing config (enable toggles, plans, features, plan-feature attachments) is editable via PLAPI without touching the Dashboard. Useful for agents seeding plans, replicating config across instances, or version-controlling billing structure.
Pre-req: project linked to the Clerk app (clerk auth login + clerk link, see clerk-cli). Billing is account-only — unlike orgs, it cannot be enabled on an unclaimed app; claim the app first.
Enable Billing via CLI
Pull current billing config
This writes the current billing config (toggles + plans + features) for the linked instance to billing.json.
Edit and apply
Edit billing.json to add/remove plans or features, then preview the diff and apply:
Pass --instance prod to target the production instance instead of dev.
Raw PATCH (full control)
For one-shot plan/feature updates without a config file:
Notes
- This handles billing config (toggles + plans + features catalog). Subscription lifecycle (users picking a plan, checkout, renewal, cancellation) still flows through
<PricingTable />+ billing webhooks, seeclerk-webhooksskill for the lifecycle events. - Top-level
featuresmap manipulation and plan-feature attachments (sync) are fully supported via the PLAPI billing config handler.
What Do You Need?
References
Documentation
Features vs Plans: When to Use Which
Use has({ feature: 'slug' }) when gating a specific capability, export, analytics, API access, audit logs.
Use has({ plan: 'slug' }) when gating a tier, showing the pro dashboard, checking org subscription level, redirecting free users.
When a user says "gate the export feature" or "gate analytics", always use has({ feature }). Only use has({ plan }) when the gate is the plan tier itself, not a specific capability within it.
Key Patterns
1. Render the Pricing Table
Show available plans to users with a single component:
<PricingTable /> automatically renders all plans configured in the Clerk Dashboard. Selecting a plan opens Clerk's in-app checkout drawer. No props needed for basic usage. For B2B, pass for="organization" to render org-level plans instead of user plans.
2. Check Feature Entitlements (Server-Side)
Gate by individual features, this is the preferred approach for specific capabilities:
Features are configured in Clerk Dashboard → Billing → Features and assigned to plans. Use has({ feature }) instead of has({ plan }) when gating granular capabilities, check the feature, not the plan.
3. Check Feature Entitlements (Client-Side)
Use useAuth() for client-side feature gating. Combine with server-side checks for full coverage:
Server Components use auth(), Client Components use useAuth(). Both support has({ feature }) and has({ plan }).
4. Check Subscription Plan Server-Side
Gate access by subscription plan (use this for tier-level gates, not individual features):
5. Client-Side Plan Checks
Use useAuth() hook for client components:
6. B2B Seat-Based Billing with Organizations
Org plans can carry a seat limit (membership cap) that Clerk enforces at invite time. Use the org: slug prefix on org-side plan checks (e.g. has({ plan: 'org:team' })) to keep gating unambiguous. Render the B2B pricing page with <PricingTable for="organization" />, and use <OrganizationProfile /> for the org account billing UI.
See references/b2b-patterns.md for tiered plan naming, seat-limit invariants, admin-only billing, and webhook handlers.
7. Display Subscription Status
Check specific plans with has({ plan }), or use useSubscription() for full subscription details in client components. Do not read plan information from sessionClaims directly, that is not the supported path.
Server component, check for specific plans:
Client component, full subscription details via useSubscription():
useSubscription()is for display only. For authorization checks (gating content or routes), always usehas({ plan })orhas({ feature }).
8. Protect API Routes by Plan
Gate API routes using auth():
9. Handle Billing Webhooks
Clerk event names differ from Stripe event names. Clerk billing webhooks use dot-notation and camelCase, not Stripe's underscore format.
There is no
subscription.canceledevent. Cancellation fires at the item level assubscriptionItem.canceled.Always use Clerk's event names, never Stripe's, in
evt.typechecks.
Payload shape. Clerk billing webhook payloads are nested. The subscribing entity lives under
evt.data.payer(fields:user_id?,organization_id?). The plan info is on each item underevt.data.items[i].plan.slug. The subscription id is simplyevt.data.id. Subscription items do not carry asubscription_idfield back-reference, so insubscriptionItem.*handlers you identify the record by the item id (evt.data.id) or look up by payer plus plan.
Minimal handler to anchor the pattern (import from @clerk/nextjs/webhooks, verify, branch on Clerk event name):
For the full template covering all 15 events, the TS type declarations from @clerk/backend, the proxy.ts public-route setup, and the subscription status value table, see references/billing-webhooks.md.
10. Upgrade / Downgrade Flow
Let users manage their subscription from inside the app:
<PricingTable /> renders differently for subscribed users, it shows the current plan and allows upgrades or cancellations, all through Clerk's in-app checkout drawer.
Plan and Feature Naming
Plan slugs and feature slugs are defined in Clerk Dashboard → Billing. Common conventions:
Use lowercase slugs matching what you define in the dashboard.
B2B vs B2C Billing
For B2B, ensure the user has an active org session. The has() check evaluates the active entity (user or org).
Checkout Flows
Clerk renders its own checkout drawer automatically through <PricingTable /> and <CheckoutButton />. Plans and pricing live in Clerk. To trigger checkout from a server action, redirect to a page that renders <PricingTable />:
Error Signatures (diagnose fast)
When you see any of these errors or symptoms, the fix is almost always a Dashboard toggle, not a code change. Do not start editing components.
Billing Gates Permissions
When Billing is enabled, has({ permission: 'org:posts:edit' }) returns false if the Feature associated with that permission is not included in the organization's active Plan, even if the user has the permission assigned via their role. This is by design: billing gates permissions at the feature level. Always ensure the required Feature is attached to the Plan in Dashboard → Billing → Plans → Features.
See Also
clerk-setup- Initial Clerk installclerk-orgs- B2B organizations (required for B2B billing and seat-limit plans)clerk-webhooks- Webhook signature verification and routing
