This guide shows how to create a complete billing flow programmatically: define pricing, create a plan, embed checkout, and route billable traffic per customer.
MCP flow
If your agent is already using the Lava MCP server, the end-to-end billing flow is:
lava_login
lava_create_meter
lava_create_plan
lava_create_checkout_session
lava_generate_forward_token
lava_chat_completions or lava_forward
Use the SDK code below only if you are implementing the same flow directly in your application code.
SDK flow
Overview
The billing flow has four parts:
- Meter - Defines how usage is priced (e.g., $3 per million tokens)
- Plan - A subscription that includes an optional 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 ties together an optional recurring price, included credits, and one or more meters (product features included in your plan):
const plan = await lava.subscriptions.createPlan({
name: 'Pro Plan',
period_amount: '49.00',
billing_interval: 'month',
included_credit: '25.00',
meter_ids: [meter.meter_id] // Link meters to this plan
});
console.log('Plan ID:', plan.plan_id);
The meter_ids field links meters to the plan. 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.createPlan({
name: 'Pro Plan',
period_amount: '99.00',
billing_interval: 'month',
included_credit: '50.00',
rollover_type: 'full',
meter_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',
plan_id: plan.plan_id
});
// Return session.checkout_session_token to your frontend
Frontend: Open checkout
import { useLavaCheckout } from '@lavapayments/checkout';
const { open } = useLavaCheckout({
onSuccess: ({ customerId }) => {
// Save customerId — 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 customerId.
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 completed checkout, generate forward tokens scoped to their customer ID and meter. Every request made with this token is tracked and billed against their subscription:
// Get the customer (from your database, or list them)
const { data: customers } = await lava.customers.list();
const customer = customers[0];
// Generate a forward token for this customer
const forwardToken = lava.generateForwardToken({
customer_id: customer.customer_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],
customer_id: customer.customer_id,
});
console.log('Customer requests:', usage.totals.total_requests);
console.log('Customer cost:', usage.totals.total_cost);
// Check their subscription status
const sub = await lava.customers.getSubscription(customer.customer_id);
if (sub.subscription) {
console.log('Plan:', sub.subscription.plan.name);
console.log('Credits remaining:', sub.subscription.credits.total_remaining);
}
What’s Next?