Introduction & Motivation
It started with a customer call, “We are launching our new AI feature next month and want to monetize it in credits. How can we use multiple usage-based features to draw down from the same credit pool?”
If you are building SaaS today, that probably sounds familiar. AI features are everywhere: copilots, enrichment tools, generators. AI monetization is evolving rapidly and that it's critical for SaaS vendors to keep up with the pace. Pricing them is tricky. Costs are high and usage is unpredictable.it was originally monetized as pure PAYGO, but as more companies introduce multiple AI-based features monetizing each of them becomes complex. That is why credits have become the new currency of AI. They offer a simple abstraction that makes pricing feel fair, maps directly to value, and protects margins. A single currency, and then consume multiple features from it - similarly to an arcade of a casino
On paper, credits sound straightforward. In practice, they are anything but. What starts as a pricing question quickly becomes an engineering marathon: real-time deduction, expirations, multiple usage-based features draw-down from the same credit pool, revenue recognition, charts and in-app widgets. The moment you introduce credits, you are not just adding a billing unit. You are building an economy.
At Stigg, the monetization infrastructure for tech companies, our job is to give engineering and product teams the building blocks to launch new pricing fast. Our customers are at the forefront of where the tech world is heading. They're rolling out AI features every day. so, we've built a way for them to monetize those features.
So we built them. And along the way, we discovered just how deep the rabbit hole goes.
This article is our field report: everything we learned building credits for Stigg, what worked, what broke, and what you should know before you build them yourself.
The First Decision: Which Model to Support
The first decision we faced was deceptively simple: how should credits be structured?
We saw two dominant models:
- Recurring credits bundled with subscriptions
Customers receive a recurring allocation of credits (monthly or annually) as part of their plan. Unused credits may roll over or expire, depending on configuration. Additional credits can be unlocked by upgrading tiers or purchasing top-ups. This model works well when customers are willing to commit to AI features. Those features can be add-ons to existing SaaS plans or the entire monetization strategy. - Prepaid dollar-based credits
Customers deposit a fixed dollar amount and consume credits until the balance runs out. Customers can top up their balance anytime. This approach is often used when customers need more flexibility, and the product experience is entirely usage-driven.
As we started mapping customer needs, the separation became clearer and clearer. Companies were not randomly picking one model or the other. The choice almost always depended on the type of product they were pricing, and the level of commitment to pay by their customers. For products with high commitment, subscription made sense. Otherwise, flexibility is needed, prepaid packs were the natural fit. It was an enlightening moment for us: this was not just a design option, it was a structural split across the market.
In practice, we ended up building support for both. Supporting both gave us flexibility, but it also multiplied the complexity. Looking back, this was one of the bigger forks in the road. For many companies, choosing one model would be enough. The right decision depends on your product, your pricing strategy, and your customers’ expectations.
Designing a Credit System: Core Building Blocks
Very quickly, we learned that a credit system is not just a balance counter. It is a stack of interdependent concepts, flows, and enforcement mechanisms.
The main challenges with building credit systems, and our approach to solve them
- Accuracy, a Ledger is required
- Support high throughput and scale - we leverage our high-throughput ingestion pipeline (Note: If you have to build this one, too, we wrote some useful resources on implementing high-scale usage-based pricing here, here, and here.)
- Draw-down of multiple usage-based features - from the same credit pool, credit pools are required
- Enforcement logic on credits expiration - we leverage our battle-tested entitlement infrastructure
- Transparency - we provide visibility for all grant and consumptions events, both inside the Stigg app and to customers, via our widgets.
- Notification - Allow customers to be notified before the credit balance runs out. We leverage our existing webhook and workflow capabilities
Key Terms
- Credit currency / Credit type - the unit of value (e.g., “Tokens” or “Credits”), abstracting currency like tokens or compute units.
- Credit Wallet / Pool - the container holding all credit grants for a customer. A wallet contains a single credit type and multiple grants (recurring, top-up, promotional), each with its own rules (expiry, rollover, priority).
- Feature Consumption Rate - mapping between a feature and its credit cost (e.g., 1 API call = 1 credit, 1,000 tokens = 2 credits).
- Balance - the total usable credits across all active, unexpired grants in the wallet.
- Credit Grant & Packages - an event that adds credits to a wallet. Grants can be tied to subscriptions, add-ons, or one-time purchases.
- Credit Withdraw - deduction of credits when features are consumed.
- Rollover Policy - determines whether unused credits persist into the next cycle or expire.
- Top-up - a user-initiated one-time purchase of additional credits.
The Five Building Blocks
1. Granting Credits
Credits are allocated in multiple ways:
- Recurring grants tied to subscription plans.
- Promotional grants (e.g., daily free credits) (docs)
- Add-ons purchased alongside a plan.
- One-time top-ups purchased on demand (docs) or enable self-serve top-ups (docs).
The code to grant credits looks like:
const credits = await stiggClient.grantCredits({
customerId: 'test-customer-id',
amount: 100,
currencyId: 'cred-type-ai-token',
displayName: 'AI Token',
grantType: CreditGrantType.PAID,
});
And get granted credits for a customer looks like:
const creditGrants = await stiggClient.getCreditGrants({
customerId: 'test-customer-id',
currencyId: 'cred-type-ai-token', // Optional, if not provided, all credit grants will be returned
});
Every grant is logged in a ledger with effective date, expiration date, and source. Ensuring traceability, compliance, and auditability. Here’s how to get the ledger:
const ledger = await stiggClient.getCreditLedger({
customerId: 'test-customer-id',
});
2. Consuming Credits
Once credits are granted, they need to be consumed as customers use features. This sounds simple: each event has a cost, deduct that cost from the customer’s balance, and move on.
In practice, this is where some of the hardest problems showed up. Accuracy and transparency is everything. Usage pipelines retry events by design, which means every deduction must be idempotent. Double-charging damages trust instantly, while missing a deduction undermines revenue. To keep things consistent, we had to treat every event as unique, enforce idempotency across multiple data stores, and add de-duplication patterns that were invisible to customers but vital to our sanity.
We also had to decide what happens when customers run out of credits. Do you cut them off immediately? Allow soft limits? Trigger overage charges? Each option carries product and financial implications, and the enforcement logic has to reflect those rules instantly.
.png)
Here’s how to report credits based features:
await stiggClient.reportEvent({
customerId: 'test-customer-id',
eventName: 'email_enrichment',
idempotencyKey: '227c1b73-883a-457b-b715-6ba5a2c69ce4',
dimensions: {
user_id: 'user-01',
user_email: 'john@example.com'
},
timestamp: '2022-10-26T15:01:55.768Z' // optional, pass it to report event with specific timestamp
});
3. Scheduling & Expirations
Credits aren’t meant to last forever. They can be recurring, promotional, or tied to specific packages. Managing when they’re issued and when they expire isn’t just about accounting, it’s about behavior. SaaS vendors, much like telecom or credit card providers, use expirations to encourage ongoing engagement and future purchases. If credits vanish without notice, customers feel shortchanged; if they never expire, margins and incentives fade.
To handle this, we built scheduling logic for recurring grants, automatically fill up credit pools, and credits expiration, along with webhooks to keep customers and systems in sync. The most important are credits.granted, credits.grant.usage_low, credits.expired and credits.grant.depleted. Together, they ensure customers know when credits arrive, when they are about to expire, and when they run out.
Getting this right is not just a backend detail. It directly shapes the customer experience and protects revenue.
4. Live Enforcement
Enforcement is what makes credits real. The system must be able to block, allow, or charge for usage in the moment credits are consumed. Without it, credits are just accounting entries.
Why is this so important? Because AI features are expensive to run. Each API call, prompt, or image generation can cost real dollars in compute or third-party LLM fees. If enforcement lags, customers can continue using features even after their balance has run out. For high-volume customers, that can translate directly into thousands of dollars lost before the system catches up.
That is why we invested heavily in live enforcement. Every usage event must be checked against the current balance and the configured rules, whether that means applying hard limits (deny usage when balance is 0), soft limits (allow overage and charge later), or triggering overage charges. Enforcement is not just a guardrail, it is the difference between sustainable monetization and runaway costs.
At Stigg, usage-based entitlements are tied to credit balance of the customers, and automatically blocked once credit balance is depleted. Here’s how to obtain credits based feature entitlements:
const messagesEntitlement = await stiggClient.getMeteredEntitlement({
featureId: 'ai-tokens',
customerId: 'test-customer-id',
});
5. Billing Integration & Revenue Recognition
Credits may be virtual, but billing and revenue recognition are very real. If the numbers do not line up, finance notices immediately.
Finance added another layer of challenge. Credits had to integrate seamlessly with existing billing systems so that top-ups, recurring grants, and overages could all be invoiced correctly. On top of that, we had to track the cost basis of every credit, align recognition with consumption, and make sure rollovers, refunds, and promotions could all withstand an audit.
Why does this matter? Because if billing integration breaks, customers are either overcharged (destroying trust) or undercharged (bleeding revenue). If revenue recognition is wrong, audits fail, compliance issues surface, and finance ends up rewriting product logic in spreadsheets. None of those outcomes are acceptable.
One of the considerations companies need to make is switching billing providers to support credits, and its nearly impossible project for bigger companies. Instead, we built credits to integrate with billing providers rather than replace them. That way, companies don’t face a painful rip-and-replace project just to support a new pricing model. Our approach was to meet finance where they already work.
Building for engineers to meet finance requirements was required, and it often meant revisiting earlier engineering decisions. For example, how we logged credit events directly shaped whether finance could trace them back to invoices. What seemed like a purely technical decision - ledger granularity, event ordering, became a financial requirement.

Widgets, UX & Transparency
Even if the engine is perfect, credits live and die by their user experience. Customers expect transparency. They want to see their balance, review their usage history, and receive alerts when they are close to running out. They want to top up quickly and securely without opening a support ticket.
Admins expect the same level of clarity, but from the other side. They need to be able to define feature mappings and understand how overrides behave across different plans. They need an audit log that captures every action, with actor and timestamp, to satisfy compliance. And they need tools to adjust balances manually, or grant promotions
We learned that building the internal and external interfaces was just as important as building the credit engine itself, aligned with Stigg core principle for transparency. Without them, the system is opaque and untrustworthy. With them, credits become tangible and easy to work with.
Customer Experience
- Balance visibility: Customers can see their remaining credits at any time.
- Usage history: Usage charts show how credits were spent by date and by feature.
- Credit ledgers: Keep track of the credits related operations.
- Notifications: Configurable alerts warn customers when balances are low or about to expire.
- Top-up flows: Customers can purchase additional credits securely through the checkout page.
Admin Experience
- Audit log: Every adjustment and grant is recorded with actor, timestamp, and context for compliance.
- Feature mapping: Define how many credits each feature costs and configure overrides for specific plans.
- Management tools: Admins can adjust balances manually, grant promotional credits, or revoke expired ones.
Widgets in Practice
Widgets make transparency tangible and save teams from reinventing the wheel. Out-of-the-box components can expose credits directly to customers and streamline top-ups.
By combining these widgets into pricing tables, customer portals, and checkout flows, providers create a transparent and self-service experience. Customers always know where they stand, and providers gain built-in upsell opportunities.
Credits Balance widget
Presents the live balance and lets customers top up directly. It includes a two-step checkout flow (select amount, then pay), showing subtotal, taxes, and resulting balance.

import { StiggProvider, CreditBalance } from '@stigg/react-sdk';
function App() {
return (
<StiggProvider
apiKey="your-stigg-api-key"
customerId="customer-demo-01"
>
<div className="credit-widgets">
{/* Basic Credit Balance */}
<CreditBalance
currencyId="cred-type-ai-tokens" // Required: currency identifier
showUnits={true} // Optional: show units text
showSymbol={false} // Optional: show currency symbol
decimalPlaces={2} // Optional: limit decimal places (null = no limit)
localization={{
title: "Available Credits" // Optional: custom title
}}
/>
</div>
</StiggProvider>
);
}
export default App;
Credits Utilization widget
Shows a snapshot of total credits used vs. available, active balance, and current plan. It often includes actions like “Add to credit balance” or enable auto-recharge.

import React from 'react';
import { StiggProvider, CreditUtilization } from '@stigg/react-sdk';
function App() {
return (
<StiggProvider
apiKey="your-stigg-api-key"
customerId="customer-demo-01"
>
<div className="credit-widgets">
{/* Credit Utilization with Progress Bar */}
<CreditUtilization
currencyId="cred-type-ai-tokens" // Required: currency identifier
showUnits={true} // Optional: show units text
showSymbol={false} // Optional: show currency symbol
decimalPlaces={0} // Optional: no decimals for whole numbers
localization={{
title: "Credit Usage", // Optional: custom title
remaining: "Available" // Optional: custom remaining label
}}
/>
</div>
</StiggProvider>
);
}
export default App;
Credits Grants widget
Lists all grants with their status (active, expired, voided, or scheduled), effective/expiry dates, remaining balance, and reason/actor. This creates a transparent audit trail.

import React from 'react';
import { StiggProvider, CreditGrants } from '@stigg/react-sdk';
function App() {
return (
<StiggProvider
apiKey="your-stigg-api-key"
customerId="customer-demo-01"
>
<div className="credit-widgets">
{/* Credit Grants Table */}
<CreditGrants
currencyId="cred-type-ai-tokens" // Required: currency identifier
// Optional: Custom column labels and messages
localization={{
receivedAt: "Grant Date",
grantType: "Type",
status: "Status",
balance: "Amount",
expiryDate: "Expires",
emptyState: "No credit grants found for this currency",
loadingState: "Loading grants...",
errorState: "Unable to load credit grants"
}}
/>
</div>
</StiggProvider>
);
}
export default App;
Credits Usage Chart widget
Displays a time-series usage view, broken down by feature, so customers see exactly how and when credits were used.

import React from 'react';
import { StiggProvider, CreditUsageChart } from '@stigg/react-sdk';
function App() {
return (
<StiggProvider
apiKey="your-stigg-api-key"
customerId="customer-demo-01"
>
<div className="credit-widgets">
<CreditUsageChart
currencyId="cred-type-ai-tokens" // Required: currency identifier
timeRange=CreditUsageTimeRange.LAST_MONTH
localization={{
title: "Credit Usage Chart", // Optional: custom title
}}
/>
</div>
</StiggProvider>
);
}
export default App;
Engineering Considerations & Challenges
Building a credit system is not just about the concepts. The real challenge is making it correct, consistent, and scalable while still keeping the user experience smooth. Along the way, we hit plenty of forks in the road, underestimated edge cases, and had to revisit decisions we thought were settled. Here are the core engineering considerations, and what we learned from them.
Live Enforcement
From day one, we knew enforcement would make or break credits. The debate started simple: do we block usage the moment credits hit zero, or allow customers to go negative and bill them later?
The cost of a single LLM request can be dollars, not cents. If enforcement lags, one enthusiastic customer can burn through thousands of credits and thousands of dollars before the system catches up. That realization shifted enforcement from “just a rule” to “a financial safety net.”
Correctness and Consistency
Usage pipelines are at-least-once by design. We thought this would be a minor detail, but retries quickly became one of the hardest problems.
We ended up treating every usage event as unique, carrying a unique event_id, and making deduction flows idempotent across multiple data stores (PostgreSQL for truth, DynamoDB for edge lookups, ClickHouse for analytics). After a long whiteboard session, we introduced a de-duplication mechanism to mark what had already been processed. It felt like overkill at first, but without it we could not sleep at night.
Lifecycle Management
We initially thought credit lifecycles were just about granting and expiring. In reality, the lifecycle is messy:
- Grants: recurring, promotional, top-up.
- Effective date: credits can be consumed after the effective date.
- Grant priority: determine from which grant to consume first.
- Expirations: voiding balances after expiration date.
- Adjustments: manual overrides with audit logs.
- Migrations: updating feature-to-credit mappings.
One surprise was how often admins needed to step in manually, whether for promotions, or quick fixes for unhappy customers. Without a clear audit log of every adjustment, the system quickly became untrustworthy, and finance requirements were not fulfilled.
Migrations and Plan changes are a huge pain in the Entitlements space, and a significant engineering challenge. Stigg’s CTO Anton Zagrebelny breaks it down in his blog “Entitlements untangled: The modern way to software monetization / Changes and migrations”
Revenue Recognition and Cost Basis
Even though credits are virtual, their valuation must be precise. The main challenge in RevRec is that when credits don't expire at the end of the billing cycle, their revenue can only be recognized upon their consumption.
We had to track the cost basis (for example, if $10,000 buys 8,500 credits, the effective rate is $0.85/credit), ensure recognition at the point of consumption, and handle rollovers, and promotions in ways that auditors could trace. What surprised us most was how financial rules directly shaped technical decisions, especially around how granular our ledgers needed to be.
Final Thoughts
Credits are a powerful way to monetize AI and other usage-based products. They align pricing with customer activity, protect margins, and give companies a unified currency across multiple features.
When we started building credits at Stigg, we thought it would be straightforward. It wasn’t. Very quickly, we ran into the hidden depth: real-time usage ingestion at scale, real-time credit burn down, enforcement at near real-time level, execute expiration policies, cost basis tracking, revenue recognition and billing integration, customer-facing widgets, admin overrides. None of these are optional. Get them wrong and you risk broken customer experiences, compliance issues, or revenue leakage.
That’s the key lesson we learned: credits look simple from the outside, but under the hood you’re not just adding a pricing unit, you’re building an economy.
So should you build or buy? The honest answer is: it depends. If your business needs very unique rules, has a long runway, dedicated headcount and time to build (few good months), and wants to control every edge case, building might make sense. But if you want to launch quickly, avoid costly mistakes, and integrate seamlessly with your existing billing provider, moving fast in the rapidly changing domain of AI monetization, buying is often the better choice. So if you want to future proof yourself, leave it to Stigg - we're always at the forefront of monetization.
That’s why we built credits as a first-class primitive inside Stigg. They come with live enforcement, a wallet and ledger model, integration with existing billing systems, and customer-facing widgets for transparency. In other words, the painful parts we wrestled with are already solved.
Our advice: weigh the trade-offs carefully. If you do decide to build, we hope our experience helps you avoid some of the pitfalls we hit.