This guide shows how to create a complete billing flow programmatically: define pricing, create a plan, embed checkout, and route billable traffic per customer.
Overview
The billing flow has four parts:
- Meter - Defines how usage is priced (e.g., $3 per million tokens)
- Plan - A subscription that includes a monthly fee, credit allowance, and linked meters
- Checkout - An embedded flow where your customer subscribes and pays
- Forward tokens - Per-customer auth tokens that route traffic through the gateway with billing
Step 1: Create a Meter
A meter defines the pricing rules for a type of usage. For example, charging per token for AI model calls:
import { Lava } from '@lavapayments/nodejs';
const lava = new Lava(); // reads LAVA_SECRET_KEY from env
const meter = await lava.meters.create({
name: 'AI Chat Tokens',
rate_type: 'fixed',
tier_type: 'tokens_1m',
tiers: [
{ start: '0', rate: '3.00' } // $3.00 per million tokens
]
});
console.log('Meter ID:', meter.meter_id);
console.log('Meter slug:', meter.meter_slug);
Meter options
| Parameter | Options | Description |
|---|
rate_type | fixed, percentage | Fixed price per unit, or percentage markup on base cost |
tier_type | tokens_1m, characters_1m, minutes, requests | What unit is being measured |
You can also add volume-based tiers for graduated pricing:
const meter = await lava.meters.create({
name: 'API Requests',
rate_type: 'fixed',
tier_type: 'requests',
tiers: [
{ start: '0', rate: '0.01' }, // $0.01/request for first 10k
{ start: '10000', rate: '0.005' } // $0.005/request after 10k
]
});
Step 2: Create a Plan
A plan is a subscription configuration that ties together a monthly price, included credit, and one or more meters:
const plan = await lava.subscriptions.createConfig({
name: 'Pro Plan',
period_amount: '49.00',
billing_interval: 'month',
included_credit: '25.00',
product_ids: [meter.meter_id] // Link meters to this plan
});
console.log('Plan ID:', plan.subscription_config_id);
The product_ids field links meters to the plan. Despite the name (a legacy holdover), these are meter IDs. You can link multiple meters to a single plan.
Plan options
| Parameter | Options | Description |
|---|
billing_interval | day, week, month, year | How often the subscription renews |
rollover_type | full, none | Whether unused included credit carries over |
included_credit | Any decimal string | Credit included with each billing cycle |
credit_bundles | Array of bundles | Optional add-on credit packs customers can purchase |
Example with credit bundles:
const plan = await lava.subscriptions.createConfig({
name: 'Pro Plan',
period_amount: '99.00',
billing_interval: 'month',
included_credit: '50.00',
rollover_type: 'full',
product_ids: [meterA.meter_id, meterB.meter_id],
credit_bundles: [
{ name: '$25 Top-up', cost: '25.00', credit_amount: '25.00' },
{ name: '$100 Top-up', cost: '100.00', credit_amount: '100.00' }
]
});
Step 3: Embed Checkout
Checkout is a two-step flow: your backend creates a session, your frontend opens it with the @lavapayments/checkout SDK.
npm install @lavapayments/checkout
Backend: Create a session
const session = await lava.checkoutSessions.create({
checkout_mode: 'subscription',
origin_url: 'https://yourapp.com',
subscription_config_id: plan.subscription_config_id
});
// Return session.checkout_session_token to your frontend
Frontend: Open checkout
import { useLavaCheckout } from '@lavapayments/checkout';
const { open } = useLavaCheckout({
onSuccess: ({ connectionId }) => {
// Save connectionId — this is the billing relationship with your customer
}
});
// Call with the token from your backend
open(checkoutSessionToken);
The checkout overlay handles phone verification, payment method collection, and subscription creation. When complete, onSuccess fires with the connectionId.
Checkout sessions expire after 60 minutes. Create a new session for each checkout attempt.
The checkout_session_token is an opaque token — do not try to construct URLs from it. Always use the SDK to open checkout.
Step 4: Route Billable Traffic
Once a customer has a connection, generate forward tokens scoped to their connection and meter. Every request made with this token is tracked and billed against their subscription:
// Get the connection (from your database, or list them)
const { data: connections } = await lava.connections.list();
const connection = connections[0];
// Generate a forward token for this customer
const forwardToken = lava.generateForwardToken({
connection_id: connection.connection_id,
meter_slug: meter.meter_slug
});
// Use it exactly like in the Route Traffic guide
const response = await fetch(lava.providers.openai + '/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${forwardToken}`
},
body: JSON.stringify({
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: 'Hello!' }]
})
});
Step 5: Monitor Customer Usage
Track usage and costs per customer:
// Get usage for a specific customer
const usage = await lava.usage.retrieve({
start: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
connection_id: connection.connection_id
});
console.log('Customer requests:', usage.totals.total_requests);
console.log('Customer cost:', usage.totals.total_request_cost);
// Check their subscription status
const sub = await lava.connections.getSubscription(connection.connection_id);
if (sub.subscription) {
console.log('Plan:', sub.subscription.plan.name);
console.log('Credits remaining:', sub.subscription.credits.total_remaining);
}
What’s Next?