Skip to main content
Three ideas explain most of the component’s behaviour. Read this once and the rest of the documentation stops holding surprises.

The billing entity

Every subscription, order, and credit balance belongs to one billing entity, identified by the entityId your resolver returns. It is the first decision you make and the most awkward one to change later. For personal billing, return the user ID. For organization or team billing, return the org ID. Checkout metadata and webhook resolution follow automatically, with no other code changes:
Return null for an unauthenticated caller. Anything thrown from the resolver is treated as a real failure: it is logged and rethrown instead of quietly degrading a signed-in user to the logged-out pricing page. (Throwing the exported CreemNotAuthenticatedError is also accepted as an anonymous signal.)
By default, the component tracks the entity’s active app-owned plan itself. Apps that own their plan assignment can override that from the resolver — see Advanced → Resolver plan overrides.

The billing state model

Billing state comes from four places. Three of them store data. The fourth is derived from the other three, and that distinction is worth learning early: Write to the first three through checkout, webhooks, and creem.appPlans.activate. Do not treat access as a source of truth. It is recomputed on every read.
snapshot (generated by creem.api({ resolve })) and creem.getBillingSnapshot(ctx, { entityId }) both return this shape. Everything is synced into Convex, so reading it is a local reactive query with no Creem API round-trip.

Easily confused pairs

The connected API contract

Widgets hold no Convex function references of their own. Every backend call goes through one ConnectedBillingApi object they read from the provider, and its fields decide what the UI can do. Export and wire only what your product should allow. connectCreemApi maps the conventional convex/billing.ts export names onto that shape for you. Every reference is typed by function kind, args, and return type, so a missing or mis-wired export is a compile error rather than a blank widget:
Building the object by hand still works when you wrap the generated functions in your own RBAC actions. See Advanced → Custom auth and RBAC.

Where to go next