
Customer Stories
700+ customers in year one: How Blacksmith built scalable usage-based billing for CI/CD with Lago
Anh-Tho Chuong • 2 min read
Mar 30
/12 min read
Prepaid credit systems have become essential infrastructure for modern SaaS platforms, enabling flexible consumption-based billing models that align revenue recognition with actual usage. According to recent industry data, 67% of B2B SaaS companies now offer prepaid credit options as part of their pricing strategy [1], recognizing that prepaid models increase customer lifetime value by an average of 34% compared to traditional monthly subscriptions [2].
Implementing a robust prepaid credit system requires careful API design that handles credit allocation, consumption tracking, expiration management, and reconciliation. This guide walks through the technical architecture and implementation patterns for building production-grade prepaid credit functionality.
A prepaid credit system allows customers to purchase account credit in advance, which is then consumed by service usage or transactions. Rather than billing for usage after the fact, prepaid models establish a customer balance that decrements with each billable action. This mechanism serves multiple purposes: improving cash flow predictability for vendors, reducing churn through invested customer capital, and simplifying billing workflows by eliminating complex usage reconciliation delays.
Prepaid credits differ fundamentally from postpaid consumption models in that the revenue recognition happens at purchase time rather than consumption time, requiring different accounting treatments and regulatory considerations. The system must maintain accurate ledgers of credit allocation, usage, refunds, and expirations to ensure compliance with customer contracts and tax obligations.
A production prepaid credit system consists of five interconnected components: a wallet service that manages customer credit balances, a consumption API that decrements credits during transactions, an expiration engine that processes credit lifecycle, a reconciliation system that audits all movements, and a webhook notification system that alerts downstream services about balance changes.
The wallet service maintains the authoritative ledger of customer credit. Each customer account contains one or more wallets (often segmented by product, region, or currency), with every wallet recording granular transactions. These transactions include initial credit purchases, consumption events, manual adjustments, refunds, and expirations. The API must support real-time balance queries with sub-second latency and transactional consistency guarantees to prevent overselling.
The consumption API handles the decrement of customer credit during billable events. This component must be idempotent—processing the same consumption event twice should not double-decrement credits. Designing for idempotency requires associating consumption requests with globally unique identifiers that persist across retries, enabling safe replay in distributed systems. The consumption API also manages the selection logic when customers hold multiple wallet tiers with different expiration dates, typically following a "use-oldest-first" principle to minimize forfeited credits.
An effective prepaid credit API should expose four primary endpoint groups: wallet management, credit purchase, consumption tracking, and reporting. The wallet management group includes endpoints to retrieve current balances, list historical transactions, and fetch wallet metadata. Credit purchase endpoints accept customer payment and create confirmed credit allocations with specified expiration terms. Consumption endpoints process usage events and return updated balances. Reporting endpoints provide audit trails and reconciliation data.
For wallet balance queries, the API should return not only the current balance but also a breakdown by expiration cohort. This detailed response structure helps customers and support teams understand their credit position and anticipate expirations. A typical balance response includes total balance, available balance (excluding reserved amounts), reserved balance (for in-flight transactions), breakdown by expiration date, and next expiration timestamp.
The consumption endpoint accepts a consumption request object containing a unique idempotency key, the amount to consume, optional metadata tags, and the transaction reason. The endpoint returns either a success response with updated balance and remaining credits, or a failure response if insufficient balance exists. For failed transactions, the API should return whether the failure stems from insufficient balance, wallet expiration, or other validation errors, enabling client applications to implement appropriate error handling and retry logic.
Credit expiration introduces significant complexity to prepaid systems. Unlike traditional account balances that persist indefinitely, prepaid credits typically expire after a specified period (often 12-24 months), requiring automated processing to remove expired credits from customer accounts. The expiration engine must run periodically, identify credits that have reached their expiration timestamp, and process removal while maintaining immutable audit trails.
Organizations should implement a grace period before actual expiration to provide customer notification opportunities. A recommended pattern involves marking credits as "expiring soon" (30 days before expiration), sending automated notifications, and only processing actual expiration after the grace period. This approach reduces customer friction and support tickets while maintaining compliance with contract terms.
The expiration process should generate accounting entries that reconcile the expired credit amount against revenue. Depending on the business model, expired credits may be recognized as revenue if they represent non-refundable purchases, or they may be reserved as a liability if contracts include refund provisions. The API should expose endpoints to query expiring credits and process manual expirations when necessary for customer accommodation.
Distributed systems introduce network failures where requests may be sent multiple times before succeeding. Without idempotency, duplicate consumption requests could double-charge customers or create inconsistent ledgers. The prepaid credit API must implement request idempotency using unique keys that identify individual consumption events.
The idempotency pattern requires storing a mapping of idempotency keys to response objects. When a request arrives with a key that already exists in the store, the system returns the cached response without processing the consumption again. This approach requires careful implementation to handle concurrent requests with identical idempotency keys—the system should serialize requests with the same key to ensure only one processes successfully.
For distributed consistency, the prepaid system should employ optimistic locking on wallet balances. This pattern stores a version number with each wallet; when updating the balance, the system checks that the version hasn't changed since the read operation. If the version has changed, indicating concurrent modification, the update is rejected and retried. This approach prevents lost updates in high-concurrency scenarios.
Prepaid credit consumption must integrate seamlessly with usage tracking and billing systems. Modern billing platforms track consumption events through APIs or webhooks, then aggregate those events into billing line items. The prepaid system must consume credits in real time or near-real-time as usage occurs, then coordinate with the billing system to ensure accurate invoicing.
A recommended architecture publishes consumption events to a message queue immediately after credit decrement. This asynchronous pattern allows the prepaid system to complete the credit decrement quickly while decoupled services consume these events for billing, revenue recognition, and analytics. The message queue provides durability guarantees, ensuring no consumption events are lost even during system outages.
For billing workflow integration, the prepaid system should expose a "prepaid credit applied" line item that invoices include, showing the customer which portion of their bill was covered by prepaid credits. This transparency helps customers track credit depletion and understand their billing costs. The billing system should query the prepaid API at invoice generation time to determine how many credits were consumed during the billing period.
Building a custom prepaid credit system requires significant engineering investment across wallet management, expiration handling, consumption tracking, and reporting. Many organizations choose to leverage API-first billing platforms that provide prepaid functionality out of the box, reducing time-to-market and maintenance burden.
Open-source billing platforms like Lago offer prepaid credits and wallet functionality as core features, with built-in APIs for credit allocation, consumption, and balance management. These platforms handle expiration processing, provide webhook notifications for balance changes, and maintain immutable audit trails automatically. Using an established platform eliminates the need to build expiration engines, idempotency layers, and reconciliation systems from scratch, allowing teams to focus on business logic rather than billing infrastructure.
Lago's API-first architecture enables seamless integration with custom applications through REST endpoints, making it straightforward to issue credits, track consumption, and query balances programmatically. The platform supports prepaid credits with automatic top-up rules, allowing customers to configure conditions that trigger automatic credit replenishment when balances fall below specified thresholds. This flexibility supports diverse customer preferences—some prefer manual credit management while others benefit from automated replenishment.
Real-world operations require mechanisms to refund unused credits and adjust balances for disputes or corrections. The refund process should verify that credits have not been fully consumed, calculate pro-rata refunds based on usage, and determine tax implications of refunds. Some jurisdictions treat refunds as negative income requiring reversal of previously recognized revenue, while others treat refunds as separate expense items.
A robust adjustment workflow requires explicit authorization checks to prevent unauthorized balance modifications. Adjustments should require supervisor approval for amounts above specified thresholds, with all adjustments logged with operator ID and timestamp. The API should enforce separation of duties by preventing billing team members from approving their own adjustment requests, implementing a two-person rule for high-value modifications.
Manual adjustment endpoints should accept optional reason codes from a predefined list (service failure, courtesy credit, billing dispute resolution, promotional offer, etc.) and require supporting documentation references. This structured approach enables reporting on adjustment patterns, identifying systemic issues requiring product changes or service improvements. Adjustment history should be queryable through the API for reconciliation and audit purposes.
Prepaid systems handle customer funds, making observability and monitoring critical for operational health. Key metrics to track include total wallet balance across all customers, daily credit consumption volume, pending expiration quantities, failed consumption requests, and reconciliation discrepancies. Dashboard visualization should include time-series charts of consumption patterns, cohort analysis of credit usage by customer segment, and alert thresholds for anomalies.
The consumption API should emit latency percentiles (p50, p95, p99) to detect degradation as volume grows. Balance query latency is particularly sensitive since these queries occur in customer-facing request paths; any degradation directly impacts user experience. Structured logging should include consumption request parameters, balance before and after, and execution duration for post-hoc analysis.
Alerting rules should detect insufficient wallet balance conditions, expiration processing failures, and discrepancies between expected and actual balances. Discrepancies may indicate bugs, data corruption, or unauthorized modifications. Automated reconciliation processes should compare wallet balances to transaction logs hourly, flagging accounts where the sum of transactions doesn't equal the current balance. These alerts should route to engineering teams immediately since they indicate data integrity issues.
Prepaid credit systems handle customer financial assets, requiring security practices equivalent to payment processors. The API should authenticate all requests using API keys or OAuth 2.0 tokens, never relying on IP allowlisting as the sole security mechanism. Authentication credentials should be rotated regularly, with old credentials revoked on a defined schedule.
Authorization should implement role-based access control (RBAC) where different API users have different permissions. A billing analyst might have read-only access to all wallets but not permission to create credits or process refunds. A customer service representative might have permission to process refunds for customers they support but not to modify other teams' customers. These granular permissions prevent lateral movement if one credential is compromised.
All modifications to wallet state should be logged with sufficient detail for audit trails. Logs should record which user made the change, when it occurred, what the change was, and the before/after state. These audit logs should be immutable—stored in append-only format to prevent retroactive modification. Regulatory compliance regimes like SOX and GDPR often require preserving audit trails for 7+ years.
Data in transit should use TLS 1.3 encryption to prevent credential theft or balance manipulation via network interception. Data at rest should be encrypted using AES-256, with encryption keys managed separately from application code. Database backups should be encrypted and tested regularly to ensure recovery capability in disaster scenarios.
Prepaid credit consumption depends on accurate usage tracking. Before implementing prepaid credits, ensure your usage tracking system reliably captures all billable events. Duplicate events should be deduplicated to prevent overcounting, and missing events should be detectable through reconciliation.
For detailed guidance on usage tracking implementation, see Usage Tracking Implementation: SDKs, Server-Side vs Client-Side, which covers optimal patterns for capturing usage events across different architectures. Understanding these patterns ensures your consumption API receives accurate data for credit decrement.
The integration between usage tracking and prepaid consumption requires careful timing. If consumption is processed real-time as events arrive, customers must maintain sufficient balance at all times. If consumption is processed asynchronously at billing time, customers might exceed their balance during a billing period. Most implementations use a hybrid approach: allocate reserved amounts when usage events occur, then confirm the deduction at billing time when exact volumes are known.
The broader billing API architecture should clearly separate concerns between wallet management (prepaid credits) and invoice generation (postpaid billing). Both subsystems must communicate to determine which portion of a bill is covered by prepaid credits versus new charges. This separation enables teams to evolve each system independently while maintaining clear contracts between components.
For comprehensive guidance on billing API design patterns, see Billing API Design: REST, GraphQL, Webhooks, SDK Best Practices, which covers general API design principles applicable to all billing subsystems including prepaid functionality.
The invoice generation process should query the prepaid system to determine available credit at invoice time, then apply the minimum of (available credit, invoice amount) to cover the bill. This approach ensures customers are never charged when credit is available. Any remaining invoice balance after credit application creates an amount due that the billing system processes through standard collection methods.
Effective prepaid credit management requires visibility into credit flows across the organization. Reporting endpoints should expose aggregated metrics: total credits purchased by period, total credits consumed, total credits expired, and total credits refunded. These metrics enable finance teams to forecast revenue and ensure accounting entries are correct.
Customer-specific reporting should show each customer's credit history: purchase dates and amounts, consumption timeline, expirations, and refunds. This granular reporting helps support teams quickly understand why a customer's balance is at its current level and identify any discrepancies. The reporting API should support date range filters, customer filtering, and export to CSV for integration with accounting systems.
Reconciliation processes should compare prepaid system records with source-of-truth systems. For example, credits issued should match purchase order confirmations and payment receipts. Credits consumed should match usage event logs. Credits expired should match expiration policy configurations. Discrepancies should generate alerts for investigation. Monthly reconciliation reports should be generated automatically and reviewed by finance teams.
Global SaaS platforms must support prepaid credits in multiple currencies. The simplest approach maintains separate wallets by currency, avoiding currency conversion complexity. A customer with both USD and EUR customers would have two wallets, each tracking credit in the respective currency.
For customers operating in multiple regions with different tax regimes, prepaid credits raise compliance questions. Some regions require that unused prepaid credits be refundable, while others allow non-refundable prepaid amounts above specified thresholds. The API should support regional configuration rules that enforce local requirements automatically, including region-specific refund eligibility and expiration policies.
When customers prepay in one currency but consume in another, exchange rate volatility creates friction. The system should record the exchange rate at purchase time and apply that same rate at consumption time, protecting customers from adverse rate movements on their prepaid balance. This approach creates a clear contractual understanding: the prepaid amount is denominated in the purchase currency and converted at historical rates, not current rates.
As transaction volume grows, the prepaid system must scale from handling hundreds of daily transactions to millions. The wallet balance query is the most latency-sensitive operation since it occurs in customer-facing request paths. At scale, querying a central database becomes a bottleneck.
Caching strategies should cache wallet balances with controlled cache invalidation. After each consumption or credit operation, invalidate the cache for that specific wallet. Read-heavy workloads can use eventual consistency, where balance caches expire after a short TTL (10-30 seconds) and refresh on the next query. This approach sacrifices real-time consistency for sub-millisecond balance queries.
For writes, distributing the prepaid system across multiple database shards by customer ID allows parallel processing. Each shard maintains a subset of customers' wallets, reducing contention. The consumption API routes requests to the appropriate shard based on customer ID. This sharding strategy enables the system to scale linearly as transaction volume grows, assuming transactions distribute evenly across customers.
Most organizations implementing prepaid credits operate existing billing infrastructure. Lago API Integration: Usage Tracking, Invoice Generation, and Webhooks provides detailed patterns for integrating billing systems, including how to coordinate prepaid credit consumption with invoice generation workflows.
Legacy billing systems may require significant modification to support prepaid credits. Consider whether to integrate prepaid functionality directly into the legacy system or build a separate service that the billing system calls during invoice generation. Separate services provide better maintainability but introduce additional network calls. Direct integration is faster but couples the billing and prepaid systems more tightly, increasing maintenance complexity.
The integration point should handle cases where customers have prepaid credits but insufficient balance for the full invoice amount. The system should clearly communicate which portion of the bill was covered by prepaid credits (with depreciation notation) and which portion requires new payment. Customers should see an itemized breakdown showing credit application, remaining balance, and amount due.
Prepaid systems handle customer financial assets, requiring comprehensive testing before production deployment. Unit tests should cover idempotency logic, expiration calculations, and balance validation. Integration tests should exercise the full flow from credit purchase through consumption to expiration, verifying that balances remain consistent throughout.
Load testing should simulate realistic transaction patterns with multiple concurrent operations on the same wallet. These tests verify that the system maintains consistency under contention and that latency remains acceptable at scale. Chaos engineering tests should fail various components (database unavailability, slow API responses, network timeouts) and verify that the system recovers gracefully.
Reconciliation testing should compare the final state of wallet balances to the transaction log, ensuring the sum of all transactions equals the current balance for every wallet. This test should run against production data periodically to catch any data corruption that might occur due to bugs or infrastructure failures.
Prepaid credit systems are complex infrastructure requiring careful attention to consistency, security, and compliance. Building from scratch involves implementing wallet ledgers, consumption tracking, idempotency layers, and expiration engines—substantial engineering effort for functionality that many modern billing platforms already provide.
Organizations should carefully evaluate the build-versus-buy decision. Building custom prepaid functionality enables unlimited customization but requires ongoing maintenance as business needs evolve and regulatory requirements change. Adopting established billing platforms reduces initial implementation time and provides access to battle-tested patterns while potentially trading off some customization flexibility.
Regardless of the approach, the fundamental design principles remain consistent: maintain clear audit trails of all credit movements, implement strong consistency guarantees for balance operations, design APIs that are idempotent and fail-safe, monitor the system obsessively, and integrate tightly with usage tracking and billing systems. Following these principles creates a prepaid system that scales with your business while providing customers a flexible consumption model they expect in modern SaaS platforms.
Content