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.


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:
- Hosted checkout or payment links with a recurring
product_id - Your storefront subscribe flow
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_RENEWEDon success - Webhook:
PAYMENT_FAILEDon 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:
- Notification crons email or message a hosted renewal checkout link before
next_billing_date. - The customer pays on that link; webhooks confirm the cycle.
- If the customer does not pay in time, overdue processing may set the subscription to
past_due,paused, orcancelledper the product'sfailed_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:
- Listen for
SUBSCRIPTION_RENEWED: successful billing cycle; extend access or send a receipt. - Listen for
PAYMENT_FAILEDon renewal transactions, customer may need to pay the renewal checkout link or update a card in the customer portal. - Poll or list subscriptions via
GET /subscriptions(optionally withcustomer_idorstatusfilters) when webhooks are delayed; usestatusandnext_billing_datefrom 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:
| Status | Meaning |
|---|---|
pending | Created but not yet active (rare at signup). |
trial | Free trial in progress; no charge until trial ends. |
active | Paid and renewing on schedule. |
past_due | A renewal payment failed; retries or merchant action may apply. |
paused | Billing paused (e.g. after failed payment when product is configured to pause). |
cancelled | Cancelled by merchant or customer. |
expired | Fixed-term subscription ended or trial converted without payment method. |
First payment behavior is controlled on the product via first_payment_type:
| Value | First checkout charge |
|---|---|
initial | Full recurring price (or prorated amount if combined with proration rules). |
non_initial | $0 at signup; first charge on the first billing date. |
prorated | Partial 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):
- Call lomi. APIs when they cancel, pause, upgrade, or resume (
POST /subscriptions/{id}/cancel,PATCH /subscriptions/{id}, etc.). - Listen for
SUBSCRIPTION_UPDATEDandSUBSCRIPTION_CANCELLEDwebhooks (same events whether the change came from your API, the portal, or cron). - Reconcile entitlements with
GET /subscriptions/{id}when you need authoritative state. - 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):
| Event | Description |
|---|---|
SUBSCRIPTION_CREATED | New subscription after successful signup (including trial signup). |
SUBSCRIPTION_UPDATED | Subscription changed (pause, resume, plan change, cancel scheduled, uncancel, etc.). Payload includes previous_attributes. |
SUBSCRIPTION_RENEWED | A billing cycle renewed successfully. |
SUBSCRIPTION_CANCELLED | Subscription 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.