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.
Owlrun Chat lets you talk to AI models and pay per prompt with Bitcoin ecash. No signup, no accounts — your sats are your session.
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.
Minibits — recommended. Available on iOS and Android. Supports cashuA and cashuB token formats.
Use Minibits to:
Phoenix — recommended. Scan the Lightning invoice QR in the chat to top up your browser wallet.
Any Lightning wallet works for top-up:
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.
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.
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.
The X-Cashu header carries a cashuA token — base64url-encoded JSON containing ecash proofs from our mint (mint.owlrun.me).
| Header | Value | Required |
|---|---|---|
Authorization | Bearer YOUR_API_KEY | Yes |
X-Cashu | cashuA... (ecash token) | Yes (for paid inference) |
Content-Type | application/json | Yes |
cashu_change SSE event at the end of the stream.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.
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).
| Endpoint | Method | Description |
|---|---|---|
/v1/chat/completions | POST | OpenAI-compatible chat inference (streaming) |
/v1/completions | POST | Completion inference |
/v1/models | GET | List available models with pricing (USD per million tokens) |
/v1/oracle/rate | GET | Current BTC/USD rate, margins, pricing formula |
/v1/broadcasts | GET | System announcements |
/healthz | GET | Gateway liveness check (returns {"status":"ok"}) |
| Status | Meaning | Action |
|---|---|---|
| 400 | Invalid X-Cashu token | Check token format (must start with cashuA) |
| 401 | Invalid API key | Check Authorization header |
| 402 | Proofs spent or invalid | Mint fresh ecash |
| 503 | No nodes available | Check X-Cashu-Change header for refund, retry |
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
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 -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}'
Every sat is accounted for. Check the live rate and formula at /v1/oracle/rate.
| Item | Value |
|---|---|
| Minimum per job | 2 sats — anti-spam floor (1 to provider, 1 to gateway). Every request costs real sats, preventing micro-prompt flood attacks. |
| Gateway margin | Under 10% (9% inference + ~1% FX) |
| Provider share | 90%+ of every job |
| BTC/USD rate | 24h average, published daily at midnight UTC |
| Lightning fees | Paid by the party withdrawing (not per-job) |
Use the Lightning top-up in chat.owlrun.me — scan the QR with any Lightning wallet. Or send ecash from Minibits.
Your ecash proofs live in your browser's local storage. If you close the tab without withdrawing, the proofs are lost. Always withdraw first.
Yes. The API is OpenAI-compatible. Add the X-Cashu header with ecash proofs and you're good. See the code examples above.
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
}
}
]
}
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.
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.
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.
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.