# Hybrid Billing with Entitlements Combine traditional [fixed-fee pricing](https://docs.credyt.ai/features/product-catalog/fixed-fee-pricing.md) with bundled credit entitlements to mix recurring revenue with usage-based overages seamlessly. Hybrid billing lets you charge a monthly subscription fee while granting customers a fixed credit allowance each billing cycle. When customers exceed their included credits, they pay for additional usage at your defined overage rates. This model works for AI products where you want predictable recurring revenue while maintaining the flexibility of usage-based pricing. ## When hybrid billing fits[​](#when-hybrid-billing-fits "Direct link to When hybrid billing fits") Hybrid billing works when: * You want predictable monthly revenue from subscriptions * Customers need a base credit allowance included with their plan * You charge for usage beyond the included allowance * Different plan tiers provide different credit allocations Common examples include SaaS products with tiered plans ($20/month with 1,000 credits included), developer tools with base usage quotas, and AI platforms with plan-based credit allowances. ## How entitlements work[​](#how-entitlements-work "Direct link to How entitlements work") Entitlements are "right-to-use" grants that decouple service access from direct credit purchases. While a commitment represents pre-funded balance intended for consumption, an entitlement represents a bundled allocation granted at the product level. When you subscribe a customer to a product with entitlements, Credyt automatically: 1. Grants the specified credit amount to the customer's wallet 2. Refreshes this allowance according to your defined schedule (daily, monthly, etc.) 3. Tracks consumption against the entitlement balance 4. Charges overage fees when credits are exhausted ## Entitlement types[​](#entitlement-types "Direct link to Entitlement types") Entitlements are categorized by their accounting purpose, which dictates how Credyt handles revenue recognition and cost tracking. ### Bundled entitlements[​](#bundled-entitlements "Direct link to Bundled entitlements") Credits included as a feature of a fixed-fee plan. The subscription fee is recognized ratably over the billing cycle, while the credit value is generally assigned a revenue basis of $0 (the value is captured by the recurring fee). Example: "$20/month includes 1,000 credits" ### Promotional entitlements[​](#promotional-entitlements "Direct link to Promotional entitlements") Free credits granted to incentivize signups, login streaks, or referrals. No revenue is recognized. These track a cost basis to record marketing expense when consumed. Example: "$2 free daily credits for new users" ## Creating a hybrid billing product[​](#creating-a-hybrid-billing-product "Direct link to Creating a hybrid billing product") Entitlements can grant credits in either fiat currency or [custom assets](https://docs.credyt.ai/features/assets.md), for example credits, tokens or minutes. To use custom assets, first create the asset and define the buy rate, then create a product that uses the asset. ### Step 1: Create a custom credit asset[​](#step-1-create-a-custom-credit-asset "Direct link to Step 1: Create a custom credit asset") Before creating your product, define a custom asset for your credits. This asset has a buy rate that determines how much customers pay when purchasing additional credits. In this example, we create a `CREDIT` asset where each credit costs $0.05 (or inversely, $1.00 buys 20 credits). * REST API * TypeScript SDK * Python SDK POST https\://api.credyt.ai/assets ```json { "code": "CREDIT", "name": "Credits", "precision": 0, "symbol": "⭐", "label": "CR", "rates": [ { "source": "USD", "rate": 0.05 } ] } ``` ```typescript await client.assets.create({ code: "CREDIT", name: "Credits", precision: 0, symbol: "⭐", label: "CR", rates: [ { source: "USD", rate: 0.05, }, ], }); ``` [View full sample on GitHub](https://github.com/credyt/sdk-ts/blob/main/samples/assetsCreateSample.ts#L11) ```python response = client.assets.create( body={ "code": "CREDIT", "name": "Credits", "precision": 0, "symbol": "⭐", "label": "CR", "rates": [ { "source": "USD", "rate": 0.05, }, ], }, ) ``` [View full sample on GitHub](https://github.com/credyt/sdk-python/blob/main/samples/assets_create_credit_asset.py#L20) ### Step 2: Create the product with entitlements[​](#step-2-create-the-product-with-entitlements "Direct link to Step 2: Create the product with entitlements") Now create a product that grants bundled credits and charges for actual usage, for example "AI Image Generations". #### Example: monthly subscription with bundled credits[​](#example-monthly-subscription-with-bundled-credits "Direct link to Example: monthly subscription with bundled credits") This example creates a Premium Plan that costs $20/month and includes 1,000 credits that refresh monthly. The product charges 10 credits per image generation. When customers run out of bundled credits, they can purchase more at the $0.05 rate defined in the asset. * REST API * TypeScript SDK * Python SDK POST https\://api.credyt.ai/products ```json { "name": "Premium Plan", "code": "premium_monthly_01", "entitlements": [ { "name": "Monthly Credit Allowance", "asset": "CREDIT", "amount": 1000, "purpose": "bundled", "lifecycle": { "duration": "month", "refresh": { "strategy": "expire_and_replace" } }, "accounting": { "revenue_basis": 0, "cost_basis": "auto" } } ], "prices": [ { "name": "Monthly Subscription Fee", "type": "fixed", "billing_model": { "type": "recurring", "recurring": { "interval": "month" } }, "pricing": [ { "asset": "USD", "values": [ { "unit_price": 20 } ] } ] }, { "name": "Image Generation", "type": "usage_based", "billing_model": { "type": "real_time" }, "usage_calculation": { "event_type": "image_generated", "usage_type": "unit" }, "pricing": [ { "asset": "CREDIT", "values": [ { "unit_price": 10 } ] } ] } ], "publish": true } ``` ```typescript await client.products.create({ name: "Premium Plan", code: "premium_monthly_01", entitlements: [ { name: "Monthly Credit Allowance", asset: "CREDIT", amount: 1000, purpose: "bundled", lifecycle: { duration: "month", refresh: { strategy: "expire_and_replace", }, }, accounting: { revenueBasis: 0, costBasis: "auto", }, }, ], prices: [ { name: "Monthly Subscription Fee", type: "fixed", billingModel: { type: "recurring", recurring: { interval: "month", }, }, pricing: [ { asset: "USD", values: [ { unitPrice: 20, }, ], }, ], }, { name: "Image Generation", type: "usage_based", billingModel: { type: "real_time", }, usageCalculation: { eventType: "image_generated", usageType: "unit", }, pricing: [ { asset: "CREDIT", values: [ { unitPrice: 10, }, ], }, ], }, ], publish: true, }); ``` [View full sample on GitHub](https://github.com/credyt/sdk-ts/blob/main/samples/productsCreateSample.ts#L186) ```python response = client.products.create( body={ "name": "Premium Plan", "code": "premium_monthly_01", "entitlements": [ { "name": "Monthly Credit Allowance", "asset": "CREDIT", "amount": 1000, "purpose": "bundled", "lifecycle": { "duration": "month", "refresh": { "strategy": "expire_and_replace", }, }, "accounting": { "revenue_basis": 0, "cost_basis": "auto", }, }, ], "prices": [ { "name": "Monthly Subscription Fee", "type": "fixed", "billing_model": { "type": "recurring", "recurring": { "interval": "month", }, }, "pricing": [ { "asset": "USD", "values": [ { "unit_price": 20, }, ], }, ], }, { "name": "Image Generation", "type": "usage_based", "billing_model": { "type": "real_time", }, "usage_calculation": { "event_type": "image_generated", "usage_type": "unit", }, "pricing": [ { "asset": "CREDIT", "values": [ { "unit_price": 10, }, ], }, ], }, ], "publish": True, }, ) ``` [View full sample on GitHub](https://github.com/credyt/sdk-python/blob/main/samples/products_create_premium_plan.py#L20) With this configuration: * Customer pays $20/month for the subscription * Receives 1,000 credits each month * Each image generation costs 10 credits * When credits are exhausted, customer can purchase more at $0.05/credit * Customer can generate 100 images with their included credits #### Example: free tier with promotional credits[​](#example-free-tier-with-promotional-credits "Direct link to Example: free tier with promotional credits") This example models a Free Plan that costs $0 but includes a recurring daily entitlement of 50 credits. Each image generation costs 5 credits. * REST API * TypeScript SDK * Python SDK POST https\://api.credyt.ai/products ```json { "name": "Free Tier Product", "code": "free_tier_01", "entitlements": [ { "name": "Daily Reward", "asset": "CREDIT", "amount": 50, "purpose": "promotion", "lifecycle": { "duration": "day", "refresh": { "strategy": "expire_and_replace" } }, "accounting": { "revenue_basis": 0, "cost_basis": "auto" } } ], "prices": [ { "name": "Image Generation", "type": "usage_based", "billing_model": { "type": "real_time" }, "usage_calculation": { "event_type": "image_generated", "usage_type": "unit" }, "pricing": [ { "asset": "CREDIT", "values": [ { "unit_price": 5 } ] } ] } ], "publish": true } ``` ```typescript await client.products.create({ name: "Free Tier Product", code: "free_tier_01", entitlements: [ { name: "Daily Reward", asset: "CREDIT", amount: 50, purpose: "promotion", lifecycle: { duration: "day", refresh: { strategy: "expire_and_replace", }, }, accounting: { revenueBasis: 0, costBasis: "auto", }, }, ], prices: [ { name: "Image Generation", type: "usage_based", billingModel: { type: "real_time", }, usageCalculation: { eventType: "image_generated", usageType: "unit", }, pricing: [ { asset: "CREDIT", values: [ { unitPrice: 5, }, ], }, ], }, ], publish: true, }); ``` [View full sample on GitHub](https://github.com/credyt/sdk-ts/blob/main/samples/productsCreateSample.ts#L64) ```python response = client.products.create( body={ "name": "Free Tier Product", "code": "free_tier_01", "entitlements": [ { "name": "Daily Reward", "asset": "CREDIT", "amount": 50, "purpose": "promotion", "lifecycle": { "duration": "day", "refresh": { "strategy": "expire_and_replace", }, }, "accounting": { "revenue_basis": 0, "cost_basis": "auto", }, }, ], "prices": [ { "name": "Image Generation", "type": "usage_based", "billing_model": { "type": "real_time", }, "usage_calculation": { "event_type": "image_generated", "usage_type": "unit", }, "pricing": [ { "asset": "CREDIT", "values": [ { "unit_price": 5, }, ], }, ], }, ], "publish": True, }, ) ``` [View full sample on GitHub](https://github.com/credyt/sdk-python/blob/main/samples/products_create_free_tier.py#L20) With this configuration: * No monthly fee * Customer receives 50 free credits daily * Each image generation costs 5 credits * Customer can generate 10 images per day with free credits * When credits are exhausted, customer can purchase more at $0.05/credit ## Entitlement configuration[​](#entitlement-configuration "Direct link to Entitlement configuration") | Field | Type | Description | | --------------- | ------- | ----------------------------------------------------------------------------------------- | | `purpose` | Enum | Defines the accounting track: `bundled` or `promotion` | | `revenue_basis` | Decimal | Amount of revenue earned per unit used. Defaults to `0.00` for bundled/promotional grants | | `cost_basis` | Decimal | Internal cost to expense per unit used. Defaults to `auto` to follow the usage price | | `lifecycle` | Object | Defines the refresh lifecycle (e.g. refresh daily and do not rollover unused credits) | | `priority` | Integer | Dictates consumption order. Lower scores are burned first | ### Refresh strategies[​](#refresh-strategies "Direct link to Refresh strategies") The `lifecycle` object controls how entitlements replenish: * `duration`: How often credits refresh (`day`, `month`, etc.) * `duration_count`: The number of duration units. A value of 2 with a month duration means 2 months. Defaults to 1 if not specified. * `refresh`: Refresh object to define strategy. * `strategy`: What happens to unused credits * `expire_and_replace`: Unused credits expire, new allocation granted * `rollover`: Unused credits carry forward (up to defined limits) ## Consumption and priority rules[​](#consumption-and-priority-rules "Direct link to Consumption and priority rules") By default, Credyt consumes grants in the order they expire (FIFO). However, you can assign a higher `priority` to promotional grants to ensure free credits are exhausted before customers touch bundled or paid balances. ### Example consumption order[​](#example-consumption-order "Direct link to Example consumption order") 1. Promotional credits (highest priority) 2. Bundled subscription credits 3. Purchased credits or pay-as-you-go This ensures customers use their free promotional credits first, then their included subscription credits, before incurring overage charges. ## Revenue recognition[​](#revenue-recognition "Direct link to Revenue recognition") When a bundled entitlement has a `revenue_basis > 0`, Credyt manages revenue recognition: * **Usage**: Revenue is recognized unit-by-unit as consumed * **Expiry**: Remaining balance at period end moves from contract liability to revenue as breakage For bundled entitlements with `revenue_basis: 0.00`, the subscription fee is recognized ratably over the billing cycle regardless of credit consumption. ## Subscribing customers[​](#subscribing-customers "Direct link to Subscribing customers") Once you've created your hybrid product, subscribe customers by referencing the product code when creating the customer: * REST API * TypeScript SDK * Python SDK POST https\://api.credyt.ai/customers ```json { "name": "John Doe", "external_id": "18991", "email": "j.doe@gmail.com", "subscriptions": [ { "return_url": "https://glitch.ai/account", "failure_url": "https://glitch.ai/callbacks/credyt/failure", "redirect_to": "portal", "products": [ { "code": "premium_monthly_01" } ] } ] } ``` ```typescript await client.customers.create({ name: "John Doe", externalId: "18991", email: "j.doe@gmail.com", subscriptions: [ { returnUrl: "https://glitch.ai/account", failureUrl: "https://glitch.ai/callbacks/credyt/failure", redirectTo: "portal", products: [ { code: "premium_monthly_01", }, ], }, ], }); ``` [View full sample on GitHub](https://github.com/credyt/sdk-ts/blob/main/samples/customersCreateSample.ts#L129) ```python response = client.customers.create( body={ "name": "John Doe", "external_id": "18991", "email": "j.doe@gmail.com", "subscriptions": [ { "return_url": "https://glitch.ai/account", "failure_url": "https://glitch.ai/callbacks/credyt/failure", "redirect_to": "portal", "products": [ { "code": "premium_monthly_01", }, ], }, ], }, ) ``` [View full sample on GitHub](https://github.com/credyt/sdk-python/blob/main/samples/customers_create_subscribe_to_premium_plan.py#L20) Return and Failure URLs When adding a customer to a paid recurring subscription product, a link is provided for the customer to enter their card details. You specify both the URL the customer should return to when exiting the portal (`return_url`) and the failure URL (`failure_url`) we will redirect them to if their session expires or there are any issues. You can find more about failure handling [here](https://docs.credyt.ai/features/billing-portal.md). `redirect_to` can be set to `return_url` so the customer is returned to the `return_url` instead of the portal. When the subscription activates, Credyt automatically grants the entitlement credits to the customer's wallet and begins tracking consumption.