lomi.

Subscriptions

Manage recurring customer subscriptions.

The Subscriptions API lists and retrieves subscription instances (a customer on a recurring plan). These are typically created when customers buy recurring products through Checkout sessions or Payment links.

Subscription detail in lomi. dashboard

SDKs expose /subscriptions helpers for listing, retrieving, updating, and canceling subscriptions. For route-specific examples and status transitions, see Subscriptions. Immutable fields and cancel-only rules follow the OpenAPI descriptions for each operation.

For signup flows (trials, first payment types, checkout session amounts), see Checkout behavior: Subscription checkout.

How subscriptions are created

Subscription instances are created when a customer buys a recurring product through:

There is no standalone "create subscription" API for signup, the checkout or link flow creates the instance after the first successful payment (or trial rules on the product).

Monitor webhook events to track payment confirmations for each billing cycle. Listen for SUBSCRIPTION_RENEWED and renewal-related PAYMENT_FAILED events, see Checkout behavior: Subscription checkout.

Renewals and failed payments

lomi. uses two renewal paths, depending on how the customer pays:

Card subscriptions (automatic)

When signup saved a card payment method (provider_customer_id + provider_payment_method_id), renewals run off-session on next_billing_date. A daily job charges the saved card and records the renewal transaction.

  • Webhook: SUBSCRIPTION_RENEWED on success
  • Webhook: PAYMENT_FAILED on renewal failure (with processor retry/dunning where configured)

Wave / MTN subscriptions (manual renewal checkout)

Mobile money does not expose a saved off-session token like cards. When no card payment method exists, lomi. does not auto-debit the wallet. Instead:

  1. Notification crons email or message a hosted renewal checkout link before next_billing_date.
  2. The customer pays on that link; webhooks confirm the cycle.
  3. If the customer does not pay in time, overdue processing may set the subscription to past_due, paused, or cancelled per the product's failed_payment_action.

This matches how async mobile-money rails work in production (PIN approval, no standing mandate). It is intentional, not a missing card feature.

Support shortcut: "Why didn't my Wave subscription auto-charge?" → Mobile money renewals require the customer to open the renewal checkout link and approve payment. Cards renew automatically when a payment method was saved at signup.

Your integration should:

  1. Listen for SUBSCRIPTION_RENEWED: successful billing cycle; extend access or send a receipt.
  2. Listen for PAYMENT_FAILED on renewal transactions, customer may need to pay the renewal checkout link or update a card in the customer portal.
  3. Poll or list subscriptions via GET /subscriptions (optionally with customer_id or status filters) when webhooks are delayed; use status and next_billing_date from the API response only.

Cancel or pause via POST /subscriptions/{id}/cancel or PATCH /subscriptions/{id} as documented in the Subscriptions API.

For metered / usage-based products (API calls, credits, seats), see Usage billing-usage subscriptions and meters are separate from recurring checkout subscriptions.

List subscriptions

API reference: List subscriptions, supports page and pageSize.

import { LomiSDK } from '@lomi./sdk';

const lomi = new LomiSDK({
  apiKey: process.env.LOMI_SECRET_KEY!,
  environment: 'live',
});

const subscriptions = await lomi.subscriptions.list({
  page: 1,
  pageSize: 20,
});

subscriptions.forEach(sub => {
  console.log(`${sub.id}: ${sub.status} - ${sub.amount} ${sub.currency_code}`);
});
from lomi import LomiClient
import os

client = LomiClient(
    api_key=os.environ["LOMI_SECRET_KEY"],
    environment="test"
)

subscriptions = client.subscriptions.list(page=1, pageSize=20)

for sub in subscriptions:
    print(f"{sub['id']}: {sub['status']}")
curl -X GET "https://api.lomi.africa/subscriptions?page=1&pageSize=20" \
  -H "X-API-KEY: $LOMI_SECRET_KEY"

Get subscriptions for a customer

API reference: Subscriptions by customer

const customerSubs = await lomi.subscriptions.findByCustomer('cus_abc123...');
customer_subs = client.subscriptions.get_by_customer('cus_abc123...')
curl -X GET "https://api.lomi.africa/subscriptions/customer/cus_abc123..." \
  -H "X-API-KEY: $LOMI_SECRET_KEY"

Get a subscription

API reference: Get subscription

const subscription = await lomi.subscriptions.get('sub_abc123...');
console.log(`Status: ${subscription.status}`);
console.log(`Next billing: ${subscription.current_period_end}`);
subscription = client.subscriptions.get('sub_abc123...')
print(f"Status: {subscription['status']}")
curl -X GET "https://api.lomi.africa/subscriptions/sub_abc123..." \
  -H "X-API-KEY: $LOMI_SECRET_KEY"

Subscription lifecycle

Subscriptions are created when a customer completes checkout for a recurring product (hosted checkout, payment link, storefront subscribe flow, or API session with product_type: recurring). Typical status flow:

StatusMeaning
pendingCreated but not yet active (rare at signup).
trialFree trial in progress; no charge until trial ends.
activePaid and renewing on schedule.
past_dueA renewal payment failed; retries or merchant action may apply.
pausedBilling paused (e.g. after failed payment when product is configured to pause).
cancelledCancelled by merchant or customer.
expiredFixed-term subscription ended or trial converted without payment method.

First payment behavior is controlled on the product via first_payment_type:

ValueFirst checkout charge
initialFull recurring price (or prorated amount if combined with proration rules).
non_initial$0 at signup; first charge on the first billing date.
proratedPartial amount for the remainder of the current billing period.

Trials (trial_enabled + trial_period_days): signup charge is $0. the card processor saves the card for later billing; mobile money can complete signup without an immediate charge. After the trial, status moves to active and billing begins on next_billing_date.

Renewals run automatically for saved card payment methods. If no method is on file (common for mobile money), lomi. may fall back to a manual renewal checkout link for the customer.


Merchant-owned billing UI

If customers manage subscriptions in your app (not lomi. customer portal):

  1. Call lomi. APIs when they cancel, pause, upgrade, or resume (POST /subscriptions/{id}/cancel, PATCH /subscriptions/{id}, etc.).
  2. Listen for SUBSCRIPTION_UPDATED and SUBSCRIPTION_CANCELLED webhooks (same events whether the change came from your API, the portal, or cron).
  3. Reconcile entitlements with GET /subscriptions/{id} when you need authoritative state.
  4. For failed renewals, use the customer portal retry payment headless endpoint or redirect customers to the hosted portal billing/subscriptions pages.

lomi. is the billing source of truth; your app should not cancel only in your database without calling the API.


Cancel a subscription

API reference: Cancel subscription

Pass cancel_at_period_end: true to cancel at the end of the billing period; omit or set false to cancel immediately. Optional cancellation_reason for your records.

// Cancel immediately
const cancelled = await lomi.subscriptions.cancel('sub_abc123...');

// Cancel at period end
const scheduledCancel = await lomi.subscriptions.cancel('sub_abc123...', {
  cancel_at_period_end: true,
  cancellation_reason: 'Customer requested cancellation',
});

console.log(`Subscription will cancel at: ${scheduledCancel.cancel_at}`);
# Cancel immediately
cancelled = client.subscriptions.cancel('sub_abc123...')

# Cancel at period end
scheduled_cancel = client.subscriptions.cancel('sub_abc123...', {
    "cancel_at_period_end": True,
    "cancellation_reason": "Customer requested cancellation"
})
curl -X POST "https://api.lomi.africa/subscriptions/sub_abc123.../cancel" \
  -H "X-API-KEY: $LOMI_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "cancel_at_period_end": true,
    "cancellation_reason": "Customer requested cancellation"
  }'

Uncancel a scheduled cancellation

API reference: Uncancel subscription

curl -X POST "https://api.lomi.africa/subscriptions/sub_abc123.../uncancel" \
  -H "X-API-KEY: $LOMI_SECRET_KEY"

Change plan

API reference: Change subscription plan

curl -X POST "https://api.lomi.africa/subscriptions/sub_abc123.../change-plan" \
  -H "X-API-KEY: $LOMI_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"price_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"}'

Webhooks

Subscribe to these SCREAMING_SNAKE_CASE events (same as Webhooks):

EventDescription
SUBSCRIPTION_CREATEDNew subscription after successful signup (including trial signup).
SUBSCRIPTION_UPDATEDSubscription changed (pause, resume, plan change, cancel scheduled, uncancel, etc.). Payload includes previous_attributes.
SUBSCRIPTION_RENEWEDA billing cycle renewed successfully.
SUBSCRIPTION_CANCELLEDSubscription was cancelled or expired.

Renewal payment failures do not emit a separate subscription webhook. Listen for PAYMENT_FAILED on the failed renewal transaction, and monitor subscription status (past_due, paused, etc.) via the API.

lomi. does not emit a webhook for trial card setup (setup_intent.succeeded). Listen for SUBSCRIPTION_CREATED when signup completes, not when the card form first loads.

Customer notifications: Trial and $0 signups (no completed payment transaction) trigger a subscription signup confirmation email when customer_notifications.subscription_signups.email is enabled (default: on). Paid initial signups receive the standard transaction receipt instead.


On this page