Skip to main content
This guide shows how to create a complete billing flow programmatically: define pricing, create a plan, embed checkout, and route billable traffic per customer.
Prerequisites: Complete Sign Up & Authenticate and Route Traffic first.

Overview

The billing flow has four parts:
  1. Meter - Defines how usage is priced (e.g., $3 per million tokens)
  2. Plan - A subscription that includes a monthly fee, credit allowance, and linked meters
  3. Checkout - An embedded flow where your customer subscribes and pays
  4. 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

ParameterOptionsDescription
rate_typefixed, percentageFixed price per unit, or percentage markup on base cost
tier_typetokens_1m, characters_1m, minutes, requestsWhat 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

ParameterOptionsDescription
billing_intervalday, week, month, yearHow often the subscription renews
rollover_typefull, noneWhether unused included credit carries over
included_creditAny decimal stringCredit included with each billing cycle
credit_bundlesArray of bundlesOptional 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?