Pay-per-prompt AI

No accounts. No API keys. No credit cards. Just sats and inference.

BETA  Owlrun is in public beta. Model availability depends on connected provider nodes. Small amounts of ecash may be lost due to bugs or network issues. Don't load more sats than you're willing to lose.

Contents

For Humans

Owlrun Chat lets you talk to AI models and pay per prompt with Bitcoin ecash. No signup, no accounts — your sats are your session.

Three steps

  1. Top up — Load ecash from a Cashu wallet, or scan a Lightning QR to top up directly.
  2. Prompt — Type your message. Each prompt costs a few sats, deducted automatically. Change is returned instantly.
  3. Take your change — Withdraw your remaining sats before closing the tab. Your ecash lives in the browser — if you close without withdrawing, it's gone.

How it works

Your browser holds ecash proofs (digital cash tokens) in local storage. When you send a prompt, the browser attaches those proofs to the request. The gateway verifies them, runs inference on a GPU node, and returns the AI response along with your change.

The gateway never holds your funds. It claims your proofs at the moment of inference and returns change in the same response. Zero custody.

Compatible wallets

Ecash (load & withdraw)

Minibits — recommended. Available on iOS and Android. Supports cashuA and cashuB token formats.

Use Minibits to:

  • Send ecash tokens to the chat (Load sats)
  • Receive ecash tokens from the chat (Withdraw)
  • Scan QR codes to transfer

Lightning (top up)

Phoenix — recommended. Scan the Lightning invoice QR in the chat to top up your browser wallet.

Any Lightning wallet works for top-up:

  • Phoenix, Wallet of Satoshi, Muun, Breez
  • Scan the QR → sats arrive in browser

Important: withdraw before closing

Your ecash lives only in this browser tab. If you close it without withdrawing, your sats are lost forever. No recovery. Always click "Withdraw" and scan the QR with your Cashu wallet before leaving.

🦞 For Agents/Lobsters

🦞 Agents speak fluent Sats.

One HTTP header. No keys, no accounts, no CAPTCHA. Your agent pays in sats and gets inference. OpenAI-compatible — swap your base URL and add X-Cashu.

The simplest AI inference API

curl -X POST https://api.owlrun.me/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "X-Cashu: cashuAeyJ0b2..." \
  -d '{"model":"qwen2.5:0.5b","messages":[{"role":"user","content":"Hello"}],"stream":true}'

Attach ecash proofs in the X-Cashu header. Change returned in the SSE stream. That's it.

X-Cashu header

The X-Cashu header carries a cashuA token — base64url-encoded JSON containing ecash proofs from our mint (mint.owlrun.me).

HeaderValueRequired
AuthorizationBearer YOUR_API_KEYYes
X-CashucashuA... (ecash token)Yes (for paid inference)
Content-Typeapplication/jsonYes

Payment flow

  1. Claim — Gateway swaps your proofs at the mint (atomic, pre-job). If proofs are invalid or spent, you get 402.
  2. Inference — Your prompt is routed to the best available GPU node.
  3. Billing — Actual cost calculated from token count. Minimum 2 sats per job.
  4. Change — Overpayment returned as a cashu_change SSE event at the end of the stream.

Change delivery

Change arrives as the last SSE event after inference completes:

data: {"model":"qwen2.5:0.5b","message":{"content":"Hello!"},"done":false}
data: {"model":"qwen2.5:0.5b","message":{"content":""},"done":true,...}
data: {"type":"cashu_change","token":"cashuAeyJ0b2...","change_sats":48}

Critical: Your SSE parser must NOT stop at "done":true. Keep reading until the stream closes to catch the change event.

Pre-job refund

If inference fails before starting (no nodes available, node rejected), your proofs are returned via the X-Cashu-Change response header — not SSE (streaming hasn't started).

API endpoints

EndpointMethodDescription
/v1/chat/completionsPOSTOpenAI-compatible chat inference (streaming)
/v1/completionsPOSTCompletion inference
/v1/modelsGETList available models with pricing (USD per million tokens)
/v1/oracle/rateGETCurrent BTC/USD rate, margins, pricing formula
/v1/broadcastsGETSystem announcements
/healthzGETGateway liveness check (returns {"status":"ok"})

Error responses

StatusMeaningAction
400Invalid X-Cashu tokenCheck token format (must start with cashuA)
401Invalid API keyCheck Authorization header
402Proofs spent or invalidMint fresh ecash
503No nodes availableCheck X-Cashu-Change header for refund, retry

Code examples

Python

import requests
import json

API = "https://api.owlrun.me"
KEY = "owlr_buy_YOUR_KEY"
TOKEN = "cashuAeyJ0b2..."  # your ecash token

resp = requests.post(f"{API}/v1/chat/completions",
    headers={
        "Authorization": f"Bearer {KEY}",
        "X-Cashu": TOKEN,
        "Content-Type": "application/json",
    },
    json={
        "model": "qwen2.5:0.5b",
        "messages": [{"role": "user", "content": "What is Bitcoin?"}],
        "stream": True,
    },
    stream=True,
)

change_token = None
for line in resp.iter_lines():
    if not line:
        continue
    text = line.decode()
    if text.startswith("data: "):
        text = text[6:]
    try:
        data = json.loads(text)
        if data.get("type") == "cashu_change":
            change_token = data["token"]
            print(f"\nChange: {data['change_sats']} sats")
        elif data.get("message", {}).get("content"):
            print(data["message"]["content"], end="", flush=True)
    except json.JSONDecodeError:
        pass

# change_token contains your remaining sats — save it for next request

JavaScript (Node.js)

const resp = await fetch("https://api.owlrun.me/v1/chat/completions", {
  method: "POST",
  headers: {
    "Authorization": "Bearer owlr_buy_YOUR_KEY",
    "X-Cashu": "cashuAeyJ0b2...",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "qwen2.5:0.5b",
    messages: [{ role: "user", content: "What is Bitcoin?" }],
    stream: true,
  }),
});

const reader = resp.body.getReader();
const decoder = new TextDecoder();
let buffer = "";

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  buffer += decoder.decode(value, { stream: true });
  const lines = buffer.split("\n");
  buffer = lines.pop();
  for (const line of lines) {
    const trimmed = line.trim();
    if (!trimmed) continue;
    const data = trimmed.startsWith("data: ") ? trimmed.slice(6) : trimmed;
    try {
      const parsed = JSON.parse(data);
      if (parsed.type === "cashu_change") {
        console.log(`\nChange: ${parsed.change_sats} sats`);
        // Save parsed.token for next request
      } else if (parsed.message?.content) {
        process.stdout.write(parsed.message.content);
      }
    } catch {}
  }
}

curl (one-liner)

curl -N https://api.owlrun.me/v1/chat/completions \
  -H "Authorization: Bearer owlr_buy_YOUR_KEY" \
  -H "X-Cashu: cashuAeyJ0b2..." \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen2.5:0.5b","messages":[{"role":"user","content":"Hello"}],"stream":true}'

Pricing

Transparent, verifiable

Every sat is accounted for. Check the live rate and formula at /v1/oracle/rate.

ItemValue
Minimum per job2 sats — anti-spam floor (1 to provider, 1 to gateway). Every request costs real sats, preventing micro-prompt flood attacks.
Gateway marginUnder 10% (9% inference + ~1% FX)
Provider share90%+ of every job
BTC/USD rate24h average, published daily at midnight UTC
Lightning feesPaid by the party withdrawing (not per-job)

FAQ

Where do I get ecash?

Use the Lightning top-up in chat.owlrun.me — scan the QR with any Lightning wallet. Or send ecash from Minibits.

What happens if I close the tab?

Your ecash proofs live in your browser's local storage. If you close the tab without withdrawing, the proofs are lost. Always withdraw first.

Can I use this from my own code?

Yes. The API is OpenAI-compatible. Add the X-Cashu header with ecash proofs and you're good. See the code examples above.

What models are available?

Check GET /v1/models for the current list with live pricing. During beta, availability depends on connected provider nodes.

curl https://api.owlrun.me/v1/models | jq
{
  "object": "list",
  "data": [
    {
      "id": "qwen2.5:0.5b",
      "object": "model",
      "owned_by": "owlrun",
      "pricing": {
        "per_m_input_usd": 0.02,
        "per_m_output_usd": 0.06
      }
    }
  ]
}

Is this custodial?

No. The gateway never holds your funds. Your ecash proofs are in your browser (or your agent's memory). The gateway claims proofs at the moment of inference and returns change in the same response. Zero custody.

What's the minimum payment?

2 sats per job — an anti-spam floor. Even on micro-prompts, 1 sat goes to the provider and 1 sat to the gateway. This "pay to play" minimum makes DDoS-by-microprompt economically unviable while keeping real usage nearly free.

Can agents use this without an API key?

Currently an API key is required for authentication. The ecash provides payment. We're working toward making ecash the only credential needed — sats are the CAPTCHA.

What if my payment fails?

If proofs are invalid or spent, you get a 402 error. If no nodes are available, your proofs are returned via the X-Cashu-Change response header. The gateway never charges for failed inference.