Error Code Reference
This comprehensive guide documents all error codes returned by the Lightning Enable API, organized by domain. Use this reference to implement robust error handling in your integration.
Error Response Format
All API errors return a consistent JSON structure:
{
"error": "Error Type",
"message": "Human-readable description of the error",
"code": "MACHINE_READABLE_CODE",
"details": {
"additional": "context"
}
}
| Field | Type | Description |
|---|---|---|
error | string | HTTP status text (e.g., "Unauthorized", "Bad Request") |
message | string | Human-readable error description |
code | string | Machine-readable error code for programmatic handling |
details | object | Additional context (optional, varies by error) |
Every error response also includes an X-Correlation-Id response header that you can use when contacting support. See Request Headers for details.
Unhandled Exceptions
Unhandled server errors return a correlation ID for support inquiries:
{
"error": "An error occurred processing your request.",
"correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"timestamp": "2026-01-09T12:00:00Z"
}
The correlationId field in the JSON body matches the X-Correlation-Id response header. Include this value in any support requests to help us locate your request in our logs.
Error Sanitization (Production vs. Development)
In production, error responses for unhandled exceptions are sanitized to prevent leaking internal details. You will only see a generic error message, a correlation ID, and a timestamp.
In development, additional debugging fields are included:
{
"error": "An error occurred processing your request.",
"correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"timestamp": "2026-01-09T12:00:00Z",
"detail": "Specific exception message",
"exceptionType": "System.InvalidOperationException",
"stackTrace": "..."
}
The detail, exceptionType, and stackTrace fields are never included in production responses. Do not write code that depends on these fields in production.
HTTP Status Codes
Success Codes
| Code | Meaning | When Used |
|---|---|---|
200 OK | Request succeeded | GET, PUT, POST (when returning data) |
201 Created | Resource created | POST when creating payments, refunds, proxies |
204 No Content | Success, no response body | DELETE operations |
Client Error Codes
| Code | Meaning | Common Causes |
|---|---|---|
400 Bad Request | Invalid request | Missing required fields, invalid format, validation errors |
401 Unauthorized | Authentication failed | Missing/invalid API key, expired credentials |
402 Payment Required | L402 payment needed | Accessing L402-protected endpoints without valid token |
403 Forbidden | Access denied | Inactive account, subscription issues, feature not available |
404 Not Found | Resource not found | Invalid invoice ID, order ID, proxy ID |
409 Conflict | Resource conflict | Duplicate order ID, concurrent modification |
422 Unprocessable Entity | Semantic error | Valid syntax but business logic violation |
429 Too Many Requests | Rate limit exceeded | Too many requests in time window |
Server Error Codes
| Code | Meaning | When Used |
|---|---|---|
500 Internal Server Error | Server error | Unexpected errors, includes correlationId |
502 Bad Gateway | Upstream error | OpenNode/Stripe API failures, target API unreachable |
503 Service Unavailable | Temporary outage | Maintenance, circuit breaker open |
504 Gateway Timeout | Upstream timeout | OpenNode/target API timeout |
Authentication Errors
Missing API Key
HTTP Status: 401 Unauthorized
{
"error": "API key required",
"message": "Please provide API key in X-API-Key header"
}
Cause: Request missing the X-API-Key header.
Solution: Include your API key in every request:
curl -H "X-API-Key: lgw_your_api_key_here" https://api.lightningenable.com/api/payments
Invalid API Key
HTTP Status: 401 Unauthorized
{
"error": "Invalid API key",
"message": "The provided API key is invalid or inactive"
}
Cause: API key doesn't match any active merchant account.
Solution:
- Verify you're using the correct API key
- Check if your account is active
- Contact support if you've lost your API key
Invalid Admin API Key
HTTP Status: 401 Unauthorized
{
"error": "Unauthorized",
"message": "Invalid admin API key"
}
Cause: Admin endpoint accessed with incorrect admin credentials.
Solution: Use the correct admin API key for /api/admin/* endpoints.
Server Configuration Error
HTTP Status: 500 Internal Server Error
{
"error": "Server configuration error"
}
Cause: Admin API key not configured on server.
Solution: Contact system administrator.
Subscription Errors
Account Inactive
HTTP Status: 403 Forbidden
{
"error": "Account inactive",
"message": "Your account is inactive. Please contact support.",
"action_required": "contact_support"
}
Cause: Merchant account has been deactivated.
Solution: Contact support@lightningenable.com to reactivate.
Subscription Required
HTTP Status: 403 Forbidden
{
"error": "Subscription required",
"message": "Your plan tier requires an active subscription. Please subscribe to continue using the service.",
"current_plan": "standard",
"action_required": "subscribe"
}
Cause: Non-pilot tier account without valid Stripe subscription.
Solution: Subscribe at lightningenable.com.
Subscription Not Active
HTTP Status: 403 Forbidden
{
"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"
}
Possible Status Values:
| Status | Message | Action Required |
|---|---|---|
past_due | Payment is past due | update_payment_method |
canceled | Subscription canceled | renew_subscription |
unpaid | Subscription unpaid | update_payment_method |
incomplete | Setup incomplete | Complete payment process |
incomplete_expired | Setup expired | Start new subscription |
Subscription Period Expired
HTTP Status: 403 Forbidden
{
"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"
}
Cause: The subscription's CurrentPeriodEnd has passed even though the status may still show active. This typically occurs when a Stripe webhook is delayed. The middleware catches expired billing periods as a safety net.
Solution: Renew your subscription or wait for Stripe to process the renewal. If your payment method is valid, this should resolve automatically once the Stripe webhook updates the billing period.
Feature Not Available
HTTP Status: 403 Forbidden
{
"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-to-Plan Requirements:
| Feature | Required Plan | Endpoint |
|---|---|---|
refunds | Kentico Commerce+ | /api/refunds |
multi_currency | Kentico Commerce+ | /api/payments/convert |
l402 | Agentic Commerce | /api/proxy, /l402/proxy |
See Subscription & Plan Enforcement for full details on plan tiers and feature gating.
Payment Errors
Payment Not Found
HTTP Status: 404 Not Found
{
"error": "Payment not found"
}
Cause: Invoice ID doesn't exist or doesn't belong to your merchant account.
Solution: Verify the invoice ID is correct and belongs to your account.
Invalid Payment Request
HTTP Status: 400 Bad Request
{
"error": "Invalid request"
}
Common Validation Errors:
- Amount must be greater than 0
- Currency must be supported (USD, EUR, GBP, BTC, sats)
- Order ID is required
- Invalid callback URL format
Duplicate Order
HTTP Status: 409 Conflict
{
"error": "Order already exists",
"details": {
"orderId": "ORDER-123",
"existingInvoiceId": "inv_abc123"
}
}
Cause: A payment with this order ID already exists.
Solution: Use unique order IDs for each payment request.
Refund Errors
Invalid Refund Request
HTTP Status: 400 Bad Request
{
"error": "Cannot refund: invoice status is 'pending'"
}
Refundable Statuses: Only invoices with status paid, underpaid, or processing can be refunded.
Refund Not Found
HTTP Status: 404 Not Found
{
"error": "Refund not found"
}
Invoice Not Found (for refund)
HTTP Status: 404 Not Found
{
"error": "Invoice not found"
}
L402 Protocol Errors
Payment Required (402)
HTTP Status: 402 Payment Required
Headers:
WWW-Authenticate: L402 macaroon="AgEB...", invoice="lnbc..."
X-L402-Error: No Authorization header provided
Body:
{
"error": "Payment Required",
"message": "Pay the Lightning invoice to access this API",
"proxy": {
"id": "my-api-1234",
"name": "My API",
"description": "AI services monetized with Lightning"
},
"l402": {
"macaroon": "AgEBYXBpLmxpZ2h0bmluZ2VuYWJsZS5jb20...",
"invoice": "lnbc100n1pj...",
"amount_sats": 100,
"payment_hash": "abc123...",
"expires_at": "2026-01-09T13:00:00Z"
},
"instructions": {
"step1": "Pay the Lightning invoice using any Lightning wallet",
"step2": "Copy the preimage (proof of payment) from your wallet",
"step3": "Include in request: Authorization: L402 <macaroon>:<preimage>"
}
}
Invalid L402 Credential
HTTP Status: 402 Payment Required
Header: X-L402-Error: Invalid L402 format. Expected: L402 <macaroon>:<preimage>
Common L402 Errors:
| Error Message | Cause |
|---|---|
| No Authorization header provided | Missing Authorization header |
| Invalid authorization scheme | Using Basic/Bearer instead of L402 |
| Invalid L402 format | Malformed macaroon:preimage format |
| Preimage does not match payment hash | Incorrect preimage |
| L402 verification failed | Invalid or expired macaroon |
Proxy Not Found
HTTP Status: 404 Not Found
{
"error": "Proxy not found",
"message": "No active proxy configuration found for ID: invalid-proxy"
}
Proxy Unavailable
HTTP Status: 404 Not Found
{
"error": "Proxy unavailable",
"message": "This API proxy is currently unavailable"
}
Cause: The merchant account owning the proxy is inactive.
L402 Proxy Gateway Errors
Bad Gateway (502):
{
"error": "Bad Gateway",
"message": "Unable to connect to the target API",
"proxy_id": "my-api-1234",
"details": "Connection refused"
}
Gateway Timeout (504):
{
"error": "Gateway Timeout",
"message": "The target API did not respond in time",
"proxy_id": "my-api-1234"
}
Webhook Errors
Invalid Webhook Payload
HTTP Status: 400 Bad Request
{
"error": "Invalid JSON",
"details": "Unexpected character at position 42"
}
Invalid Payload Structure
HTTP Status: 400 Bad Request
{
"error": "Invalid payload"
}
Invoice Not Found (Webhook)
HTTP Status: 404 Not Found
{
"error": "Invoice not found"
}
Cause: Webhook received for unknown OpenNode charge ID.
Invalid Webhook Signature
HTTP Status: 401 Unauthorized
{
"error": "Invalid signature"
}
Cause: OpenNode webhook signature verification failed.
Solution: Ensure your OpenNode API key is correctly configured.
Webhook Signature Required (Production)
HTTP Status: 401 Unauthorized
{
"error": "Webhook signature verification required in production"
}
Cause: In production, all webhooks must include verifiable signatures.
Invalid Stripe Signature
HTTP Status: 400 Bad Request
{
"error": "Invalid signature"
}
Cause: Stripe webhook signature verification failed.
Rate Limiting Errors
Rate Limit Exceeded
HTTP Status: 429 Too Many Requests
Headers:
Retry-After: 45
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704067200
Body:
{
"error": "Too Many Requests",
"message": "Rate limit exceeded",
"code": "RATE_LIMIT_EXCEEDED",
"details": {
"limit": 100,
"window": "1 minute",
"retryAfter": 45
}
}
Rate Limits by Policy:
| Policy | Limit | Window | Endpoints |
|---|---|---|---|
| Global | 100 | 1 min | All authenticated requests |
| Read | 200 | 1 min | GET operations |
| Payment Create | 10 | 1 min | POST /api/payments, refunds |
| Checkout Create | 5 | 1 min | Stripe checkout |
| Admin | 30 | 1 min | /api/admin/* |
Solution: Wait for Retry-After seconds, then retry.
OpenNode Integration Errors
OpenNode API Error
HTTP Status: 502 Bad Gateway
{
"error": "OpenNode API error",
"details": "Invalid API key"
}
Common OpenNode Errors:
- Invalid API key
- Insufficient balance
- Invalid charge request
- Rate limited by OpenNode
OpenNode Timeout
HTTP Status: 504 Gateway Timeout
{
"error": "OpenNode request timed out"
}
Solution: Retry the request. OpenNode has a 30-second timeout.
OpenNode Circuit Breaker Open
When OpenNode experiences multiple consecutive failures, the circuit breaker opens:
HTTP Status: 503 Service Unavailable
The circuit breaker:
- Opens after 5 consecutive failures
- Stays open for 30 seconds
- Automatically tests recovery in half-open state
Stripe Integration Errors
Checkout Session Errors
HTTP Status: 400 Bad Request
{
"error": "Failed to create checkout session. Please try again."
}
Common Causes:
- Invalid email format
- Missing required fields
- Stripe API error
Subscription Result Errors
Missing Session ID:
{
"error": "session_id is required"
}
Session Not Found:
{
"error": "Checkout session not found"
}
Payment Not Completed:
{
"error": "Payment not completed",
"paymentStatus": "unpaid"
}
Customer Not Found:
{
"error": "Customer not found for this session"
}
Merchant Not Found:
{
"error": "Merchant account not found. Please wait a moment and try again."
}
Customer Portal Errors
HTTP Status: 401 Unauthorized
{
"error": "API key authentication required"
}
HTTP Status: 400 Bad Request
{
"error": "No Stripe customer ID associated with this account"
}
Proxy Management Errors
Invalid Target URL
HTTP Status: 400 Bad Request
{
"error": "Invalid target URL. Must be a valid HTTP or HTTPS URL."
}
Invalid Path Pattern
HTTP Status: 400 Bad Request
{
"error": "Invalid path pattern. Must start with '/' and be a valid glob pattern."
}
Proxy Not Found
HTTP Status: 404 Not Found
{
"error": "Proxy not found"
}
Endpoint Pricing Not Found
HTTP Status: 404 Not Found
{
"error": "Endpoint pricing not found"
}
Merchant Settings Errors
Authentication Required
HTTP Status: 401 Unauthorized
{
"error": "Authentication required"
}
Merchant Not Found
HTTP Status: 404 Not Found
{
"error": "Merchant not found"
}
Invalid OpenNode Key
HTTP Status: 400 Bad Request
{
"error": "OpenNode API key is required"
}
Invalid Webhook URL
HTTP Status: 400 Bad Request
{
"error": "Invalid webhook URL format"
}
Error Handling Best Practices
1. Always Check HTTP Status First
const response = await fetch(url, options);
if (!response.ok) {
const error = await response.json();
throw new ApiError(response.status, error);
}
return response.json();
2. Handle Errors by Code
class ApiError extends Error {
constructor(status, data) {
super(data.message);
this.status = status;
this.code = data.code;
this.details = data.details;
}
}
function handleError(error) {
switch (error.code) {
case 'RATE_LIMIT_EXCEEDED':
const retryAfter = error.details?.retryAfter || 60;
return scheduleRetry(retryAfter);
case 'INVALID_API_KEY':
return showConfigurationError('Check your API key');
case 'SUBSCRIPTION_REQUIRED':
return redirectToSubscription();
default:
return logAndAlert(error);
}
}
3. Implement Retry Logic
async function withRetry(fn, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (!isRetryable(error) || attempt === maxRetries) {
throw error;
}
const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
await new Promise(r => setTimeout(r, delay));
}
}
}
function isRetryable(error) {
// Retry server errors and rate limits
if (error.status >= 500) return true;
if (error.status === 429) return true;
if (error.code === 'OPENNODE_TIMEOUT') return true;
return false;
}
4. Log Errors with Context
function logError(error, context) {
console.error({
timestamp: new Date().toISOString(),
status: error.status,
code: error.code,
message: error.message,
correlationId: error.correlationId,
context: {
endpoint: context.endpoint,
orderId: context.orderId,
merchantId: context.merchantId
}
});
}
5. User-Friendly Messages
const userMessages = {
'INVALID_API_KEY': 'Authentication failed. Please check your settings.',
'RATE_LIMIT_EXCEEDED': 'Too many requests. Please wait a moment.',
'PAYMENT_NOT_FOUND': 'Payment not found. It may have expired.',
'SUBSCRIPTION_REQUIRED': 'Please subscribe to continue.',
'OPENNODE_ERROR': 'Payment service temporarily unavailable.'
};
function getUserMessage(error) {
return userMessages[error.code] || 'An error occurred. Please try again.';
}
Retry Strategy Summary
| Error Type | Retry? | Strategy |
|---|---|---|
| 400 Bad Request | No | Fix request data |
| 401 Unauthorized | No | Fix credentials |
| 402 Payment Required | No | Complete payment |
| 403 Forbidden | No | Check subscription/features |
| 404 Not Found | No | Check resource ID |
| 429 Rate Limited | Yes | Wait for Retry-After |
| 500 Server Error | Yes | Exponential backoff |
| 502 Bad Gateway | Yes | Exponential backoff |
| 503 Service Unavailable | Yes | Wait, then retry |
| 504 Gateway Timeout | Yes | Retry immediately |
Next Steps
- Rate Limiting - Detailed rate limit information
- Authentication - API key setup and security
- Webhooks - Webhook configuration and signatures
- Payments API - Payment creation and status