Skip to main content

Developer Docs

L402 + MCP + WebLN. One scrollable page.

How It Works

Every paid endpoint uses the L402 protocol — a three-step challenge/response flow where the Lightning invoice is the credential. No API keys, no accounts, no signup.

C
Client / Agent
POST /api/models/image (no auth)
402 + WWW-Authenticate: L402 macaroon="...", invoice="lnbc..."
Pay invoice with any Lightning wallet → get preimage
POST + Authorization: L402 macaroon:preimage
200 OK + result (image URL, text, etc.)
S
Sats4AI
Request (no auth) 402 Challenge Authenticated
1. Request

Hit any endpoint without auth. You get back HTTP 402 with a Lightning invoice and a macaroon.

2. Pay

Pay the invoice with any Lightning wallet. The payment proof (preimage) is your credential.

3. Use

Resend with Authorization: L402 macaroon:preimage. Get your result.

How this differs from a normal AI API

Payment is authorization, not authentication — there isn't even a login endpoint.

Sats4AIA typical hosted AI API
SignupNoneAccount + email
CredentialA paid Lightning invoiceAPI key / OAuth token
Identity / KYCNoneOften required
BillingPer request, in satsMonthly plan / prepaid balance
If a call failsAutomatic Lightning refund (lnurl_withdraw)Credits, or a support ticket
Data retentionNot retainedLogged to your account

Getting Started

Follow these steps to go from zero to your first API call.

  1. 1
    Get a Lightning wallet. For the manual L402 flow, your wallet must show the payment preimage after paying — that's your credential. Tested and confirmed: Phoenix, Breez, Zeus, and Blink all display it in payment details. Some wallets (Muun, Wallet of Satoshi) hide the preimage — see the wallet matrix. Using MCP instead? Any BOLT11 wallet works — the paymentId flow never needs the preimage. For agents, use Lightning Wallet MCP for automated payments.
  2. 2
    Pick your integration. Three options:
    • L402 (HTTP) — standard REST calls with Lightning auth. Works with any language.
    • MCP — add one line to your Claude/Cursor config. Payment negotiated in-protocol.
    • WebLN — browser apps with Bitcoin Connect or Alby.
  3. 3
    Discover services. Browse the service catalog, or let your agent discover tools programmatically:
    • GET /api/discover?q=generate portrait — semantic search
    • GET /api/estimate-cost?service=image — pricing before payment
    • GET /.well-known/l402-services — full machine-readable catalog
  4. 4
    Make your first call. Copy the quickstart example below, pay the invoice, and get your result. Most services cost 5-200 sats ($0.005-$0.20).

Quickstart — pick a flow

Four ways to use Sats4AI. Pick the one that matches your client.

L402 (HTTP + Lightning)

For agents and CLI clients. 402 challenge, pay, retry. Works with any language.

# 1. Hit the endpoint without auth → get a 402 + invoice
curl -i -X POST https://sats4ai.com/api/models/image \
  -H "Content-Type: application/json" \
  -d '{"prompt":"a cat in a tophat"}'

# Response: 402 Payment Required
# WWW-Authenticate: L402 macaroon="...", invoice="lnbc..."

# 2. Pay the invoice with any Lightning wallet, get a preimage

# 3. Re-send with Authorization header
curl -X POST https://sats4ai.com/api/models/image \
  -H "Authorization: L402 <macaroon>:<preimage>" \
  -H "Content-Type: application/json" \
  -d '{"prompt":"a cat in a tophat"}'

Agent SDKs (zero protocol code)

Lightning Labs' L402sdk handles the full 402 flow for you — agent hits any Sats4AI endpoint, SDK pays the invoice automatically. Works with LND, CLN, or any NWC wallet (Alby, Mutiny, Phoenix).

// Lightning Labs L402sdk — Vercel AI SDK tools
// npm i @lightninglabs/l402-ai ai @ai-sdk/openai
import { createL402Tools, WasmL402Client, WasmBudgetConfig } from "@lightninglabs/l402-ai";
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";

// Connect the SDK to a Lightning backend (LND, CLN, NWC, or SwissKnife)
const client = WasmL402Client.withLndRest(
  process.env.LND_URL!,
  process.env.LND_MACAROON!,
  // Budget caps: perRequest=1000 sats, daily=50_000 sats
  new WasmBudgetConfig(1000, 0, 50_000, 0),
  100, // request timeout (s)
);

// Gives the agent l402_fetch / l402_get_balance / l402_get_receipts
const tools = createL402Tools({ client });

const result = await generateText({
  model: openai("gpt-4o"),
  tools,
  maxSteps: 5,
  prompt: "Generate an image of a cat in a tophat using https://sats4ai.com/api/models/image (model: nano-banana-2). Return the image URL.",
});
// The agent autonomously hits the 402, pays the invoice, retries with L402 auth.

MCP (Claude / Cursor)

Inline payment negotiation handled by the MCP transport. Plain HTTP can't do this.

// claude_desktop_config.json (or ~/.claude.json / Cursor)
{
  "mcpServers": {
    "sats4ai": {
      "url": "https://sats4ai.com/api/mcp"
    }
  }
}

// Then in your MCP client:
// 1. tools/call create_payment → returns Lightning invoice
// 2. Pay with any Lightning wallet
// 3. tools/call <tool_name> with the paymentId → results
//
// Or use lightning-wallet MCP for fully-automated payments.

Client quirks: Windsurf uses serverUrl (not url) in ~/.codeium/windsurf/mcp_config.json, and Cascade caps 100 tools across all MCP servers. Claude Code: claude mcp add sats4ai --transport http https://sats4ai.com/api/mcp. Clients without remote-HTTP support: run the stdio proxy, npx sats4ai-mcp. Restart the client after config changes.

WebLN (Browser)

For browser apps with Alby / Bitcoin Connect.

// Browser flow with Bitcoin Connect / Alby
import { requestProvider } from "@getalby/bitcoin-connect";

const res = await fetch("/api/charge?service=image&model=...");
const { invoice, paymentId } = await res.json();

const provider = await requestProvider();
await provider.sendPayment(invoice);

// Submit the work request
const fd = new FormData();
fd.set("paymentId", paymentId);
fd.set("prompt", "a cat in a tophat");
const out = await fetch("/api/models/image", { method: "POST", body: fd });

Payment Protocols

  • L402 — HTTP 402 + Lightning. Macaroon issued in WWW-Authenticate; pay invoice; resend with Authorization: L402 macaroon:preimage. /l402.
  • MCP — JSON-RPC 2.0 over Streamable HTTP at /api/mcp. Tools negotiate payment in-protocol; the agent never has to parse 402 responses. /mcp.
  • MPP — Machine Payment Protocol. Same surface as L402, slightly different header naming. Both are accepted on every paid endpoint.
  • WebLN — for browser flows. Use /api/charge to mint an invoice, pay via the wallet provider, then submit the work with the paymentId.
  • Refunds — every post-payment failure includes an LNURL-withdraw link in the refund field. No support tickets needed.

Macaroon Caveats

Macaroons we issue are fail-closed: any unrecognised caveat rejects the request. Currently enforced:

  • RequestPath — macaroon binds to the exact endpoint path used for the 402 challenge.
  • ExpiresAt — typically 10 minutes from issuance.
  • PaymentHash — bound to the Lightning invoice; preimage required.
  • Service + Model — bound to the model that priced the call. Auto-routed calls must be retried with the routed model id (see Auto-Routing below).

Error Codes

Every error response includes a machine-readable error_code in both the JSON body and the X-Error-Code header. Full catalog at /api/error-codes.

curl https://sats4ai.com/api/error-codes
# {
#   "version": 1,
#   "codes": {
#     "TIMEOUT": "Request or upstream provider timed out. Retry later.",
#     "CONTENT_FILTERED": "Output blocked by safety/content moderation. Rephrase the prompt.",
#     "L402_INVALID_PARAMS": "Pre-payment validation failed. No invoice was created; no sats charged.",
#     "L402_REFUND_ISSUED": "Response payload includes a refund object with an LNURL-withdraw link.",
#     ...
#   }
# }

# Every error response includes:
#   - JSON: { "error": "...", "error_code": "TIMEOUT" }
#   - Header: X-Error-Code: TIMEOUT

Async Jobs

Long-running services (audiobook, video, transcription, AI calls, 3D models) return HTTP 202 with a standard shape. Poll the poll_url at poll_interval_ms.

# 1. Pay + submit a long-running job
curl -X POST https://sats4ai.com/api/models/epub-audiobook \
  -H "Authorization: L402 ..." \
  -F "file=@book.epub" -F "voice=Ashley"

# Response: 202 Accepted
# {
#   "status": "queued",
#   "job_id": "abc123",
#   "poll_url": "https://sats4ai.com/api/models/epub-audiobook/status?id=abc123",
#   "poll_interval_ms": 3000,
#   "estimated_completion_ms": 600000
# }
# Headers: X-Job-Id, X-Poll-Url, X-Poll-Interval-Ms

# 2. Poll the status URL until status === "completed"
curl https://sats4ai.com/api/models/epub-audiobook/status?id=abc123

Webhooks / Callbacks

Skip the polling loop. Pass callback_url + callback_id on any async job and we'll POST you when it finishes. HMAC-signed, SSRF-validated, opt-in.

# OPT-IN — include callback_url + callback_id in your async request.
# Polling keeps working; the webhook is a supplement, not a replacement.

curl -X POST https://sats4ai.com/api/models/epub-audiobook \
  -H "Authorization: L402 ..." \
  -F "file=@book.epub" -F "voice=Ashley" \
  -F "callback_url=https://your-app.example.com/hooks/sats4ai" \
  -F "callback_id=user-42-job-7"

# Response: 202 Accepted
# {
#   "status": "queued",
#   "job_id": "abc123",
#   "poll_url": "...",
#   "callback_id": "user-42-job-7",
#   "callback_registered": true,
#   "callback_secret": "<per-job 64-hex HMAC secret>"
# }

# When the job finishes we POST your callback_url with:
#   Headers: X-Sats4AI-Signature: sha256=<hex>
#   Body: {
#     "job_id": "abc123",
#     "callback_id": "user-42-job-7",
#     "status": "completed" | "failed",
#     "result_url" | "error_code" + "error",
#     "timestamp": "2026-04-13T..."
#   }

# Verify the signature (Node):
#   const mac = crypto.createHmac("sha256", callback_secret)
#                     .update(rawBody).digest("hex");
#   const ok  = req.headers["x-sats4ai-signature"] === "sha256=" + mac;

# Validation:
#   - callback_url MUST be public HTTPS (no localhost / private ranges / IP literals in RFC1918)
#   - callback_id is OPAQUE, max 128 chars, no control chars
#     → do NOT embed PII or secrets; it is logged server-side
#   - rejection → response includes "callback_registered": false + a reason.
#     Your job still runs; poll poll_url instead.
#   - retries: at 0s / 5s / 30s. 4xx aborts. Best-effort (single instance).

Privacy note: callback_id is echoed back in our logs and the webhook body — treat it as an opaque correlation string, not a place to stash user data. Validation rejects http://, loopback, link-local, and RFC1918 hosts so an attacker can't point us at your metadata service.

Auto-Routing

For categories with multiple models (text, image, audio), pass "model": "auto" to let the server pick the best default. The chosen model id is returned in the X-Route-Model response header.

# Send model: "auto" — server picks the best for the category
curl -i -X POST https://sats4ai.com/api/models/image \
  -H "Content-Type: application/json" \
  -d '{"prompt":"a cat","model":"auto"}'

# Response: 402 Payment Required
# X-Route-Model: nano-banana-2          ← chosen model id
# X-Route-Category: Image
# X-Error-Code: L402_AUTO_ROUTED

# IMPORTANT: on the paid retry, send the *routed* model id, NOT "auto":
curl -X POST https://sats4ai.com/api/models/image \
  -H "Authorization: L402 ..." \
  -d '{"prompt":"a cat","model":"nano-banana-2"}'

# Otherwise the macaroon's price-per-model caveat may reject the request.

URL-path model selection

Convenience for clients that prefer paths over body fields:

# Convenience: model in URL path
curl -X POST https://sats4ai.com/api/m/text-generation/gpt-oss-120b \
  -H "Content-Type: application/json" \
  -d '{"prompt":"hello"}'

# Forwards to /api/models/text-generation with model injected.
# Body field still wins if both are set.

Estimate Cost

Pre-payment quotes for budget-aware agents. No auth, no side effects. /api/estimate-cost with no params returns the catalog.

# Get a quote before paying
curl 'https://sats4ai.com/api/estimate-cost?service=text-to-speech&model=omnivoice&chars=1500'
# {
#   "service": "text-to-speech",
#   "amount_sats": 15,
#   "currency": "BTC",
#   "breakdown": { "type": "per-character", "chars": 1500, "chars_per_sat": 100, "model": "omnivoice" },
#   "error_code": "L402_ESTIMATE_ONLY"
# }

# List the catalog
curl https://sats4ai.com/api/estimate-cost

Request Deduplication

Identical POST bodies from the same IP within 30 seconds are returned from cache. Useful when an agent retries due to a network blip and you don't want to be charged twice.

# Identical POST within 30s returns the cached response (no new payment)
# Response headers:
#   X-Dedup: hit       ← cached
#   X-Dedup: miss      ← fresh

# Bypass the cache with X-No-Cache: true
curl -X POST https://sats4ai.com/api/models/image \
  -H "X-No-Cache: true" -d '{...}'

# Note: dedup is best-effort, single-instance, 30s TTL.
# For strong idempotency use a unique paymentId per job.

CORS / Browser Use

All payment + routing headers are explicitly listed in Access-Control-Expose-Headers so browser fetch() can read them.

# Headers exposed to browser fetch():
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, X-No-Cache,
  Payment-Signature, X-Cashu
Access-Control-Expose-Headers: WWW-Authenticate, Payment-Receipt,
  Payment-Required, X-Route-Model, X-Route-Category, X-Dedup,
  X-Job-Id, X-Poll-Url, X-Poll-Interval-Ms, X-Error-Code

# So in the browser:
const res = await fetch("/api/models/image", { method: "POST", body });
res.headers.get("WWW-Authenticate");   // ← visible
res.headers.get("X-Error-Code");       // ← visible

Services

30+ AI services. Per-service docs live at /l402 (curl + payment examples) and the MCP tool catalog at /mcp.

Machine-readable discovery:

Compound endpoints (recipes)

Chained pipelines with a single payment and an outcome-shaped output. Cheaper than chaining primitives manually for the common case.

  • transcribe_translate — audio → transcript → translation (119 target languages). Perfect for WhatsApp voice messages in a language you don't speak, or reading a meeting recorded in another language. Auto-detects source.
  • open_voice_bridge + voice_bridge_say / _poll / _end — live phone call where your LLM is the brain. PSTN + 602-lang TTS + ~100-lang STT.
  • epub_to_audiobook — EPUB/PDF/TXT → M4B audiobook in 600+ languages with optional translation.
  • extract_receipt — receipt image → structured JSON (merchant, total, line items).

Capability-first GitHub repos

Per-service landing repos with runnable curl, Python, and TypeScript examples. MIT-licensed. Use as drop-in starting points or reference implementations.

  • ai-caller — AI voice agent API (alternative to Vapi, Retell, Bland)
  • book-to-audiobook — EPUB/PDF/TXT to audiobook in 600+ languages, with optional translation
  • pay-per-use-fax — Send a fax with Lightning, no contract or monthly fee
  • fax-to-email — Receive faxes to your inbox, no monthly number rental

Voice Bridge — a la carte phone calls

Live phone calls where your LLM is the brain. Sats4AI supplies composable primitives: PSTN + streaming STT + TTS. You drive each turn. Conversation context stays on your side — we never see it.

Four endpoints: /open (pay + place call) → /poll (get transcripts) → /say (text or raw audio) → /end (hangup + refund). Deposit billing: ~10 sats/min US/CA, ~30 intl, ~80 rare. Unused time auto-refunded.

For a turnkey voice agent where we run the brain, use ai_call instead. Voice Bridge is for agents that already have a brain and just need a phone line, ears, and a mouth.

  • STT: Gladia primary (~100 languages, free first 10 h/mo), Deepgram Nova-3 failover.
  • TTS: OmniVoice (602 languages). BYO audio supported via audio_base64 + encoding: mulaw_8000 | pcm_l16_16000.
  • Coverage matrix: /api/l402/voice-bridge/coverage.
  • MCP tools: open_voice_bridge, voice_bridge_say, poll_voice_bridge, end_voice_bridge.

Choosing a Voice Tier (TTS)

Three tiers optimized for different use cases:

TierLanguagesPriceQualityWhen to Choose
OmniVoice Global602+100 chars/satGoodRare/underserved languages (Yoruba, Marathi, Twi, Cebuano). Widest coverage by far.
Inworld Premium~950 chars/satBest (ELO #1)English and major languages where quality matters most. Highest fidelity voice.
Minimax Studio~910 chars/satGreatVoice cloning. Use when you need a specific voice (your own, a character, a brand).

Choosing a Text Model (LLM)

Pass model: "auto" to let us route to the cheapest model that meets quality bar, or pick explicitly:

ModelMakerPrice (sats)When to Choose
Qwen3 32BAlibaba~0.001 sats/charCheapest. 119 languages, ultra-fast. Best for translation and multilingual tasks.
GPT-OSS 120BOpenAI~0.003 sats/charMid-tier. Ultra-fast, 131K context. Code gen, quick answers, everyday tasks.
Kimi K2.5Moonshot AI~0.01 sats/charSmartest. 1T params, 262K context, vision, thinking mode. Complex reasoning and analysis.

Recipes (Compound Outcomes)

Two kinds of services live on Sats4AI: primitives (single capability, one call — generate-image, translate-text, send-sms) and recipes (compound outcomes we assemble from multiple primitives so you don't have to — stateful, real-time, or multi-step). Both are first-class. Orchestrators that want fine control use primitives. Agents and users that want an outcome in one call use recipes.

Every discovery entry carries endpoint_type: "primitive" | "recipe". Filter on it to list only recipes:

curl https://sats4ai.com/.well-known/l402-services \
  | jq '.services[] | select(.endpoint_type == "recipe") | .id'

Recipes available today

RecipePrimitives chainedOutcome
extract-receiptOCR + LLMReceipt or invoice → structured JSON (merchant, line items, totals, tax, currency, category).
extract-documentpdf.js + OCR + layout analysisPDF or image → clean Markdown. Smart routing per page: text-layer when present, OCR when scanned, hybrid when mixed.
epub-audiobookparse + TTS + translate (optional) + assembleEPUB/PDF/TXT → M4B audiobook with chapter markers. Resumable, 602-language narration, optional translation before narration.
ai-callPSTN + STT + LLM + TTSSend an AI agent to make a two-way phone call. Our brain. Auto-retries, transcript + analysis returned.
voice-bridgePSTN + streaming STT + TTS (session)Real-time phone call where YOUR LLM is the brain. /open/poll/say/end. Conversation context never leaves your side.
send-faxFax transport + page accountingSend a fax (PDF URL or typed text) to any number worldwide. Optional cover page. Tiered pricing per page.
receive-faxFax receive + caller-ID match + email delivery (+ optional OCR)Open a 24h receive window at our shared number, matched by caller ID. Delivered to your email with optional OCR add-on.

When to prefer a recipe over chaining primitives

A recipe earns its existence when external orchestration is genuinely hard — real-time sync (voice-bridge, ai-call), cross-step state (epub-audiobook resumability, fax page accounting), regulatory bundling (fax), or a coverage/quality chain only we have end-to-end. If an agent could replicate the outcome with two independent calls, it stays a primitive. New recipes follow the naming pattern <outcome>-<input-or-format> (e.g., extract-receipt, not receiptExtractor).

Limits & Constraints

The hard boundaries of each service, stated up front — on a pay-per-call API every limit you discover by trial costs sats. Parameters are validated before an invoice is created wherever possible; the gotchas column lists the cases that can only fail after payment (those responses include a refund link).

Applies to every service:

  • The L402 macaroon expires 10 minutes after the 402 challenge — pay and redeem promptly, or request a fresh invoice.
  • Identical POST bodies within 30 seconds return the cached response (see Request Dedup).
  • Result download URLs are temporary (~1 hour) — fetch your output promptly.
  • Outbound communication (SMS, email, calls) is scam-screened after payment; flagged content is held and not auto-refunded (see Scam Screening).
ServiceHard limitsWon't work / gotchas
generate-imageReference-image capacity varies by model (1 to 14 images).A prompt can be rejected by the safety filter after payment.
generate-textmaxTokens default 16,384. Vision (imageData) on the Best tier only.Raw PDFs are not accepted — run extract-document first and pass the text as fileContext.
send-sms126 user characters max (a compliance footer uses the rest of the segment). Per-destination caps: 3/hour, 8/day — counted across ALL customers.URLs are rejected before payment (422). Accented characters switch encoding and cost more segments. Non-+1 destinations are sent from an alphanumeric sender — the recipient cannot reply.
generate-video / animate-imageDuration 4-15 s. Resolutions: 480p, 720p, 1080p only. animate-image needs a base64 first frame.Async — poll the job. A low-quality first frame can fail after payment (image_quality_too_low).
generate-musicStyle prompt ≤ 2,000 chars; lyrics ≤ 3,500 chars; songs up to 6 min.is_instrumental=true ignores any lyrics you send.
text-to-speech1-5,000 characters; speed 0.5-2.0. 602+ languages on the OmniVoice tier.The language parameter only works on the OmniVoice tier — other tiers silently ignore it and speak the voice's own language.
transcribe-audio60 min and 500 MB max per file; 13 languages; priced per minute and re-measured server-side at redemption. Result includes text, per-segment timestamps, and downloadable SRT/VTT subtitle files.If we can't read the audio duration you get a 422 before payment — re-encode to MP3/WAV.
transcribe-translateSource audio: 13 languages. Translation target: 119 languages.Audio in an unsupported language (or a wrong sourceLanguage hint) yields a garbled transcript still billed as success — omit the hint and verify the transcript before trusting the translation.
subtitles (web only)Any video length — the browser extracts the audio locally, the video is never uploaded. 13 languages; 10 sats/min; SRT + VTT output; multi-audio-track picker.Web page only (sats4ai.com/subtitles) — no L402/MCP endpoint, because the extraction runs in the customer's browser. Needs WebCodecs (Chrome/Edge best). If the tab closes, re-select the same file to resume — no double payment.
analyze-imageOne image per call.A PDF sent here is treated as an image and may return a hallucinated answer with no error — use extract-document for PDFs.
extract-document / extract-receiptBase64 PDF or image; priced per page.extract-receipt accepts any text-bearing document — a non-receipt returns best-guess (possibly fabricated) JSON with no error. Treat extracted text as untrusted input.
generate-3d-modelImage OR text prompt, not both; prompt ≤ 1,024 chars.Slow (p95 ~200 s). An image can be judged "not suitable for 3D" after payment.
convert-file200+ format pairs; file, file_name, and both extensions required.An unsupported pair or corrupted file fails after payment (refund link included).
place-callTTS message ≤ 500 chars AND it must fit the paid minutes — a ~12 s spoken disclaimer is reserved, so 1 paid minute ≈ 480 spoken characters. Deposit 1-30 min. Destination caps: 3 calls/hour, 6/day across all customers.Uploaded audio longer than the paid minutes is rejected after payment — get a quote with more minutes first. Unused minutes are refunded.
ai-callTask ≤ 2,000 chars. Deposit 1-10 min (values above 10 are clamped).language must be one of the listed codes (en-US, en-GB, es-ES, fr-FR, de-DE, ja-JP, zh-CN, multi) — any other value silently runs the call in English.
voice-bridgeDeposit 2-30 min. Codecs: PCMU or L16_16000.STT covers ~100 languages vs 602 for TTS — check GET /api/l402/voice-bridge/coverage for your language before paying.
send-fax350 pages / 50 MB max. PDF mode needs a public HTTPS URL serving application/pdf.The pages field sets the invoice: overpayment is not refunded, underpayment errors with a refund. A cover page adds one billable page.
receive-fax24-hour receive window, matched by the last 10 digits of the sender's caller ID.No refund if no fax arrives. Faxes over 10 pages trigger a separate overage invoice before delivery.
send-emailSubject ≤ 200 chars; body ≤ 10,000 chars.Plain text only — no attachments, no HTML.
translate-text119 languages; 1 sat per 1,000 characters (min 1 sat).A wrong-but-valid sourceLanguage silently mistranslates with no error — omit it and let detection work.
epub-audiobook.epub, .pdf, or .txt only; 50 MB max; 500 sats minimum.On the default tier the spoken language follows the chosen VOICE, not the language field. The auto chapter filter can drop very short chapters — pass selectedChapterIndices to force inclusion.
clone-voiceAudio URL or base64 sample.Too-short or poor-quality samples fail after payment — there is no pre-payment quality check.
image utilities (upscale, restore-face, deblur, colorize, remove-object, remove-background)upscale: factor 2 or 4 only. deblur: camera-shake/uniform blur only.Content mismatches fail after payment: no face in restore-face, an already-color image in colorize, artistic bokeh in deblur.
merge-pdfsMinimum 2 files.The output URL is temporary (~1 hour) — download promptly.

Wallets

Human wallets — manual L402 flow

The manual flow (pay invoice, copy preimage, send Authorization: L402 macaroon:preimage) only works if your wallet shows the preimage after paying. Verified status per wallet:

WalletShows preimage?Notes
Phoenix ★YesCopyable “Preimage” row in payment details (Android + iOS). Self-custodial. Smoothest first-time path.
Blink ★Yes“Preimage / Proof of Payment” in transaction detail. Easiest custodial on-ramp, instant setup.
Zeus ★YesPreimage in the payment view. Best for node-runners (works standalone or against your own node).
BreezYesPayment-details sheet shows the preimage.
Alby (extension / Hub)YesWebLN sendPayment() returns the preimage to your code; Alby Hub shows a copyable row.
lncli / lightning-cliYespayment_preimage in the command output, by design.
MuunNoNo preimage in the UI as far as we can verify. Fine for paying, but you can't complete the manual flow.
Wallet of SatoshiNoCommunity reports say the preimage isn't shown. Use the MCP flow instead.

★ = recommended starting point. The MCP paymentId flow works with any BOLT11 wallet — no preimage needed. Statuses last verified July 2026; wallet UIs change, so tell us if one is wrong.

Agent wallets

AI agents need a Lightning wallet to pay invoices autonomously. These are complementary tools that give your agent a wallet to spend at Sats4AI:

WalletTypeBest For
L402sdkSDK (TS/Python/Go/Rust)Vercel AI SDK + LangChain agents. Auto-pays L402, built-in budgets. Supports LND, CLN, NWC.
Lightning Wallet MCPMCP toolClaude, Cursor, any MCP client. Fully automated L402 payments.
Alby MCPMCP toolAlby Hub users. Self-custodial.
lngetCLIShell scripts, CI pipelines. Auto-pays L402 invoices.
CLW CashCLI walletBitcoin CLI wallet purpose-built for AI agents.
Glow CloudREST APISelf-deployable wallet API (Breez Spark SDK). Deploy to Vercel free tier.

Any wallet that can pay a BOLT11 invoice can pay us; the tools above just automate the payment step so agents can operate without human intervention. For the manual L402 flow the wallet must also show the preimage — see the matrix above.

Production Checklist

Before going live, verify your integration handles these scenarios:

  • Handle refunds. Post-payment errors include a refund object with an LNURL-withdraw link. Surface this to users or redeem it programmatically.
  • Check error_code not just HTTP status. Use the X-Error-Code header or JSON error_code field to decide retry vs. rephrase vs. escalate. Fetch /api/error-codes once at startup.
  • Respect poll_interval_ms on async jobs. Polling faster wastes requests and may trigger rate limits.
  • Set a budget. If your agent auto-pays invoices, configure a per-call or daily spending limit in your wallet to prevent runaway costs.
  • Handle macaroon expiry. Macaroons expire after ~10 minutes. If payment takes longer, request a fresh invoice.
  • Use auto-routing carefully. When model: "auto" returns a routed model in X-Route-Model, use that exact model id on the paid retry. See Auto-Routing.
  • Validate callback signatures. If using webhooks, verify the X-Sats4AI-Signature HMAC before trusting the payload. See Webhooks.
  • Estimate costs first. For budget-sensitive flows, call /api/estimate-cost before committing to payment.

Troubleshooting

I paid the invoice but got "invalid macaroon"

The macaroon expires ~10 minutes after the 402 challenge. If you took too long to pay, request a new invoice by re-sending the original request without auth. Also verify you're sending both the macaroon and the preimage separated by a colon: Authorization: L402 macaroon:preimage.

My async job is stuck in "processing"

Some jobs (audiobooks, 3D models) can take several minutes. Check estimated_completion_ms in the 202 response. If a job exceeds 2x the estimate and is still processing, the upstream provider may have failed. The job will eventually time out and the response will include a refund LNURL-withdraw link.

I paid, then my client crashed (or the tab closed) and I never got the result

You don't lose the sats. Every paid-but-undelivered charge is caught by an automatic reconciler: if a payment settles and the service is never delivered — tab closed, network dropped, your agent died before polling — the payment is queued as a refund after a grace period (roughly 15–90 minutes depending on the service).

Claim it at /recover with your Payment Hash — the 64-character hex string in your wallet's payment details (not the preimage). No email or account needed. Completed audiobooks can also be re-downloaded there.

If the grace period has clearly passed and /recover finds nothing, the service most likely did deliver — /recover will show what the charge was for.

I got L402_INVALID_PARAMS before paying

Parameters are validated before an invoice is created. No sats were charged. Check the error field for specifics — common causes: missing required field, unsupported model name, file too large, or invalid enum value. Use /api/discover or /.well-known/l402-services to see valid options.

CORS errors in the browser

All endpoints return Access-Control-Allow-Origin: * and expose payment headers. If you're seeing CORS errors, check that you're not sending custom headers that aren't in our allow list. See CORS / Browser for the full header list.

My agent keeps getting charged for retries

Request dedup caches identical POST bodies for 30 seconds. If your agent is retrying with a different body (e.g., re-serialized JSON with different key order), dedup won't match. Use the same exact request body for retries, or pass a unique paymentId for strong idempotency. See Request Dedup.

How do I get a refund?

Every post-payment error includes a refund field with an LNURL-withdraw link. Open it in any LNURL-compatible wallet to claim the refund. If a refund link is missing or expired, go to /recover and enter your Payment Hash — pending refunds are claimable there self-service, no email needed. If /recover also comes up empty, email support with the payment hash and error details.

Which model should I use?

Use model: "auto" and let the server pick. Or call /api/estimate-cost to compare pricing, and /api/l402/models for the full model list with capabilities. The /.well-known/l402-services manifest includes performance metadata (latency, reliability) per model.

API Changelog

Dated record of changes to the external surface — new tools, renamed parameters, pricing-shape changes, deprecations. Internal changes aren't listed. If you integrated a while ago, scan this before assuming your cached tool list is current.

  • 2026-07[MCP] Payment-lifecycle error codes (PAYMENT_NOT_FOUND / PAYMENT_PENDING / PAYMENT_ALREADY_USED) with recovery hints; check_payment_status returns a next field and readyToUse is false once a refund is queued; tools/list now carries MCP ToolAnnotations so clients can auto-approve free status polls.
  • 2026-07[MCP] send_sms / place_call / ai_call destinations are validated as E.164 before any invoice is created — malformed numbers get a clear fix-it error instead of an opaque failure; formatted numbers like +1 (415) 555-0100 are accepted.
  • 2026-06[L402] L402 macaroons now use a standards-compatible identifier — off-the-shelf L402 clients (e.g. lnget) pay any endpoint without custom handling.
  • 2026-06[all] International (non-+1) SMS sends from the "Sats4AI" alphanumeric sender ID by default; UK (+44) destinations enabled.
  • 2026-06[all] Video generation: new resolution parameter (480p / 720p / 1080p) replaces mode (still accepted as a legacy alias); price is now resolution × duration.
  • 2026-06[all] Pricing parity: per-character TTS and per-minute STT now priced identically on MCP, L402, and web; underpaying a job is rejected instead of executed.
  • 2026-06[MCP / L402] Rate limits: POST /api/mcp capped at 240 req/min/IP; unauthenticated L402 challenges rate-limited per IP (429 before an invoice is created).
  • 2026-06[MCP] New tool await_result: stream an async job to completion over SSE in a single call instead of polling.
  • 2026-06[all] SMS contract: per-segment pricing, links in message bodies rejected, short service-disclosure footer appended; abuse screening runs before an invoice is created, with a 1-sat /appeal review.
  • 2026-06[all] TTS and audiobook rates are BTC-pegged: characters-per-sat re-prices hourly against spot BTC.
  • 2026-05[all] Call pricing is carrier-aware per destination — most international calls got cheaper.
  • 2026-05[web] New /recover self-service page: claim a pending refund or re-download a finished audiobook with only your payment hash.
  • 2026-05[all] Image generation prompt limit raised from 500 to 2,000 characters.
  • 2026-04[discovery] Every catalog entry now carries endpoint_type: "primitive" | "recipe"; first recipe transcribe-translate launched (audio → transcript → translation in one payment).
  • 2026-04[L402 / MCP] Agent-ergonomics upgrade: this docs page, pre-payment cost estimates, machine-readable error codes, standardized async responses, opt-in signed webhooks (callback_url), request dedup, CORS; translate accepts ISO codes and locale tags.
  • 2026-04[all] Refund model: prepaid invoices + LNURL-withdraw refund links replaced hold-invoice escrow — post-payment failures return a self-serve withdraw link.
  • 2026-04[all] Fax launched: send and receive (optional OCR), per-page pricing, receive webhook with signing secret.
  • 2026-04[discovery / web] New discovery surfaces: /api/openapi.json, /llms.txt, per-service health flags; the whole site became reachable over a Tor .onion address.
  • 2026-04[all] Text-to-speech reorganized into 3 tiers — Global (602 languages), Premium, Studio (voice clone); music generation upgraded with BPM/key control and longer songs.
  • 2026-03[all] epub-audiobook, AI phone call, and automated phone call launched; roadmap voting tools (list_planned_services / vote_on_service) added.
  • 2026-03[L402] All endpoints renamed to the verb-noun convention (image → generate-image, sms → send-sms, …).
  • 2026-03[all] March service wave: OCR, speech-to-text, send-email, HTML-to-PDF, receipt extraction, voice clone, image edit, PDF merge, and specialist image tools; translate expanded to 119 languages; image results moved from raw base64 to hosted URLs.

Tor Access

Every endpoint is available as a Tor hidden service. No clearnet required, no IP logged.

Hidden service address:

j5tfaz7s7osapdbry4d2wb5usyhtcvrm7kutonliqq7sjv2c47lsgoid.onion

curl

curl --socks5-hostname 127.0.0.1:9050 \
  http://j5tfaz7s7osapdbry4d2wb5usyhtcvrm7kutonliqq7sjv2c47lsgoid.onion/api/discover

Node.js

import { SocksProxyAgent } from "socks-proxy-agent";
const agent = new SocksProxyAgent("socks5h://127.0.0.1:9050");
const res = await fetch(
  "http://j5tfaz7s7osapdbry4d2wb5usyhtcvrm7kutonliqq7sjv2c47lsgoid.onion/api/l402/generate-image",
  { method: "POST", agent, headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ prompt: "a cat", model: "auto" }) }
);

Tor provides end-to-end encryption — http:// is correct and secure over .onion. Tor Browser users visiting sats4ai.com see an automatic “.onion available” banner via the Onion-Location header. See the announcement post for more details.

Scam Screening

Outbound communication services — SMS, AI Call, Automated Call, and email sending — are screened for scam content after payment, before anything is delivered.

What gets flagged

  • Impersonation of a company, bank, exchange, or government
  • Threats, extortion, or harassment of the recipient
  • Solicitation of credentials, passwords, seed phrases, or OTP/2FA codes
  • Payment-pressure scams (fake fees, fines, customs charges, prizes)

Flagged content is held: it is not sent, and the payment is not automatically refunded. If you believe your message was flagged by mistake, request a human review at /appeal (costs 1 sat, anti-spam) with your payment ID and an email address — we reply with the outcome. Approved refunds are claimed at /recover.

Independently of content, per-destination send limits apply: each phone number can only receive a few messages or calls per hour and per day, across all customers. Over-limit requests are rejected before payment.

Security

Sats4AI handles Bitcoin Lightning payments. We take security seriously.

Reporting Vulnerabilities

Do not open a public GitHub issue. Email sats4ai@gmail.com with a description, reproduction steps, and impact assessment. We acknowledge within 48 hours and provide a status update within 7 days.

In scope

  • L402 authentication bypass or macaroon forgery
  • Payment logic errors (double-spend, underpayment acceptance, invoice reuse)
  • API endpoint vulnerabilities (injection, SSRF, IDOR)
  • Information disclosure (API keys, wallet credentials)
  • Denial of service against payment or API infrastructure

Full policy including safe harbor and out-of-scope items: /.well-known/security.txt | SECURITY.md