Skip to main content

Shopify API Reference

All public store endpoints are unauthenticated — L402 payment proof serves as authorization. Admin endpoints require your Lightning Enable API key.

Base URL: https://api.lightningenable.com


Public Endpoints

These endpoints are accessible without an API key. The {slug} parameter is the URL-safe identifier you chose when creating the integration.

Get Catalog

Fetches the product catalog from your Shopify store. Results are cached.

GET /api/shopify/{slug}/catalog

Response: 200 OK

{
"storeName": "your-store.com",
"slug": "your-store",
"shipping": {
"domesticUsd": 5.99,
"domesticOnly": true,
"freeShippingEnabled": true,
"freeShippingThresholdUsd": 50.00
},
"products": [
{
"productId": 8234567890123,
"title": "Premium Coffee Beans",
"description": "Single-origin Ethiopian coffee, light roast",
"imageUrl": "https://cdn.shopify.com/s/files/...",
"variants": [
{
"variantId": 44567890123456,
"title": "12oz Bag",
"priceUsd": 18.99,
"available": true
},
{
"variantId": 44567890123457,
"title": "2lb Bag",
"priceUsd": 34.99,
"available": true
}
]
}
]
}

Error Responses:

StatusMeaning
404Integration not found or inactive

Create Checkout

Creates an L402 checkout with a Lightning invoice. Returns HTTP 402 with the payment challenge.

POST /api/shopify/{slug}/checkout

Headers (optional):

HeaderFormatDescription
X-Buyer-Location{country}-{state}-{zip}Buyer location for tax calculation (e.g., US-FL-34787). Falls back to merchant's defaultTaxLocation if not provided.

Request Body:

{
"items": [
{ "variantId": 44567890123456, "quantity": 2 },
{ "variantId": 44567890123457, "quantity": 1 }
]
}

Validation Rules:

  • 1–10 items per checkout
  • Each item quantity: 1–10
  • Total quantity across all items: max 10
  • All variant IDs must exist in the catalog
  • All variants must be available (in stock)

Response: 402 Payment Required

The response includes a WWW-Authenticate header with the L402 challenge:

WWW-Authenticate: L402 macaroon="...", invoice="lnbc..."

Response Body:

{
"orderId": "shpfy_a1b2c3d4e5f6",
"items": [
{
"variantId": 44567890123456,
"productTitle": "Premium Coffee Beans",
"variantTitle": "12oz Bag",
"quantity": 2,
"priceUsd": 18.99
},
{
"variantId": 44567890123457,
"productTitle": "Premium Coffee Beans",
"variantTitle": "2lb Bag",
"quantity": 1,
"priceUsd": 34.99
}
],
"subtotalUsd": 72.97,
"shippingUsd": 0.00,
"taxUsd": 5.11,
"taxNote": null,
"totalUsd": 78.08,
"totalSats": 77200,
"claimToken": "SC-x7k9m2p4",
"claimExpiresAt": "2026-03-05T18:30:00Z",
"invoice": "lnbc721500n1pn...",
"macaroonBase64": "AgELbGlnaHRuaW5n...",
"paymentHash": "a1b2c3d4e5f6...",
"invoiceExpiresAt": "2026-02-26T18:40:00Z"
}

Key Fields:

  • invoice — BOLT11 Lightning invoice to pay
  • macaroonBase64 — L402 macaroon (save this for the claim step)
  • paymentHash — links the macaroon to the invoice
  • claimToken — used to claim the order with shipping details (configurable expiry, default 30 days)
  • totalSats — BTC amount locked at current exchange rate
  • taxUsd — tax amount calculated via Shopify's Draft Order API
  • taxNote — present only when tax is estimated from the merchant's defaultTaxLocation (not the buyer's explicit location). Example: "Estimated tax based on store location (US-FL-33139). Pass X-Buyer-Location header for exact destination-based tax."
  • totalUsd — total including subtotal + shipping + tax

Tax Calculation:

Tax is calculated at checkout using Shopify's Draft Order API. A temporary draft order is created with the cart items and a partial address (derived from the buyer location), Shopify computes the tax, the amount is read, and the draft is deleted. The tax is included in the Lightning invoice total.

The buyer location is determined in this order:

  1. X-Buyer-Location request header (format: {country}-{state}-{zip}, e.g., US-FL-34787)
  2. Merchant's defaultTaxLocation setting (configured in the Shopify integration dashboard)
  3. If neither is available, checkout returns a 400 error

Error Responses:

StatusMeaning
400Invalid cart (bad variant ID, unavailable item, exceeds limits) or no tax location available
404Integration not found

Claim Order

Claims an order after payment: verifies L402 proof, saves shipping details, and creates a Shopify order.

POST /api/shopify/{slug}/claim

Headers:

Authorization: L402 {macaroonBase64}:{preimageHex}
Content-Type: application/json

The Authorization header format is L402 <macaroon>:<preimage>:

  • macaroon — the macaroonBase64 from the checkout response
  • preimage — the 64-character hex preimage obtained after paying the invoice

Request Body:

{
"claimToken": "SC-x7k9m2p4",
"email": "customer@example.com",
"shippingAddress": {
"firstName": "Jane",
"lastName": "Doe",
"address1": "123 Main St",
"address2": "Apt 4B",
"city": "Austin",
"province": "TX",
"zip": "78701",
"country": "US",
"phone": "+15125551234"
}
}

Shipping Address Fields:

FieldRequiredMax Length
firstNameYes200
lastNameYes200
address1Yes500
address2No500
cityYes100
provinceNo100
zipNo20
countryYes100
phoneNo30

Response: 200 OK

{
"orderId": "shpfy_a1b2c3d4e5f6",
"status": "PaidWithDetails",
"shopifyOrderNumber": "#1042",
"message": "Order #1042 created successfully. You'll receive shipping confirmation at customer@example.com."
}

Error Responses:

StatusMeaning
400Invalid claim token, expired token, already claimed, invalid L402 proof
401L402 verification failed
404Claim token not found

Get Order Status

Check the status of an order. Requires the claim token as a query parameter.

GET /api/shopify/{slug}/orders/{orderId}?claimToken={claimToken}

Response: 200 OK

{
"orderId": "shpfy_a1b2c3d4e5f6",
"status": "Shipped",
"totalUsd": 72.97,
"paidSats": 72150,
"shopifyOrderNumber": "#1042",
"trackingNumber": "1Z999AA10123456784",
"trackingCarrier": "UPS",
"trackingUrl": "https://www.ups.com/track?tracknum=1Z999AA10123456784",
"createdAt": "2026-02-26T18:30:00Z",
"paidAt": "2026-02-26T18:31:15Z",
"shippedAt": "2026-02-28T14:22:00Z"
}

Error Responses:

StatusMeaning
401Claim token doesn't match the order
404Order not found

Merchant Admin Endpoints

These endpoints require your Lightning Enable API key in the X-API-Key header.

Get Integration

GET /api/merchant/shopify

Returns your Shopify integration configuration. The Admin API token is never returned — only hasAdminApiToken: true/false.

Create Integration

POST /api/merchant/shopify

See Setup Guide for the full request schema.

Update Integration

PUT /api/merchant/shopify

Partial update — include only the fields you want to change.

{
"domesticShippingUsd": 6.99,
"freeShippingEnabled": true,
"defaultTaxLocation": "US-FL-34787",
"isActive": false,
"listInRegistry": true,
"registryCategories": "[\"commerce\",\"food-and-beverage\"]",
"registryDescription": "Your store description for agent discovery."
}

Invalidate Cache

POST /api/merchant/shopify/invalidate-cache

Forces a refresh of the cached product catalog on the next request.

List Orders

GET /api/merchant/shopify/orders
GET /api/merchant/shopify/orders?status=PaidWithDetails
GET /api/merchant/shopify/orders?page=2&pageSize=10

Query Parameters:

ParameterDefaultDescription
status(all)Filter by status: PendingPayment, PaidAwaitingDetails, PaidWithDetails, Fulfilled, Shipped
page1Page number
pageSize20Results per page

Response:

[
{
"orderId": "shpfy_a1b2c3d4e5f6",
"status": "PaidWithDetails",
"totalUsd": 72.97,
"paidSats": 72150,
"email": "customer@example.com",
"shopifyOrderNumber": "#1042",
"createdAt": "2026-02-26T18:30:00Z"
}
]

Complete Purchase Flow Example

Here's the full flow using curl:

# 1. Browse products
CATALOG=$(curl -s https://api.lightningenable.com/api/shopify/my-store/catalog)
echo "$CATALOG" | jq '.products[0].variants[0]'

# 2. Create checkout (get variant ID from catalog)
# Pass X-Buyer-Location for tax calculation (or rely on merchant's defaultTaxLocation)
CHECKOUT=$(curl -s -X POST https://api.lightningenable.com/api/shopify/my-store/checkout \
-H "Content-Type: application/json" \
-H "X-Buyer-Location: US-FL-34787" \
-d '{"items": [{"variantId": 44567890123456, "quantity": 1}]}')

# Extract the invoice and macaroon
INVOICE=$(echo "$CHECKOUT" | jq -r '.invoice')
MACAROON=$(echo "$CHECKOUT" | jq -r '.macaroonBase64')
CLAIM_TOKEN=$(echo "$CHECKOUT" | jq -r '.claimToken')

# 3. Pay the invoice (using Lightning Enable MCP, lncli, or any Lightning wallet)
# This gives you the preimage (64 hex chars)
PREIMAGE="your_preimage_hex_here"

# 4. Claim the order
curl -X POST https://api.lightningenable.com/api/shopify/my-store/claim \
-H "Content-Type: application/json" \
-H "Authorization: L402 ${MACAROON}:${PREIMAGE}" \
-d "{
\"claimToken\": \"${CLAIM_TOKEN}\",
\"email\": \"customer@example.com\",
\"shippingAddress\": {
\"firstName\": \"Jane\",
\"lastName\": \"Doe\",
\"address1\": \"123 Main St\",
\"city\": \"Austin\",
\"province\": \"TX\",
\"zip\": \"78701\",
\"country\": \"US\"
}
}"

# 5. Check order status
curl "https://api.lightningenable.com/api/shopify/my-store/orders/shpfy_abc123?claimToken=${CLAIM_TOKEN}"

Edge Cases

ScenarioWhat Happens
BTC price moves after checkoutThe sats amount is locked in the Lightning invoice at checkout time. The invoice expires in ~10 minutes.
Product goes out of stockChecked at checkout time against cached catalog. At claim time, Shopify's decrement_obeying_policy handles inventory.
Claim token used twiceSecond claim is rejected — tokens are single-use.
Shopify order creation failsOrder stays in PaidAwaitingDetails. Payment is safe. Can be retried via admin.
No tax location providedCheckout returns 400 if neither X-Buyer-Location header nor defaultTaxLocation is configured.
Invoice expires before paymentOrder stays in PendingPayment. Agent must create a new checkout.
Claim token expiresContact the merchant for manual resolution. Payment is recorded. Default expiry is 30 days (configurable).