Subscription & Plan Enforcement
Lightning Enable enforces active subscriptions and plan-specific feature access on every API request. This page explains how plan tiers work, what happens when a subscription lapses, and how feature gating controls access to plan-specific functionality.
Plan Tiers
Lightning Enable offers three plan tiers. Pricing is based on capabilities, never transaction volume.
| Plan | Tier ID | Price |
|---|---|---|
| Agentic Commerce — Individual | individual | $99/month |
| Kentico Commerce | standard | $249/month |
| Agentic Commerce — Business | l402 | $299/month (default) |
All plans include annual pricing at approximately two months free (e.g., $2,490/year for Kentico Commerce).
Feature Comparison
Every plan includes core API access. Higher tiers unlock additional capabilities.
| Feature | Agentic Commerce — Individual | Kentico Commerce | Agentic Commerce — Business |
|---|---|---|---|
| Full REST API | Yes | Yes | Yes |
| Lightning Network payments | Yes | Yes | Yes |
| Refund support | Yes | Yes | Yes |
| Multi-currency (USD, EUR, GBP, BTC) | Yes | Yes | Yes |
| Analytics | Yes | Yes | Yes |
| Priority support | Yes | Yes | Yes |
| Max environments | 2 | 2 | 2 |
| Max webhook endpoints | 5 | 5 | 5 |
| Kentico Commerce integration | No | Yes | No |
| L402 protocol (server-side) | Yes | No | Yes |
| Pay-per-request monetization | Yes | No | Yes |
| MCP AI agent integration | Yes | No | Yes |
| Custom branding | No | No | No |
Checking Your Plan
Use the merchant settings endpoint to see your current plan and features:
curl https://api.lightningenable.com/api/merchant/me \
-H "X-API-Key: le_merchant_abc123"
The response includes your planTier, subscriptionStatus, and a features object with all feature flags.
Subscription Lifecycle
Valid Subscription States
The subscription enforcement middleware checks every authenticated API request. Only two statuses grant access:
| Status | Meaning | API Access |
|---|---|---|
active | Subscription is current and paid | Allowed |
trialing | In free trial period | Allowed |
past_due | Payment failed, awaiting retry | Blocked |
canceled | Subscription was canceled | Blocked |
unpaid | Payment not received | Blocked |
incomplete | Initial payment not completed | Blocked |
incomplete_expired | Initial payment window expired | Blocked |
Subscription Validation Flow
The middleware performs these checks in order for every authenticated request:
- Path exemption -- Certain paths skip subscription checks entirely (Stripe endpoints, webhooks, health checks, Swagger).
- Account active check -- If the merchant account is deactivated (
isActive = false), the request is immediately blocked with a403. - Pilot tier bypass -- Pilot accounts are allowed through without subscription validation (see Pilot Tier below).
- Stripe subscription required -- Non-pilot accounts must have a valid
StripeSubscriptionId. Accounts without one receive a403withaction_required: "subscribe". - Subscription status check -- The status must be
activeortrialing. Any other status returns a403with a status-specific message. - Billing period validation -- If
CurrentPeriodEndis set and has passed, the request is blocked even if the status field still showsactive. This catches expired subscriptions before the Stripe webhook updates the status. - Feature gating -- Plan-specific features are checked against the requested endpoint (see Feature Gating below).
Request
│
├─ Exempt path? ──── Yes ──→ Allow
│
├─ No MerchantId? ── Yes ──→ Allow (unauthenticated)
│
├─ Account inactive? ────── → 403 "Account inactive"
│
├─ Pilot tier? ───── Yes ──→ Allow (with monitoring)
│
├─ No Stripe sub? ─────────→ 403 "Subscription required"
│
├─ Status not active/trialing? → 403 "Subscription not active"
│
├─ CurrentPeriodEnd passed? ──→ 403 "Subscription period expired"
│
├─ Feature not available? ────→ 403 "Feature not available"
│
└─ All checks pass ─────────→ Allow (features set in context)
Subscription Expiration
What Happens When a Subscription Expires
When a subscription expires or is canceled, API requests return 403 Forbidden with a JSON body describing the issue and what action to take.
Canceled subscription:
{
"error": "Subscription not active",
"message": "Your subscription has been canceled. Please subscribe again to continue using the service.",
"subscription_status": "canceled",
"action_required": "renew_subscription"
}
Past due payment:
{
"error": "Subscription not active",
"message": "Your subscription payment is past due. Please update your payment method to continue using the service.",
"subscription_status": "past_due",
"action_required": "update_payment_method"
}
Billing period expired (webhook delay protection):
{
"error": "Subscription period expired",
"message": "Your subscription billing period has expired. Please renew your subscription to continue using the service.",
"subscription_status": "active",
"current_period_end": "2026-01-15T00:00:00.0000000Z",
"action_required": "renew_subscription"
}
Even if the subscription status still reads active, the middleware checks whether CurrentPeriodEnd has passed. This provides a safety net for cases where Stripe webhook delivery is delayed, ensuring expired subscriptions are caught in near-real-time.
Grace Period Behavior
Lightning Enable relies on Stripe's built-in retry and grace period logic:
- Stripe retries failed payments automatically according to your Stripe account's Smart Retries settings (typically 3-4 attempts over several days).
- During retries, the subscription status transitions to
past_due. API access is blocked during this period. - If all retries fail, Stripe marks the subscription as
canceledorunpaiddepending on your Stripe settings. - There is no additional grace period built into Lightning Enable beyond what Stripe provides. The moment the subscription status leaves
activeortrialing, API access is blocked.
To restore access after a lapsed subscription:
- Update your payment method via the Stripe customer portal.
- Or subscribe again at lightningenable.com.
Exempt Paths
These paths are never subject to subscription enforcement, even for expired accounts:
| Path | Reason |
|---|---|
/api/stripe/create-checkout-session | Must be accessible to (re)subscribe |
/api/stripe/customer-portal | Must be accessible to manage billing |
/api/stripe/subscription | Must be accessible to check status |
/api/stripe/pricing | Public pricing information |
/api/webhooks/stripe | Incoming Stripe webhooks |
/api/webhooks/opennode | Incoming OpenNode webhooks |
/api/l402 | L402 demo endpoints (use L402 token auth) |
/health | Health check |
/swagger | API documentation |
This ensures merchants can always manage their subscription and billing even when their API access is blocked.
Pilot Tier
The pilot tier is a special account type used for evaluation and onboarding. Pilot accounts:
- Have full API access without requiring a Stripe subscription.
- Do not require
StripeSubscriptionId,SubscriptionStatus, orCurrentPeriodEnd. - Are monitored -- if a pilot account has been active for more than 90 days, a warning is logged encouraging conversion to a paid plan.
- Are not publicly available -- pilot accounts are created by administrators only.
Pilot accounts are intended for short-term evaluation. They have no expiration enforcement but are expected to convert to a paid plan.
Feature Gating
Beyond subscription status, the middleware enforces plan-specific feature access on certain endpoints.
Gated Features
| Feature | Gated Endpoint | Description |
|---|---|---|
refunds | /api/refunds/* | Refund processing |
multi_currency | /api/payments/*/convert | Multi-currency conversion |
If a merchant attempts to access a gated endpoint without the required feature flag, they receive:
{
"error": "Feature not available",
"message": "Refund processing is not enabled for your account. Please contact support.",
"feature": "refunds",
"current_plan": "pilot",
"required_plan": "standard",
"action_required": "upgrade_plan"
}
Feature Flags in Context
When a request passes all subscription and feature checks, the middleware populates MerchantFeatures in the request context. Controllers can use these flags for fine-grained access control:
| Feature Flag | Type | Description |
|---|---|---|
RefundsEnabled | boolean | Can process refunds |
MultiCurrencyEnabled | boolean | Can use multi-currency conversion |
MaxWebhookEndpoints | int | Maximum webhook endpoints allowed |
AnalyticsEnabled | boolean | Access to analytics |
PrioritySupport | boolean | Priority support access |
CustomBrandingEnabled | boolean | Custom branding on checkout |
L402 Feature Gating
L402 server-side features (creating proxies, configuring endpoint pricing) require the Agentic Commerce — Business plan (l402 tier). This is enforced separately from the middleware via the L402Enabled flag on the merchant entity.
Check your L402 status:
curl https://api.lightningenable.com/api/merchant/l402-status \
-H "X-API-Key: le_merchant_abc123"
| Plan | L402 Server-Side | Price |
|---|---|---|
| Agentic Commerce — Individual | Yes | $99/mo |
| Kentico Commerce | No | $249/mo |
| Agentic Commerce — Business | Yes | $299/mo |
The MCP server's L402 client tools (access_l402_resource, pay_l402_challenge) are free for everyone. No subscription is needed to pay L402 invoices -- only to create L402-protected endpoints.
Handling Subscription Errors in Your Integration
Detecting Subscription Issues
All subscription-related errors return HTTP 403 with an action_required field. Use this field to determine the appropriate response:
action_required | Meaning | Recommended Action |
|---|---|---|
contact_support | Account deactivated | Contact support@lightningenable.com |
subscribe | No active subscription | Redirect to subscription page |
update_payment_method | Payment failed | Redirect to Stripe customer portal |
renew_subscription | Subscription expired or canceled | Redirect to subscription page |
upgrade_plan | Feature requires higher plan | Show upgrade options |
Example Error Handler
async function callLightningEnableApi(endpoint) {
const response = await fetch(`https://api.lightningenable.com${endpoint}`, {
headers: { 'X-API-Key': process.env.LIGHTNING_API_KEY }
});
if (response.status === 403) {
const error = await response.json();
switch (error.action_required) {
case 'subscribe':
case 'renew_subscription':
console.error('Subscription issue:', error.message);
// Redirect user to subscription page
break;
case 'update_payment_method':
console.error('Payment issue:', error.message);
// Redirect user to Stripe customer portal
break;
case 'upgrade_plan':
console.error(`Feature "${error.feature}" requires ${error.required_plan} plan`);
// Show upgrade options
break;
case 'contact_support':
console.error('Account issue:', error.message);
break;
}
throw new Error(error.message);
}
return response.json();
}
Next Steps
- Product Overview -- Compare plan features
- Error Code Reference -- All API error codes
- Merchant Settings -- Check subscription status via API
- FAQ -- Common questions about plans and pricing