Skip to main content

Overview

The Subscriptions resource handles recurring billing for your customers. It consists of two related concepts:
  1. Subscription Configurations - Your plan templates (e.g., “Basic 10/month","Premium10/month", "Premium 25/month”)
  2. Active Subscriptions - Customer enrollments in those plans
Create subscription configurations once, then customers can subscribe through checkout sessions.

Subscription Configurations

listConfigs()

List all subscription plan templates for your merchant account. Signature
listConfigs(): Promise<RestSubscriptionConfig[]>
Returns
Array<{
  subscription_config_id: string;
  merchant_id: string;
  name: string; // Plan name (e.g., "Premium Plan")
  period_amount: string; // USD amount per billing period
  rollover_type: 'full' | 'none'; // Whether unused credits roll over
  created_at: string;
}>
Example
const configs = await lava.subscriptions.listConfigs();

for (const config of configs) {
  console.log(`${config.name}: $${config.period_amount}/month`);
  console.log(`Rollover: ${config.rollover_type}`);
}

createConfig()

Create a new subscription plan template. Signature
createConfig(params: CreateSubscriptionConfigParams): Promise<RestSubscriptionConfig>
Parameters
NameTypeRequiredDescription
namestringYesDisplay name for the plan
period_amountstringYesUSD amount per billing period (decimal string)
rollover_type'full' | 'none'YesWhether unused credits carry over to next period
Returns Single RestSubscriptionConfig object. Example: Basic Plan
const basicPlan = await lava.subscriptions.createConfig({
  name: 'Basic Plan',
  period_amount: '10.00',
  rollover_type: 'none'
});

console.log('Created plan:', basicPlan.subscription_config_id);
Example: Premium Plan with Rollover
const premiumPlan = await lava.subscriptions.createConfig({
  name: 'Premium Plan',
  period_amount: '25.00',
  rollover_type: 'full' // Unused credits roll over
});

console.log('Created plan:', premiumPlan.subscription_config_id);

updateConfig()

Update an existing subscription plan. Signature
updateConfig(
  id: string,
  params: UpdateSubscriptionConfigParams
): Promise<RestSubscriptionConfig>
Parameters
NameTypeRequiredDescription
idstringYesSubscription config ID to update
namestringNoNew display name
period_amountstringNoNew monthly amount
rollover_type'full' | 'none'NoNew rollover policy
Returns Updated RestSubscriptionConfig object. Example
const updated = await lava.subscriptions.updateConfig('subconf_abc123', {
  period_amount: '15.00' // Price increase
});

console.log('Updated plan:', updated.name);
Updating a subscription config affects all future billing cycles for existing subscribers. Past billing cycles are not affected.

deleteConfig()

Delete a subscription plan template. Signature
deleteConfig(id: string): Promise<{ success: boolean }>
Parameters
NameTypeRequiredDescription
idstringYesSubscription config ID to delete
Returns
{
  success: boolean;
}
Example
const result = await lava.subscriptions.deleteConfig('subconf_old_plan');

if (result.success) {
  console.log('Plan deleted successfully');
}
You cannot delete a subscription config that has active subscribers. Cancel all active subscriptions first.

Active Subscriptions

listActiveSubscriptions()

List all customers currently subscribed to your plans. Signature
listActiveSubscriptions(): Promise<RestActiveSubscription[]>
Returns
Array<{
  active_subscription_id: string;
  wallet_id: string;
  subscription_config_id: string;
  started_at: string; // ISO 8601 timestamp
  billing_anchor: number; // Day of month (1-31) for billing
  cancelled_at?: string;
  status: 'active' | 'cancelled';
  created_at: string;
  subscription_config: RestSubscriptionConfig; // Nested plan details
  wallet: {
    wallet_id: string;
    phone: string;
    email?: string;
    first_name?: string;
    last_name?: string;
    active_balance: string;
    created_at: string;
  };
}>
Example: List All Subscribers
const subscriptions = await lava.subscriptions.listActiveSubscriptions();

console.log(`Total subscribers: ${subscriptions.length}`);

for (const sub of subscriptions) {
  console.log(`${sub.wallet.email}: ${sub.subscription_config.name}`);
  console.log(`  Billing on day ${sub.billing_anchor} of each month`);
  console.log(`  Status: ${sub.status}`);
}
Example: Filter by Plan
const subs = await lava.subscriptions.listActiveSubscriptions();

const premiumSubs = subs.filter(
  sub => sub.subscription_config.name === 'Premium Plan'
);

console.log(`Premium subscribers: ${premiumSubs.length}`);
Example: Find Upcoming Renewals
const subs = await lava.subscriptions.listActiveSubscriptions();

const today = new Date().getDate();
const upcomingRenewals = subs.filter(sub =>
  sub.billing_anchor >= today && sub.billing_anchor <= today + 7
);

console.log(`Renewals in next 7 days: ${upcomingRenewals.length}`);

cancelActiveSubscription()

Cancel a customer’s subscription. They will not be charged again. Signature
cancelActiveSubscription(id: string): Promise<{ success: boolean }>
Parameters
NameTypeRequiredDescription
idstringYesActive subscription ID to cancel
Returns
{
  success: boolean;
}
Example
const result = await lava.subscriptions.cancelActiveSubscription('sub_abc123');

if (result.success) {
  console.log('Subscription cancelled');
  // Customer will not be charged again
  // Existing balance is preserved
}
Cancelling a subscription does not refund the current period. The customer retains their wallet balance and can continue using credits until depleted.

Common Use Cases

Setting Up Subscription Tiers

Create multiple plan levels:
async function setupSubscriptionTiers() {
  const tiers = [
    { name: 'Starter', amount: '5.00', rollover: 'none' },
    { name: 'Pro', amount: '15.00', rollover: 'full' },
    { name: 'Enterprise', amount: '50.00', rollover: 'full' }
  ];

  const configs = await Promise.all(
    tiers.map(tier =>
      lava.subscriptions.createConfig({
        name: tier.name,
        period_amount: tier.amount,
        rollover_type: tier.rollover as 'full' | 'none'
      })
    )
  );

  return configs.map(config => ({
    id: config.subscription_config_id,
    name: config.name
  }));
}

// Store config IDs in environment variables
const tiers = await setupSubscriptionTiers();
console.log('STARTER_PLAN_ID=' + tiers[0].id);
console.log('PRO_PLAN_ID=' + tiers[1].id);
console.log('ENTERPRISE_PLAN_ID=' + tiers[2].id);

Customer Subscription Flow

Integrate subscriptions with checkout:
// 1. Customer selects a plan on your frontend
app.post('/api/subscribe/:tier', async (req, res) => {
  const tier = req.params.tier; // 'starter', 'pro', 'enterprise'

  // Map tier to subscription config ID
  const configId = {
    starter: process.env.STARTER_PLAN_ID,
    pro: process.env.PRO_PLAN_ID,
    enterprise: process.env.ENTERPRISE_PLAN_ID
  }[tier];

  if (!configId) {
    return res.status(400).json({ error: 'Invalid tier' });
  }

  // 2. Create subscription checkout session
  const session = await lava.checkoutSessions.create({
    checkout_mode: 'subscription',
    origin_url: 'https://yourapp.com',
    subscription_config_id: configId,
    reference_id: req.user.id
  });

  // 3. Return checkout token to frontend
  res.json({
    checkoutToken: session.checkout_session_token
  });
});

// 4. After checkout completes, subscription is automatically created
// Listen for webhook: subscription.created
app.post('/webhooks/lava/subscription-created', async (req, res) => {
  const { active_subscription_id, wallet_id, subscription_config_id } = req.body;

  console.log('New subscription:', active_subscription_id);

  // Update your database
  await database.subscriptions.create({
    userId: req.body.reference_id,
    lavaSubscriptionId: active_subscription_id,
    tier: getTierFromConfigId(subscription_config_id)
  });

  res.json({ received: true });
});

Subscription Management Dashboard

Display subscriber analytics:
async function getSubscriptionMetrics() {
  const subs = await lava.subscriptions.listActiveSubscriptions();

  const byPlan = subs.reduce((acc, sub) => {
    const planName = sub.subscription_config.name;
    if (!acc[planName]) {
      acc[planName] = {
        count: 0,
        mrr: 0, // Monthly Recurring Revenue
        customers: []
      };
    }

    acc[planName].count++;
    acc[planName].mrr += parseFloat(sub.subscription_config.period_amount);
    acc[planName].customers.push({
      email: sub.wallet.email,
      startedAt: sub.started_at
    });

    return acc;
  }, {} as Record<string, any>);

  const totalMRR = Object.values(byPlan).reduce(
    (sum: number, plan: any) => sum + plan.mrr,
    0
  );

  return {
    totalSubscribers: subs.length,
    totalMRR,
    byPlan,
    activeCount: subs.filter(s => s.status === 'active').length,
    cancelledCount: subs.filter(s => s.status === 'cancelled').length
  };
}

// Usage
const metrics = await getSubscriptionMetrics();
console.log('Total MRR:', metrics.totalMRR);
console.log('Subscribers by plan:', metrics.byPlan);

Automatic Cancellation for Unpaid Invoices

Cancel subscriptions with failed payments:
// Webhook handler for failed subscription charge
app.post('/webhooks/lava/subscription-charge-failed', async (req, res) => {
  const { active_subscription_id, wallet_id } = req.body;

  console.log('Subscription charge failed:', active_subscription_id);

  // Give customer 3 days to update payment method
  setTimeout(async () => {
    // Check if payment was made
    const wallet = await lava.connections.list({ /* filter by wallet_id */ });

    if (wallet.data[0]?.wallet.balance < '5.00') {
      // Still unpaid, cancel subscription
      await lava.subscriptions.cancelActiveSubscription(active_subscription_id);

      // Notify customer
      await sendEmail({
        to: wallet.data[0].wallet.email,
        subject: 'Subscription Cancelled',
        body: 'Your subscription was cancelled due to insufficient funds.'
      });
    }
  }, 3 * 24 * 60 * 60 * 1000); // 3 days

  res.json({ received: true });
});

Plan Upgrade/Downgrade

Handle tier changes:
async function changeSubscriptionPlan(
  subscriptionId: string,
  newConfigId: string
) {
  // 1. Cancel existing subscription
  await lava.subscriptions.cancelActiveSubscription(subscriptionId);

  // 2. Create new checkout session for new plan
  // (Customer will need to complete checkout for new plan)
  const session = await lava.checkoutSessions.create({
    checkout_mode: 'subscription',
    origin_url: 'https://yourapp.com',
    subscription_config_id: newConfigId
  });

  return session.checkout_session_token;
}
Plan changes require a new checkout session. There is no built-in upgrade/downgrade flow - you’ll need to cancel the old subscription and have the customer subscribe to the new plan.

Churn Analysis

Track subscription cancellations:
async function analyzeChurn(days: number = 30) {
  const subs = await lava.subscriptions.listActiveSubscriptions();

  const cutoffDate = new Date();
  cutoffDate.setDate(cutoffDate.getDate() - days);

  const recentlyCancelled = subs.filter(sub =>
    sub.cancelled_at &&
    new Date(sub.cancelled_at) > cutoffDate
  );

  const activeAtStart = subs.filter(sub =>
    new Date(sub.started_at) < cutoffDate
  ).length;

  const churnRate = recentlyCancelled.length / activeAtStart;

  return {
    cancelledCount: recentlyCancelled.length,
    activeCount: subs.filter(s => s.status === 'active').length,
    churnRate: churnRate * 100, // Percentage
    cancelledByPlan: recentlyCancelled.reduce((acc, sub) => {
      const plan = sub.subscription_config.name;
      acc[plan] = (acc[plan] || 0) + 1;
      return acc;
    }, {} as Record<string, number>)
  };
}

Rollover Behavior

Full Rollover (rollover_type: 'full')

Unused credits carry over to the next billing period:
Month 1: Customer receives $25 credit, uses $15
  → Balance: $10 remaining

Month 2: Customer receives $25 + $10 rollover = $35 credit
Best for: Higher-tier plans, enterprise customers

No Rollover (rollover_type: 'none')

Unused credits are forfeited at the end of each period:
Month 1: Customer receives $10 credit, uses $6
  → Balance: $4 remaining (forfeited at period end)

Month 2: Customer receives $10 credit (fresh start)
Best for: Entry-level plans, predictable monthly costs