L402 Producer API: Agents That Earn
Until now, AI agents could only spend money. With the L402 Producer API, your agents can earn money too. This is true agentic commerce — agents that both buy and sell services autonomously.
The L402 Producer API lets Agentic Commerce subscribers turn their AI agents into service providers. Instead of just consuming paid APIs, your agent can create L402 payment challenges and verify payments before granting access to its own capabilities.
The Two Sides of Agentic Commerce
| Side | What It Does | Who Pays | MCP Tools |
|---|---|---|---|
| Consumer (existing) | Agent accesses paid APIs | Your agent pays | access_l402_resource, pay_l402_challenge |
| Producer (new) | Agent charges for its services | Other agents pay you | l402_producer with action="create" / action="verify" (formerly the separate create_l402_challenge / verify_l402_payment tools — see Deprecated aliases) |
Both sides use the same L402 protocol. The difference is direction: consumers pay invoices, producers create them.
Prerequisites
- Agentic Commerce subscription — Agentic Commerce ($49/mo) or Business (contact us)
LIGHTNING_ENABLE_API_KEYenvironment variable set to your merchant API key- A configured payment provider (Strike or OpenNode) on your Lightning Enable account
{
"mcpServers": {
"lightning-enable": {
"command": "dotnet",
"args": ["tool", "run", "lightning-enable-mcp"],
"env": {
"STRIKE_API_KEY": "your-strike-api-key",
"LIGHTNING_ENABLE_API_KEY": "your-merchant-api-key"
}
}
}
}
The out-of-the-box tools (access_l402_resource, pay_l402_challenge, pay_invoice, etc.) require no API key — just a wallet. l402_producer (every action) and four agent_services actions (request, publish, unpublish, attest) require a Lightning Enable API key via LIGHTNING_ENABLE_API_KEY; the other agent_services actions (discover, settle, reputation) work with just a wallet. See the MCP Complete Guide for the full tool list.
How It Works
The full agent-to-agent commerce flow:
┌──────────────────┐ ┌──────────────────┐
│ Requesting Agent │ │ Producer Agent │
│ (Consumer) │ │ (Your Agent) │
└────────┬─────────┘ └────────┬─────────┘
│ │
│ 1. "I need weather data" │
│──────────────────────────── ──────────> │
│ │
│ │ 2. create_l402_challenge(
│ │ resource="/api/weather",
│ │ priceSats=50,
│ │ description="7-day forecast"
│ │ )
│ │
│ 3. HTTP 402 Payment Required │
│ + Lightning invoice + macaroon │
│ <──────────────────────────────────────│
│ │
│ 4. pay_l402_challenge(invoice, mac) │
│ ─ ─ ─ ─ ─ ─ ─ (pays invoice) ─ ─ ─ >│
│ │
│ 5. "Here's my L402 token: │
│ macaroon:preimage" │
│──────────────────────────────────────> │
│ │
│ │ 6. verify_l402_payment(
│ │ macaroon, preimage
│ │ )
│ │
│ 7. Access granted + response data │
│ <──────────────────────────────────────│
(Steps 2 and 6 are shorthand for l402_producer with action="create" and action="verify" — see below.)
MCP Tools
create_l402_challenge and verify_l402_payment are now l402_producer with action="create" / action="verify" — one tool, selected by action, alongside the newer configure_receive/status/create_proxy/add_endpoint/publish/list_challenges actions covered in Sell With Your Agent and the MCP Complete Guide. The old tool names still work as deprecated aliases under LIGHTNING_ENABLE_TOOL_PROFILE=full, removed in v3.0.0 — the examples below use the current call form.
l402_producer with action="create"
Create an L402 payment challenge to charge another agent or user for accessing a resource. Formerly the standalone create_l402_challenge tool.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
resource | string | Yes | Resource identifier — URL, service name, or description of what you're charging for |
priceSats | long | Yes | Price in satoshis to charge |
description | string | No | Description shown on the Lightning invoice |
Example:
User: When another agent asks for weather data, charge them 50 sats.
Claude: [Uses l402_producer with action="create", resource="/api/weather/forecast",
priceSats=50, description="7-day weather forecast"]
L402 challenge created!
- Invoice: lnbc500n1p3xyza...
- Price: 50 sats
- Resource: /api/weather/forecast
Share the invoice and macaroon with the requesting agent.
After they pay, they'll send you an L402 token (macaroon:preimage).
Use l402_producer with action="verify" to confirm payment before granting access.
Response:
{
"success": true,
"challenge": {
"invoice": "lnbc500n1p3xyza...",
"macaroon": "AgELbGlnaHRuaW5n...",
"paymentHash": "abc123def456...",
"expiresAt": "2026-03-13T14:30:00Z"
},
"resource": "/api/weather/forecast",
"priceSats": 50,
"instructions": {
"forPayer": "Pay the Lightning invoice, then present the L402 token...",
"tokenFormat": "L402 {macaroon}:{preimage}",
"verifyWith": "After receiving the L402 token from the payer, use l402_producer with action=\"verify\" to confirm payment before granting access."
},
"message": "L402 challenge created for 50 sats. Share the invoice with the payer."
}
l402_producer with action="verify"
Verify an L402 token (macaroon + preimage) to confirm payment was made. Use this after a payer presents an L402 token, before granting access to the resource. Formerly the standalone verify_l402_payment tool.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
macaroon | string | Yes | Base64-encoded macaroon from the L402 token |
preimage | string | Yes | Hex-encoded preimage (proof of payment) |
This tool sends only the macaroon and preimage to the verify endpoint. The API always enforces merchant binding and expiry, but it enforces the resource caveat only when the verify request includes a resource value — which this tool does not send. valid: true therefore means "a real payment for some resource of yours" — check that the resource field in the result matches the resource the payer is asking for before granting access. See Token Binding below.
Example:
User: The requesting agent sent this L402 token. Verify it.
Claude: [Uses l402_producer with action="verify", macaroon="AgEL...", preimage="7f8a9b..."]
Payment verified! The agent has paid 50 sats for /api/weather/forecast.
Granting access now.
Response (valid):
{
"success": true,
"valid": true,
"resource": "/api/weather/forecast",
"message": "Payment verified. The payer has paid — you can now grant access to the resource."
}
Response (invalid):
{
"success": true,
"valid": false,
"message": "Payment verification failed. The token is invalid or the invoice has not been paid. Do NOT grant access."
}
Challenge Idempotency
Send an Idempotency-Key header and a retry gets the same invoice and macaroon back for the life of that invoice — never a second one your payer could also pay. The key is recorded on the challenge itself, so the replay survives a deploy, a restart, and a load balancer routing your retry elsewhere.
curl -X POST https://api.lightningenable.com/api/l402/challenges \
-H "X-API-Key: YOUR_MERCHANT_API_KEY" \
-H "Idempotency-Key: forecast-req-7f2a" \
-H "Content-Type: application/json" \
-d '{"resource": "/api/weather/forecast", "priceSats": 50}'
- Same key with the same resource and price replays the original, and the response carries
X-Idempotency-Replayed: true. - Same key with a different resource or price, while the original invoice is still live, is a
409— one key means one live charge. - Once that invoice expires (10 minutes by default,
L402Options.InvoiceExpirySeconds), the key is released and the next call mints fresh — at any price. A spent key is not poisoned. - Need a second challenge for the same resource before then? Use a different key.
Without a key, the API falls back to deduplicating on the client's IP address for the same window — fine for middleware on one server, unreliable behind a load balancer.
X-Idempotency-Key, the spelling this API shipped with, still works and behaves identically.
Full rules in the Producer API Reference.
Knowing When You Got Paid
Two ways, and you will usually want both.
Poll — GET /api/l402/challenges lists what you have minted with a status of paid, unpaid, or expired:
curl "https://api.lightningenable.com/api/l402/challenges?status=paid&limit=50" \
-H "X-API-Key: YOUR_MERCHANT_API_KEY"
Get notified — configure a callback URL under Dashboard → Settings → Webhooks and Lightning Enable POSTs l402.challenge.paid the first time a credential from one of your challenges verifies, HMAC-signed like every other Lightning Enable webhook:
{
"event": "l402.challenge.paid",
"paymentHash": "abc123...",
"resource": "/api/weather/forecast",
"amountSats": 50,
"paidAt": "2026-09-05T18:00:41Z",
"idempotencyKey": "forecast-req-7f2a"
}
It fires once per challenge — L402 tokens verify many times inside their window, and only the first transition notifies. It is proof of payment, not a settlement record: the sats settled with your payment provider when the invoice was paid, and Lightning Enable does not hold funds. An invoice paid but never presented back to Lightning Enable produces no event, which is what the status=unpaid listing is for.
See Payment webhooks.
REST API Quick Start
The MCP tools call two REST endpoints under the hood. You can call them directly from any language.
The Producer API Reference is the authoritative page for these endpoints — full request/response contracts (including the mppChallenge field), error tables, idempotency semantics, and the caveat-enforcement rules. The two calls below are just the quick start.
Create a challenge:
curl -X POST https://api.lightningenable.com/api/l402/challenges \
-H "X-API-Key: YOUR_MERCHANT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"resource": "/api/weather/forecast",
"priceSats": 50,
"description": "7-day weather forecast"
}'
Verify a token — pass resource so the API enforces the macaroon's path caveat server-side (omit it and the bound resource is only reported back, not compared):
curl -X POST https://api.lightningenable.com/api/l402/challenges/verify \
-H "X-API-Key: YOUR_MERCHANT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"macaroon": "AgELbGlnaHRuaW5n...",
"preimage": "7f8a9b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a",
"resource": "/api/weather/forecast"
}'
The verify endpoint returns 200 OK for both valid and invalid tokens — read the valid field, not the status code.
End-to-End Example: Weather Data Agent
Here is a complete example of a producer agent that charges for weather data.
Producer Agent Setup
{
"mcpServers": {
"lightning-enable": {
"command": "dotnet",
"args": ["tool", "run", "lightning-enable-mcp"],
"env": {
"STRIKE_API_KEY": "your-strike-key",
"LIGHTNING_ENABLE_API_KEY": "your-merchant-api-key"
}
}
}
}
Producer Agent Behavior
Requesting Agent: "I need the 7-day forecast for New York City."
Producer Agent (Claude):
1. [Uses l402_producer with action="create",
resource="/api/weather/forecast/nyc",
priceSats=25,
description="7-day NYC weather forecast"
]
2. Returns to requesting agent:
"Access to this forecast costs 25 sats.
Pay this invoice: lnbc250n1p3...
Then send me: L402 AgEL...:your_preimage"
Requesting Agent:
3. [Uses pay_l402_challenge(invoice="lnbc250n1p3...", macaroon="AgEL...")]
4. "Here's my token: L402 AgEL...:7f8a9b2c..."
Producer Agent (Claude):
5. [Uses l402_producer with action="verify", macaroon="AgEL...", preimage="7f8a9b2c..."]
6. Payment verified! Now fetching the forecast...
7. "NYC 7-Day Forecast:
Monday: Sunny, 72F
Tuesday: Partly cloudy, 68F
..."
Consumer Agent Setup
The consumer agent only needs a wallet — no subscription required:
{
"mcpServers": {
"lightning-enable": {
"command": "dotnet",
"args": ["tool", "run", "lightning-enable-mcp"],
"env": {
"STRIKE_API_KEY": "consumer-strike-key"
}
}
}
}
Use Cases
AI Research Agent
Charge other agents for access to your curated research database:
l402_producer(
action="create",
resource="/research/market-analysis",
priceSats=100,
description="Q1 2026 market analysis report"
)
Code Review Agent
Offer automated code review as a paid service:
l402_producer(
action="create",
resource="/services/code-review",
priceSats=500,
description="Automated code review with security analysis"
)
Data Aggregation Agent
Sell aggregated data from multiple sources:
l402_producer(
action="create",
resource="/data/crypto-sentiment",
priceSats=50,
description="Real-time crypto sentiment score from 10 sources"
)
Translation Agent
Charge per translation request:
l402_producer(
action="create",
resource="/translate/en-to-ja",
priceSats=10,
description="English to Japanese translation"
)
Security Considerations
Always Verify Before Granting Access
Never grant access based on a payer claiming they paid. Always call l402_producer with action="verify" to cryptographically confirm:
- The preimage matches the payment hash (
SHA256(preimage) == payment_hash) - The macaroon signature is valid (not tampered with)
- The macaroon was issued under your merchant account (always enforced server-side)
- The token has not expired (always enforced server-side)
Token Binding
Each L402 token carries caveats binding it to the merchant who issued it, an expiry, the resource it was issued for, and the amount charged. What the verify endpoint enforces server-side depends on what you send:
- Merchant binding — always enforced. Verifying with your API key a token that was issued under a different merchant returns
valid: false. No opt-out. - Expiry — always enforced. A token past its validity window (60 minutes by default) returns
valid: false. - Resource (path caveat) — enforced only when your verify request includes
resource. If you omit it, the bound resource is returned in the response but not compared — the comparison is your responsibility. - Amount — enforced only when your verify request includes
amountSats; otherwise returned but not compared.
The MCP l402_producer tool with action="verify" sends only the macaroon and preimage, so path/amount enforcement does not apply on that route — always compare the resource in the tool result against the resource you're about to grant before serving it. When calling the REST endpoint directly, pass resource (and amountSats if you gate multiple price tiers) to get server-side enforcement. See caveat enforcement rules in the Producer API Reference for the full contract.
Idempotency Keys
Send an Idempotency-Key on every mint. It is what stops a retry — a timeout, a redeploy mid-request, a client that fires twice — from producing a second invoice your payer could also pay. Without one the server falls back to the client's IP, which works on a single server and not behind a load balancer.
Use a key derived from the request you are serving (an order id, a request id), not a fresh UUID per attempt: a new UUID on the retry defeats the point.
Next Steps
- Sell With Your Agent — the same producer flow driven end to end by an MCP agent, from an empty account to a live paid endpoint
- MCP Complete Guide — full tool reference
- API Monetization — Monetize existing APIs via native middleware (recommended) or hosted proxy mode
- AI Agent Integration — Consumer-side L402 tools and wallet setup
- L402 API Reference — Complete L402 protocol reference