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 has three plan tiers. Pricing is based on capabilities, never transaction volume. Platform integrations (Shopify Commerce) are included with any paid plan.

PlanTier IDPriceStripe subscription
Free Producer Sandboxfree$0Not required
Agentic Commerceindividual$49/monthRequired
Agentic Commerce — Businessl402Contact usRequired

Agentic Commerce includes a 30-day free trial through self-serve checkout; neither paid plan offers annual billing. Free is the floor: an account with no plan tier set resolves to Free, so there is no paid default.

Agentic Commerce — Business is contact-only: it is not purchasable through /Checkout, /BitcoinCheckout, POST /api/stripe/create-checkout-session, or POST /api/bitcoin/create-subscription — a request naming it on any of those surfaces is rejected with a 400. To subscribe, email support@lightningenable.com. Existing Business subscribers keep everything shown in this page's tables (feature entitlements, limits, renewal) unaffected.

Retired tier ids​

As of September 2026, three tiers exist. The pilot, standalone (also spelled standaloneapi), and standard (Kentico Commerce) tiers were removed after a production snapshot confirmed no account was on any of them.

Their spellings still resolve, so an account created before the change and an old checkout link both keep working:

Retired idResolves to
standard, kenticocommerce, kentico-commerce, kenticoindividual
standalone, standaloneapi, standalone-apiindividual
pilotfree

Retired paid ids resolve to Individual rather than Free so an existing subscriber is never silently downgraded.

Resolving is not the same as selecting. You cannot start anything new on a retired tier — POST /api/stripe/create-checkout-session and POST /api/bitcoin/create-subscription reject a retired id with a 400. The admin endpoint PUT /api/admin/merchants/{id} still accepts one and rewrites it to the live tier it maps to; a tier id that is neither live nor retired is rejected with a 400 that names the accepted values.

Feature Comparison​

Every plan includes core API access. Higher tiers unlock additional capabilities.

FeatureFree Producer SandboxAgentic CommerceAgentic Commerce — Business
Full REST APIYesYesYes
Lightning Network paymentsYesYesYes
Multi-currency (USD, EUR, GBP, BTC)NoYesYes
AnalyticsNoYesYes
Priority supportNoYesYes
Max environments122
Max webhook endpoints155
Platform integrations (Shopify)NoYesYes
L402 protocol (server-side)CappedYesYes
Pay-per-request monetizationCappedYesYes
MCP AI agent integrationYesYesYes
White-glove onboardingNoNoYes
Custom brandingNoNoNo

Free is capped rather than unlimited: 3 L402 endpoints, 200 challenges per month, 1,000 sats maximum per challenge, and 1 proxy configuration. The endpoint cap counts distinct resource paths for the life of the account; the challenge cap resets monthly.

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: YOUR_API_KEY"

The response includes your planTier, subscriptionStatus, and a features object with all feature flags.


Free Trial​

Lightning Enable offers a 30-day free trial on self-serve Agentic Commerce checkout, giving you full API access before your first payment.

How It Works​

  • Eligible plan: Agentic Commerce ($49/mo), via self-serve checkout
  • Duration: 30 days from the date of subscription
  • Card required: A valid payment method must be provided at signup. You will not be charged during the trial period.
  • Full access: Trial merchants have complete API access, identical to a paid subscription. The subscriptionStatus will show trialing.
  • Auto-converts: At the end of the 30-day trial, the subscription automatically converts to a paid plan. Your card on file will be charged at the plan's regular rate.
  • Cancel anytime: You can cancel before the trial ends to avoid being charged. Use the Stripe customer portal to manage your subscription.

Agentic Commerce — Business is contact-only and does not go through this self-serve trial flow. Email support@lightningenable.com and any trial terms will be arranged directly.

Abuse Prevention​

To maintain fair access, Lightning Enable enforces one free trial per email address. If a customer has previously used a trial (on any plan), subsequent subscriptions will skip the trial period and begin billing immediately.

Trial Eligibility by Plan​

PlanTrial Eligible
Free Producer Sandbox ($0)No -- Free is the floor, not a trial
Agentic Commerce ($49/mo)Yes, via self-serve checkout
Agentic Commerce -- Business (contact us)Arranged directly on contact, not via self-serve checkout

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. Free carve-out -- An account whose tier resolves to free skips the subscription checks entirely, because Free requires no Stripe subscription. Capacity is enforced elsewhere, by the Free caps on the L402 and proxy endpoints. Two guards narrow this carve-out (see Free carve-out guards below).
  4. L402 fast-lane trial carve-out -- An account created by the L402 Fast Lane also skips the subscription checks for the length of its trial (see L402 fast-lane trial below).
  5. Stripe subscription required -- Every account that took no carve-out must have a valid StripeSubscriptionId. Accounts without one receive a 403 with action_required: "subscribe".
  6. Subscription status check -- The status must be active or trialing. Any other status returns a 403 with a status-specific message.
  7. 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.
  8. Feature gating -- Plan-specific features are checked against the requested endpoint (see Feature Gating below). This step runs on every path that reached it, carve-outs included, so a Free account cannot reach a paid-only endpoint just because the subscription check was skipped.
Request
│
├─ Exempt path? ──── Yes ──→ Allow
│
├─ No MerchantId? ── Yes ──→ Allow (unauthenticated)
│
├─ Account inactive? ────── → 403 "Account inactive"
│
├─ Tier resolves to free
│ (and may take a carve-out)? ─ Yes ──→ Feature gate ──→ Allow
│
├─ L402 fast-lane trial,
│ still within CurrentPeriodEnd? ─ Yes ─→ Feature gate ──→ Allow
│
├─ 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)

Free carve-out guards​

The Free carve-out grants API access with no Stripe subscription, so two states are deliberately excluded from it:

  • An unrecognized tier id. A tier value that is neither live nor retired — an operator typo, say — does not take the carve-out. It falls through to the subscription checks, which fail closed. Reading an unrecognized value as Free would hand it permanent unpaid access.
  • A blank tier that carries Stripe billing state. A blank tier normally means "never set", and Free is the right reading. A blank tier on a row that also holds a stripeCustomerId or stripeSubscriptionId is a data anomaly, not a Free account, so it faces the subscription checks. Otherwise a canceled subscription would regain full access by way of the carve-out.

A blank tier with no billing state is the ordinary case and is admitted as Free.

L402 fast-lane trial​

An account created through the L402 Fast Lane has no Stripe subscription — the 100-sat Lightning payment is the proof of intent — so it needs a carve-out of its own. It applies only when all four of these hold:

  • the stored tier is exactly individual
  • subscriptionStatus is trialing
  • both stripeSubscriptionId and stripeCustomerId are empty
  • currentPeriodEnd is in the future

The first condition matches the stored value, not the resolved one. That is deliberate and narrower than it looks: an account stored under a retired id resolves to individual everywhere else but does not take this carve-out, because the background job that ends the trial filters on the stored string in SQL and could not find it either. Matching wider here than the job can find would turn a bounded trial into permanent unpaid access.

The trial ends by way of a background job that downgrades the account to Free once trialEnd passes with no Stripe conversion. That job additionally requires the account to be marked as fast-lane-originated and not to have billing deferred, so an account with deferred billing stays on Individual rather than being downgraded. Adding billing mid-trial bills immediately and does not start a second trial.


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/webhooks/strikeIncoming Strike webhooks
/api/l402/pricingL402 demo pricing (uses L402 token auth)
/api/l402/statusL402 demo status (uses L402 token auth)
/api/l402/demoL402 demo endpoint (uses L402 token auth)
/api/l402/premium-dataL402 demo premium data (uses L402 token auth)
/api/l402/contentL402-protected premium guides (uses L402 token auth)
/api/manifestsPublic manifest registry
/l402/testL402 public test endpoint (1-sat ping)
/healthHealth check
/swaggerAPI documentation

Only the specific L402 demo paths listed above are exempt. The L402 producer API (/api/l402/challenges and /api/l402/challenges/verify) is subscription-enforced and feature-gated — it requires an active Agentic Commerce subscription with L402Enabled.

This ensures merchants can always manage their subscription and billing even when their API access is blocked.


Evaluating without a subscription​

Two paths give you API access with no Stripe subscription. Both are self-serve; neither needs an administrator.

PathTierDurationLimits
Free Producer SandboxfreeIndefinite3 endpoints, 200 challenges/month, 1,000 sats max per challenge, 1 proxy
L402 Fast Laneindividual30 daysNone — a full Individual trial

The admin-created pilot tier that used to serve this purpose was removed in September 2026. An account still stored as pilot resolves to free and takes the Free carve-out, so it keeps working at Free capacity.


Feature Gating​

Beyond subscription status, the middleware enforces plan-specific feature access on certain endpoints.

Gated Features​

Each gate reads a per-account flag, not the plan table. Plan changes set those flags, but an operator can also set them individually.

FeatureGated EndpointAccount flagrequired_planaction_required
refunds/api/refunds/*refundsEnablednullcontact_support
multi_currency/api/payments/*/convertmultiCurrencyEnabledindividualupgrade_plan
l402/api/l402/challenges*l402Enabledindividualupgrade_plan

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": "free",
"required_plan": null,
"action_required": "contact_support"
}
Refunds are never granted by a plan

No plan sets refundsEnabled — it is an operator-granted per-account flag. The refunds 403 therefore sends required_plan: null and action_required: "contact_support", because upgrading would not turn the feature on. Contact support@lightningenable.com instead.

Key on feature, not on the tier name

Key your error handling on the feature field. current_plan is the normalized tier: it is always one of free, individual, or l402 for an account whose tier we recognize, even when the stored value is a retired spelling. There are two exceptions: an unrecognized tier value is echoed back raw so an operator can see what needs fixing, and a blank tier (no plan on file) is reported as null.

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, the producer API) are available on every live tier. Access is controlled by the l402Enabled flag on the merchant account, which the plan sets to true on all three. What differs is capacity, not availability: Free is capped, both paid plans are not.

Check your L402 status:

curl https://api.lightningenable.com/api/merchant/l402-status \
-H "X-API-Key: YOUR_API_KEY"
PlanL402 Server-SidePrice
Free Producer SandboxYes — capped at 3 endpoints, 200 challenges/mo, 1,000 sats per challenge$0
Agentic CommerceYes — uncapped$49/mo
Agentic Commerce — BusinessYes — uncappedContact us
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 deactivated, or a feature that no plan grants (refunds)Contact support@lightningenable.com. Do not offer an upgrade
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 a different planShow upgrade options for required_plan

Branch on action_required, not on error. contact_support covers two different causes, and error tells you which: "Account inactive" versus "Feature not available".

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':
// required_plan is null when no plan grants the feature, so guard it.
if (error.required_plan) {
console.error(`Feature "${error.feature}" requires the ${error.required_plan} plan`);
// Show upgrade options
} else {
console.error(`Feature "${error.feature}" is not available on any plan:`, error.message);
// Point the user at support, not at checkout
}
break;

case 'contact_support':
// Two causes: a deactivated account, or a feature no plan grants
// (refunds). error.feature is present only for the second.
if (error.feature) {
console.error(`Feature "${error.feature}" must be enabled by support:`, error.message);
} else {
console.error('Account issue:', error.message);
}
break;
}

throw new Error(error.message);
}

return response.json();
}

Next Steps​