Skip to main content

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.

PlanTier IDPrice
Agentic Commerce — Individualindividual$99/month
Kentico Commercestandard$249/month
Agentic Commerce — Businessl402$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.

FeatureAgentic Commerce — IndividualKentico CommerceAgentic Commerce — Business
Full REST APIYesYesYes
Lightning Network paymentsYesYesYes
Refund supportYesYesYes
Multi-currency (USD, EUR, GBP, BTC)YesYesYes
AnalyticsYesYesYes
Priority supportYesYesYes
Max environments222
Max webhook endpoints555
Kentico Commerce integrationNoYesNo
L402 protocol (server-side)YesNoYes
Pay-per-request monetizationYesNoYes
MCP AI agent integrationYesNoYes
Custom brandingNoNoNo

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:

StatusMeaningAPI Access
activeSubscription is current and paidAllowed
trialingIn free trial periodAllowed
past_duePayment failed, awaiting retryBlocked
canceledSubscription was canceledBlocked
unpaidPayment not receivedBlocked
incompleteInitial payment not completedBlocked
incomplete_expiredInitial payment window expiredBlocked

Subscription Validation Flow

The middleware performs these checks in order for every authenticated request:

  1. Path exemption -- Certain paths skip subscription checks entirely (Stripe endpoints, webhooks, health checks, Swagger).
  2. Account active check -- If the merchant account is deactivated (isActive = false), the request is immediately blocked with a 403.
  3. Pilot tier bypass -- Pilot accounts are allowed through without subscription validation (see Pilot Tier below).
  4. Stripe subscription required -- Non-pilot accounts must have a valid StripeSubscriptionId. Accounts without one receive a 403 with action_required: "subscribe".
  5. Subscription status check -- The status must be active or trialing. Any other status returns a 403 with a status-specific message.
  6. Billing period validation -- If CurrentPeriodEnd is set and has passed, the request is blocked even if the status field still shows active. This catches expired subscriptions before the Stripe webhook updates the status.
  7. 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"
}
CurrentPeriodEnd Validation

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 canceled or unpaid depending on your Stripe settings.
  • There is no additional grace period built into Lightning Enable beyond what Stripe provides. The moment the subscription status leaves active or trialing, API access is blocked.

To restore access after a lapsed subscription:

  1. Update your payment method via the Stripe customer portal.
  2. Or subscribe again at lightningenable.com.

Exempt Paths

These paths are never subject to subscription enforcement, even for expired accounts:

PathReason
/api/stripe/create-checkout-sessionMust be accessible to (re)subscribe
/api/stripe/customer-portalMust be accessible to manage billing
/api/stripe/subscriptionMust be accessible to check status
/api/stripe/pricingPublic pricing information
/api/webhooks/stripeIncoming Stripe webhooks
/api/webhooks/opennodeIncoming OpenNode webhooks
/api/l402L402 demo endpoints (use L402 token auth)
/healthHealth check
/swaggerAPI 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, or CurrentPeriodEnd.
  • 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

FeatureGated EndpointDescription
refunds/api/refunds/*Refund processing
multi_currency/api/payments/*/convertMulti-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 FlagTypeDescription
RefundsEnabledbooleanCan process refunds
MultiCurrencyEnabledbooleanCan use multi-currency conversion
MaxWebhookEndpointsintMaximum webhook endpoints allowed
AnalyticsEnabledbooleanAccess to analytics
PrioritySupportbooleanPriority support access
CustomBrandingEnabledbooleanCustom 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"
PlanL402 Server-SidePrice
Agentic Commerce — IndividualYes$99/mo
Kentico CommerceNo$249/mo
Agentic Commerce — BusinessYes$299/mo
MCP Tools Are Free

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_requiredMeaningRecommended Action
contact_supportAccount deactivatedContact support@lightningenable.com
subscribeNo active subscriptionRedirect to subscription page
update_payment_methodPayment failedRedirect to Stripe customer portal
renew_subscriptionSubscription expired or canceledRedirect to subscription page
upgrade_planFeature requires higher planShow 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