Billing Config
billing.config.ts is the source of truth for your Guapocado billing model.
Minimal Config
Section titled “Minimal Config”import { defineBilling } from "@guapocado/sdk";
export default defineBilling({ entitlements: { "advanced-analytics": { type: "feature" }, }, products: [ { key: "pro", entitlements: { "advanced-analytics": true, }, }, ],});Top-level Shape
Section titled “Top-level Shape”type BillingConfig = { entitlements: Record<string, EntitlementDefinition>; products: PlanDefinition[]; webhooks?: { devTunnel?: boolean; forwarding?: WebhookForwardingDefinition[]; }; checkout?: { allowedRedirectHosts?: string[]; }; generate?: GenerateConfig;};Checkout redirect allowlist
Section titled “Checkout redirect allowlist”checkout.allowedRedirectHosts is an optional, opt-in open-redirect guard. When
set, the API rejects checkout.create successUrl/cancelUrl values whose host
is not in the list. Only the host is checked — path and query string are
preserved, so deep links and session params still work.
checkout: { // bare hostname, or host:port (e.g. for local dev) allowedRedirectHosts: ["app.example.com", "localhost:3000"],}Config is the source of truth: pushing with hosts locks redirects to them; pushing without the field clears the lock. Omit it and redirect URLs are unrestricted (current behavior).
Entitlements
Section titled “Entitlements”entitlements: { "advanced-analytics": { type: "feature" }, "api-calls": { type: "meter", reset: "monthly" }, seats: { type: "limit" },}Supported entitlement types:
featuremeterlimit
Supported meter reset values:
dailyweeklymonthly
Products
Section titled “Products”Products are declared in products.
products: [ { key: "pro", name: "Pro", pricing: { mode: "recurring", type: "flat", amount: 4900, currency: "usd", frequency: "month", }, entitlements: { "advanced-analytics": true, "api-calls": { included: 100000 }, seats: { included: 10 }, }, },]key is the product key your app uses.
name is optional display metadata.
pricing is optional. Omit pricing for free or internal products.
Recurring Pricing
Section titled “Recurring Pricing”pricing: { mode: "recurring", type: "flat", amount: 4900, currency: "usd", frequency: "month",}Supported frequencies:
monthyear
amount is in the smallest currency unit. For USD, 4900 means $49.00.
One-time Pricing
Section titled “One-time Pricing”pricing: { mode: "one_time", type: "flat", amount: 1900, currency: "usd",}Use one-time products for:
- credit packs
- lifetime unlocks
- purchased limit increments
- one-off setup fees
Custom (“Contact Us”) Pricing
Section titled “Custom (“Contact Us”) Pricing”pricing: { mode: "custom", contact: "mailto:sales@example.com", // optional CTA link}Custom tiers have no Stripe price: they’re skipped by Stripe sync, and
self-serve checkout for them returns 409 (“contact sales”). They’re sold
through an enterprise deal — once a customer has a
contract with an inline price, checkout for that tier succeeds at the negotiated
rate. type is optional for custom tiers, and amount/frequency/interval
must be omitted.
Use a custom tier for an “Enterprise — contact us” plan that you price per deal.
Pricing Shape
Section titled “Pricing Shape”pricing.type describes the price shape:
flatper_seatusage
The current app-facing primitives are still entitlements. Do not use
pricing.type as a replacement for feature, meter, or limit.
Product Entitlement Values
Section titled “Product Entitlement Values”Feature:
"advanced-analytics": trueMeter:
"api-calls": { included: 100000,}Limit:
seats: { included: 10,}Meter Overage
Section titled “Meter Overage”"api-calls": { included: 100000, overage: { allowed: true, unit: 10000, amount: 500, currency: "usd", },}This means the product includes 100,000 API calls and can charge $5.00 per 10,000 extra calls if the customer enables overage.
When you run guap push, Guapocado creates a metered Stripe price for each
overage-enabled entitlement. A customer enabling overage (usage.configure)
adds a metered subscription item. Each consume() call past the included
balance reports usage records to Stripe, which invoices at the end of the
billing period.
If overage is not defined on the plan, usage.configure({ overageEnabled: true }) returns an error.
Limit Expansion
Section titled “Limit Expansion”seats: { included: 10, expansion: { allowed: true, unit: 1, amount: 1200, currency: "usd", },}This means the product includes 10 seats and allows extra seats at $12.00 each.
When you run guap push, Guapocado creates a licensed Stripe price for each
expansion-enabled entitlement. Calling limits.configure with a purchased
value adds or updates a subscription item at quantity = purchased / unit.
If expansion is not defined on the plan, limits.configure with a non-zero
purchased value returns an error.
Webhooks
Section titled “Webhooks”webhooks: { devTunnel: true, forwarding: [ { key: "better-auth", path: "/api/auth/guap", events: "*", integration: "better-auth", autoRegister: true, }, ],}Use path for app-relative receivers and url for absolute receivers.
events: "*" subscribes to all Guapocado domain events.
Generate
Section titled “Generate”generate: { tables: { enabled: true, orm: "drizzle", db: "sqlite", output: "src/db/guapocado.ts", },}Use this only for local read-model mode.