diff --git a/ECOSYSTEM.md b/ECOSYSTEM.md index 765385a..7a51e14 100644 --- a/ECOSYSTEM.md +++ b/ECOSYSTEM.md @@ -6,23 +6,42 @@ BlockRun is a unified API gateway — pay per request with USDC, no API keys nee | Product | Endpoint | Pricing | Status | |---------|----------|---------|--------| -| **LLM Chat** | `/v1/chat/completions` | Per token | ✅ Live | +| **LLM Chat** | `/v1/chat/completions` | Per token — provider cost, no platform margin, + $0.001/request | ✅ Live | +| **Anthropic-Compat** | `/v1/messages` | Per token + $0.001/request | ✅ Live | +| **Responses API** | `/v1/responses` | Per token + $0.001/request | ✅ Live | | **Image Generation** | `/v1/images/generations` | $0.015–0.15/image | ✅ Live | | **Image Editing** | `/v1/images/image2image` | Per request | ✅ Live | +| **Video Generation** | `/v1/videos/generations` | Per second / per M tokens | ✅ Live | +| **Music / Speech / SFX** | `/v1/audio/*` | $0.151/track · $0.05–0.10/1k chars · $0.0535/SFX | ✅ Live | +| **Voice Calls & Phone Numbers** | `/v1/voice/call`, `/v1/phone/*` | $0.541/call · $5.001/30 days | ✅ Live | | **Search** | `/v1/search` | $0.025/source | ✅ Live | +| **Exa Web Search** | `/v1/exa/*` | $0.003–0.011 | ✅ Live | | **Prediction Markets** | `/v1/pm/*` | $0.0085 | ✅ Live | +| **Surf Crypto Data** | `/v1/surf/*` (83 endpoints) | $0.0085 | ✅ Live | +| **DefiLlama** | `/v1/defillama/*` | $0.002–0.006 | ✅ Live | +| **0x Swap (DEX)** | `/v1/zerox/*` | Free | ✅ Live | +| **Multi-chain RPC** | `/v1/rpc/{network}` (40 chains) | $0.003/call | ✅ Live | +| **Market Data (Pyth)** | `/v1/{crypto,fx,commodity}/*` · `/v1/{usstock,stocks}/*` | Free · $0.002 | ✅ Live | +| **Modal Sandbox** | `/v1/modal/*` | $0.002–0.011 | ✅ Live | +| **RealFace / Virtual Portrait** | `/v1/realface/enroll`, `/v1/portrait/enroll` | $0.011 | ✅ Live | +| **Polymarket Funding** | `/v1/polymarket/fund` | $0.011 fee | ✅ Live | | **Models** | `/v1/models` | Free | ✅ Live | | **Pricing** | `/v1/pricing` | Free | ✅ Live | | **Balance** | `/v1/balance` | Free | ✅ Live | +Full catalog with per-path prices: [x402 Endpoints](./docs/x402/endpoints.md). + ## Networks | Network | Gateway | Asset | Status | |---------|---------|-------|--------| | **Base** | `blockrun.ai` | USDC | ✅ Live | | **Solana** | `sol.blockrun.ai` | USDC | ✅ Live | +| **Polygon / Arbitrum / Optimism / Unichain** | `nano.blockrun.ai` | USDC via Circle Gateway (gas-free, batched) | ✅ Live | | **Base Sepolia** | `testnet.blockrun.ai` | USDC (testnet) | ✅ Testnet | -| **Solana Devnet** | `devnet-sol.blockrun.ai` | USDC (devnet) | ✅ Testnet | +| **XRP Ledger** | `xrpl.blockrun.ai` | RLUSD | ⛔ Sunset (offline) | + +**Enterprise (coming soon):** `user.blockrun.ai` — API keys (`brk_live_…`) + wire billing, billed post-hoc at exact usage with no per-call minimum. Sign-in is not yet open. ## x402 Facilitators @@ -31,6 +50,7 @@ BlockRun works with the x402 facilitator network: | Facilitator | Network | Discovery Endpoint | |-------------|---------|-------------------| | [Coinbase CDP](https://coinbase.com/cloud) | Base, Ethereum | `api.cdp.coinbase.com/platform/v2/x402/discovery/resources` | +| [Circle Gateway](https://developers.circle.com/gateway/nanopayments) | Polygon, Arbitrum, Optimism, Unichain (batched nanopayments, `nano.blockrun.ai`) | — | | [PayAI](https://payai.network) | Base, Solana | `facilitator.payai.network/discovery/resources` | | [QuestFlow](https://questflow.ai) | Base | `facilitator.questflow.ai/discovery/resources` | | [AnySpend](https://anyspend.com) | Base | `mainnet.anyspend.com/x402/discovery/resources` | @@ -41,12 +61,19 @@ BlockRun works with the x402 facilitator network: | Partner | Relationship | |---------|--------------| -| [Circle](https://partners.circle.com/partner/blockrunai) | Alliance Partner — USDC payments on Base | +| [Circle](https://partners.circle.com/partner/blockrunai) | Alliance Partner — USDC payments on Base; Circle Gateway nanopayments on `nano.blockrun.ai` | +| [OKX OnchainOS](https://web3.okx.com) | Agentic wallet behind XClawRouter | | [Coinbase CDP](https://coinbase.com/cloud) | x402 facilitator infrastructure | | [x402 Foundation](https://x402.org) | Protocol development | | [thirdweb](https://thirdweb.com) | Wallet & payment infrastructure | | [Predexon](https://predexon.com) | Prediction market data (Polymarket, Kalshi, Limitless, Opinion, Predict.Fun, Binance) | | [Modal](https://modal.com) | Sandbox compute (managed Python sandboxes for isolated code execution) | +| [Surf (asksurf.ai)](https://asksurf.ai) | Crypto data — 83 endpoints | +| [Exa](https://exa.ai) | Neural web search | +| [0x](https://0x.org) | DEX aggregation (Swap V2 + Gasless V2) | +| [Tatum](https://tatum.io) | Multi-chain JSON-RPC | +| [Pyth](https://pyth.network) | Crypto, FX, commodity and equity prices | +| [Bland.ai](https://bland.ai) / [Twilio](https://twilio.com) | Voice calls / phone-number provisioning | ## Community Integrations @@ -60,7 +87,41 @@ BlockRun works with the x402 facilitator network: | Tool | Description | Install | |------|-------------|---------| | [blockrun-mcp](https://github.com/BlockRunAI/blockrun-mcp) | MCP Server — 20 tools across 71 AI models, image/video/music/speech gen, voice calls, crypto data (Surf), prediction markets, DEX prices, raw JSON-RPC (40 chains), DeFi TVL/yields, sandboxed code exec, search | `claude mcp add blockrun -s user -- npx -y @blockrun/mcp@latest` | -| [nano-banana-blockrun](https://github.com/BlockRunAI/nano-banana-blockrun) | Image generation skill via x402 micropayments | Claude Code skill | +| [blockrun-claude-plugin](https://github.com/BlockRunAI/blockrun-claude-plugin) | Media plugin — spend confirmation before each paid image/video/audio call, running cost meter, balance status line | `claude --plugin-dir /path/to/blockrun-plugin` | +| [Claude-Code-GPT-IMAGE2-SeeDance-BlockRun](https://github.com/BlockRunAI/Claude-Code-GPT-IMAGE2-SeeDance-BlockRun) | `/headshot`, `/dance`, `/poster`, `/launch-film` — 848 prompt cases as one-line commands, pay per image | `curl -fsSL https://raw.githubusercontent.com/BlockRunAI/Claude-Code-GPT-IMAGE2-SeeDance-BlockRun/main/install.sh \| bash` | +| [alpha-mcp](https://github.com/BlockRunAI/alpha-mcp) | AI crypto-trading MCP — technical analysis, sentiment, DEX swaps on Base, risk limits | `claude mcp add alpha npx @blockrun/alpha` | + +### Official Repos + +Every public, non-archived repo in the [BlockRunAI org](https://github.com/BlockRunAI): + +| Repo | What it is | Install | +|------|------------|---------| +| [blockrun-cli](https://github.com/BlockRunAI/blockrun-cli) | `blockrun` umbrella CLI + `@blockrun/core` shared kernel — one wallet, one x402 payment path, one output contract; generic `api`/`pay` for any x402 endpoint | `npm install -g @blockrun/cli` | +| [ClawRouter](https://github.com/BlockRunAI/ClawRouter) | The agent-native LLM router — <1ms local routing, USDC on Base & Solana via x402, OpenClaw plugin | `npm install -g @blockrun/clawrouter` | +| [router-core](https://github.com/BlockRunAI/router-core) | The routing engine behind ClawRouter, Franklin, Hermes and dsh-clawrouter — deterministic, constraint-first, no inference call | library | +| [XClawRouter](https://github.com/BlockRunAI/XClawRouter) | ClawRouter powered by the OKX OnchainOS wallet — wallet-based auth, USDC on Base & Solana | `curl -fsSL https://blockrun.ai/XClawRouter-update \| bash` | +| [ClawRouter-Hermes](https://github.com/BlockRunAI/ClawRouter-Hermes) | ClawRouter for NousResearch Hermes — Python plugin wrapping the proxy | `pip install hermes-plugin-clawrouter` | +| [dsh-clawrouter](https://github.com/BlockRunAI/dsh-clawrouter) | DeepSeek Harness safety gate — a stronger model reviews dangerous tool calls before they run; vision + full catalog | `dsh plugin --profile web add dsh-clawrouter` | +| [clawrouter-codex](https://github.com/BlockRunAI/clawrouter-codex) | OpenAI Codex ↔ BlockRun bridge via the Responses API, wallet-signed x402, zero API keys | `npx @blockrun/clawrouter-codex up` | +| [blockrun-codex-plugin](https://github.com/BlockRunAI/blockrun-codex-plugin) | Media tools for Codex — spend gate + real-ledger cost meter | `codex plugin marketplace add BlockRunAI/blockrun-codex-plugin` | +| [@blockrun/opencode](https://www.npmjs.com/package/@blockrun/opencode) | OpenCode plugin — chat, images, auto-generated wallet (npm only) | `"plugin": ["@blockrun/opencode"]` | +| [blockrun-litellm](https://github.com/BlockRunAI/blockrun-litellm) | LiteLLM adapter — custom provider or local OpenAI-compatible proxy, Base + Solana | `pip install blockrun-litellm` | +| [lobstercash-blockrun-skill](https://github.com/BlockRunAI/lobstercash-blockrun-skill) | BlockRun skill for the lobster.cash OpenClaw plugin — Solana USDC | `install.sh` in repo | +| [elizaos-plugin-blockrun](https://github.com/BlockRunAI/elizaos-plugin-blockrun) | ElizaOS plugin for x402 pay-per-request AI on Base | `npm install @blockrun/elizaos-plugin` | +| [blockrun-llm-vip](https://github.com/BlockRunAI/blockrun-llm-vip) | Native Anthropic + OpenAI passthrough (Python) — official SDK subclasses, responses verbatim, zero model substitution | `pip install blockrun-llm-vip` | +| [blockrun-llm-go-vip](https://github.com/BlockRunAI/blockrun-llm-go-vip) | Native Anthropic + OpenAI passthrough (Go) — official client types, zero reshaping | `go get github.com/BlockRunAI/blockrun-llm-go-vip` | +| [blockrun-nano-client](https://github.com/BlockRunAI/blockrun-nano-client) | TypeScript SDK for `nano.blockrun.ai` — gas-free batched USDC via Circle Gateway on Polygon, Arbitrum, Optimism, Unichain | `npm install @blockrun/nano-client` | +| [circle-nanopayment-sample](https://github.com/BlockRunAI/circle-nanopayment-sample) | Circle Gateway nanopayment sample — an agent pays for API access with gas-free USDC | `npm run setup` | +| [Franklin](https://github.com/BlockRunAI/Franklin) | The AI agent with a wallet — spends USDC autonomously to get real work done | `npm install -g @blockrun/franklin` | +| [Franklin-Trading](https://github.com/BlockRunAI/Franklin-Trading) | Wallet-native trading agent — persona debate, Backtest → Paper → Live, x402 receipt per fill | `npm install -g @blockrun/franklin-trading` | +| [franklin-canvas](https://github.com/BlockRunAI/franklin-canvas) | Node-based AI media studio — image, video, music on an infinite canvas | `git clone` + `npm start` | +| [franklin-bet](https://github.com/BlockRunAI/franklin-bet) | Frontier-model council researches, reads odds, and bets on every 2026 World Cup match | `npm run generate -- --agent` | +| [franklin-run](https://github.com/BlockRunAI/franklin-run) | Source of franklin.run — landing site, blog, docs | — | +| [polymarket-agent](https://github.com/BlockRunAI/polymarket-agent) | Autonomous prediction-market trading agent using x402 micropayments | `pip install -r requirements.txt` | +| [awesome-OpenClaw-Money-Maker](https://github.com/BlockRunAI/awesome-OpenClaw-Money-Maker) | Curated ways to make money with OpenClaw | — | +| [awesome-finance-mcp](https://github.com/BlockRunAI/awesome-finance-mcp) · [awesome-healthcare-mcp](https://github.com/BlockRunAI/awesome-healthcare-mcp) · [awesome-mcp-servers](https://github.com/BlockRunAI/awesome-mcp-servers) | Curated MCP server lists | — | +| [branding](https://github.com/BlockRunAI/branding) · [renovate-config](https://github.com/BlockRunAI/renovate-config) | Brand kit · shared Renovate preset | — | ### Framework Integrations @@ -69,6 +130,13 @@ BlockRun works with the x402 facilitator network: | [Continue](https://github.com/continuedev/continue) | IDE Extension | 32K+ | ✅ Released | [Native provider](https://github.com/continuedev/continue/pull/11751) | | [GOAT SDK](https://github.com/crossmint/goat) | Agent Framework | 150K+ downloads | In Review | - | | [ElizaOS](https://github.com/elizaOS/eliza) | Agent Framework | 60K+ | ✅ Released | [elizaos-plugin-blockrun](https://github.com/BlockRunAI/elizaos-plugin-blockrun) | +| [OpenAI Codex](https://github.com/openai/codex) | Coding Agent | — | ✅ Released | [clawrouter-codex](https://github.com/BlockRunAI/clawrouter-codex) · [blockrun-codex-plugin](https://github.com/BlockRunAI/blockrun-codex-plugin) | +| [Hermes](https://github.com/NousResearch/hermes-agent) | Agent Framework | — | ✅ Released | [ClawRouter-Hermes](https://github.com/BlockRunAI/ClawRouter-Hermes) | +| [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) | Coding Agent | — | ✅ Released | [dsh-clawrouter](https://github.com/BlockRunAI/dsh-clawrouter) | +| [OpenCode](https://opencode.ai) | Coding Agent | — | ✅ Released | [@blockrun/opencode](https://www.npmjs.com/package/@blockrun/opencode) | +| [LiteLLM](https://github.com/BerriAI/litellm) | LLM Gateway | — | ✅ Released | [blockrun-litellm](https://github.com/BlockRunAI/blockrun-litellm) | +| [OKX OnchainOS](https://web3.okx.com) | Agent Wallet | — | ✅ Released | [XClawRouter](https://github.com/BlockRunAI/XClawRouter) | +| [lobster.cash](https://lobster.cash) | OpenClaw Plugin | — | ✅ Released | [lobstercash-blockrun-skill](https://github.com/BlockRunAI/lobstercash-blockrun-skill) | | [AgentKit](https://github.com/coinbase/agentkit) | Agent Framework | Official | Planned | Example code | | [LangChain](https://github.com/langchain-ai/langchain) | LLM Framework | 100K+ | Planned | Custom LLM provider | | [OctoBot](https://github.com/Drakkar-Software/OctoBot) | Trading Bot | 5K+ | Planned | Integration | @@ -81,7 +149,13 @@ Want to add an integration? [Open an issue](https://github.com/blockrunai/awesom |----------|------------|----------|--------| | Python | [blockrun-llm](https://github.com/blockrunai/blockrun-llm) | Chat, Images, Search, Prediction Markets, Smart Routing, Solana | Released | | TypeScript | [blockrun-llm-ts](https://github.com/blockrunai/blockrun-llm-ts) | Chat, Images, Search, OpenAI drop-in, Smart Routing, Solana | Released | -| Go | [blockrun-llm-go](https://github.com/blockrunai/blockrun-llm-go) | Chat | Released | +| Go | [blockrun-llm-go](https://github.com/blockrunai/blockrun-llm-go) | Chat, Images, Video, Music, Speech, Voice, Search, Market Data, Prediction Markets, DeFi/DEX, Multi-chain RPC, Solana | Released | +| Python (native) | [blockrun-llm-vip](https://github.com/BlockRunAI/blockrun-llm-vip) | Official Anthropic/OpenAI SDK subclasses — verbatim responses, Base + Solana | Released | +| Go (native) | [blockrun-llm-go-vip](https://github.com/BlockRunAI/blockrun-llm-go-vip) | Official `anthropic-sdk-go` / `openai-go` client types with x402 transport | Released | +| TypeScript (nano) | [blockrun-nano-client](https://github.com/BlockRunAI/blockrun-nano-client) | Circle Gateway batched USDC — Polygon, Arbitrum, Optimism, Unichain | Released | +| Python (LiteLLM) | [blockrun-litellm](https://github.com/BlockRunAI/blockrun-litellm) | LiteLLM custom provider + local proxy | Released | +| CLI | [blockrun-cli](https://github.com/BlockRunAI/blockrun-cli) | `blockrun` umbrella CLI + `@blockrun/core` kernel | Released | +| Python (XRPL) | [blockrun-llm-xrpl](https://github.com/BlockRunAI/blockrun-llm-xrpl) | RLUSD on XRP Ledger | Deprecated (gateway sunset) | ## Smart Routing diff --git a/README.md b/README.md index fa70015..1678364 100644 --- a/README.md +++ b/README.md @@ -173,10 +173,12 @@ BlockRun runs on two networks with separate gateways: |---------|---------|-------|--------| | **Base** | `blockrun.ai` | USDC | ✅ Live (71 models) | | **Solana** | `sol.blockrun.ai` | USDC | ✅ Live | +| **Polygon / Arbitrum / Optimism / Unichain** | `nano.blockrun.ai` | USDC via Circle Gateway (gas-free, batched) | ✅ Live | | **Base Sepolia** | `testnet.blockrun.ai` | USDC (testnet) | ✅ Testnet | -| **Solana Devnet** | `devnet-sol.blockrun.ai` | USDC (devnet) | ✅ Testnet | -**Payment protocol:** x402 (HTTP 402 "Payment Required") — wallet signs payment, no accounts or API keys needed. +**Payment protocol:** x402 (HTTP 402 "Payment Required") — wallet signs payment, no accounts or API keys needed. Per-token chat carries no platform margin — only a flat $0.001 transaction fee per request; media generation and Live Search carry 5%. + +**Enterprise (coming soon):** `user.blockrun.ai` — API keys (`brk_live_…`) + wire billing, billed post-hoc at exact usage with no per-call minimum. Sign-in is not yet open; ask on [Telegram](https://t.me/+mroQv4-4hGgzOGUx). --- @@ -186,7 +188,12 @@ BlockRun runs on two networks with separate gateways: |:--------:|---------|----------|:----------:| | ![Python](https://img.shields.io/badge/-Python-3776AB?logo=python&logoColor=white) | `pip install blockrun-llm` | Chat, Images, Search, Prediction Markets, Smart Routing, Solana | [GitHub](https://github.com/blockrunai/blockrun-llm) | | ![TypeScript](https://img.shields.io/badge/-TypeScript-3178C6?logo=typescript&logoColor=white) | `npm i blockrun-llm` | Chat, Images, Search, OpenAI-compatible drop-in, Smart Routing, Solana | [GitHub](https://github.com/blockrunai/blockrun-llm-ts) | -| ![Go](https://img.shields.io/badge/-Go-00ADD8?logo=go&logoColor=white) | `go get github.com/blockrunai/blockrun-llm-go` | Chat | [GitHub](https://github.com/blockrunai/blockrun-llm-go) | +| ![Go](https://img.shields.io/badge/-Go-00ADD8?logo=go&logoColor=white) | `go get github.com/blockrunai/blockrun-llm-go` | Chat, Images, Video, Music, Speech, Voice, Search, Market Data, Prediction Markets, DeFi/DEX, Multi-chain RPC, Solana | [GitHub](https://github.com/blockrunai/blockrun-llm-go) | +| ![Python](https://img.shields.io/badge/-Python-3776AB?logo=python&logoColor=white) | `pip install blockrun-llm-vip` | Native Anthropic + OpenAI passthrough — subclasses the official SDKs, responses verbatim, zero model substitution | [GitHub](https://github.com/BlockRunAI/blockrun-llm-vip) | +| ![Go](https://img.shields.io/badge/-Go-00ADD8?logo=go&logoColor=white) | `go get github.com/BlockRunAI/blockrun-llm-go-vip` | Native Anthropic + OpenAI passthrough — official `anthropic-sdk-go` / `openai-go` client types | [GitHub](https://github.com/BlockRunAI/blockrun-llm-go-vip) | +| ![TypeScript](https://img.shields.io/badge/-TypeScript-3178C6?logo=typescript&logoColor=white) | `npm i @blockrun/nano-client` | Same catalog via `nano.blockrun.ai` — gas-free batched USDC (Circle Gateway) on Polygon, Arbitrum, Optimism, Unichain | [GitHub](https://github.com/BlockRunAI/blockrun-nano-client) | +| ![Python](https://img.shields.io/badge/-Python-3776AB?logo=python&logoColor=white) | `pip install blockrun-litellm` | LiteLLM adapter — custom provider or local OpenAI-compatible proxy, Base + Solana | [GitHub](https://github.com/BlockRunAI/blockrun-litellm) | +| ![CLI](https://img.shields.io/badge/-CLI-000000?logo=gnubash&logoColor=white) | `npm i -g @blockrun/cli` | `blockrun` umbrella CLI + `@blockrun/core` kernel — one wallet, generic `api`/`pay` for any x402 endpoint | [GitHub](https://github.com/BlockRunAI/blockrun-cli) | ### Solana Support @@ -251,6 +258,28 @@ claude mcp add blockrun -s user -- npx -y @blockrun/mcp@latest > - Already have API keys, want to save money? → Add **ClawRouter** (smart routing) > - Both installed? → Maximum coverage: zero-friction access + cost optimization +### ClawRouter ports and other official repos + +| Repo | What it is | Install | +|------|------------|---------| +| [router-core](https://github.com/BlockRunAI/router-core) | The routing engine behind ClawRouter, Franklin, Hermes and dsh-clawrouter — deterministic, constraint-first, <1ms, no inference call | library | +| [XClawRouter](https://github.com/BlockRunAI/XClawRouter) | ClawRouter powered by the OKX OnchainOS wallet — OpenClaw plugin, USDC on Base & Solana | `curl -fsSL https://blockrun.ai/XClawRouter-update \| bash` | +| [ClawRouter-Hermes](https://github.com/BlockRunAI/ClawRouter-Hermes) | ClawRouter for NousResearch Hermes — Python plugin wrapping the proxy | `pip install hermes-plugin-clawrouter` | +| [dsh-clawrouter](https://github.com/BlockRunAI/dsh-clawrouter) | DeepSeek Harness safety gate — a stronger model reviews dangerous tool calls before they run, plus vision and the full catalog | `dsh plugin --profile web add dsh-clawrouter` | +| [clawrouter-codex](https://github.com/BlockRunAI/clawrouter-codex) | OpenAI Codex ↔ BlockRun bridge over the Responses API, wallet-signed, zero API keys | `npx @blockrun/clawrouter-codex up` | +| [blockrun-claude-plugin](https://github.com/BlockRunAI/blockrun-claude-plugin) | Claude Code media plugin — spend confirmation, cost meter, balance status line | `claude --plugin-dir` | +| [blockrun-codex-plugin](https://github.com/BlockRunAI/blockrun-codex-plugin) | Codex port of the media plugin — spend gate + real-ledger cost meter | `codex plugin marketplace add BlockRunAI/blockrun-codex-plugin` | +| [@blockrun/opencode](https://www.npmjs.com/package/@blockrun/opencode) | OpenCode plugin — chat, images, auto-generated wallet | `"plugin": ["@blockrun/opencode"]` in `opencode.json` | +| [lobstercash-blockrun-skill](https://github.com/BlockRunAI/lobstercash-blockrun-skill) | BlockRun skill for the lobster.cash OpenClaw plugin — paid with Solana USDC | `install.sh` in repo | +| [Franklin](https://github.com/BlockRunAI/Franklin) | The AI agent with a wallet — spends USDC autonomously to get real work done | `npm i -g @blockrun/franklin` | +| [Franklin-Trading](https://github.com/BlockRunAI/Franklin-Trading) | Wallet-native trading agent — persona debate, Backtest → Paper → Live, x402 receipt per fill | `npm i -g @blockrun/franklin-trading` | +| [franklin-canvas](https://github.com/BlockRunAI/franklin-canvas) | Node-based AI media studio — image, video, music on an infinite canvas | `git clone` + `npm start` | +| [franklin-bet](https://github.com/BlockRunAI/franklin-bet) | Frontier models research, read live odds, and bet on every 2026 World Cup match — reproducible pipeline | `npm run generate -- --agent` | +| [Claude-Code-GPT-IMAGE2-SeeDance-BlockRun](https://github.com/BlockRunAI/Claude-Code-GPT-IMAGE2-SeeDance-BlockRun) | `/headshot`, `/dance`, `/poster`, `/launch-film` — 848 prompt cases as one-line Claude Code commands | `install.sh` in repo | +| [awesome-OpenClaw-Money-Maker](https://github.com/BlockRunAI/awesome-OpenClaw-Money-Maker) | Curated ways to make money with OpenClaw | — | + +Full directory with one line per public repo: [Ecosystem docs](./docs/resources/ecosystem.md). + --- ## Smart Routing @@ -275,7 +304,14 @@ Built into both Python and TypeScript SDKs. Also available as standalone: [ClawR | [Continue](https://github.com/continuedev/continue) | ✅ Released | [Native provider](https://github.com/continuedev/continue/pull/11751) — ClawRouter as built-in LLM provider (32K+ ⭐) | | [OpenClaw](https://github.com/openclaw/openclaw) | ✅ Released | [ClawRouter](https://github.com/BlockRunAI/ClawRouter) - Smart LLM router, 78% cost savings | | [ElizaOS](https://github.com/elizaOS/eliza) | ✅ Released | [elizaos-plugin-blockrun](https://github.com/BlockRunAI/elizaos-plugin-blockrun) | -| [Claude Code](https://claude.ai/code) | ✅ Released | [blockrun-mcp](https://github.com/BlockRunAI/blockrun-mcp) | +| [Claude Code](https://claude.ai/code) | ✅ Released | [blockrun-mcp](https://github.com/BlockRunAI/blockrun-mcp) · [blockrun-claude-plugin](https://github.com/BlockRunAI/blockrun-claude-plugin) | +| [OpenAI Codex](https://github.com/openai/codex) | ✅ Released | [clawrouter-codex](https://github.com/BlockRunAI/clawrouter-codex) · [blockrun-codex-plugin](https://github.com/BlockRunAI/blockrun-codex-plugin) | +| [Hermes](https://github.com/NousResearch/hermes-agent) | ✅ Released | [ClawRouter-Hermes](https://github.com/BlockRunAI/ClawRouter-Hermes) | +| [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) | ✅ Released | [dsh-clawrouter](https://github.com/BlockRunAI/dsh-clawrouter) | +| [OpenCode](https://opencode.ai) | ✅ Released | [@blockrun/opencode](https://www.npmjs.com/package/@blockrun/opencode) | +| [LiteLLM](https://github.com/BerriAI/litellm) | ✅ Released | [blockrun-litellm](https://github.com/BlockRunAI/blockrun-litellm) | +| [OKX OnchainOS](https://web3.okx.com) | ✅ Released | [XClawRouter](https://github.com/BlockRunAI/XClawRouter) — ClawRouter with the OKX Agentic Wallet | +| [lobster.cash](https://lobster.cash) | ✅ Released | [lobstercash-blockrun-skill](https://github.com/BlockRunAI/lobstercash-blockrun-skill) | | [GOAT SDK](https://github.com/crossmint/goat) | 🔄 In Review | Agent framework integration | | [AgentKit](https://github.com/coinbase/agentkit) | 📋 Planned | Coinbase agent framework | | [LangChain](https://github.com/langchain-ai/langchain) | 📋 Planned | Custom LLM provider | @@ -288,6 +324,7 @@ Built into both Python and TypeScript SDKs. Also available as standalone: [ClawR |---------|:--------:|-------------| | [PredictOS](https://github.com/PredictionXBT/PredictOS) | Prediction Markets | Prediction market analysis platform with multi-AI provider support | | [Polymarket AI Agent](https://github.com/BlockRunAI/polymarket-agent) | Prediction Markets | Autonomous AI trading agent using 3-model LLM consensus | +| [franklin-bet](https://github.com/BlockRunAI/franklin-bet) | Prediction Markets | Frontier-model council researches and bets on every 2026 World Cup match | | [LLM_trader](https://github.com/qrak/LLM_trader) | Crypto Trading | AI crypto trading bot with multi-provider support and chart analysis | | [Spraay](https://github.com/plagtech/spraay-x402-gateway) | x402 Gateway | Multi-chain x402 payment gateway with dual-provider AI inference (BlockRun + OpenRouter) | | [NoFx](https://github.com/NoFxAiOS/nofx) | Crypto Trading | Personal AI trading assistant - any market, any model, pay with USDC | @@ -312,6 +349,9 @@ Built into both Python and TypeScript SDKs. Also available as standalone: [ClawR | [Exa](https://exa.ai) | Web Search | Neural web search, find-similar, page contents, AI-grounded answers | | [Predexon](https://predexon.com) | Prediction Markets | Polymarket, Kalshi, Limitless, Opinion, Predict.Fun, Binance Futures data | | [Modal](https://modal.com) | Sandbox Compute | Managed Python sandboxes for isolated code execution | +| [Surf](https://asksurf.ai) | Crypto Data | 83 endpoints — CEX, on-chain SQL, wallet labels, social, news | +| [Tatum](https://tatum.io) | Multi-chain RPC | JSON-RPC across 40 chains | +| [Pyth](https://pyth.network) | Market Data | Crypto, FX, commodity and equity prices | ### x402 Facilitators @@ -320,6 +360,7 @@ BlockRun aggregates services from the x402 facilitator network: | Facilitator | Network | |-------------|---------| | [Coinbase CDP](https://coinbase.com/cloud) | Base, Ethereum | +| [Circle Gateway](https://developers.circle.com/gateway/nanopayments) | Polygon, Arbitrum, Optimism, Unichain (batched nanopayments via `nano.blockrun.ai`) | | [PayAI](https://payai.network) | Base, Solana | | [thirdweb](https://thirdweb.com) | Base, Ethereum | | [QuestFlow](https://questflow.ai) | Base | @@ -403,7 +444,7 @@ See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines. | **Getting Started** | [Claude Code](./docs/getting-started/claude-code.md) • [Agent Developers](./docs/getting-started/agent-developers.md) • [SDK Developers](./docs/getting-started/sdk-developers.md) • [Wallet Setup](./docs/getting-started/wallet-setup.md) | | **API Reference** | [Chat Completions](./docs/api-reference/chat-completions.md) • [Images](./docs/api-reference/image-generation.md) • [Search](./docs/api-reference/search.md) • [Prediction Markets](./docs/api-reference/prediction-markets.md) • [Models](./docs/api-reference/models.md) • [Errors](./docs/api-reference/errors.md) | | **x402 Protocol** | [How It Works](./docs/x402/how-it-works.md) • [Payment Flow](./docs/x402/payment-flow.md) • [Security](./docs/x402/security.md) | -| **Resources** | [Pricing](./docs/products/intelligence/pricing.md) • [FAQ](./docs/resources/faq.md) • [Changelog](./docs/resources/changelog.md) | +| **Resources** | [Pricing](./docs/products/intelligence/pricing.md) • [FAQ](./docs/resources/faq.md) • [Ecosystem](./docs/resources/ecosystem.md) • [Examples](./docs/resources/examples.md) • [x402 Endpoints](./docs/x402/endpoints.md) • [Changelog](./docs/resources/changelog.md) | --- @@ -425,7 +466,7 @@ BlockRun is agent-native — it uses wallet signatures for authentication instea The x402 protocol is an HTTP-native payment standard based on HTTP status code 402 ("Payment Required"). It allows any HTTP request to include a cryptographic USDC payment, enabling machine-to-machine payments without accounts, credit cards, or KYC verification. BlockRun is a leading implementation of x402. ### How much does BlockRun cost? -BlockRun uses pay-per-request pricing with no minimums or subscriptions. Prices start at $0.002 per request for the cheapest endpoints. Provider cost plus a 5% margin. $5 in USDC is enough for thousands of requests. +BlockRun uses pay-per-request pricing with no subscriptions. Per-token chat is billed at provider cost with no platform margin — only a flat $0.001 transaction fee is added per request; media generation and Live Search carry a 5% margin. Prices start at $0.002 per request for the cheapest data endpoints. $5 in USDC is enough for thousands of requests. --- diff --git a/docs/README.md b/docs/README.md index 4bfe45a..267c2df 100644 --- a/docs/README.md +++ b/docs/README.md @@ -26,7 +26,7 @@ Ask for an LLM completion, an image, a market quote, a swap. Your client signs a ::: :::step{title="Pay only for what you use"} -Each call costs a fraction of a cent — provider cost plus a small margin, with a $0.003 floor. No monthly bill, no seats, no minimums. +Each call costs a fraction of a cent — per-token chat is billed at provider cost with no platform margin, plus a flat $0.001 transaction fee; media generation and Live Search carry 5%. No monthly bill, no seats, no minimums. ::: :::: @@ -46,7 +46,7 @@ One MCP install adds 20 `blockrun_*` tools to your assistant. Best for Claude Co ::: :::card{title="I'm building an agent / backend" href="getting-started/sdk-developers.md" icon="Code"} -Drop-in SDKs for Python, TypeScript, and Go — pay-per-call, OpenAI-compatible. +Drop-in SDKs for Python, TypeScript, and Go — pay-per-call, OpenAI-compatible — plus native Anthropic/OpenAI passthrough clients, a LiteLLM adapter, and the `blockrun` CLI. ::: :::card{title="Set up my wallet" href="getting-started/wallet-setup.md" icon="Wallet"} @@ -92,3 +92,5 @@ No API keys to rotate, no subscriptions to cancel, no per-provider signups. Just - **Website:** [blockrun.ai](https://blockrun.ai) - **GitHub:** [github.com/BlockRunAI](https://github.com/BlockRunAI) - **x402 Services:** [live service directory](https://blockrun.ai/ecosystem/services) +- **Every public repo:** [Ecosystem](resources/ecosystem.md) — routers, plugins, SDKs, Franklin apps, skills +- **Enterprise (API keys + wire billing):** user.blockrun.ai — sign-in coming soon diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index c681dc6..a55557e 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -28,6 +28,7 @@ ## Creation * [nano-banana](products/creation/nano-banana.md) +* [Music Generation](products/creation/music-generation.md) ## Trading @@ -47,6 +48,7 @@ * [Python SDK](sdks/python.md) * [TypeScript SDK](sdks/typescript.md) * [Go SDK](sdks/go.md) +* [XRPL SDK (deprecated)](sdks/xrpl.md) ## API Reference diff --git a/docs/api-reference/chat-completions.md b/docs/api-reference/chat-completions.md index 8340698..79cc036 100644 --- a/docs/api-reference/chat-completions.md +++ b/docs/api-reference/chat-completions.md @@ -20,7 +20,7 @@ POST https://blockrun.ai/api/v1/chat/completions | Header | Required | Description | |--------|----------|-------------| | `Content-Type` | Yes | Must be `application/json` | -| `PAYMENT-SIGNATURE` | Conditional | Base64-encoded x402 payment payload (required after 402, x402 v2) | +| `PAYMENT-SIGNATURE` | Conditional | Base64-encoded x402 payment payload (required after 402, x402 v2). `X-Payment` is accepted as an alias for v1-era clients | ### Body Parameters @@ -28,7 +28,7 @@ POST https://blockrun.ai/api/v1/chat/completions |-----------|------|----------|-------------| | `model` | string | Yes | Model ID (e.g., `openai/gpt-5.5`) | | `messages` | array | Yes | Array of message objects | -| `max_tokens` | integer | No | Maximum tokens to generate (default: 1024) | +| `max_tokens` | integer | No | Maximum tokens to generate. Default when omitted: the smaller of the model's `max_output` and 8192. Always clamped to the model ceiling and to the remaining context window — when the served value is smaller than what you asked for, the response carries `X-Max-Tokens-Capped: true`, `X-Max-Tokens-Requested`, and `X-Max-Tokens-Effective` | | `temperature` | number | No | Sampling temperature (0-2) | | `top_p` | number | No | Nucleus sampling parameter | | `stream` | boolean | No | Stream the response as SSE chunks (default: `false`) | @@ -38,14 +38,22 @@ POST https://blockrun.ai/api/v1/chat/completions | `stop` | string \| array | No | Stop sequence(s) | | `reasoning_effort` | string | No | Reasoning depth for GPT-5.x / o-series (`none`, `minimal`, `low`, `medium`, `high`, `xhigh`) | | `thinking` | object | No | Anthropic extended thinking, e.g. `{"type":"enabled","budget_tokens":2048}` or `{"type":"disabled"}` | -| `prompt_cache` | boolean | No | Opt in to Anthropic prompt caching (Anthropic models only) | +| `prompt_cache` | boolean | No | Opt in to Anthropic prompt caching (Anthropic models only). Consumed by the gateway, never forwarded | +| `search_parameters` | object | No | xAI Live Search on `xai/*` models: `{"mode":"auto"\|"on"\|"off","sources":[…],"return_citations":true,"from_date":"YYYY-MM-DD","to_date":"YYYY-MM-DD","max_search_results":≤50}`. Adds $0.025 per source to the quote (estimated at `max_search_results`, default 10) plus a 5% margin on that search leg | + +Unknown parameters (`seed`, `n`, `logprobs`, `top_k`, …) are kept and forwarded verbatim to OpenAI-compatible upstreams. `stream_options` is the one exception: it is never forwarded — the gateway sets its own `stream_options.include_usage` because billing needs the usage frame. ### Message Object | Field | Type | Description | |-------|------|-------------| -| `role` | string | One of: `system`, `user`, `assistant` | -| `content` | string | The message content | +| `role` | string | One of: `system`, `user`, `assistant`, `tool` (`function` is accepted for legacy clients) | +| `content` | string \| array \| null | Text, or an array of content parts (`{"type":"text","text":…}`, `{"type":"image_url","image_url":{"url":…}}` on vision models). `null` is allowed on assistant turns that only carry `tool_calls` | +| `tool_calls` | array | Assistant turns: `[{"id","type":"function","function":{"name","arguments"}}]` | +| `tool_call_id` | string | Required on `tool` messages — the id of the call being answered | +| `name` | string | Optional tool/function name on `tool` messages | + +A `messages` array with only `system` entries is rejected with `400` / `EMPTY_CONVERSATION` — at least one `user`, `assistant`, or `tool` message is required. ## Response @@ -88,6 +96,42 @@ POST https://blockrun.ai/api/v1/chat/completions | `cache_creation_input_tokens` | when cache write | Same as `prompt_tokens_details.cached_creation_tokens` — Anthropic-native label | | `completion_tokens_details.reasoning_tokens` | when reasoning | GPT-5.x / o-series only; forwarded verbatim. Not surfaced for Anthropic Claude models (thinking tokens are already included in `completion_tokens`) | +### Reasoning in its own field + +Reasoning models (DeepSeek, Z.AI GLM, MiniMax, Moonshot Kimi, Claude with thinking enabled, open-weight reasoning models) return their chain of thought on `message.reasoning_content`, separate from `content`. The field is optional and absent when the model produced no reasoning. + +- **Non-streaming** (since 2026-08-27): `choices[0].message.reasoning_content` carries the reasoning; `content` carries the answer. +- **Streaming**: reasoning arrives as `delta.reasoning_content` chunks alongside `delta.content`. +- **Tool-call turns**: an assistant message that carries `tool_calls` has `content: ""` — that is the OpenAI shape for "answered by calling a tool". The gateway no longer promotes reasoning into `content` on those turns; it lives on `reasoning_content` only. If you echo assistant turns back into history, echo `reasoning_content` too — some thinking models require it on prior tool-call turns. +- A reply whose `content` would otherwise be empty on a **non**-tool turn still receives the reasoning text as `content`, so a paying caller never gets a blank string. + +```json +{ + "choices": [{ + "index": 0, + "message": { + "role": "assistant", + "content": "", + "reasoning_content": "The user wants the weather, so I should call get_weather…", + "tool_calls": [{"id": "call_abc", "type": "function", "function": {"name": "get_weather", "arguments": "{\"city\":\"SF\"}"}}] + }, + "finish_reason": "tool_calls" + }] +} +``` + +### Response headers + +| Header | When | Meaning | +|--------|------|---------| +| `PAYMENT-RESPONSE` | every paid call | Base64 JSON `{"success","transaction","network","payer"}` — the x402 v2 settlement receipt | +| `X-Payment-Receipt` | when settlement landed | The on-chain transaction hash | +| `X-Payment-Settled: false` | when settlement did not land in time | The response was served (verification is the gate; settlement is bookkeeping). `X-Payment-Settle-Error-Class` names the failure class | +| `X-Max-Tokens-Capped` / `-Requested` / `-Effective` | when `max_tokens` was clamped | See `max_tokens` above | +| `X-Context-Truncated: true` | when the prompt was trimmed to fit the context window | `X-Messages-Removed` gives the count | +| `X-Free-Model: true` | free-tier models | No payment was required | +| `X-Fallback-Used: true` | when the requested model failed over | `X-Original-Model` / `X-Fallback-Model` name both sides | + **Protocol naming convention:** - **Claude native `/v1/messages`**: `usage.input_tokens`, `usage.output_tokens`, `usage.cache_creation_input_tokens`, `usage.cache_read_input_tokens` - **OpenAI-compat `/v1/chat/completions`**: `usage.prompt_tokens_details.cached_tokens` (reads), `usage.prompt_tokens_details.cached_creation_tokens` (writes), `usage.completion_tokens_details.reasoning_tokens` (reasoning) @@ -117,33 +161,95 @@ If you use the Claude-native `POST /v1/messages` endpoint with the `context_mana When you first make a request without payment, you'll receive: +```http +HTTP/1.1 402 Payment Required +Content-Type: application/json +Cache-Control: no-store, no-cache, must-revalidate, max-age=0 +X-Payment-Required: +PAYMENT-REQUIRED: +WWW-Authenticate: X402 requirements="" +``` + ```json { "error": "Payment Required", "message": "This endpoint requires x402 payment", - "price": { - "amount": "0.001000", - "currency": "USD", - "breakdown": { - "inputCost": "0.000012", - "outputCost": "0.000600", - "margin": "0%" - } + "price": {"amount": "0.025685", "currency": "USD"}, + "paymentInfo": {"network": "base", "asset": "USDC", "x402Version": 2} +} +``` + +`price.amount` is the exact amount the header signs, transaction fee included. Decoded, the header is: + +```json +{ + "x402Version": 2, + "accepts": [{ + "scheme": "exact", + "network": "eip155:8453", + "amount": "25685", + "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "payTo": "0x…", + "maxTimeoutSeconds": 300, + "extra": {"name": "USD Coin", "version": "2"} + }], + "resource": { + "url": "https://blockrun.ai/api/v1/chat/completions", + "description": "GPT-5.5 API call (~17 input, 8192 max output tokens)", + "mimeType": "application/json" }, - "paymentInfo": { - "network": "base", - "asset": "USDC", - "x402Version": 2 - } + "extensions": {"bazaar": {…}} +} +``` + +**How the quote is computed.** Estimated input tokens at the model's input rate, plus **10% of `max_tokens`** at the output rate — most calls use a small fraction of their ceiling. Per-token chat carries **no platform margin** (list price, since 2026-08-07); the quote is floored at $0.001 and then the flat **$0.001 per-request transaction fee** is added. `amount` is in micro-USDC (6 decimals). The settled amount is the signed amount — there is no post-hoc refund when the model uses fewer tokens, so set `max_tokens` to what you need. Prompts at or above a model's long-context threshold re-price the whole request at that model's long-context rates. + +### Payment rejected (402 with a `code`) + +If you send a `PAYMENT-SIGNATURE` that does not verify, the response is still `402` but carries a machine-readable `code` so a client can branch instead of parsing prose: + +```json +{ + "error": "Payment verification failed", + "code": "PAYMENT_UNFUNDED", + "message": "The payment authorization could not be executed on-chain. The usual cause is an insufficient USDC balance on Base for the quoted amount — …", + "debug": "", + "payer": "0x…" } ``` -The `X-Payment-Required` header contains the full payment requirements. +| `code` | Meaning | What to do | +|--------|---------|------------| +| `PAYMENT_UNFUNDED` | The transfer simulation reverted — usually not enough USDC for the quoted amount (an expired or not-yet-valid authorization window reverts the same way) | Fund the wallet, or re-sign if your clock is off | +| `PAYMENT_BLOCKHASH_STALE` | Solana-signed payments: the transaction was pinned to a blockhash that has since expired (they stay valid for roughly a minute). Nothing was charged | Sign a fresh authorization against a current blockhash and resend | +| `PAYMENT_REPLAY` | The authorization nonce was already used | Sign a fresh authorization for each request | +| `PAYMENT_INVALID` | Any other verification failure (bad signature, wrong network/asset, malformed payload) | Check the signed requirements match the header you were given | + +Verification runs strictly before settlement, so a `402` with any of these codes means nothing was charged. The Solana gateway (`sol.blockrun.ai`) uses the same envelope with `PAYMENT_INVALID`, `PAYMENT_REPLAY`, and `PAYMENT_VERIFICATION_UNAVAILABLE` (the facilitator itself was unreachable — retry the same signed payment). The Anthropic-compatible `/v1/messages` and `/v1/responses` endpoints keep their vendor error envelopes and do not carry these codes. :::info{title="402 is the normal flow, not an error"} The first request returns `402 Payment Required` with a price quote. Sign it and retry with the `PAYMENT-SIGNATURE` header to get your completion. The SDKs do this round-trip automatically. ::: +### Other status codes + +| Status | `code` | When | +|--------|--------|------| +| 400 | `INVALID_JSON`, `INVALID_REQUEST_BODY` (with `details`), `EMPTY_CONVERSATION`, `INVALID_PARAMETER`, `CONTEXT_LENGTH_EXCEEDED`, `CONTENT_FILTERED`, `INVALID_IMAGE_URL`, `REQUEST_TOO_LARGE`, `STREAM_UNSUPPORTED` | Caller-side problems, including upstream rejections of your parameters (surfaced as 400, not 500) | +| 400 | — | Unknown model: `{"error":"Unknown model: . Try one of: …. Full list: GET /v1/models"}` | +| 429 | `RATE_LIMITED` | Upstream capacity exhausted for that model — `Retry-After` and `X-RateLimit-Source` set; see [Rate Limits](rate-limits.md) | +| 429 | `FREE_TIER_RATE_LIMITED` | Free models only: per-IP limit of 30 requests/minute or 300 requests/hour hit — `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` set | +| 503 | `MODEL_UNAVAILABLE`, `PROVIDER_CONFIG_ERROR` | Upstream is down or over capacity; retry or fail over | +| 504 | `TIMEOUT` | Upstream did not answer within the 120s per-call timeout. Not settled | + +Errors use the OpenAI envelope — `error.message`, `error.type`, `error.code`, `error.param` — with `message` and `code` mirrored at the top level for older clients. Full details in [Error Handling](errors.md). + +### Timeouts + +- Each upstream call has a **120s** timeout (non-streaming, and time-to-first-byte / stall between chunks when streaming). +- A streaming response has an overall deadline of **500s**; a stream that hits it is terminated. +- Payment authorizations are valid for **300s** (`maxTimeoutSeconds`); settlement happens after the response (non-streaming) or after the last chunk (streaming), so the whole call has to finish inside that window. + ## Example ::::tabs diff --git a/docs/api-reference/defillama.md b/docs/api-reference/defillama.md index 588790a..6df00a1 100644 --- a/docs/api-reference/defillama.md +++ b/docs/api-reference/defillama.md @@ -23,8 +23,12 @@ agent can budget a call the same way it budgets any other endpoint. | `/api/v1/defillama/yields` | GET | $0.006 | Every tracked yield pool (lending, LPs, staking, vaults) with current APY/TVL | | `/api/v1/defillama/prices/{coins}` | GET | $0.002 | Token price lookup, comma-separated coin identifiers | -Prices are quoted in every 402 response. Read them at request time rather than -copying from this page. +Prices above include the flat $0.001 transaction fee added to every paid call +(base $0.005 / $0.001). They are quoted in every 402 response — read them at +request time rather than copying from this page. + +Query-string parameters are forwarded to DefiLlama unchanged, so any filter the +upstream endpoint accepts works here too. --- @@ -55,8 +59,9 @@ curl https://blockrun.ai/api/v1/defillama/protocol/aave \ |-----------|----|----------|-------------| | `slug` | path | Yes | DefiLlama protocol slug — `aave`, `uniswap`, `lido`, … | -Slugs come from the `slug` field of `/protocols`. An unknown slug returns `404` -and is **not** charged. +Slugs come from the `slug` field of `/protocols`. An unknown slug is rejected +upstream and comes back as `400` (`error: "Bad Request"`, with the upstream +message in `details`) — it is **not** charged. The heaviest protocols (`uniswap`, for one) return multi-MB payloads; the upstream timeout is 25s. @@ -107,18 +112,48 @@ Identifier forms: - `coingecko:` — e.g. `coingecko:bitcoin` - `:
` — e.g. `ethereum:0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2`, `solana:So11111111111111111111111111111111111111112` -The response is an object keyed by the identifier you passed, each with -`price`, `symbol`, `decimals` and `timestamp`. +The response is `{ "coins": { ... } }` keyed by the identifier you passed, each +with `price`, `symbol`, `confidence` and `timestamp` (contract-address +identifiers also carry `decimals`). An identifier +DefiLlama does not know is simply absent from `coins` — the call still returns +`200` and is charged, so validate identifiers before batching them. --- +## Payment flow + +A request without a payment header returns `402`. The signed requirements are +in the `X-Payment-Required` / `PAYMENT-REQUIRED` headers (and +`WWW-Authenticate: X402 requirements="…"`), and the JSON body restates the +price for humans: + +```json +{ + "error": "Payment Required", + "message": "This endpoint requires x402 payment", + "endpoint": "/api/v1/defillama/prices/coingecko:bitcoin", + "method": "GET", + "description": "Token price lookup. …", + "price": { "amount": "0.0020", "currency": "USD" }, + "paymentInfo": { "network": "base", "asset": "USDC", "x402Version": 2 } +} +``` + +Successful responses carry `X-Payment-Receipt` (the settlement transaction +hash) and `X-Payment-Response` (base64 x402 settlement receipt). + ## Errors | Status | Meaning | Charged? | |--------|---------|----------| -| `402` | Payment required — the response carries the exact amount and `payTo` | No | -| `404` | Unknown protocol slug or coin identifier | No | -| `502` | DefiLlama upstream error or timeout | No | +| `400` | DefiLlama rejected the request (unknown slug, bad parameter). Upstream `4xx` codes are passed through as-is with `error: "Bad Request"` and the upstream body in `details` | No | +| `402` | No payment header — body carries `price` and the headers carry the signed requirements | No | +| `402` | `error: "Payment verification failed"` with a machine-readable `code`: `PAYMENT_INVALID`, `PAYMENT_UNFUNDED` (insufficient USDC or expired `validBefore`) or `PAYMENT_BLOCKHASH_STALE`; `message` explains the fix when one is known | No | +| `402` | `code: "PAYMENT_REPLAY"` — the payment authorization was already used. Sign a fresh one per request | No | +| `402` | `error: "Payment settlement failed"` — data was fetched but settlement did not land; `details` has the reason | No | +| `404` | Unknown BlockRun endpoint — body lists `available` paths | No | +| `502` | DefiLlama upstream error or 25s timeout (`"Payment was NOT charged."`) | No | +| `503` | Service paused | No | Payment settles only after a successful upstream response, so a failed call never costs you anything. diff --git a/docs/api-reference/errors.md b/docs/api-reference/errors.md index 414ce25..be7d9aa 100644 --- a/docs/api-reference/errors.md +++ b/docs/api-reference/errors.md @@ -12,40 +12,82 @@ BlockRun uses standard HTTP status codes and returns detailed error information. | Code | Meaning | |------|---------| | 200 | Success | -| 400 | Bad Request - Invalid parameters | -| 402 | Payment Required - Sign and retry with payment | -| 403 | Content policy violation | -| 429 | Rate limited - upstream provider limit; honor `Retry-After` | -| 500 | Server Error - Something went wrong | -| 504 | Upstream timed out - no charge taken; retry | +| 400 | Bad Request - invalid parameters, unknown model, oversized body, or an upstream rejection of one of your parameters (surfaced as 400, never 500) | +| 402 | Payment Required - sign and retry with payment; or a payment that failed verification (carries a `code`, see below) | +| 429 | Rate limited - upstream capacity for that model, or the per-IP free-tier limit; honor `Retry-After` | +| 503 | Model unavailable / upstream configuration problem - retry or fail over | +| 500 | Server Error - something unexpected went wrong | +| 504 | Upstream timed out (120s) - nothing settled; retry | ## Error Response Format +Gateway errors use the OpenAI envelope, with `message` and `code` mirrored at the top level for older clients. `debug` carries the raw upstream text when there is one. + ```json { - "error": "Error type", - "message": "Detailed error message", - "details": {} + "error": { + "message": "Invalid request parameter — Message @bc1max on Telegram for help.", + "type": "invalid_request_error", + "code": "INVALID_PARAMETER", + "param": null + }, + "message": "Message @bc1max on Telegram for help.", + "code": "INVALID_PARAMETER", + "debug": "" } ``` +`error.type` is one of `invalid_request_error`, `rate_limit_error`, `api_error`, `payment_required`. The Anthropic-compatible `/v1/messages` endpoint answers in Anthropic's `{"type":"error","error":{"type","message"}}` shape and `/v1/responses` in OpenAI's `{"error":{"message","type","param","code"}}` shape instead. + +## Error codes + +| `code` | Status | Meaning | +|--------|--------|---------| +| `INVALID_JSON` | 400 | Body is not valid JSON | +| `INVALID_REQUEST_BODY` | 400 | Schema validation failed; `details` lists the offending paths | +| `EMPTY_CONVERSATION` | 400 | `messages` had only `system` entries | +| `INVALID_PARAMETER` | 400 | Upstream rejected one of your sampling/tool parameters | +| `CONTEXT_LENGTH_EXCEEDED` | 400 | Prompt does not fit the model's context window | +| `INVALID_IMAGE_URL` | 400 | An `image_url` could not be fetched | +| `REQUEST_TOO_LARGE` | 400 | Body over the size limit | +| `CONTENT_FILTERED` | 400 | Upstream safety filter blocked the request | +| `STREAM_UNSUPPORTED` | 400 | `stream: true` on a model that only serves non-streaming | +| `REASONING_FORMAT_ERROR`, `TOOL_ID_FORMAT_ERROR`, `INVALID_REQUEST` | 400 | Message-history shape problems | +| `PAYMENT_INVALID`, `PAYMENT_UNFUNDED`, `PAYMENT_BLOCKHASH_STALE`, `PAYMENT_REPLAY` | 402 | Payment verification failed — see below | +| `RATE_LIMITED` | 429 | Upstream capacity for that model exhausted; `Retry-After` + `X-RateLimit-Source` set | +| `FREE_TIER_RATE_LIMITED` | 429 | Per-IP free-tier limit (30/min, 300/hour) | +| `STREAM_FAILED`, `FREE_MODEL_FAILED` | 429 | Free-model capacity exhausted; `Retry-After: 30` | +| `MODEL_UNAVAILABLE` | 503 | Upstream reports the model missing, overloaded, or at capacity | +| `PROVIDER_CONFIG_ERROR` | 503 | Upstream credential/quota problem on our side | +| `TIMEOUT` | 504 | Upstream did not answer within 120s | +| `INTERNAL_ERROR` | 500 | Anything unclassified | + ## Common Errors ### 400 - Invalid Request ```json { - "error": "Invalid request body", + "error": {"message": "Invalid request body", "type": "invalid_request_error", "code": "INVALID_REQUEST_BODY", "param": null}, + "message": "Invalid request body", + "code": "INVALID_REQUEST_BODY", "details": [ {"path": ["model"], "message": "Required"} ] } ``` +An unknown model is a plain-string error that suggests live IDs: + +```json +{"error": "Unknown model: openai/gpt-4. Try one of: openai/gpt-5.6-sol, …. Full list: GET /v1/models"} +``` + **Causes:** - Missing required fields (`model`, `messages`) - Invalid model ID - Malformed JSON +- A parameter the upstream model rejects (`INVALID_PARAMETER`) — the gateway surfaces these as 400, not 500 ### 402 - Payment Required @@ -53,43 +95,82 @@ BlockRun uses standard HTTP status codes and returns detailed error information. { "error": "Payment Required", "message": "This endpoint requires x402 payment", - "price": { - "amount": "0.001000", - "currency": "USD" - } + "price": {"amount": "0.025685", "currency": "USD"}, + "paymentInfo": {"network": "base", "asset": "USDC", "x402Version": 2} } ``` +The signed requirements travel in the `X-Payment-Required` / `PAYMENT-REQUIRED` headers (and `WWW-Authenticate: X402 requirements="…"`). `price.amount` equals the signed amount, including the flat $0.001 transaction fee. + :::info{title="402 is not an error"} A `402 Payment Required` is part of the normal x402 flow — the gateway is quoting a price. Sign and retry with payment and the SDKs handle this round-trip automatically. ::: ### 402 - Payment Rejected +A `PAYMENT-SIGNATURE` that fails verification is answered with `402` and a machine-readable `code` (since 2026-08-26, on every BlockRun-native paid endpoint): + ```json { "error": "Payment verification failed", - "details": "Insufficient balance" + "code": "PAYMENT_UNFUNDED", + "message": "The payment authorization could not be executed on-chain. The usual cause is an insufficient USDC balance on Base for the quoted amount — …", + "debug": "", + "payer": "0x…" } ``` -**Causes:** -- Insufficient USDC balance -- Invalid signature -- Expired payment authorization +| `code` | Meaning | Fix | +|--------|---------|-----| +| `PAYMENT_UNFUNDED` | Transfer simulation reverted — usually insufficient USDC (an authorization outside its `validAfter`/`validBefore` window reverts the same way) | Fund the wallet; re-sign if your clock is off | +| `PAYMENT_BLOCKHASH_STALE` | Solana-signed payment pinned to an expired blockhash. Nothing was charged | Re-sign against a current blockhash and resend | +| `PAYMENT_REPLAY` | That authorization nonce was already used | Sign a fresh authorization per request. (On image endpoints a replay of a paid-but-lost response returns the job you already paid for instead) | +| `PAYMENT_INVALID` | Any other verification failure — bad signature, wrong network or asset, malformed payload | Sign exactly the requirements from the 402 header | + +Verification always runs before settlement, so none of these charged you. On the Solana gateway `PAYMENT_VERIFICATION_UNAVAILABLE` means the facilitator was unreachable — retry the same signed payment. + +### 429 - Rate Limited + +```json +{ + "error": {"message": "Rate limited — … retry after 60s, or fail over to a same-tier model on a different provider.", "type": "rate_limit_error", "code": "RATE_LIMITED", "param": null}, + "code": "RATE_LIMITED", + "source": "openai", + "retry_after_seconds": 60 +} +``` + +Headers: `Retry-After: 60`, `X-RateLimit-Source: `. Free models add `FREE_TIER_RATE_LIMITED` with `X-RateLimit-Limit` / `X-RateLimit-Remaining` / `X-RateLimit-Reset`. See [Rate Limits](rate-limits.md). + +### 503 - Model Unavailable + +```json +{ + "error": {"message": "Model unavailable — Message @bc1max on Telegram for help.", "type": "api_error", "code": "MODEL_UNAVAILABLE", "param": null}, + "code": "MODEL_UNAVAILABLE", + "debug": "" +} +``` + +Retry, or fail over to a same-tier model from a different family. Not settled. ### 500 - Server Error ```json { - "error": "Internal server error" + "error": {"message": "Unexpected error — Message @bc1max on Telegram for help.", "type": "api_error", "code": "INTERNAL_ERROR", "param": null}, + "message": "Message @bc1max on Telegram for help.", + "code": "INTERNAL_ERROR", + "debug": "" } ``` **Causes:** -- Upstream provider error +- Unclassified upstream error - Temporary service issue +Settlement happens only after a successful upstream response, so a `500`/`503`/`504` never charges you. + ## SDK Error Classes ::::tabs @@ -144,9 +225,10 @@ try { ### "Payment verification failed" -1. Check your USDC balance on Base -2. Ensure your wallet is on the correct network -3. Verify your private key is correct +1. Read the `code`: `PAYMENT_UNFUNDED` → top up USDC on Base (or on Solana for `sol.blockrun.ai`) +2. `PAYMENT_REPLAY` → your client reused a nonce; sign a fresh authorization per request +3. `PAYMENT_BLOCKHASH_STALE` → re-sign; the Solana blockhash expired between signing and sending +4. `PAYMENT_INVALID` → confirm you signed the exact `accepts[0]` entry from the header (network, asset, amount, `payTo`) and that the wallet is on the right network ### "Unknown model" @@ -156,7 +238,7 @@ try { ### "Timeout" 1. Increase the timeout in client options -2. Try a faster model (e.g., `gpt-5.5` or `gpt-5.4-mini` instead of `o1`) +2. Try a faster model (e.g., `gpt-5.4-mini` instead of a `-pro` reasoning tier), or lower `max_tokens` — the gateway's own upstream timeout is 120s per call ### "Network error" diff --git a/docs/api-reference/exa-search.md b/docs/api-reference/exa-search.md index 168f6a7..9a3e011 100644 --- a/docs/api-reference/exa-search.md +++ b/docs/api-reference/exa-search.md @@ -27,6 +27,8 @@ Exa gives agents a live internet connection with structured, grounded results | `/api/v1/exa/contents` | POST | $0.002/URL + $0.001/request | Fetch full Markdown text from a list of URLs | | `/api/v1/exa/find-similar` | POST | $0.011 | Find pages similar to a given URL | +All four are `POST` only. Any other path under `/api/v1/exa/` returns `404` with an `available` list. Requests are forwarded to Exa with a 30-second timeout. + --- ## POST /api/v1/exa/search @@ -324,7 +326,27 @@ const similar = await client.exaFindSimilar("https://blockrun.ai", { numResults: | `/exa/find-similar` | $0.011 | | `/exa/contents` | $0.002 per URL + $0.001 per request | -Payment is in USDC on Base or Solana via x402. No account needed — your wallet is your identity. +Every price above already includes the flat $0.001 per-transaction fee (base $0.01 per call, or $0.002 per URL for `/contents`). Payment is in USDC on Base or Solana via x402. No account needed — your wallet is your identity. + +### The 402 response + +An unpaid request returns `402` with the exact charge in the body and the signable x402 v2 requirements in the `X-Payment-Required` / `PAYMENT-REQUIRED` headers (base64 JSON; also mirrored in `WWW-Authenticate`). For `/contents` the body is read first, so `price.amount` reflects `urls.length`: + +```json +{ + "error": "Payment Required", + "message": "This endpoint requires x402 payment", + "endpoint": "/api/v1/exa/contents", + "method": "POST", + "description": "Extract full text content from specific URLs. ...", + "price": { "amount": "0.0050", "currency": "USD" }, + "paymentInfo": { "network": "base", "asset": "USDC", "x402Version": 2 } +} +``` + +Send the signed payload back in `X-PAYMENT` (or `PAYMENT-SIGNATURE`). A `GET` on any of the four paths returns a discovery 402 whose `paymentInfo.price` is the **per-unit** base rate; for `/contents` it also carries `pricingUnit: "per-url"` and a `pricingNote`. + +Successful responses carry `X-Payment-Response` (x402 v2 settlement receipt) and `X-Payment-Receipt` (the settlement transaction hash). --- @@ -344,11 +366,25 @@ Payment is in USDC on Base or Solana via x402. No account needed — your wallet | Code | Description | |------|-------------| -| 402 | Payment required — sign and retry | -| 400 | Invalid request parameters | -| 404 | Unknown endpoint path | -| 503 | Exa integration not configured | -| 502 | Exa upstream error | +| 402 | Payment required — sign and retry. Also returned when a payment header is present but fails verification (`error: "Payment verification failed"`, see codes below), when an authorization is reused (`code: "PAYMENT_REPLAY"`), or when settlement fails after the call (`error: "Payment settlement failed"`) | +| 400–499 | Exa rejected the request — the upstream status is passed through unchanged with `error: "Bad Request"`, `status`, and Exa's own error in `details`. **Payment was NOT charged.** | +| 404 | Unknown endpoint path (`available` lists the four valid paths) | +| 502 | Exa returned a 5xx (`error: "Upstream provider error"`). Payment was NOT charged | +| 503 | Exa integration not configured, or temporarily paused | +| 500 | Gateway error (`error: "Internal server error"`), including a 30-second upstream timeout | + +Payment is verified before the upstream call and settled only after Exa answers, so a failed or rejected request never costs anything. + +### Payment verification codes + +A verification `402` spreads a machine-readable `code` so clients can branch without parsing the human-readable `details`: + +| `code` | Meaning | +|--------|---------| +| `PAYMENT_INVALID` | Signature, amount, network or recipient did not match the requirements (default; `message` omitted) | +| `PAYMENT_UNFUNDED` | The authorization could not execute on-chain — usually insufficient USDC on Base, or an expired `validAfter`/`validBefore` window | +| `PAYMENT_BLOCKHASH_STALE` | Solana gateway only: signed against an expired blockhash — re-sign and retry | +| `PAYMENT_REPLAY` | That authorization was already used. Sign a fresh one for each request | ## What's next? diff --git a/docs/api-reference/image-editing.md b/docs/api-reference/image-editing.md index 342d5f3..acb42e7 100644 --- a/docs/api-reference/image-editing.md +++ b/docs/api-reference/image-editing.md @@ -9,7 +9,7 @@ Edit existing images — pass one or more source images and describe what to cha :::info{title="Two request formats are accepted"} - **`application/json`** — `image` as a `data:image/...;base64,…` string, or a JSON **array** of them for multi-image fusion. (BlockRun-native.) -- **`multipart/form-data`** — OpenAI's format: repeated `image[]=@file` fields (plus `mask`, `model`, `prompt`, `size`, `n`). Files are converted internally. +- **`multipart/form-data`** — OpenAI's format: repeated `image[]=@file` fields (a single `image=@file` also works), plus `mask`, `model`, `prompt`, `size`, `n`. Files are converted internally to data URIs; a file without an `image/*` MIME type is treated as PNG. **Payment is always x402** (the `X-Payment` / `PAYMENT-SIGNATURE` header), regardless of body format. So you can send an OpenAI-shaped multipart body, but you still attach an x402 payment — the OpenAI SDK's native Bearer-key auth does **not** settle payment here. Use the BlockRun SDK / ClawRouter (which handle x402), or send either body format with your own x402 header. ::: @@ -24,6 +24,7 @@ Edit existing images — pass one or more source images and describe what to cha ``` POST https://blockrun.ai/api/v1/images/image2image # Base POST https://sol.blockrun.ai/api/v1/images/image2image # Solana +GET https://blockrun.ai/api/v1/images/generations/{id} # poll (async slow path only — edits share the generation job store) ``` ## Request @@ -43,21 +44,35 @@ POST https://sol.blockrun.ai/api/v1/images/image2image # Solana | Parameter | Type | Required | Description | |-----------|------|----------|-------------| -| `model` | string | No | Model to use (default: `openai/gpt-image-2`). Supported: `openai/gpt-image-1`, `openai/gpt-image-2`, `google/nano-banana`, `google/nano-banana-pro` | +| `model` | string | No | Model to use (default: `openai/gpt-image-2`). Supported: `openai/gpt-image-1`, `openai/gpt-image-2`, `google/nano-banana`, `google/nano-banana-2`, `google/nano-banana-pro`. Any other ID (including generation-only models such as `bytedance/seedream-5-pro`) returns `400` listing the supported set. | | `prompt` | string | Yes | Description of edits to make | -| `image` | string \| string[] | Yes | Source image as a base64 data URI, **or an array of data URIs** to fuse multiple sources into one variant (e.g. a reference image + a brand logo). OpenAI-compatible `image[]`. Max 4 images for OpenAI, 3 for Google. | -| `mask` | string | No | Mask image as base64 data URI (white = area to edit). Single-image only — cannot be combined with a multi-image `image[]` array. | -| `size` | string | No | Output size (default: `1024x1024`) | -| `n` | integer | No | Number of images to generate (default: 1) | +| `image` | string \| string[] | Yes | Source image as a base64 data URI, **or an array of data URIs** to fuse multiple sources into one variant (e.g. a reference image + a brand logo). OpenAI-compatible `image[]`. Max 4 images for OpenAI, 3 for Google. See [Accepted image formats](#accepted-image-formats). | +| `mask` | string | No | Mask as a base64 data URI, validated like `image`. OpenAI models only. Single-image only — cannot be combined with a multi-image `image[]` array. | +| `size` | string | No | Output size (default: `1024x1024`). Must be one of the model's listed sizes, otherwise `400`. | +| `n` | integer | No | Number of images to generate (default: 1, min 1, max 10). Validated before the 402. | + +Unknown fields are ignored. A body that is not valid JSON (or an unparsable multipart form) returns `400`; a body that fails validation returns `400 { "error": "Invalid request body", "details": [...] }`. + +### Accepted image formats + +Every `image` and `mask` value is decoded and sniffed **before** the `402`, so bad input is a `400` and never costs a signed round-trip. To pass the gate a data URI must: + +- start with `data:image/`, and +- decode to real image bytes — PNG, JPEG, GIF, BMP, WEBP, or an ISO-BMFF container (HEIC / HEIF / AVIF). A syntactically valid URI with garbage bytes (e.g. `data:image/png;base64,AAAA`) is rejected with `image data URI does not decode to a recognizable image (expected PNG, JPEG, GIF, BMP, WEBP, or HEIC/HEIF/AVIF)`. + +The gate only rejects undecodable data; the model is the final authority on formats. Google Nano Banana models accept HEIC/HEIF (the iPhone camera default), while OpenAI GPT Image models do not — sending one there returns `400 { "error": "Invalid input image", "details": "..." }` from the model's own check, still without a charge. There is no separate byte cap on the source images beyond what fits in one HTTP request. ### Supported Models -| Model ID | Provider | Mask | Sizes | Pricing | -|----------|----------|------|-------|---------| -| `openai/gpt-image-1` | OpenAI | ✅ | 1024x1024, 1536x1024, 1024x1536 | $0.02-0.04 | -| `openai/gpt-image-2` | OpenAI | ✅ | 1024x1024, 1536x1024, 1024x1536 | $0.06-0.12 | -| `google/nano-banana` | Google (Gemini 2.5 Flash Image) | ❌ prompt-only | 1024x1024 | $0.05 | -| `google/nano-banana-pro` | Google (Gemini 3 Pro Image) | ❌ prompt-only | 1024x1024, 2048x2048, 4096x4096 | $0.10-0.15 | +Prices are what the `402` challenge quotes and what is billed for **one** edit at that size — catalog rate + 5% + the flat $0.001 transaction fee (see [Pricing](#pricing)). + +| Model ID | Provider | Mask | Max source images | Sizes | Price (n=1) | +|----------|----------|------|-------|-------|---------| +| `openai/gpt-image-1` | OpenAI | ✅ | 4 | 1024x1024, 1536x1024, 1024x1536 | $0.022 / $0.043 | +| `openai/gpt-image-2` | OpenAI | ✅ | 4 | 1024x1024, 1536x1024, 1024x1536 | $0.064 / $0.127 | +| `google/nano-banana` | Google (Gemini 2.5 Flash Image) | ❌ prompt-only | 3 | 1024x1024 | $0.0535 | +| `google/nano-banana-2` | Google (Gemini 3.1 Flash Image) | ❌ prompt-only | 3 | 1024x1024 | $0.0955 | +| `google/nano-banana-pro` | Google (Gemini 3 Pro Image) | ❌ prompt-only | 3 | 1024x1024, 2048x2048, 4096x4096 | $0.106 / $0.106 / $0.1585 | ## Hybrid sync/async flow & settlement @@ -65,22 +80,24 @@ Like [`/v1/images/generations`](image-generation.md#how-it-works--hybrid-syncasy this endpoint is **hybrid** — it is *not* always synchronous: - **Fast edits (≤30s inline window):** `200` with the standard - `{ created, data: [...] }` body below. Payment settles inside this response. -- **Slow edits (>30s — multi-image gpt-image-2 fusion routinely takes longer):** - `202` with an async job envelope `{ id, status, poll_url, price, - payment_status: "verified" }`. **Nothing has been charged yet.** Poll - `GET {poll_url}` (it lives under `/v1/images/generations/{id}` — edits share - the same job store) with an `x-payment` header from the same wallet every - 2–5s. USDC settles exactly once, on the first poll that observes + `{ id, created, data: [...] }` body below. Payment settles inside this response. +- **Slow edits (>30s — `openai/gpt-image-2` single-image edits routinely take + 45–90s, and multi-image fusion longer; its upstream call is allowed up to 600s):** + `202` with an async job envelope `{ id, object: "image.edit.job", status, + poll_url, price, payment_status: "verified" }`. **Nothing has been charged + yet.** Poll `GET {poll_url}` (it lives under `/v1/images/generations/{id}` — + edits share the same job store) with an `x-payment` header from the same + wallet every 2–5s. USDC settles exactly once, on the first poll that observes `status: "completed"`. A job that ends `failed`, or that you never poll, is **never charged**. :::note{title="No charge until the image is ready"} -On the slow async path nothing is debited at submit time (`payment_status: "verified"`). USDC settles exactly once — on the first poll that sees `status: "completed"`. Failed jobs and never-polled jobs are never charged. +On the slow async path nothing is debited at submit time (`payment_status: "verified"`). USDC settles exactly once — on the first poll that sees `status: "completed"`. Failed jobs and never-polled jobs are never charged. The signed authorization is valid for 600s (`maxTimeoutSeconds` in the challenge), so finish polling within 10 minutes of signing. ::: The envelope fields, poll responses, status enum -(`queued → in_progress → completed | failed`) and settlement guarantees are +(`queued → in_progress → completed | failed`), replay recovery +(`402 PAYMENT_REPLAY` with `job_id` + `poll_url`) and settlement guarantees are identical to image generation — see the [full spec there](image-generation.md#how-it-works--hybrid-syncasync-flow--settlement). Go SDK `blockrun-llm-go ≥ v0.17.0` (`ImageClient.Edit`) handles the hybrid @@ -90,27 +107,31 @@ flow transparently and always returns the synchronous shape. ```json { + "id": "img_8f3a…", "created": 1706000000, "data": [ { - "url": "https://...", + "url": "data:image/png;base64,…", "revised_prompt": "A photo with the background changed to..." } ], - "price": { "amount": "0.063000", "currency": "USD" }, + "price": { "amount": "0.064000", "currency": "USD" }, "payment": { "status": "settled", "tx_hash": "0x…", "network": "base" } } ``` +Headers: `PAYMENT-RESPONSE` (x402 settlement receipt) and `X-Payment-Receipt` (the on-chain tx hash). + ### Response Fields | Field | Type | Description | |-------|------|-------------| +| `id` | string | Per-call job id (same value as `job_id` in the async envelope, poll responses and replay `402`s) | | `created` | integer | Unix timestamp | | `data` | array | Array of edited images | -| `data[].url` | string | URL to edited image | -| `data[].revised_prompt` | string | Expanded prompt | -| `price.amount` | string | USD charged for this call | +| `data[].url` | string | **Fast path (`200` from the POST):** the model's raw output, which for every supported model is a `data:image/...;base64,` URI — it is *not* mirrored to durable storage, so persist it yourself. **Slow path (`200` from the poll):** a permanent BlockRun-hosted proxy URL, with `source_url` and `backed_up` alongside, exactly like [image generation](image-generation.md#response-fields). | +| `data[].revised_prompt` | string | Expanded prompt (when the model returns one) | +| `price.amount` | string | USD billed for this call (includes the 5% margin and the $0.001 transaction fee) | | `payment.status` | string | `settled` \| `already_settled` | | `payment.tx_hash` | string | On-chain USDC settlement tx (also in `X-Payment-Receipt` header) | @@ -175,15 +196,15 @@ curl -X POST https://sol.blockrun.ai/api/v1/images/image2image \ ## Mask Usage :::warning{title="Mask is OpenAI-only"} -Masks apply to `openai/gpt-image-1` and `openai/gpt-image-2`. Google `nano-banana` / `nano-banana-pro` are prompt-only — passing a `mask` to a Google model returns `400`. +Masks apply to `openai/gpt-image-1` and `openai/gpt-image-2`. Google `nano-banana` / `nano-banana-2` / `nano-banana-pro` are prompt-only — passing a `mask` to a Google model returns `400` before payment. ::: -The mask defines which parts of the image to edit: +The mask is passed to the model unchanged, so it follows the OpenAI Images edit convention: a PNG with an alpha channel, the same dimensions as the source image. -- **White pixels** = areas to edit (AI generates new content here) -- **Black pixels** = areas to keep unchanged +- **Fully transparent pixels (alpha 0)** = areas to edit (the model generates new content here) +- **Opaque pixels** = areas to keep unchanged -If no mask is provided, the AI will edit the entire image based on the prompt. +If no mask is provided, the model edits the entire image based on the prompt. The mask goes through the same pre-402 byte check as `image`. ## Multi-image input (image fusion) @@ -191,9 +212,9 @@ Pass an **array** of source images in `image` to fuse several inputs into one result — e.g. a reference image + a brand logo, composed by a single prompt. - **OpenAI** `gpt-image-1` / `gpt-image-2`: up to **4** source images. -- **Google** `nano-banana` / `nano-banana-pro`: up to **3** source images (earlier images act as primary composition anchors). +- **Google** `nano-banana` / `nano-banana-2` / `nano-banana-pro`: up to **3** source images (earlier images act as primary composition anchors). - `mask` **cannot** be combined with a multi-image array (mask is single-region only) → `400`. -- Exceeding a provider's image cap → `400`. +- Exceeding a model's image cap → `400` (`Model … accepts at most N source images per edit`). :::info{title="Two ways to send multiple images"} - **JSON** (shown below): `image` as a JSON **array of base64 data URIs**. @@ -237,30 +258,39 @@ Single-image requests are unchanged — `image` as a plain string (JSON) or a si ## Pricing -Prices include 5% BlockRun margin: - -| Model | Size | Price | -|-------|------|-------| -| GPT Image 1 | 1024x1024 | $0.021 | -| GPT Image 1 | 1536x1024 | $0.042 | -| GPT Image 1 | 1024x1536 | $0.042 | -| ChatGPT Images 2.0 | 1024x1024 | $0.063 | -| ChatGPT Images 2.0 | 1536x1024 | $0.126 | -| ChatGPT Images 2.0 | 1024x1536 | $0.126 | -| Nano Banana | 1024x1024 | $0.0525 | -| Nano Banana Pro | 1024x1024 | $0.105 | -| Nano Banana Pro | 2048x2048 | $0.105 | -| Nano Banana Pro | 4096x4096 | $0.1575 | +Edits bill exactly like generations on the same model: + +``` +billed = catalog rate for the size × n × 1.05 (5% platform margin on media) + + $0.001 (flat transaction fee, once per call) +``` + +| Model | Size | Catalog rate | Billed (n=1) | +|-------|------|-------|-------| +| GPT Image 1 | 1024x1024 | $0.02 | $0.022 | +| GPT Image 1 | 1536x1024 | $0.04 | $0.043 | +| GPT Image 1 | 1024x1536 | $0.04 | $0.043 | +| ChatGPT Images 2.0 | 1024x1024 | $0.06 | $0.064 | +| ChatGPT Images 2.0 | 1536x1024 | $0.12 | $0.127 | +| ChatGPT Images 2.0 | 1024x1536 | $0.12 | $0.127 | +| Nano Banana | 1024x1024 | $0.05 | $0.0535 | +| Nano Banana 2 | 1024x1024 | $0.09 | $0.0955 | +| Nano Banana Pro | 1024x1024 | $0.10 | $0.106 | +| Nano Banana Pro | 2048x2048 | $0.10 | $0.106 | +| Nano Banana Pro | 4096x4096 | $0.15 | $0.1585 | + +The number of *source* images does not change the price — you pay per output image. ## Error Codes | Code | Description | |------|-------------| -| 400 | Invalid request (bad image format, unsupported model, invalid size) | -| 402 | Payment required | -| 429 | Rate limit exceeded | -| 500 | Server error | -| 504 | Image editing timed out | +| 400 | Invalid request: malformed JSON / multipart, failed validation (`details[]` — including an `image` or `mask` that does not decode to a real image), unsupported model (message lists the supported set), mask on a Google model, mask combined with multiple images, too many source images, invalid size for the model, `n` outside 1–10, `Invalid input image` (the model rejected the file, e.g. HEIC on an OpenAI model), or `Content policy violation`. None of these are billed. | +| 402 | Payment required / rejected / replayed. Same shapes and machine-readable `code`s (`PAYMENT_INVALID`, `PAYMENT_UNFUNDED`, `PAYMENT_BLOCKHASH_STALE`, `PAYMENT_REPLAY`) as [image generation](image-generation.md#402-responses); the unpaid body has no `generation_info` block. | +| 403 / 404 | Poll only — payer mismatch / job not found (see the [poll responses](image-generation.md#poll-responses)). | +| 429 | Upstream rate limit (`{ "error": "Rate limit exceeded", "details" }`). BlockRun applies no per-IP limit of its own to this endpoint. | +| 500 | Server error (`Image editing failed` / `Internal server error`, with `details` and the `job_id`). Settlement never ran, so nothing was billed. | +| 504 | `Image editing timed out` — rare: an edit that outlives the 30s inline window normally goes async (`202`) instead of timing out. | ## What's next? diff --git a/docs/api-reference/image-generation.md b/docs/api-reference/image-generation.md index b66f8c8..e352f35 100644 --- a/docs/api-reference/image-generation.md +++ b/docs/api-reference/image-generation.md @@ -1,18 +1,24 @@ --- title: Image Generation API -description: Generate images with GPT Image, Nano Banana, CogView-4, or Grok Imagine through one OpenAI-compatible endpoint, paid per call in USDC over x402. +description: Generate images with GPT Image, Nano Banana, Seedream, CogView-4, or Grok Imagine through one OpenAI-compatible endpoint, paid per call in USDC over x402. --- # Image Generation API -Generate images using GPT Image (including ChatGPT Images 2.0), Google Nano Banana, CogView-4, or xAI Grok Imagine. +Generate images using GPT Image (including ChatGPT Images 2.0), Google Nano Banana (including Nano Banana 2 and Pro), ByteDance Seedream 5.0 Pro, CogView-4, or xAI Grok Imagine. ## Endpoint ``` POST https://blockrun.ai/api/v1/images/generations +GET https://blockrun.ai/api/v1/images/generations/{id} # poll (async slow path only) +GET https://blockrun.ai/api/v1/images/models # catalog + per-size prices, free ``` +:::note +The live gateway path is `/api/v1/...`. Bare `/v1/...` on `blockrun.ai` returns `404`; the `/v1` form is the local ClawRouter surface. +::: + ## Request ```json @@ -24,43 +30,51 @@ POST https://blockrun.ai/api/v1/images/generations } ``` +The body must be `application/json`. A body that is not valid JSON returns `400 { "error": "Invalid JSON", ... }`; a body that fails validation returns `400 { "error": "Invalid request body", "details": [...] }` with the per-field issues. + ### Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| -| `model` | string | Yes | Model to use (see below) | +| `model` | string | Yes | Model to use (see below). An unknown ID returns `400` whose `error` lists every available model ID. | | `prompt` | string | Yes | Image description | -| `size` | string | No | Image dimensions (default: "1024x1024") | -| `quality` | string | No | "standard" or "hd" (model-dependent) | -| `n` | integer | No | Number of images (default: 1) | +| `size` | string | No | Image dimensions (default: `"1024x1024"`). Must be one of the model's listed sizes — anything else returns `400` naming the valid sizes for that model. | +| `n` | integer | No | Number of images (default: 1, min 1, max 10). Validated before the 402 so an out-of-range value never costs a signed round-trip. | + +Unknown fields (for example OpenAI's `quality`, `style`, `response_format`) are ignored, not rejected. ### Available Models -| Model ID | Provider | Sizes | Price | +Prices are what the `402` challenge quotes and what is billed for **one** image at that size: the catalog rate + 5% + the flat $0.001 transaction fee. See [Pricing](#pricing) for the formula and every size. + +| Model ID | Provider | Sizes | Price (1 image) | |----------|----------|-------|-------| -| `openai/gpt-image-1` | OpenAI | 1024x1024, 1536x1024, 1024x1536 | $0.021 | -| `openai/gpt-image-2` | OpenAI | 1024x1024, 1536x1024, 1024x1536 | $0.063 | -| `google/nano-banana` | Google | 1024x1024 | $0.053 | -| `google/nano-banana-pro` | Google | 1024x1024, 2048x2048, 4096x4096 | $0.105 | -| `zai/cogview-4` | Zhipu AI | 512x512 – 1440x1440 | $0.015 | -| `xai/grok-imagine-image` | xAI | 1024x1024 | $0.021 | -| `xai/grok-imagine-image-pro` | xAI | 1024x1024 | $0.074 | -| `bytedance/seedream-5-pro` | ByteDance | 1024x1024 – 2848x1600 (8 sizes) | $0.047–$0.095 | +| `openai/gpt-image-1` | OpenAI | 1024x1024, 1536x1024, 1024x1536 | $0.022 / $0.043 | +| `openai/gpt-image-2` | OpenAI | 1024x1024, 1536x1024, 1024x1536 | $0.064 / $0.127 | +| `google/nano-banana` | Google | 1024x1024 | $0.0535 | +| `google/nano-banana-2` | Google | 1024x1024 | $0.0955 | +| `google/nano-banana-pro` | Google | 1024x1024, 2048x2048, 4096x4096 | $0.106 / $0.106 / $0.1585 | +| `zai/cogview-4` | Zhipu AI | 512x512 – 1440x1440 | $0.01675 / $0.022 | +| `xai/grok-imagine-image` | xAI | 1024x1024 | $0.022 | +| `xai/grok-imagine-image-pro` | xAI | 1024x1024 | $0.0745 | +| `bytedance/seedream-5-pro` | ByteDance | 1024x1024 – 2848x1600 (8 sizes) | $0.04825 / $0.0955 | + +`GET /api/v1/images/models` returns the same catalog with `pricing.sizes[]` per model (catalog rates, before margin and fee). `openai/dall-e-3` was removed from the catalog and now returns the unknown-model `400`. #### Seedream 5.0 Pro Sizes `bytedance/seedream-5-pro` is ByteDance's flagship image model (up to 4K-class resolution). Pricing is by output pixel count — sizes at or below ~2.36M pixels -are $0.047, larger sizes are $0.095: +bill $0.04825, larger sizes bill $0.0955 (catalog $0.045 / $0.09 + 5% + $0.001): | Size | Price | Use Case | |------|-------|----------| -| `1024x1024` | $0.047 | Standard (default) | -| `1280x720` | $0.047 | HD landscape | -| `2048x1024` | $0.047 | Wide banner | -| `2048x2048` | $0.095 | High-resolution square | -| `2304x1728` / `1728x2304` | $0.095 | 4:3 / 3:4 print-quality | -| `2848x1600` / `1600x2848` | $0.095 | 16:9 / 9:16 4K-class | +| `1024x1024` | $0.04825 | Standard (default) | +| `1280x720` | $0.04825 | HD landscape | +| `2048x1024` | $0.04825 | Wide banner | +| `2048x2048` | $0.0955 | High-resolution square | +| `2304x1728` / `1728x2304` | $0.0955 | 4:3 / 3:4 print-quality | +| `2848x1600` / `1600x2848` | $0.0955 | 16:9 / 9:16 4K-class | Seedream 5.0 Pro generations take **~2 minutes** — calls always resolve through the async `202` + poll flow described below, and you are only charged when the @@ -68,16 +82,16 @@ image completes. #### CogView-4 Sizes -`zai/cogview-4` supports flexible sizes (multiples of 16, max 1440×1440): +`zai/cogview-4` accepts exactly these sizes (any other value returns `400`): -| Size | Use Case | -|------|----------| -| `512x512` | Thumbnails, icons | -| `768x768` | Social media | -| `1024x1024` | Standard (default) | -| `768x1344` | Portrait / mobile | -| `1344x768` | Landscape / banner | -| `1440x1440` | High resolution | +| Size | Price | Use Case | +|------|-------|----------| +| `512x512` | $0.01675 | Thumbnails, icons | +| `768x768` | $0.01675 | Social media | +| `1024x1024` | $0.01675 | Standard (default) | +| `768x1344` | $0.01675 | Portrait / mobile | +| `1344x768` | $0.01675 | Landscape / banner | +| `1440x1440` | $0.022 | High resolution | ## How it works — hybrid sync/async flow & settlement @@ -86,14 +100,17 @@ switch to an async job you poll. The split is purely by elapsed time: 1. **`POST /v1/images/generations`** with an `x-payment` (or `PAYMENT-SIGNATURE`) header. The gateway **verifies** the payment - authorization (no USDC moves yet) and starts generation. + authorization (no USDC moves yet), claims the authorization's nonce so it + cannot be reused, creates a job, and starts generation. 2. **Fast path (≤30s inline window — most models):** generation finishes - inline. The gateway settles the payment and returns **`200`** with the - standard `{ created, data: [...] }` body below. This is the only moment a - fast-path call is charged. -3. **Slow path (>30s — e.g. `openai/gpt-image-2` under load):** the gateway - returns **`202`** with an async job envelope `{ id, status: "queued", - poll_url, price, payment_status: "verified" }`. **No USDC has moved.** + inline. The gateway mirrors the image to durable storage, settles the + payment and returns **`200`** with the standard `{ id, created, data: [...] }` + body below. This is the only moment a fast-path call is charged. +3. **Slow path (>30s — `bytedance/seedream-5-pro` always, `openai/gpt-image-2` + under load):** the gateway returns **`202`** with an async job envelope + `{ id, status: "queued", poll_url, price, payment_status: "verified" }`. + **No USDC has moved.** Generation keeps running in the background — the + upstream call for `openai/gpt-image-2` is allowed up to 600s. 4. **`GET {poll_url}`** — poll every 2–5s with an `x-payment` header signed by the **same wallet** (a fresh signature works; the gateway enforces wallet binding, not signature byte-equality; with no header it returns a normal @@ -108,7 +125,10 @@ switch to an async job you poll. The split is purely by elapsed time: | `payment_status: "verified"` | Signature/authorization checked only — **not** a charge. | | Upstream fails (`status: "failed"`) | `payment_status: "not_charged"` — no USDC is ever transferred. | | You never poll | Nothing settles; the signed authorization simply expires. You are not charged. | -| Idempotent re-polls | Polling an already-settled job returns the same URLs again (`payment.status: "already_settled"`) — never double-charged. | +| Idempotent re-polls | Polling an already-settled job returns the same URLs again (`payment.status: "already_settled"`) — never double-charged. A settle-once claim per job also covers the race between an inline settle and a concurrent poll. | +| Lost response is recoverable, not re-billable | Re-sending the **same** signed authorization returns `402 { code: "PAYMENT_REPLAY", job_id, poll_url, recoverable: true }` pointing at the job that authorization already paid for — `GET poll_url` to collect it instead of signing again. | +| Authorization lifetime | The `402` challenge sets `maxTimeoutSeconds: 600`; the same authorization must still be valid when the settling poll lands, so finish polling within 10 minutes of signing. | +| Stalled jobs | A job still `in_progress` after 1 hour is marked `failed` ("Job stalled …") and is never charged. | | Settlement timing | Fast path: inside the `200` POST. Slow path: on the first `completed` poll. Identical to `/v1/videos/generations` and `/v1/images/image2image`. | ### Async job envelope (`202`) @@ -121,7 +141,7 @@ switch to an async job you poll. The split is purely by elapsed time: "model": "openai/gpt-image-2", "size": "1024x1024", "n": 1, - "price": { "amount": "0.063000", "currency": "USD" }, + "price": { "amount": "0.064000", "currency": "USD" }, "payment_status": "verified", "created": 1706000000, "poll_url": "/api/v1/images/generations/img_8f3a…", @@ -129,15 +149,20 @@ switch to an async job you poll. The split is purely by elapsed time: } ``` -`status` enum: `queued` → `in_progress` → `completed` | `failed`. +`status` enum: `queued` → `in_progress` → `completed` | `failed`. `poll_url` is a path — prefix it with the origin you posted to. ### Poll responses -- **`202` running:** `{ id, object, status: "queued" | "in_progress", model, payment_status: "verified" }` +- **`202` running:** `{ id, object, status: "queued" | "in_progress", model, payment_status: "verified", note }` - **`200` completed (charged here):** the standard body below plus `price: { amount, currency }` and `payment: { status: "settled", tx_hash, network }`, with `PAYMENT-RESPONSE` and `X-Payment-Receipt` (on-chain tx hash) headers. -- **`200` failed (not charged):** `{ id, object, status: "failed", model, error, payment_status: "not_charged" }` +- **`200` completed, already settled (not charged again):** same body with `payment.status: "already_settled"`. +- **`200` failed (not charged):** `{ id, object, status: "failed", model, error, payment_status: "not_charged", note }` +- **`402` no header:** a fresh x402 challenge for this job (`job_id`, `model`, `price` in the body; requirements in the headers) — sign with the original wallet and retry. +- **`402` settlement failed:** `{ error: "Payment settlement failed", details, note }` — the image is ready but the authorization could not be settled; retry the poll, and re-sign if the authorization has expired. +- **`403` payer mismatch:** `{ error: "Payment payer mismatch" }` — the presenting wallet is not the one that submitted the job. +- **`404` job not found:** `{ error: "Job not found" }` — expired or never created. :::warning{title="OpenAI-compatible clients and the async envelope"} Plain OpenAI SDKs don't understand the `202` envelope. Use a model that completes inline, or the official BlockRun SDKs — Go `blockrun-llm-go ≥ v0.17.0` handles the hybrid flow transparently and always returns the synchronous `{data:[...]}` shape. @@ -147,6 +172,7 @@ Plain OpenAI SDKs don't understand the `202` envelope. Use a model that complete ```json { + "id": "img_8f3a…", "created": 1706000000, "data": [ { @@ -156,21 +182,27 @@ Plain OpenAI SDKs don't understand the `202` envelope. Use a model that complete "revised_prompt": "..." } ], - "price": { "amount": "0.015000", "currency": "USD" }, + "price": { "amount": "0.016750", "currency": "USD" }, "payment": { "status": "settled", "tx_hash": "0x…", "network": "base" } } ``` +Headers: `PAYMENT-RESPONSE` (x402 settlement receipt) and `X-Payment-Receipt` (the on-chain tx hash). + ### Response Fields | Field | Type | Description | |-------|------|-------------| +| `id` | string | Per-call job id — the same value used as `job_id` in the async envelope, poll responses and replay `402`s, so you can reconcile 1:1 | | `created` | integer | Unix timestamp | | `data` | array | Array of generated images | -| `data[].url` | string | Permanent URL. When GCS backup succeeds, this is a blockrun-hosted proxy URL; otherwise the upstream URL. For `openai/gpt-image-1` and `openai/gpt-image-2` this is a base64 data URI | -| `data[].source_url` | string | Original upstream URL (omitted for data URIs) | -| `data[].backed_up` | boolean | `true` when the image was mirrored to BlockRun's GCS bucket (omitted for data URIs) | +| `data[].url` | string | Permanent BlockRun-hosted proxy URL when the mirror to durable storage succeeds — this applies to every model, including the ones whose raw output is base64 (`openai/gpt-image-*`, `google/nano-banana*`, `xai/grok-imagine-image*`). If the mirror fails, `url` is the raw upstream value instead: a temporary upstream URL or a `data:image/...;base64,` URI | +| `data[].source_url` | string | Original upstream URL; for base64 outputs only the MIME type is kept (e.g. `"data:image/png"`), never the payload | +| `data[].backed_up` | boolean | `true` when the image was mirrored to BlockRun's durable storage | | `data[].revised_prompt` | string | Expanded prompt (when the model rewrites it) | +| `price.amount` | string | USD billed for this call (includes the 5% margin and the $0.001 transaction fee) | +| `payment.status` | string | `settled` \| `already_settled` | +| `payment.tx_hash` | string | On-chain USDC settlement tx (also in `X-Payment-Receipt`) | :::info{title="Why both url and source_url?"} Upstream image URLs are usually temporary (they can expire within an hour). BlockRun mirrors each generated image to durable cloud storage and returns the permanent proxy URL as `url`; `source_url` is the original (possibly short-lived) upstream URL. @@ -266,28 +298,69 @@ console.log(result2.data[0].url); ## Pricing -| Model | Size | Price | -|-------|------|-------| -| CogView-4 | up to 1440x1440 | **$0.015** | -| CogView-4 | 1440x1440 | $0.02 | -| GPT Image 1 | 1024x1024 | $0.021 | -| ChatGPT Images 2.0 | 1024x1024 | $0.063 | -| ChatGPT Images 2.0 | 1536x1024 / 1024x1536 | $0.126 | -| Grok Imagine | 1024x1024 | $0.021 | -| Grok Imagine Pro | 1024x1024 | $0.074 | -| Nano Banana | 1024x1024 | $0.053 | -| Nano Banana Pro | 1024x1024 | $0.105 | -| Nano Banana Pro 4K | 4096x4096 | $0.158 | +Every price below is the amount quoted in the `402` challenge (`price.amount`) and billed in USDC: + +``` +billed = catalog rate for the size × n × 1.05 (5% platform margin on media) + + $0.001 (flat transaction fee, once per call) +``` + +`n` multiplies the margined rate; the transaction fee is charged once per call, not per image. The catalog rates themselves are published by `GET /api/v1/images/models`. + +| Model | Size | Catalog rate | Billed (n=1) | +|-------|------|-------|-------| +| CogView-4 | 512x512 – 1344x768 | $0.015 | **$0.01675** | +| CogView-4 | 1440x1440 | $0.02 | $0.022 | +| GPT Image 1 | 1024x1024 | $0.02 | $0.022 | +| GPT Image 1 | 1536x1024 / 1024x1536 | $0.04 | $0.043 | +| ChatGPT Images 2.0 | 1024x1024 | $0.06 | $0.064 | +| ChatGPT Images 2.0 | 1536x1024 / 1024x1536 | $0.12 | $0.127 | +| Grok Imagine | 1024x1024 | $0.02 | $0.022 | +| Grok Imagine Pro | 1024x1024 | $0.07 | $0.0745 | +| Nano Banana | 1024x1024 | $0.05 | $0.0535 | +| Nano Banana 2 | 1024x1024 | $0.09 | $0.0955 | +| Nano Banana Pro | 1024x1024 / 2048x2048 | $0.10 | $0.106 | +| Nano Banana Pro 4K | 4096x4096 | $0.15 | $0.1585 | +| Seedream 5.0 Pro | ≤ ~2.36M pixels (1024x1024, 1280x720, 2048x1024) | $0.045 | $0.04825 | +| Seedream 5.0 Pro | > 2.36M pixels (2048x2048 and larger) | $0.09 | $0.0955 | ## Error Codes | Code | Description | |------|-------------| -| 400 | Invalid request (bad prompt or parameters) | -| 402 | Payment required (insufficient balance) | -| 403 | Content policy violation | -| 429 | Rate limit exceeded | -| 500 | Server error | +| 400 | Invalid request: malformed JSON, failed validation (`details[]`), unknown model (the message lists the available IDs), invalid size for the model, `n` outside 1–10, or a **content-policy rejection** (`{ "error": "Content policy violation", "details" }`) — content-policy rejections are `400`, not `403` | +| 402 | Payment required, rejected, or replayed — see below. A `402` never means the image was generated and billed. | +| 403 | Poll only — `Payment payer mismatch`: the polling wallet is not the one that submitted the job | +| 404 | Poll only — `Job not found` (expired or never created) | +| 429 | Upstream rate limit: `{ "error": "Rate limit exceeded", "code": "RATE_LIMITED", "source": "", "retry_after_seconds", "details" }` with `Retry-After` and `X-RateLimit-Source` headers. BlockRun applies no per-IP limit of its own to this endpoint (only `GET /api/v1/images/models` is limited, 100 req/hour per IP) | +| 500 | Server error (`Image generation failed` / `Internal server error`, with `details` and, on the inline path, the `job_id`). Settlement never ran, so nothing was billed | + +### 402 responses + +The unpaid `402` is a normal x402 challenge: the signable requirements live in the `X-Payment-Required` / `PAYMENT-REQUIRED` / `WWW-Authenticate` headers (base64 JSON, `x402Version: 2`, `maxTimeoutSeconds: 600`); the body is informational. + +```json +{ + "error": "Payment Required", + "message": "This endpoint requires x402 payment", + "price": { "amount": "0.053500", "currency": "USD", "pricePerImage": 0.05, "totalImages": 1 }, + "generation_info": { "generation_time": "~10s-10min upstream depending on model and prompt complexity", "flow": "hybrid", "note": "..." }, + "paymentInfo": { "network": "base", "asset": "USDC", "x402Version": 2 } +} +``` + +`price.amount` is the billed total; `pricePerImage` is the catalog rate before margin and fee. + +When a payment header is present but rejected, the body is `{ "error": "Payment verification failed", "code", "message"?, "details" }` and `code` is machine-readable: + +| `code` | Meaning | What to do | +|--------|---------|------------| +| `PAYMENT_INVALID` | Default — the signature or authorization did not verify | Check the header; `details` carries the raw reason | +| `PAYMENT_UNFUNDED` | The authorization could not execute on-chain — usually an insufficient USDC balance on Base for the quoted amount; can also be an expired `validAfter`/`validBefore` window | Fund the wallet, or sign a fresh authorization | +| `PAYMENT_BLOCKHASH_STALE` | Solana-signed payments — the transaction was pinned to a blockhash that has expired. Nothing was charged | Re-sign against a current blockhash and resend | +| `PAYMENT_REPLAY` | The authorization was already used. If that earlier request completed, the body carries `job_id`, `poll_url` and `recoverable: true` | `GET poll_url` with the same wallet to collect the result you already paid for; do not sign again | + +A `402 { "error": "Payment settlement failed" }` (with a `PAYMENT-RESPONSE` header) means verification passed but settlement did not; on the fast path nothing was billed, on the poll path retry the poll. ## Model Selection Guide @@ -296,10 +369,12 @@ console.log(result2.data[0].url); | Cheapest | `zai/cogview-4` | | Chinese prompts | `zai/cogview-4` | | Highest quality | `google/nano-banana-pro` | +| Pro-level quality at Flash speed | `google/nano-banana-2` | | Fast & reliable | `google/nano-banana` | | Best prompt following | `openai/gpt-image-2` | -| Image editing (img2img) | `openai/gpt-image-1`, `openai/gpt-image-2`, `google/nano-banana`, or `google/nano-banana-pro` | -| Multi-image fusion (ref + logo → 1 image) | `google/nano-banana(-pro)` (≤3) or `openai/gpt-image-1/2` (≤4) — see [Image Editing](image-editing.md) | +| Largest output (up to 2848x1600 / 4K-class) | `bytedance/seedream-5-pro` (async, ~2 min) | +| Image editing (img2img) | `openai/gpt-image-1`, `openai/gpt-image-2`, `google/nano-banana`, `google/nano-banana-2`, or `google/nano-banana-pro` | +| Multi-image fusion (ref + logo → 1 image) | `google/nano-banana(-2/-pro)` (≤3) or `openai/gpt-image-1/2` (≤4) — see [Image Editing](image-editing.md) | | Multilingual text in images / character consistency | `openai/gpt-image-2` | | xAI-style stylization | `xai/grok-imagine-image-pro` | @@ -323,6 +398,8 @@ response = client.images.generate( print(response.data[0].url) ``` +The extra `id`, `price` and `payment` fields are additive — OpenAI clients ignore them. The OpenAI SDK's bearer key does **not** pay: you still need an x402 header, so pair it with a client that signs one (BlockRun SDKs, ClawRouter). + ## What's next? ::::cards diff --git a/docs/api-reference/market-data.md b/docs/api-reference/market-data.md index be88225..ab7d0cc 100644 --- a/docs/api-reference/market-data.md +++ b/docs/api-reference/market-data.md @@ -1,6 +1,6 @@ --- title: Market Data (Pyth) -description: Spot prices and OHLC history for stocks, crypto, FX and commodities from Pyth Network — crypto, FX and commodities are free; equities are $0.0010 per call. +description: Spot prices and OHLC history for stocks, crypto, FX and commodities from Pyth Network — crypto, FX and commodities are free; equities are $0.002 per call. --- # Market Data (Pyth) @@ -9,7 +9,8 @@ Spot prices and historical OHLC bars across four asset classes, grounded in [Pyth Network](https://pyth.network) on-chain feeds. **Crypto, FX and commodity prices are free.** Equities — US and international — -are `$0.0010` per call, because those feeds are broker-fed rather than +are `$0.002` per call (`$0.001` base plus the flat `$0.001` transaction fee; +the 402 quotes `"0.0020"`), because those feeds are broker-fed rather than open on-chain data. Every `/list` endpoint is free regardless of asset class. ## Endpoints @@ -26,14 +27,15 @@ open on-chain data. Every `/list` endpoint is free regardless of asset class. | `/api/v1/commodity/price/{symbol}` | GET | Free | Commodity spot price | | `/api/v1/commodity/history/{symbol}` | GET | Free | Commodity OHLC bars | | `/api/v1/usstock/list` | GET | Free | US tickers | -| `/api/v1/usstock/price/{symbol}` | GET | $0.0010 | US equity spot price | -| `/api/v1/usstock/history/{symbol}` | GET | $0.0010 | US equity OHLC bars | +| `/api/v1/usstock/price/{symbol}` | GET | $0.002 | US equity spot price | +| `/api/v1/usstock/history/{symbol}` | GET | $0.002 | US equity OHLC bars | | `/api/v1/stocks/{market}/list` | GET | Free | Tickers for one non-US market | -| `/api/v1/stocks/{market}/price/{symbol}` | GET | $0.0010 | Non-US equity spot price | -| `/api/v1/stocks/{market}/history/{symbol}` | GET | $0.0010 | Non-US equity OHLC bars | +| `/api/v1/stocks/{market}/price/{symbol}` | GET | $0.002 | Non-US equity spot price | +| `/api/v1/stocks/{market}/history/{symbol}` | GET | $0.002 | Non-US equity OHLC bars | -`GET` and `POST` both work on every path; `POST` exists so callers that cannot -attach headers to a `GET` still have a route. +`GET` and `POST` both work on the `/price` and `/history` paths; `POST` exists +so callers that cannot attach headers to a `GET` still have a route. `/list` is +`GET` only. ## Symbol formats @@ -45,14 +47,17 @@ Always resolve symbols from the matching `/list` endpoint rather than guessing. | FX | `BASE-QUOTE` | `EUR-USD`, `GBP-USD`, `JPY-USD` | | Commodity | `METAL-USD` / ticker | `XAU-USD` (gold), `XAG-USD` (silver) | | US equity | Plain ticker | `AAPL`, `TSLA`, `NVDA`, `SPY` | -| Non-US equity | Per-market convention | HKEX `-HK` suffix, TSE 4-digit, KRX 6-digit, LSE/XETRA/Euronext alpha | +| Non-US equity | Per-market convention | HKEX `-HK` suffix (`0005-HK`), TSE 4-digit (`7203`), KRX 6-digit (`005930`), LSE/XETRA/Euronext alpha (`HSBA`, `SAP`, `MC`) | -`/list` takes `q` (substring filter) and `limit` (max 2000, default 100). +`/list` takes `q` (substring filter) and `limit` (max 2000, default 100) and +returns `{ category, label, count, example, endpoints, symbols: [{ symbol, +description }] }`. ## Markets `{market}` for the `/stocks/` family: `us`, `hk`, `jp`, `kr`, `gb`, `de`, `fr`, -`nl`, `ie`, `lu`, `cn`, `ca`. `/api/v1/usstock/*` is a legacy alias for +`nl`, `ie`, `lu`, `cn`, `ca`. An unknown market returns `404` with the +`supported` list. `/api/v1/usstock/*` is a legacy alias for `/api/v1/stocks/us/*` and behaves identically. --- @@ -63,7 +68,7 @@ Always resolve symbols from the matching `/list` endpoint rather than guessing. # Free — no payment header needed curl https://blockrun.ai/api/v1/crypto/price/BTC-USD -# Paid — $0.0010 +# Paid — $0.002 curl https://blockrun.ai/api/v1/usstock/price/AAPL \ -H "X-Payment: " ``` @@ -71,11 +76,13 @@ curl https://blockrun.ai/api/v1/usstock/price/AAPL \ | Parameter | In | Required | Description | |-----------|----|----------|-------------| | `symbol` | path | Yes | Any symbol from the matching `/list` | -| `session` | query | No | Trading session hint — `regular` or `extended` | +| `session` | query | No | Equity-only session hint — `pre`, `post` or `on`. Omit for regular hours; ignored for non-equity symbols | -The response carries `symbol`, `price`, `confidence` (Pyth's interval around -the price), `publishTime` and `source`. Treat `confidence` as real: a wide -interval means the feed is uncertain, not that the price is precise. +The response carries `symbol`, `category`, `price`, `confidence` (Pyth's +interval around the price), `publishTime` (unix seconds), `timestamp` +(ISO-8601), `assetType`, `feedId` and `source: "pyth"`; free feeds add +`free: true`. Treat `confidence` as real: a wide interval means the feed is +uncertain, not that the price is precise. --- @@ -88,25 +95,45 @@ curl "https://blockrun.ai/api/v1/crypto/history/BTC-USD?resolution=D&from=173568 | Parameter | In | Required | Description | |-----------|----|----------|-------------| | `symbol` | path | Yes | Any symbol from the matching `/list` | -| `resolution` | query | No | Bar size — `1`, `5`, `15`, `60`, `240`, `D`, `W`, `M`. Default `D` | -| `from` | query | No | Start, unix seconds | -| `to` | query | No | End, unix seconds. Defaults to now | -| `session` | query | No | `regular` or `extended` | +| `resolution` | query | No | Bar size — `1`, `5`, `15`, `60`, `240`, `D`, `W`, `M`. Default `D`; anything else is `400` | +| `from` | query | Yes | Start, unix seconds. Missing or non-positive is `400` | +| `to` | query | No | End, unix seconds. Defaults to now; must be greater than `from` | +| `session` | query | No | Equity-only — `pre`, `post` or `on` | + +The response is `{ symbol, category, resolution, from, to, bars, source }` +where each bar is `{ t, o, h, l, c, v }` (`t` = bar start in unix seconds). A +valid symbol with no bars in the window returns `404` before any settlement, +so an empty range is never charged. + +On paid feeds the `402` is issued before parameter validation, so an unpaid +probe always gets a clean payment challenge; validation errors only surface +once a payment header is attached. --- ## Discovery -Free endpoints still answer a `402` when you ask for one without payment — that -response is x402 discovery metadata for indexers, not a charge. A plain `GET` -returns `200` and the data. +Free endpoints answer `HEAD` and `OPTIONS` with a `402` whose body is x402 +discovery metadata (`"error": "Payment Required (discovery)"`, `free: true`, +amount `0`) so indexers can register them — that is not a charge. A plain +`GET` returns `200` and the data. + +Paid feeds return a real `402` on an unpaid `GET`: the signed requirements are +in the `X-Payment-Required` / `PAYMENT-REQUIRED` headers (and +`WWW-Authenticate`), and the JSON body carries `price.amount` (`"0.0020"`), +an `example` call and the same requirements under `x402`. Settled responses +carry `PAYMENT-RESPONSE` and `X-Payment-Receipt` (transaction hash) headers. ## Errors | Status | Meaning | Charged? | |--------|---------|----------| -| `402` | Payment required (paid feeds), or discovery metadata (free feeds) | No | -| `404` | Symbol not found — check the matching `/list` | No | +| `400` | Bad `resolution`, missing/invalid `from`, or `to` ≤ `from` on `/history` | No | +| `402` | Payment required (paid feeds), or discovery metadata (`HEAD`/`OPTIONS` on free feeds) | No | +| `402` | `error: "Payment verification failed"` — `details` has the verifier's reason. These feeds do not yet attach a machine-readable `code` | No | +| `402` | `error: "Payment settlement failed"` — data was fetched but settlement did not land; `PAYMENT-RESPONSE` carries `errorReason` | No | +| `404` | Symbol not found for that category (body has a `hint`), unknown `{market}`, or no bars in the requested window | No | +| `502` | Price feed upstream unavailable (5s timeout, one retry) | No | ## What's next? diff --git a/docs/api-reference/modal-sandbox.md b/docs/api-reference/modal-sandbox.md index 00d84a6..102ad12 100644 --- a/docs/api-reference/modal-sandbox.md +++ b/docs/api-reference/modal-sandbox.md @@ -11,27 +11,52 @@ Secure code runtime for AI agents. Create a sandbox session, execute commands, i AI agents that need to run code face a dilemma: executing on the host is unsafe, and provisioning cloud VMs is slow and expensive. Modal Sandbox gives agents a safe execution layer they can call on demand, keep alive across multiple steps, and tear down when the job is finished. -**A typical sandbox workflow costs $0.018:** +**A typical short CPU sandbox workflow costs $0.015:** - 1 sandbox create ($0.011) — boot a Python container - 1 exec ($0.002) — run the code - 1 terminate ($0.002) — clean up +Every price on this page includes the flat $0.001 transaction fee added to each +paid call. + :::warning{title="Public beta limits"} - Base only -- Managed Python 3.11 sandbox -- Up to 1 vCPU, 1 GiB RAM, 5 minute sandbox lifetime -- Custom images, GPU sandboxes, and `setup_commands` are not enabled on the public API yet +- Managed Python 3.11 image only — custom images and `setup_commands` are not enabled on the public API yet +- CPU sandboxes: up to 1 vCPU and 1 GiB RAM. GPU sandboxes (`gpu` set): up to 8 vCPU and 32 GiB RAM +- Sandbox lifetime (`timeout`): 10 s to 24 h. Up to 300 s is billed at the flat create price; anything longer is billed per hour of the *requested* lifetime, charged upfront, with no refund on early terminate +- `exec` commands are capped at 60 s per call ::: ## Endpoints | Endpoint | Method | Price | Description | |----------|--------|-------|-------------| -| `/api/v1/modal/sandbox/create` | POST | $0.011 | Create a managed sandbox session | +| `/api/v1/modal/sandbox/create` | POST | from $0.011 (see [pricing](#create-pricing)) | Create a managed sandbox session | | `/api/v1/modal/sandbox/exec` | POST | $0.002 | Execute a command inside a running sandbox | | `/api/v1/modal/sandbox/status` | POST | $0.002 | Check if a sandbox is running or terminated | | `/api/v1/modal/sandbox/terminate` | POST | $0.002 | Terminate a sandbox and release resources | +### Create pricing + +`sandbox/create` is priced by the requested `timeout` and `gpu`. The 402 +response tells you which mode applies (`price.model` is `"flat"` or +`"hourly"`; hourly quotes also carry `hourly_rate_usd`, `duration_hours` and +`refund_policy`). + +| Sandbox | Flat rate (`timeout` ≤ 300 s) | Hourly rate (`timeout` > 300 s) | +|---------|-------------------------------|---------------------------------| +| CPU (no `gpu`) | $0.011 | $0.10 / hour | +| `T4` | $0.051 | $1.50 / hour | +| `L4` | $0.081 | $2.00 / hour | +| `A10G` | $0.101 | $2.50 / hour | +| `A100` | $0.201 | $4.00 / hour | +| `H100` | $0.401 | $8.00 / hour | + +Hourly billing is exact, not rounded up — a 3600 s CPU sandbox is quoted at +`$0.1010` (one hour at $0.10 plus the $0.001 fee), and 1801 s on a `T4` is +1801/3600 × $1.50. The full requested duration is settled in one x402 payment +at create time whether or not you terminate early. + --- ## POST /api/v1/modal/sandbox/create @@ -43,9 +68,14 @@ Create a managed Python 3.11 sandbox session with bounded resource and lifetime | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `image` | string | No | Managed image. Only `python:3.11` is currently available | -| `timeout` | integer | No | Sandbox lifetime in seconds, max 300 (default: 300) | -| `cpu` | number | No | CPU cores, max 1.0 (default: 1.0) | -| `memory` | integer | No | Memory in MB, max 1024 (default: 512) | +| `timeout` | integer | No | Sandbox lifetime in seconds, 10–86400 (default: 300). Above 300 switches to hourly billing | +| `cpu` | number | No | CPU cores, max 1.0 for CPU sandboxes, 8 with a `gpu` (default: 1.0) | +| `memory` | integer | No | Memory in MB, max 1024 for CPU sandboxes, 32768 with a `gpu` (default: 512) | +| `gpu` | string | No | GPU type: `T4`, `L4`, `A10G`, `A100` or `H100`. Omit for a CPU-only sandbox | + +The body is validated strictly: unknown fields, a non-managed `image`, an +unsupported `gpu`, out-of-range values, or a non-empty `setup_commands` array +return `400` with the offending `path` in `details`. ### Response @@ -75,8 +105,8 @@ Execute a command inside a running sandbox session. Returns stdout, stderr, and | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `sandbox_id` | string | Yes | Sandbox ID from `/sandbox/create` | -| `command` | array | Yes | Command as array, e.g. `["python", "-c", "print(1)"]` | -| `timeout` | integer | No | Execution timeout in seconds (default: 60) | +| `command` | array | Yes | Command as array, e.g. `["python", "-c", "print(1)"]` — at most 32 segments of up to 2000 characters each | +| `timeout` | integer | No | Execution timeout in seconds, 1–60 (default: 60) | ### Response @@ -145,6 +175,25 @@ curl -X POST https://blockrun.ai/api/v1/modal/sandbox/create \ # Returns: 402 with price and payment instructions ``` +The signed requirements are in the `X-Payment-Required` / `PAYMENT-REQUIRED` +headers (and `WWW-Authenticate: X402 requirements="…"`); the body restates the +price: + +```json +{ + "error": "Payment Required", + "message": "This endpoint requires x402 payment", + "endpoint": "/api/v1/modal/sandbox/create", + "method": "POST", + "description": "Create a managed Python 3.11 sandbox. …", + "price": { "amount": "0.0110", "currency": "USD", "model": "flat" }, + "paymentInfo": { "network": "eip155:8453", "networkName": "Base", "asset": "USDC", "x402Version": 2 } +} +``` + +A `GET` on any `/api/v1/modal/*` path also returns a `402` — it is discovery +metadata listing the `available` endpoints; the real calls are all `POST`. + ### Full session with payment ::::steps @@ -178,11 +227,33 @@ curl -X POST https://blockrun.ai/api/v1/modal/sandbox/terminate \ :::: +## Errors + +| Status | Meaning | Charged? | +|--------|---------|----------| +| `400` | Invalid JSON, or the body failed validation — `details` lists each issue with its `path` | No | +| `400`–`4xx` | The sandbox service rejected the request (for example an unknown `sandbox_id`). Upstream `4xx` codes are passed through with `error: "Bad Request"` and the upstream body in `details` | No | +| `402` | No payment header — body carries `price`, headers carry the signed requirements | No | +| `402` | `error: "Payment verification failed"` with a machine-readable `code`: `PAYMENT_INVALID`, `PAYMENT_UNFUNDED` (insufficient USDC or expired `validBefore`) or `PAYMENT_BLOCKHASH_STALE`; `message` explains the fix when one is known | No | +| `402` | `code: "PAYMENT_REPLAY"` — the payment authorization was already used. Sign a fresh one per request | No | +| `402` | `error: "Payment settlement failed"` — the sandbox call ran but settlement did not land; `details` has the reason | No | +| `404` | Unknown endpoint — body lists `available` paths | No | +| `502` | Sandbox service error or timeout (`"Payment was NOT charged."`) | No | +| `503` | Sandbox integration not configured or paused | No | + +Payment settles only after a successful upstream response. Successful responses +carry `X-Payment-Receipt` (transaction hash) and `X-Payment-Response`. + +Upstream timeouts: `create` waits up to 120 s for the sandbox to boot (the +requested lifetime does not extend this); `exec` waits the command `timeout` +plus 5 s, capped at 120 s; `status` and `terminate` wait 15 s. + ## Notes -- Sandboxes auto-terminate after the configured timeout (default: 5 minutes, max: 5 minutes) -- Sandbox creation may take 5-15 seconds depending on the image +- Sandboxes auto-terminate after the configured `timeout` (default: 5 minutes, max: 24 hours) +- Sandbox creation may take 5-15 seconds depending on the image; GPU cold starts take longer - The `sandbox_id` is required for all subsequent operations — store it after creation +- Responses are served with `Cache-Control: no-store` ## What's next? diff --git a/docs/api-reference/multi-chain-rpc.md b/docs/api-reference/multi-chain-rpc.md index c3c57c1..00c4248 100644 --- a/docs/api-reference/multi-chain-rpc.md +++ b/docs/api-reference/multi-chain-rpc.md @@ -19,12 +19,12 @@ Traditional RPC providers (Alchemy, Infura, QuickNode) make you sign up, manage POST /api/v1/rpc/{network} ``` -Swap `{network}` for the chain. Body is a standard JSON-RPC 2.0 request; the response is returned verbatim from the upstream node. EVM (`eth_*`) and non-EVM (`getSlot`, …) methods both work. A JSON-RPC **batch** (array body) is priced per element. +Swap `{network}` for the chain. Body is a standard JSON-RPC 2.0 request; the response is returned verbatim from the upstream node. EVM (`eth_*`) and non-EVM (`getSlot`, …) methods both work. A JSON-RPC **batch** (array body) is priced per element. Upstream calls time out after **20 seconds**. -**Flat price: $0.003 per call.** No payment header → HTTP 402 quoting the exact price; add an x402 payment header (a wallet signature) → the result, settled in USDC on Base. Hot reads are cached. +**Flat price: $0.003 per call** ($0.002 base + the $0.001 transaction fee). No payment header → HTTP 402 quoting the exact price; add an x402 payment header (a wallet signature) → the result, settled in USDC on Base. Hot, low-volatility reads (`eth_chainId`, `eth_getTransactionReceipt`, `getTransaction`, …) are served from a short TTL cache — the `X-Cache: HIT|MISS` response header tells you which; writes and time-sensitive reads (`eth_sendRawTransaction`, `eth_gasPrice`, `getLatestBlockhash`) are never cached. :::info{title="402 quotes the price"} -A request with no payment header returns `402 Payment Required` with the exact $0.003 quote and Base USDC instructions. Re-send with the `PAYMENT-SIGNATURE` header to get the result. +A request with no payment header returns `402 Payment Required` with the exact $0.003 quote (`price.amount: "0.0030"`, scaled ×N for a batch) and Base USDC instructions. The x402 requirements are in the `X-Payment-Required` / `PAYMENT-REQUIRED` headers (base64) and `WWW-Authenticate: X402 requirements="…"`. Re-send with the signed authorization in `X-Payment` (or `Payment-Signature`) to get the result. ::: ## Supported networks (40+) @@ -36,21 +36,23 @@ EVM and non-EVM, one path each. A selection: | Ethereum | `ethereum` | `eth` | | Base | `base` | | | Solana | `solana` | `sol` | -| Polygon | `polygon` | `matic` | -| BNB Smart Chain | `bsc` | `bnb` | -| Arbitrum | `arbitrum` | `arb` | +| Polygon | `polygon` | `matic`, `pol` | +| BNB Smart Chain | `bsc` | `bnb`, `binance` | +| Arbitrum One | `arbitrum` | `arb`, `arbitrum-one` | | Optimism | `optimism` | `op` | | Avalanche | `avalanche` | `avax` | | zkSync | `zksync` | | | Bitcoin | `bitcoin` | `btc` | +| Bitcoin Cash | `bitcoin-cash` | `bch` | | Litecoin | `litecoin` | `ltc` | | Dogecoin | `dogecoin` | `doge` | -| XRP Ledger | `xrp` | `ripple` | +| XRP Ledger | `ripple` | `xrp`, `xrpl` | | Near | `near` | | | Sui | `sui` | | | Polkadot | `polkadot` | `dot` | +| Zcash | `zcash` | `zec` | -…and more. An unsupported `{network}` returns `400` with the full supported list. The live list is in `/openapi.json` and on [/services/rpc](https://blockrun.ai/services/rpc). +…and more. Slugs are case-insensitive. A **malformed** `{network}` (anything other than `[a-z0-9-]`, up to 41 chars) returns `400` with the full curated list in `supportedNetworks`. A well-formed slug that is not in the curated list is still attempted as `-mainnet` against the upstream gateway (forward-compatible with newly added chains); if the chain does not exist there, the upstream `404` is relayed to you and nothing is charged. The live list is in `/openapi.json`, `/.well-known/x402`, and on [/services/rpc](https://blockrun.ai/services/rpc). ## Example @@ -60,7 +62,8 @@ curl -X POST https://blockrun.ai/api/v1/rpc/ethereum \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","id":1}' # → 402 with price $0.0030 + Base USDC payment instructions -# Re-send with the x402 PAYMENT-SIGNATURE header → {"jsonrpc":"2.0","id":1,"result":"0x..."} +# Re-send with the x402 X-Payment header → {"jsonrpc":"2.0","id":1,"result":"0x..."} +# response headers: X-Network: ethereum · X-Cache: HIT|MISS · X-Payment-Receipt: · X-Payment-Response: ``` ```bash @@ -70,6 +73,31 @@ curl -X POST https://blockrun.ai/api/v1/rpc/solana \ -d '{"jsonrpc":"2.0","method":"getSlot","id":1}' ``` +## Solana-only endpoint + +There is also a cheaper Solana-only passthrough to public mainnet-beta: + +``` +POST /api/v1/solana/rpc +``` + +Same JSON-RPC body and x402 flow, **$0.0015 per call** ($0.0005 base + the $0.001 transaction fee), batches priced per element, 15-second upstream timeout. `/api/v1/rpc/solana` ($0.003) routes through the multi-chain gateway instead; either works for standard Solana JSON-RPC. + +## Errors + +| Status | Body `error` | Meaning | Charged? | +|--------|--------------|---------|----------| +| `400` | `Bad Request` | Malformed `{network}` (with `supportedNetworks`), or — once a payment header is present — a body that is not a JSON-RPC request/array with a `method` | No | +| `402` | `Payment Required` | No payment header; body carries `price`, `endpoint`, `network`, `paymentInfo` | No | +| `402` | `Payment verification failed` | Signature/amount mismatch. `code` is `PAYMENT_INVALID`, `PAYMENT_UNFUNDED` (insufficient USDC or expired window) or `PAYMENT_BLOCKHASH_STALE`; `message` explains when known | No | +| `402` | `Payment authorization already used` | `code: "PAYMENT_REPLAY"` — sign a fresh authorization per request | No | +| `402` | `Payment settlement failed` | Upstream answered but settlement did not go through | No | +| `4xx` | `Bad Request` | The chain rejected your JSON-RPC (relayed status + `details`) | No | +| `502` | `Upstream provider error` | Upstream timeout (20 s) or fault — including an upstream auth/quota problem that is ours, not yours, and a stale slug on a known chain. Sent with `Retry-After: 30`. Your single-use authorization is **released**, so retrying with the same signed header verifies cleanly | No | +| `503` | `Service temporarily unavailable` | The RPC partner is paused or not configured | No | + +Every non-2xx body states "Payment was NOT charged" where it applies; settlement only happens after the upstream returns 2xx. + ## MCP Via the BlockRun MCP server, agents call the `blockrun_rpc` tool: `{ network, method, params }`. @@ -87,7 +115,7 @@ When to use pay-per-call RPC versus a traditional node-provider plan. ::: :::card{title="Error handling" href="errors.md" icon="Code"} -Status codes and how unsupported networks and payment failures surface. +Cross-service status codes and the `PAYMENT_*` error codes. ::: :::: diff --git a/docs/api-reference/music-generation.md b/docs/api-reference/music-generation.md index 6626e89..490cf34 100644 --- a/docs/api-reference/music-generation.md +++ b/docs/api-reference/music-generation.md @@ -7,14 +7,16 @@ description: Generate full-length music tracks with lyrics, instrumental, or cus Generate full-length music tracks with lyrics, instrumental, or custom style prompts. -:::warning{title="Generation is slow — raise your timeout"} -MiniMax generates a ~3 minute track per call regardless of duration hints. Generation takes 1-3 minutes. Set your client timeout to at least 200 seconds. +:::warning{title="Generation is slow — be ready to poll"} +`minimax/music-2.5+` produces a ~3 minute track per call regardless of duration hints, and takes 1–3 minutes to do it. Tracks that finish within **60s** come back inline as `200`; slower ones return **`202` + `poll_url`** and you poll until `completed`. Set your client timeout to at least 100 seconds per request and handle both shapes. ::: ## Endpoint ``` -POST https://blockrun.ai/api/v1/audio/generations +POST https://blockrun.ai/api/v1/audio/generations # submit (returns 200 inline or 202 + poll_url) +GET https://blockrun.ai/api/v1/audio/generations/{id} # poll an async job (settles on first completed poll) +GET https://blockrun.ai/api/v1/audio/models # list music models (free, rate-limited) ``` ## Request @@ -32,45 +34,112 @@ POST https://blockrun.ai/api/v1/audio/generations | Parameter | Type | Required | Description | |-----------|------|----------|-------------| -| `model` | string | No | Model ID (default: `minimax/music-2.5+`) | +| `model` | string | No | Model ID (default: `minimax/music-2.5+`). The legacy id `minimax/music-2.5` is remapped to `minimax/music-2.5+` (same price, strict superset). | | `prompt` | string | Yes | Music style, mood, or description | | `instrumental` | boolean | No | No vocals (default: false) | -| `lyrics` | string | No | Custom lyrics. Cannot combine with `instrumental: true` | -| `duration_seconds` | integer | No | Duration hint 5-240s (default: 30). Note: MiniMax ignores this — output is always ~3 min. | +| `lyrics` | string | No | Custom lyrics. Cannot combine with `instrumental: true` (returns `400`). When omitted and not instrumental, lyrics are auto-generated. | +| `duration_seconds` | integer | No | Duration hint 5–240s (default: 30), appended to the prompt as a target. The model ignores it — output is always ~3 min. | ### Available Models -| Model ID | Price | Notes | +| Model ID | Price (quoted) | Notes | |----------|-------|-------| -| `minimax/music-2.5+` | $0.1575 | MiniMax flagship — supports lyrics, instrumental, and style prompts | +| `minimax/music-2.5+` | $0.1585 | MiniMax flagship — supports lyrics, instrumental, and style prompts; ~3 min output; up to 240s billed as one track | + +`GET /api/v1/audio/models` returns the same list with `billing_mode: "per_track"`, `pricing.per_track` (the $0.15 base rate before margin and fee), `supports_lyrics` and `supports_instrumental`. + +## How it works — inline or async + +1. **`POST /api/v1/audio/generations`** without a payment header returns a `402` challenge (headers `X-Payment-Required` / `PAYMENT-REQUIRED` / `WWW-Authenticate`) whose body quotes the full price and repeats the ~3 min / 1–3 min facts under `generation_info`. +2. Re-send the POST with a signed `X-Payment` header. The gateway **verifies only** (no charge yet), claims the authorization's nonce so it cannot be reused, and starts generation. +3. **Fast path (finished within 60s):** the track is mirrored to BlockRun storage, the payment is settled, and you get `200` with the classic `{ created, model, data: [...] }` body plus `PAYMENT-RESPONSE` and `X-Payment-Receipt` headers. +4. **Slow path:** you get `202` with `{ id, poll_url, status: "queued" }`. Generation keeps running server-side. Poll `GET {poll_url}` every 3–10s with an `x-payment` header signed by the **same wallet** (a fresh signature is fine; an unsigned poll returns its own `402` challenge so x402 clients re-sign automatically). The first poll that observes `completed` mirrors the track, **settles the payment, and returns the URL**. A job that never completes is marked `failed` after 1 hour and is never charged. + +:::note{title="No charge on failure"} +Settlement is the only step that moves USDC and it runs only after a track exists. A generation that throws, times out, or is polled as `failed` returns with `PAYMENT-RESPONSE: {success:false}` (or `payment_status: "not_charged"` on a poll) and nothing is charged. The gateway takes a per-job settlement lock before charging, so re-polling a finished job — or a concurrent poll — returns `payment.status: "already_settled"` and never bills twice. +::: ## Response +### `200` — inline (fast path) + ```json { "created": 1775488202, "model": "minimax/music-2.5+", "data": [ { - "url": "https:///music/...", - "duration_seconds": 186 + "url": "https://blockrun.ai/api/media/audios/2026/08/12/.mp3", + "duration_seconds": 186, + "lyrics": "..." } ] } ``` +### `202` — async job submitted (slow path) + +```json +{ + "id": "", + "object": "audio.generation.job", + "status": "queued", + "model": "minimax/music-2.5+", + "price": { "amount": "0.158500", "currency": "USD" }, + "payment_status": "verified", + "created": 1775488202, + "poll_url": "/api/v1/audio/generations/", + "poll_instructions": "Slow model — generation exceeded the inline window. Send GET to poll_url with an x-payment header signed by the SAME wallet …" +} +``` + +### `GET {poll_url}` → `202` (still generating) + +```json +{ + "id": "", + "object": "audio.generation.job", + "status": "in_progress", + "model": "minimax/music-2.5+", + "payment_status": "verified", + "note": "Generation in progress. Poll again in 3-10s. No charge until status=completed." +} +``` + +### `GET {poll_url}` → `200` (completed — charged here) + +```json +{ + "id": "", + "object": "audio.generation.job", + "status": "completed", + "model": "minimax/music-2.5+", + "created": 1775488202, + "data": [ + { "url": "https://blockrun.ai/api/media/audios/2026/08/12/.mp3", "duration_seconds": 186, "lyrics": "..." } + ], + "price": { "amount": "0.158500", "currency": "USD" }, + "payment": { "status": "settled", "tx_hash": "0x…", "network": "base" } +} +``` + +A re-poll returns the same body with `payment.status: "already_settled"`. A failed job returns `200` with `status: "failed"`, `error`, and `payment_status: "not_charged"`. + ### Response Fields | Field | Type | Description | |-------|------|-------------| | `created` | integer | Unix timestamp | | `model` | string | Model used | -| `data[].url` | string | Time-limited CDN URL (expires ~24h). Download immediately. | -| `data[].duration_seconds` | integer | Actual duration of generated track | -| `data[].lyrics` | string | Generated lyrics (if not instrumental) | - -:::note{title="Download the track immediately"} -`data[].url` is a time-limited CDN URL that expires in roughly 24 hours. Fetch and store the file as soon as the call returns. +| `data[].url` | string | **Permanent BlockRun-hosted URL** — the track is mirrored to BlockRun storage before settlement. Only if the mirror fails does it fall back to the upstream's expiring URL. | +| `data[].duration_seconds` | integer | Actual duration of the generated track (omitted if the upstream did not report one) | +| `data[].lyrics` | string | Generated lyrics (omitted for instrumental tracks) | +| `price.amount` | string | Async responses only: the amount charged, fee included | +| `payment.status` | string | Async responses only: `settled` \| `already_settled` | +| `payment.tx_hash` | string | On-chain USDC settlement tx (also in the `X-Payment-Receipt` header) | + +:::note{title="Save the track when convenient, not urgently"} +`data[].url` is BlockRun-hosted and does not expire. Download it whenever you like. ::: ## Examples @@ -102,6 +171,7 @@ curl -L "$URL" -o track.mp3 ### Direct API (Python) ```python +import time import requests from blockrun_llm.x402 import parse_payment_required, create_payment_payload from blockrun_llm import load_wallet @@ -109,79 +179,86 @@ from eth_account import Account wallet_key = load_wallet() account = Account.from_key(wallet_key) +BASE = "https://blockrun.ai" +body = { + "model": "minimax/music-2.5+", + "prompt": "epic orchestral film score, dramatic strings", + "instrumental": True, +} -# Step 1: get payment requirements -resp = requests.post( - "https://blockrun.ai/api/v1/audio/generations", - json={ - "model": "minimax/music-2.5+", - "prompt": "epic orchestral film score, dramatic strings", - "instrumental": True, - }, -) -pr = parse_payment_required(resp.headers["PAYMENT-REQUIRED"]) -acc = pr["accepts"][0] - -# Step 2: sign payment -payload = create_payment_payload( - account=account, - recipient=acc["payTo"], - amount=str(acc["amount"]), - network=acc["network"], - resource_url=pr["resource"]["url"], - resource_description="BlockRun music generation", - max_timeout_seconds=acc["maxTimeoutSeconds"], - extra=acc.get("extra"), -) - -# Step 3: generate (takes 1-3 minutes) +def sign(resp): + """Sign the x402 challenge carried by a 402 response.""" + pr = parse_payment_required(resp.headers["PAYMENT-REQUIRED"]) + acc = pr["accepts"][0] + return create_payment_payload( + account=account, + recipient=acc["payTo"], + amount=str(acc["amount"]), + network=acc["network"], + resource_url=pr["resource"]["url"], + resource_description="BlockRun music generation", + max_timeout_seconds=acc["maxTimeoutSeconds"], + extra=acc.get("extra"), + ) + +# Step 1: get the challenge, sign it, submit (waits up to ~60s inline) +challenge = requests.post(f"{BASE}/api/v1/audio/generations", json=body) result = requests.post( - "https://blockrun.ai/api/v1/audio/generations", - json={ - "model": "minimax/music-2.5+", - "prompt": "epic orchestral film score, dramatic strings", - "instrumental": True, - }, - headers={"X-Payment": payload}, - timeout=200, + f"{BASE}/api/v1/audio/generations", + json=body, + headers={"X-Payment": sign(challenge)}, + timeout=100, ) +# Step 2: fast path returned the track inline; slow path returned 202 + poll_url +if result.status_code == 202: + poll_url = BASE + result.json()["poll_url"] + while True: + time.sleep(5) + # An unsigned poll returns its own 402 — sign it with the SAME wallet + poll = requests.get(poll_url, timeout=100) + if poll.status_code == 402: + poll = requests.get(poll_url, headers={"X-Payment": sign(poll)}, timeout=100) + job = poll.json() + if poll.status_code == 200 and job.get("status") == "completed": + result = poll + break + if job.get("status") == "failed": + raise RuntimeError(f"generation failed, not charged: {job.get('error')}") + track = result.json()["data"][0] print(f"Track URL: {track['url']}") -print(f"Duration: {track['duration_seconds']}s") -print(f"Tx: {result.headers['X-Payment-Receipt']}") +print(f"Duration: {track.get('duration_seconds')}s") +print(f"Tx: {result.headers.get('X-Payment-Receipt')}") ``` ### With Lyrics ```python -result = requests.post( - "https://blockrun.ai/api/v1/audio/generations", - json={ - "model": "minimax/music-2.5+", - "prompt": "upbeat pop with piano", - "lyrics": "Sunshine in the morning\nBrightens up my day\nEvery step I'm taking\nLeads me on my way", - "instrumental": False, - }, - headers={"X-Payment": payload}, - timeout=200, -) +body = { + "model": "minimax/music-2.5+", + "prompt": "upbeat pop with piano", + "lyrics": "Sunshine in the morning\nBrightens up my day\nEvery step I'm taking\nLeads me on my way", + "instrumental": False, +} +# then run the same sign → submit → (poll) loop as above ``` ## Pricing -| Model | Price/track | +| Model | Price/track (quoted) | |-------|-------------| -| `minimax/music-2.5+` | $0.1575 | +| `minimax/music-2.5+` | $0.1585 | -Price = `$0.15 × 1.05` (includes the 5% BlockRun margin), minimum $0.003. Paid in USDC on Base or Solana. +Price = `$0.15 × 1.05 + $0.001` — the $0.15 base rate, the 5% BlockRun media margin, and the flat $0.001 per-transaction fee charged on every paid call. That is the `price.amount` the `402` body quotes and the amount settled on-chain; there is no other minimum. Paid in USDC on Base (or Solana via `sol.blockrun.ai`). ## Limitations | Limitation | Detail | |------------|--------| -| Output duration | Always ~3 minutes (MiniMax limitation) | -| Generation time | 1-3 minutes per call | +| Output duration | Always ~3 minutes (model behaviour) | +| Generation time | 1–3 minutes per call; inline `200` only if it finishes within 60s, otherwise `202` + poll | +| Job lifetime | A job still `queued` / `in_progress` after 1 hour is marked `failed` (not charged) | | Format | MP3 only | | Tracks per request | 1 | | Duration control | Not supported (prompt hint ignored) | @@ -191,11 +268,16 @@ Price = `$0.15 × 1.05` (includes the 5% BlockRun margin), minimum $0.003. Paid | Code | Description | |------|-------------| -| 400 | Invalid request (bad parameters or conflicting lyrics + instrumental) | -| 402 | Payment required or verification failed | -| 429 | Rate limit exceeded | -| 504 | Generation timed out (retry) | -| 500 | Server error | +| 400 | Invalid JSON or parameters (`details` carries the schema issues), unknown model, or conflicting `lyrics` + `instrumental: true` | +| 402 | Payment required (challenge), verification failed (`code`: `PAYMENT_INVALID` default, `PAYMENT_UNFUNDED` for an unexecutable authorization — usually insufficient USDC on Base, `PAYMENT_REPLAY` for a reused authorization), or `Payment settlement failed` on a finished job (the track exists; retry the poll with a fresh signature — an expired authorization is the usual cause) | +| 403 | Poll signed by a wallet other than the one that submitted the job (`Payment payer mismatch`) | +| 404 | Poll for an unknown or expired job id | +| 429 | Upstream rate limit (`code: "RATE_LIMITED"`, `Retry-After` header) | +| 502 | Upstream provider rejected the request (content policy, quota) — not charged | +| 504 | Generation timed out — not charged; retry | +| 500 | Server error — not charged | + +Every failure after a verified payment carries a `PAYMENT-RESPONSE` header with `success: false` so you can confirm nothing was settled. ## Prompt Tips @@ -220,7 +302,7 @@ Price = `$0.15 × 1.05` (includes the 5% BlockRun margin), minimum $0.003. Paid "instrumental": false } ``` -MiniMax will auto-generate matching lyrics. +The model will auto-generate matching lyrics and return them in `data[].lyrics`. ## What's next? @@ -231,7 +313,7 @@ Generate images from a text prompt with the same pay-per-call model. ::: :::card{title="Video Generation" href="video-generation.md" icon="Image"} -Generate video clips with Grok Imagine and Seedance. +Generate video clips with Sora 2, Grok Imagine and Seedance. ::: :::card{title="Error handling" href="errors.md" icon="Code"} diff --git a/docs/api-reference/polymarket-funding.md b/docs/api-reference/polymarket-funding.md index f6afa20..1c5c719 100644 --- a/docs/api-reference/polymarket-funding.md +++ b/docs/api-reference/polymarket-funding.md @@ -46,7 +46,7 @@ Both authorizations you sign — the `$0.011` fee and the deposit — are **Base | Item | Amount | Paid to | |------|--------|---------| -| Service fee | `$0.011` (`POLYMARKET_FUND_FEE_USD`) | BlockRun treasury (x402) | +| Service fee | `$0.011` — `$0.01` base (`POLYMARKET_FUND_FEE_USD`) + the `$0.001` transaction fee | BlockRun treasury (x402) | | Gas | Sponsored by BlockRun | — | | Deposit principal | Your chosen amount | Polymarket bridge (non-custodial) | @@ -78,13 +78,23 @@ curl -X POST https://blockrun.ai/api/v1/polymarket/fund ```json { "error": "Payment Required", + "message": "This endpoint requires an x402 fee payment ($0.0110). Include the signed deposit authorization in the body.", "endpoint": "/api/v1/polymarket/fund", "method": "POST", - "price": { "amount": "0.0100", "currency": "USD" }, + "description": "Gaslessly fund your Polymarket deposit wallet: …", + "price": { "amount": "0.0110", "currency": "USD" }, + "body_fields": { + "depositWallet": { "type": "string", "required": true, "description": "Your Polymarket deposit wallet address" }, + "recipient": { "type": "string", "required": true, "description": "The Polymarket bridge address for your vault (from bridge /deposit)" }, + "amountMicro": { "type": "string", "required": true, "description": "Deposit amount in micro-USDC (6 decimals) — must equal your signed authorization value" }, + "depositAuthorization": { "type": "string", "required": true, "description": "base64 x402 payload: your EIP-3009 signed USDC transfer to `recipient`" } + }, "paymentInfo": { "network": "base", "asset": "USDC", "x402Version": 2 } } ``` +The x402 requirements (amount `11000` micro-USDC, `exact` scheme on Base) are also in the `X-Payment-Required` / `PAYMENT-REQUIRED` headers and `WWW-Authenticate: X402 requirements="…"`. + ### Success (200) ```json @@ -112,13 +122,17 @@ The confirmed deposit transaction hash is also returned in the `X-Deposit-Tx` re ### Errors -| Status | Meaning | Billed? | -|--------|---------|---------| -| `400` | Missing/invalid field, or `amountMicro` does not match the signed authorization value | No | -| `402` | Fee authorization missing or failed verification | No | -| `403` | `recipient` is not the Polymarket bridge address for `depositWallet` | No | -| `502` | Deposit failed to settle / reverted on-chain — **no USDC moved** | No | -| `503` | Polymarket bridge temporarily unreachable — try again shortly | No | +| Status | Body | Meaning | Billed? | +|--------|------|---------|---------| +| `400` | `Bad Request` | Missing/invalid field, amount over the cap, or `amountMicro` does not match the signed authorization value | No | +| `400` | `Deposit authorization invalid` | The deposit authorization itself failed verification (wrong `payTo`/amount/signature) | No | +| `402` | `Payment Required` | No fee authorization in `X-Payment` (discovery) | No | +| `402` | `Payment verification failed` | Fee authorization rejected. `code` is `PAYMENT_INVALID`, `PAYMENT_UNFUNDED` (insufficient USDC or expired window) or `PAYMENT_BLOCKHASH_STALE`, with a `message` when known | No | +| `402` | `Payment authorization already used` | `code: "PAYMENT_REPLAY"` — the **fee** authorization was already used; sign a fresh one | No | +| `403` | `Forbidden` | `recipient` is not the Polymarket bridge address for `depositWallet` | No | +| `409` | `Deposit authorization already used` | `code: "PAYMENT_REPLAY"` — the **deposit** authorization was already submitted; sign a fresh one | No | +| `502` | `Deposit settlement failed` | Deposit failed to settle / reverted on-chain — **no USDC moved** | No | +| `503` | `Bridge unavailable` | Polymarket bridge unreachable for recipient validation — try again shortly | No | Every non-2xx response that could otherwise be ambiguous states explicitly whether your USDC moved and whether the fee was charged. **The fee is charged only on a confirmed deposit.** @@ -148,12 +162,12 @@ Fund my Polymarket wallet with $25 from my Base USDC. - **Polymarket-only** — `recipient` must be your own vault's bridge address, verified live before any settlement. This is not a general transfer relay. - **Non-custodial** — the deposit settles directly to the bridge via the CDP facilitator; BlockRun never receives or holds the principal, and runs no server-side broadcaster or hot wallet. Gas is sponsored the same way as every other x402 call. - **Amount integrity** — `amountMicro` is validated against the signed authorization value, so the amount you see is the amount that moves. -- **Idempotent** — retrying a request whose deposit already settled returns the original result instead of double-depositing. +- **Replay-guarded, not idempotent** — both authorizations are single-use. Re-submitting a spent deposit authorization is refused with `409 PAYMENT_REPLAY` (never double-deposited), and a spent fee authorization with `402 PAYMENT_REPLAY`; the original result is not replayed, so keep the `X-Deposit-Tx` from the first response. ## Configuration | Env var | Default | Purpose | |---------|---------|---------| -| `POLYMARKET_FUND_FEE_USD` | `0.01` | Service fee per funding call | +| `POLYMARKET_FUND_FEE_USD` | `0.01` | Base service fee per funding call (the `$0.001` transaction fee is added on top) | | `POLYMARKET_FUND_MAX_USD` | `10000` | Maximum deposit per call | | `POLYMARKET_BRIDGE_HOST` | `https://bridge.polymarket.com` | Polymarket bridge host used for recipient validation | diff --git a/docs/api-reference/prediction-markets.md b/docs/api-reference/prediction-markets.md index b4ff161..e51d69e 100644 --- a/docs/api-reference/prediction-markets.md +++ b/docs/api-reference/prediction-markets.md @@ -27,6 +27,8 @@ Mirrors the Predexon **v2 Data API** (`docs.predexon.com/openapi-v2.json`) — r | Tier 1 | $0.0085 | Market data, events, trades, orderbooks, positions, leaderboards | | Tier 2 | $0.0085 | Wallet analytics (incl. identity + clustering), smart money, cross-venue search, Binance data | +Both tiers are a flat $0.0075 base plus the $0.001 per-transaction fee, so every call costs $0.0085 regardless of tier. The `402` body reports the charged price (`price.amount: "0.0085"`) together with `endpoint`, `method`, `description`, `category` and `tier`; the signable x402 v2 requirements are in the `X-Payment-Required` / `PAYMENT-REQUIRED` headers (base64 JSON, also mirrored in `WWW-Authenticate`). + --- ## Endpoints @@ -118,17 +120,24 @@ Cross-context wallet labels and on-chain relationship graph data. |----------|--------|-------------| | `/api/v1/pm/markets/search` | GET | Search markets across Polymarket, Kalshi, Limitless, Opinion, and Predict.Fun in a single call | -> **Retired 2026-07-20.** Predexon discontinued market matching, and the whole +> **Retired.** Predexon discontinued market matching on 2026-07-20, and the whole > canonical layer went with it: `matching-markets`, `matching-markets/pairs`, -> `markets`, `markets/listings` and `outcomes/{predexon_id}` all return `410`. -> `markets/search` above is the surviving cross-venue endpoint. +> `markets`, `markets/listings` and `outcomes/{predexon_id}` were removed from +> the gateway (upstream answers `410`; the gateway now returns `404 Not Found` +> for them). The `dflow/*` endpoints (`dflow/trades`, `dflow/wallet/positions/{wallet}`, +> `dflow/wallet/pnl/{wallet}`) were removed on 2026-08-04 for the same reason — +> they no longer exist upstream. `markets/search` above is the surviving +> cross-venue endpoint. ### Sports Markets — temporarily unavailable -`/api/v1/pm/sports/*` is returning an upstream `500` as of 2026-08-04 and is -withheld from discovery until Predexon restores it. The routes still resolve, so +`/api/v1/pm/sports/*` (`sports/categories`, `sports/markets`, +`sports/markets/{game_id}`, `sports/outcomes/{predexon_id}`, all Tier 1) is +returning an upstream `500` as of 2026-08-04 and is withheld from discovery until +Predexon restores it. The routes still resolve (they answer `402` unpaid), so existing integrations keep working the moment upstream recovers; new ones should -not build on it yet. +not build on it yet. While upstream is down a paid call returns `502` and is not +charged. ### Other Platforms (Tier 1: $0.0085) @@ -356,6 +365,38 @@ Works on all clients: `LLMClient` (Base), `AsyncLLMClient`, and `SolanaLLMClient --- +## Errors + +Payment is verified before the request is forwarded and settled only after Predexon answers, so a rejected or failed request is never charged. Upstream calls time out after 30 seconds. + +| Code | Description | +|------|-------------| +| 402 | Payment required (no payment header), payment verification failed (see codes below), authorization reused (`code: "PAYMENT_REPLAY"`), or settlement failed after the call (`error: "Payment settlement failed"`) | +| 400–499 | Predexon rejected the request — its status is passed through unchanged with `error: "Bad Request"`, a `message` of the form `Predexon : (payment NOT charged)`, `status`, `endpoint`, `method`, Predexon's body in `details`, and for known endpoints a `hint` with the correct call shape. `422` is what a missing required param (e.g. `q` on `markets/search`) produces | +| 404 | Unknown or retired endpoint (`error: "Not Found"`) | +| 502 | Predexon returned a 5xx (`error: "Upstream provider error"`). Payment was NOT charged, and the payment nonce is released so the SDKs can retry with the same signed header | +| 503 | Predexon integration not configured, or temporarily paused | +| 500 | Gateway error (`error: "Internal server error"`), including an upstream timeout | + +Successful responses carry `X-Payment-Response` (x402 v2 settlement receipt) and `X-Payment-Receipt` (the settlement transaction hash). + +:::info +Predexon returns `200` with **unfiltered** data when it receives an unknown query-parameter name — it never rejects one. Use the exact names above: free-text search is `search` on every list endpoint but `q` on `markets/search`; the slug filter is `slug` on `polymarket/events` but `event_slug` / `market_slug` on `polymarket/markets`. +::: + +### Payment verification codes + +A verification `402` spreads a machine-readable `code`: + +| `code` | Meaning | +|--------|---------| +| `PAYMENT_INVALID` | Signature, amount, network or recipient did not match the requirements (default; `message` omitted) | +| `PAYMENT_UNFUNDED` | The authorization could not execute on-chain — usually insufficient USDC on Base, or an expired `validAfter`/`validBefore` window | +| `PAYMENT_BLOCKHASH_STALE` | Solana gateway only: signed against an expired blockhash — re-sign and retry | +| `PAYMENT_REPLAY` | That authorization was already used. Sign a fresh one for each request | + +--- + ## Partner These endpoints are powered by [Predexon](https://predexon.com) — a unified prediction market data aggregator. Payments go directly to the Predexon treasury via x402. diff --git a/docs/api-reference/rate-limits.md b/docs/api-reference/rate-limits.md index 4c12c6d..cd2501a 100644 --- a/docs/api-reference/rate-limits.md +++ b/docs/api-reference/rate-limits.md @@ -8,7 +8,7 @@ description: Paid inference has no BlockRun-side quota — your only ceiling is BlockRun's rate-limiting model is intentionally minimal: **paid inference endpoints have no platform-side quota.** You pay per call in USDC via x402, and the cost of a paid request is the only cap on call volume. The effective rate limit your code will see comes from the upstream capacity behind the model you called — not from BlockRun's gateway. :::info{title="No platform quota on paid inference"} -There is **no per-wallet quota, no daily cap, no TPM/RPM limit** imposed by BlockRun on paid inference. The economic cost of each call (settled in USDC at request time) is the abuse boundary. Only discovery/metadata endpoints carry small per-IP limits. +There is **no per-wallet quota, no daily cap, no TPM/RPM limit** imposed by BlockRun on paid inference. The economic cost of each call (settled in USDC at request time) is the abuse boundary. Only the **free-tier chat models** and a few discovery/metadata endpoints carry per-IP limits. ::: ## Summary @@ -16,17 +16,51 @@ There is **no per-wallet quota, no daily cap, no TPM/RPM limit** imposed by Bloc | Surface | Platform quota | Notes | |---------|----------------|-------| | `POST /v1/chat/completions` (paid LLMs) | none | upstream limit applies | +| `POST /v1/chat/completions` (**free** models, `billing_mode: "free"`) | **30 req / minute and 300 req / hour per IP** | `429` + `FREE_TIER_RATE_LIMITED`, see below | | `POST /v1/messages` (Anthropic-compatible) | none | upstream limit applies | -| `POST /v1/images/generations` | none | upstream limit applies | +| `POST /v1/responses` | none | upstream limit applies | +| `POST /v1/images/generations`, `/v1/images/image2image` | none | upstream limit applies | | `POST /v1/videos/generations` | none | upstream limit applies | -| `POST /v1/audio/generations` | none | upstream limit applies | -| `POST /v1/voice/call` | none | upstream limit applies | -| `GET /v1/models`, `/v1/{image,video,audio}/models` | 100 req / hour per IP | metadata endpoints | +| `POST /v1/audio/generations`, `/v1/audio/speech`, `/v1/audio/sound-effects` | none | upstream limit applies | +| `POST /v1/voice/call`, `/v1/phone/*` | none | upstream limit applies | +| `GET /v1/models`, `/v1/{images,video,audio}/models` | 100 req / hour per IP | metadata endpoints | | `GET /api/pricing` | 100 req / hour per IP | metadata endpoint | -| `GET /api/health/*` | 60 req / minute per IP | infrastructure health | +| `GET /api/health` | 60 req / minute per IP | infrastructure health | +| `GET /v1/wallet/{address}/reconciliation` (and `/portraits`, `/realfaces`), `GET /v1/realface/status` | 120 req / hour per IP | per-wallet lookups | +| `POST /v1/realface/init` | 10 req / hour per IP | each init has real upstream cost | +| `POST /v1/onramp/token` | 30 req / hour per IP, 10 req / hour per wallet | free ($0) link minting | There is **no per-wallet quota, no daily cap, no TPM/RPM limit imposed by BlockRun on paid inference.** The economic cost of each call (settled in USDC at request time) is the abuse mitigation. +## Free-tier limits + +Free chat models (the ones `GET /v1/models` lists with `"billing_mode": "free"`) need no wallet and no payment, so they are the one place BlockRun throttles per source IP: **30 requests per minute** (burst) and **300 requests per hour** (sustained). Paid requests are never counted against these buckets, even from the same IP. + +Over the limit you get: + +```http +HTTP/1.1 429 Too Many Requests +Retry-After: 42 +X-RateLimit-Limit: 30 +X-RateLimit-Remaining: 0 +X-RateLimit-Reset: 1756486200000 +``` + +```json +{ + "error": { + "message": "Free tier rate limit reached (30 requests/minute per IP). Retry after 42s, or use a paid model — pricing starts at $0.002/request with no signup.", + "type": "rate_limit_error", + "code": "FREE_TIER_RATE_LIMITED", + "param": null + } +} +``` + +`X-RateLimit-Reset` is a unix timestamp in **milliseconds**. Integrators whose end users share one egress IP should route sustained traffic to a paid model — the cheapest paid call is $0.002 all-in. + +Separately, when the free model pool itself is out of capacity (every free rung is throttled or unhealthy upstream), the response is `429` with `code: "STREAM_FAILED"` (streaming) or `"FREE_MODEL_FAILED"` (non-streaming) and `Retry-After: 30`. That is capacity, not your quota — back off or use a paid model. + ## How upstream rate limits surface When an upstream rate-limits a request, BlockRun returns a `429 Rate Limited` response **with a source tag and a retry hint**, so your client can either retry or fail over to a same-tier model. @@ -35,11 +69,17 @@ When an upstream rate-limits a request, BlockRun returns a `429 Rate Limited` re ```json { - "error": "Rate limited", + "error": { + "message": "Rate limited — … Upstream provider rate limit hit — retry after 60s, or fail over to a same-tier model on a different provider.", + "type": "rate_limit_error", + "code": "RATE_LIMITED", + "param": null + }, + "message": "…", "code": "RATE_LIMITED", "source": "", "retry_after_seconds": 60, - "details": "" + "debug": "" } ``` @@ -52,7 +92,7 @@ X-RateLimit-Source: ``` - `Retry-After` — RFC-7231 compliant; seconds to wait before retrying. BlockRun extracts this from the upstream error when available, otherwise defaults to `60`. -- `X-RateLimit-Source` — an opaque source tag for the capacity pool that hit the limit. Treat it as a coarse failover hint, not a stable identifier. +- `X-RateLimit-Source` — the model-family prefix of the model you called (e.g. `openai`, `anthropic`), i.e. the capacity pool that hit the limit. Treat it as a coarse failover hint, not a stable identifier. - `source` field in JSON body — same value, mirrored for clients that prefer body parsing over headers. ### Recommended client behavior @@ -82,7 +122,7 @@ These are the orders of magnitude BlockRun's shared capacity currently runs at, |---|---|---|---| | Flagship chat (`openai/*`, `anthropic/*`, `google/*`) | thousands / model | hundreds of K–millions / model | shared capacity across all paid traffic | | Cost-efficient chat (`deepseek/*`, `xai/*`, `moonshot/*`, `minimax/*`, `zai/*`) | thousands+ / model | generous | usually no observed throttling at current traffic | -| Free tier (`nvidia/*` open-weight) | ~60 RPM per IP | varies | per-source IP throttling on the free tier; high-concurrency callers should use a paid model | +| Free tier (`billing_mode: "free"` open-weight models) | 30 RPM / 300 per hour per IP | varies | gateway-enforced per-IP limit (above); high-concurrency callers should use a paid model | | Video (`bytedance/*`, `*/sora-2`) | varies per model | varies | generation jobs are async; throttling typically surfaces as long queue waits, not `429` | | Music / speech / voice | per-job | n/a | per-job or per-account concurrency caps | @@ -98,7 +138,7 @@ If you need **guaranteed capacity** (dedicated key pool, reserved provider TPM, ## Discovery endpoint quotas (metadata only) -The IP-throttled endpoints listed at the top of this page protect against discovery-endpoint scraping. Real product traffic should never hit these limits. +The IP-throttled metadata endpoints listed at the top of this page protect against discovery-endpoint scraping. Real product traffic should never hit these limits. If you exceed them you'll get: @@ -106,11 +146,12 @@ If you exceed them you'll get: { "error": "Rate limit exceeded" } ``` -with HTTP 429 and `X-RateLimit-Reset: `. Wait until reset, then retry. +with HTTP 429 and `X-RateLimit-Reset: ` (no `Retry-After` on these). Wait until reset, then retry. Client IP is taken from the edge's connecting-IP header, so `X-Forwarded-For` cannot be used to rotate identities. ## Need higher limits? - **Paid inference:** there is no platform cap; the upstream provider's per-model RPM/TPM is your ceiling. Concurrency above that ceiling requires either fail-over to other providers or enterprise dedicated capacity. +- **Free tier:** the per-IP limits are fixed; move sustained or multi-user traffic to a paid model. - **Discovery endpoints:** cache locally — `/v1/models` updates only when we ship a model change. - **Enterprise dedicated capacity:** isolated key pools, reserved provider TPM, custom SLAs. Contact us. diff --git a/docs/api-reference/realface.md b/docs/api-reference/realface.md index 693aed5..e153340 100644 --- a/docs/api-reference/realface.md +++ b/docs/api-reference/realface.md @@ -84,7 +84,7 @@ POST https://blockrun.ai/api/v1/realface/init ### Rate limiting -Free but **rate-limited to 10 init calls per hour per IP** because each call generates an upstream session (real cost). For honest single-user usage this is plenty; if you hit `429`, wait or use the refresh path on an existing group. +Free but **rate-limited to 10 init calls per hour per IP** because each call generates an upstream session (real cost). For honest single-user usage this is plenty; if you hit `429` (`{ "error": "Rate limit exceeded", "retryAfterSeconds": n }` with a `Retry-After` header), wait or use the refresh path on an existing group. The refresh response carries `"refreshed": true` and no `next_steps`. An upstream failure on either path is `502`. ::: :::step{title="H5 (the rights-holder's phone)"} @@ -153,7 +153,7 @@ GET https://blockrun.ai/api/v1/realface/status?groupId=legacy_rf_… When `status` transitions to `"active"` (and `ready_to_finalize: true`), the rights-holder has completed the H5 and you can move to step 4. -Poll every 3-5 seconds. Free but rate-limited (same bucket as wallet reconciliation, 20/hour/IP). +Poll every 3-5 seconds. Free but rate-limited (same bucket as wallet reconciliation, 120/hour/IP). A `groupId` that is missing or not of the form `legacy_rf_` is `400`; a well-formed id that does not exist is `404` (`{ "error": "Asset group not found: …" }`) — only a genuine upstream failure is `502`. ::: :::step{title="Finalize (PAID, $0.011 USDC)"} @@ -182,15 +182,16 @@ POST https://blockrun.ai/api/v1/realface/enroll Same two-step pattern as other paid BlockRun endpoints: -1. First call without `X-Payment` → server returns `402 Payment Required` with x402 challenge headers -2. Sign the EIP-3009 transfer authorization for **$0.011 USDC on Base** -3. Retry the same request with `X-Payment: ` +1. First call without `X-Payment` → server returns `402 Payment Required` with x402 challenge headers (`X-Payment-Required` / `PAYMENT-REQUIRED` base64, plus `WWW-Authenticate: X402 requirements="…"`) and a body of `{ "error": "Payment Required", "message": "Enrolling a RealFace asset costs $0.0110 USDC. …", "price": { "amount": "0.0110", "currency": "USD" }, "paymentInfo": { "network": "base", "asset": "USDC", "x402Version": 2 } }` +2. Sign the EIP-3009 transfer authorization for **$0.011 USDC on Base** (`$0.01` enrolment + `$0.001` transaction fee — the requirements say `11000` micro-USDC) +3. Retry the same request with `X-Payment: ` (`Payment-Signature` is accepted too) Settlement happens **after** the upstream face-match succeeds. Failure modes that do NOT settle: -- `425 Too Early` — group is not yet `active` (rights-holder hasn't done H5) -- `422 Unprocessable` — face-match failed (uploaded photo doesn't match the live face) -- `502 Bad Gateway` — upload to the inference partner failed +- `402` — payment verification failed: `{ "error": "Payment verification failed", "code": "PAYMENT_INVALID" | "PAYMENT_UNFUNDED" | "PAYMENT_BLOCKHASH_STALE", "message"?, "debug", "payer" }`, or `code: "PAYMENT_REPLAY"` when the authorization was already used — sign a fresh one +- `425 Too Early` — group is not yet `active` (rights-holder hasn't done H5); body carries `group_status` and a `hint` +- `422 Unprocessable` — the face service rejected the image (bad/oversized photo, no clear single face; may carry a `code`) or the face-match failed (uploaded photo doesn't match the live face) +- `502 Bad Gateway` — the upstream group-status check or the upload itself failed If settlement itself fails after a successful enrollment, BlockRun absorbs the cost. @@ -209,7 +210,7 @@ If settlement itself fails after a successful enrollment, BlockRun absorbs the c "compatible_models": ["bytedance/seedance-2.0", "bytedance/seedance-2.0-fast"], "how_to_use": "Pass \"real_face_asset_id\": \"ta_f85b20b9394e47be9502d819bee7929c\" on a Seedance video generation request." }, - "price": { "amount": "0.010000", "currency": "USD" }, + "price": { "amount": "0.0110", "currency": "USD" }, "settlement": { "success": true, "tx_hash": "0x…", @@ -217,6 +218,8 @@ If settlement itself fails after a successful enrollment, BlockRun absorbs the c } } ``` + +The settlement receipt is also returned in the `X-Payment-Response` / `PAYMENT-RESPONSE` headers. ::: :::: @@ -243,7 +246,7 @@ The same `ta_xxx` can be reused across as many videos as you want — pay only t GET https://blockrun.ai/api/v1/wallet/
/realfaces ``` -Returns the wallet's enrolled RealFaces. Free, rate-limited (same bucket as `/portraits`). +Returns the wallet's enrolled RealFaces. Free, rate-limited (same 120/hour/IP bucket as `/portraits`); accepts EVM `0x…` or Solana base58 addresses (`400` otherwise). Responses are cacheable for 30 s. ```json { @@ -269,12 +272,13 @@ The video playground reads this same list and shows it in the `real_face_asset_i | Code | When | Did payment settle? | |------|------|---------------------| -| 400 | Invalid request body, malformed `group_id`, bad image URL | – (pre-payment) | -| 402 | Payment Required (first probe) or payment verification failed | – | +| 400 | Invalid request body, malformed `group_id`, bad image URL; on `/status`, missing/malformed `groupId` | – (pre-payment) | +| 402 | Payment Required (first probe), or payment verification failed with `code` `PAYMENT_INVALID` / `PAYMENT_UNFUNDED` / `PAYMENT_BLOCKHASH_STALE` / `PAYMENT_REPLAY` | – | +| 404 | `/status`: `groupId` does not exist upstream | – | | 425 | `group_id` is in `pending_validation` — the rights-holder hasn't completed the H5 yet | **No** | -| 422 | Face-match failed — uploaded photo doesn't match the live H5 face | **No** | -| 429 | Rate limit on `/init` or `/status` | – | -| 502 | Upload to inference partner failed | **No** | +| 422 | Image rejected by the face service, or face-match failed — uploaded photo doesn't match the live H5 face | **No** | +| 429 | Rate limit on `/init` (10/hour/IP), `/status` or the listing (120/hour/IP) | – | +| 502 | Upstream failure on `/init`, `/status`, the group-status check or the upload | **No** | | 200 | Asset enrolled and active | **Yes** | ## Storage and privacy diff --git a/docs/api-reference/responses.md b/docs/api-reference/responses.md index 51449c0..650731f 100644 --- a/docs/api-reference/responses.md +++ b/docs/api-reference/responses.md @@ -22,7 +22,7 @@ POST https://blockrun.ai/api/v1/responses | Header | Required | Description | |--------|----------|-------------| | `Content-Type` | Yes | Must be `application/json` | -| `PAYMENT-SIGNATURE` | Conditional | Base64-encoded x402 payment payload (required after 402, x402 v2) | +| `PAYMENT-SIGNATURE` | Conditional | Base64-encoded x402 payment payload (required after 402, x402 v2). `X-Payment` is accepted as an alias | ### Body Parameters @@ -31,7 +31,7 @@ POST https://blockrun.ai/api/v1/responses | `model` | string | Yes | OpenAI model ID (e.g., `gpt-5.5`, `openai/gpt-5.4-pro`, `gpt-5.3-codex`) | | `input` | string \| array | Yes | A prompt string, or an array of Responses input items (messages, `function_call_output`, reasoning replays, …) | | `instructions` | string | No | System/developer instructions | -| `max_output_tokens` | integer | No | Maximum tokens to generate (bounds the x402 quote) | +| `max_output_tokens` | integer | No | Maximum tokens to generate. Defaults to the model's `max_output` when omitted, and is clamped to it — it bounds the x402 quote, so set it deliberately | | `stream` | boolean | No | Stream native Responses SSE events (default: `false`) | | `tools` | array | No | Responses tool definitions — works together with `reasoning` on all GPT-5.x models | | `tool_choice` | string \| object | No | Tool selection strategy | @@ -42,7 +42,7 @@ POST https://blockrun.ai/api/v1/responses ### Stateless gateway — what is different from OpenAI -BlockRun's upstream calls run under one org key shared by all payers, so server-side state is disabled. `store: false` is **enforced** on every request, and these parameters are rejected with a `400`: +BlockRun's upstream calls run under shared credentials, so server-side state is disabled. `store: false` is **enforced** on every request, and these parameters are rejected with a `400` (`invalid_request_error`): | Parameter | Why | |-----------|-----| @@ -50,17 +50,32 @@ BlockRun's upstream calls run under one org key shared by all payers, so server- | `previous_response_id` | No stored responses to reference — resend full context in `input` | | `conversation` | No server-side conversation state | | `prompt` | No stored prompt templates | -| `background: true` | Background jobs complete after the HTTP exchange — nothing to bill against | This is the same model Codex CLI uses by default (`store: false` + full-context resend). For reasoning continuity across turns, request `include: ["reasoning.encrypted_content"]` and replay the returned reasoning items in the next call's `input`. +`background: true` is accepted only for enterprise-allowlisted wallets (`403` otherwise) and cannot be combined with `stream: true`: create the response non-streaming, it returns immediately with `status: "queued"`, then attach with `GET /v1/responses/{id}?stream=true` (resume after a disconnect with `&starting_after=`) or cancel with `POST /v1/responses/{id}/cancel`. The signed quote is settled at create time. Everyone else: keep generations inside one HTTP exchange. + +Missing `model`, or a body with neither `input` nor `instructions`, is a `400`. A non-OpenAI or free model id is a `400` pointing you to `/v1/chat/completions`. + ## Supported models All paid OpenAI models: GPT-5.x (including `-pro` tiers), o-series, and the codex family. `GET /v1/models` lists current IDs and prices; the `openai/` prefix is optional. Other providers (Claude, Gemini, DeepSeek, …) are served via [Chat Completions](chat-completions.md) and [Anthropic Messages](chat-completions.md#anthropic-compatible-endpoint). ## Payment flow -Identical to Chat Completions: send the request without payment, receive a `402` with a USDC quote (estimated input tokens + 10% of `max_output_tokens`, minimum $0.003), sign the x402 authorization, and resend with the `PAYMENT-SIGNATURE` header. The [SDKs](../getting-started/sdk-developers.md) handle this automatically. +Identical to Chat Completions: send the request without payment, receive a `402` with a USDC quote, sign the x402 authorization, and resend with the `PAYMENT-SIGNATURE` header. The [SDKs](../getting-started/sdk-developers.md) handle this automatically. + +The quote is estimated input tokens plus **10% of `max_output_tokens`** at the model's list rates (per-token chat carries no platform margin), floored at $0.001, plus the flat **$0.001 transaction fee** — so the smallest possible charge is $0.002. The `402` body uses the OpenAI error envelope: + +```json +{ + "error": {"message": "This endpoint requires x402 payment", "type": "payment_required", "param": null, "code": null}, + "price": {"amount": "0.024685", "currency": "USD"}, + "paymentInfo": {"network": "base", "asset": "USDC", "x402Version": 2} +} +``` + +The signed requirements are in the `X-Payment-Required` / `PAYMENT-REQUIRED` / `WWW-Authenticate` headers; the header amount is authoritative and includes the transaction fee (the body `price.amount` on this endpoint is the pre-fee quote). A payment that fails verification is a `402` in the same OpenAI envelope — `"Payment verification failed: …"` — and a reused authorization is `402` `"Payment authorization already used — sign a fresh authorization for each request."`; this endpoint keeps OpenAI's error schema rather than the `code` field the native BlockRun endpoints carry. Nothing is charged on either. ## Examples @@ -105,4 +120,4 @@ Set `"stream": true` and consume native Responses SSE events (`response.created` ## Response -The native OpenAI Responses object (or SSE event stream) is returned unmodified — `id` (`resp_…`), `output` array with `reasoning` / `message` / `function_call` items, and `usage` with `input_tokens`, `output_tokens`, `input_tokens_details.cached_tokens`, and `output_tokens_details.reasoning_tokens`. The `X-Payment-Response` header carries the settlement transaction hash on non-streaming calls. +The native OpenAI Responses object (or SSE event stream) is returned unmodified — `id` (`resp_…`), `output` array with `reasoning` / `message` / `function_call` items, and `usage` with `input_tokens`, `output_tokens`, `input_tokens_details.cached_tokens`, and `output_tokens_details.reasoning_tokens`. The `X-Payment-Response` header (base64 JSON `{"success","transaction","network","payer"}`) carries the settlement transaction hash on non-streaming calls. Upstream 4xx/5xx errors are passed through in OpenAI's envelope with the upstream status; nothing is settled on a failed call. diff --git a/docs/api-reference/search.md b/docs/api-reference/search.md index e7b052d..85870ab 100644 --- a/docs/api-reference/search.md +++ b/docs/api-reference/search.md @@ -20,7 +20,7 @@ POST https://blockrun.ai/api/v1/search | Header | Required | Description | |--------|----------|-------------| | `Content-Type` | Yes | Must be `application/json` | -| `PAYMENT-SIGNATURE` | Conditional | Base64-encoded x402 payment payload (required after 402, x402 v2) | +| `PAYMENT-SIGNATURE` | Conditional | Base64-encoded x402 payment payload (required after 402, x402 v2). `X-PAYMENT` is accepted as an alias. | ### Body Parameters @@ -69,9 +69,11 @@ POST https://blockrun.ai/api/v1/search | `citations[].url` | string | URL of the cited source | | `citations[].title` | string | Title or description of the source | | `citations[].source` | string | Source type (`web`, `news`) | -| `sources_used` | integer | Number of sources actually queried | +| `sources_used` | integer | Number of sources actually queried (falls back to `max_results` when upstream does not report it) | | `model` | string | Model used for search (currently `xai/grok-3-mini`) | +Successful responses also carry two headers: `PAYMENT-RESPONSE` (the x402 v2 settlement receipt — base64 JSON with `success`, `transaction`, `network`, `payer`) and `X-Payment-Receipt` (the on-chain settlement transaction hash). + ### Payment Required (402) When you first make a request without payment, you'll receive: @@ -94,23 +96,50 @@ When you first make a request without payment, you'll receive: } ``` -The `X-Payment-Required` header contains the full payment requirements. +The full x402 v2 payment requirements are in the `X-Payment-Required` and `PAYMENT-REQUIRED` headers (base64 JSON, identical content) and in `WWW-Authenticate: X402 requirements="..."`. Sign against the header, not the body: `price.amount` in the body is the per-source cost plus margin **before** the flat $0.001 transaction fee, while `accepts[0].amount` in the header is the exact USDC (6-decimal) amount you will be charged — for the default 10 sources that is `263500`, i.e. $0.2635. Payment authorizations are valid for `maxTimeoutSeconds: 300`. + +A `GET` to the same URL returns a 402 quoting the default price (10 sources) — useful for discovery. + +### Payment verification failed (402) + +If a payment header is present but does not verify, the body carries a machine-readable `code` so a client can branch on it instead of parsing `details`: + +```json +{ + "error": "Payment verification failed", + "code": "PAYMENT_UNFUNDED", + "message": "The payment authorization could not be executed on-chain. ...", + "details": "" +} +``` + +| `code` | Meaning | +|--------|---------| +| `PAYMENT_INVALID` | Signature, amount, network or recipient did not match the requirements (default when nothing more specific applies; `message` is omitted) | +| `PAYMENT_UNFUNDED` | The authorization could not execute on-chain — usually insufficient USDC on Base, or an expired `validAfter`/`validBefore` window | +| `PAYMENT_BLOCKHASH_STALE` | Solana-gateway only: signed against an expired blockhash — re-sign against a current one | +| `PAYMENT_REPLAY` | The same authorization was already used (`error: "Payment authorization already used"`). Sign a fresh authorization for every request | + +A `402` with `error: "Payment settlement failed"` means the search ran but settlement did not complete; nothing was charged. ## Pricing -Search pricing is per-source with a 5% BlockRun margin: +Search pricing is per-source with a 5% BlockRun margin, plus the flat $0.001 per-transaction fee charged on every paid call: - **Base cost:** $0.025 per source - **Margin:** 5% -- **Formula:** `max_results × $0.025 × 1.05` +- **Transaction fee:** $0.001 per request (flat) +- **Formula:** `max_results × $0.025 × 1.05 + $0.001` + +| max_results | Base Cost | With Margin | Charged (incl. fee) | +|-------------|-----------|-------------|---------------------| +| 1 | $0.025 | $0.02625 | $0.02725 | +| 5 | $0.125 | $0.13125 | $0.13225 | +| 10 (default) | $0.250 | $0.26250 | $0.26350 | +| 25 | $0.625 | $0.65625 | $0.65725 | +| 50 | $1.250 | $1.31250 | $1.31350 | -| max_results | Base Cost | With Margin | -|-------------|-----------|-------------| -| 1 | $0.025 | $0.02825 | -| 5 | $0.125 | $0.13325 | -| 10 (default) | $0.250 | $0.26450 | -| 25 | $0.625 | $0.65825 | -| 50 | $1.250 | $1.31450 | +The price depends only on `max_results`, not on how many sources you list in `sources` or how many the search actually used. ## Examples @@ -212,10 +241,11 @@ This gives you the raw chat completion response with search-augmented context. T | Code | Description | |------|-------------| -| 400 | Invalid request (bad query or parameters) | -| 402 | Payment required or payment verification failed | -| 429 | Rate limit exceeded | -| 500 | Server error | +| 400 | Invalid request — body is not JSON (`code: "INVALID_JSON"`) or fails validation (`error: "Invalid request body"` with Zod `details`) | +| 402 | Payment required, payment verification failed (see `code` above), replayed authorization (`PAYMENT_REPLAY`), or settlement failed | +| 500 | Server error (`error: "Internal server error"`, with `details`) — includes upstream search failures | + +Upstream calls are made only after payment verifies; settlement happens after the search returns, so a failed search is never charged. ### Error Response diff --git a/docs/api-reference/surf.md b/docs/api-reference/surf.md index 25405a3..bd1785d 100644 --- a/docs/api-reference/surf.md +++ b/docs/api-reference/surf.md @@ -7,7 +7,7 @@ description: 83 crypto-data endpoints (CEX, on-chain SQL, prediction markets, wa Real-time crypto data for AI agents. 83 endpoints across exchanges, on-chain analytics, prediction markets, wallet labels, social mindshare, news, and search — all priced per call in USDC. No Surf account, no API key, just a wallet. -Powered by [Surf](https://asksurf.ai) (asksurf.ai). BlockRun proxies the same endpoints with x402 settlement directly to Surf's treasury — your USDC routes 1:1 to them, BlockRun takes no spread. +Powered by [Surf](https://asksurf.ai) (asksurf.ai). BlockRun proxies the same endpoints with x402 settlement, and every call is priced the same flat rate — the price you see in the 402 is the price you pay. ## The Problem Surf Solves @@ -25,6 +25,8 @@ Surf unifies all of it behind one schema, billed per-call in stablecoins. An age | Tier 2 | **$0.0085 / call** | Time-series, technical indicators, orderbook depth | | Tier 3 | **$0.0085 / call** | On-chain SQL — raw queries against 80+ ClickHouse tables | +Every tier is the same network-uniform data price ($0.0075 base + the flat $0.001 transaction fee = $0.0085). The tiers describe endpoint weight, not price. The unpaid 402 body reports the charged price (`price.amount`, e.g. `"0.0085"`) along with `method`, `description` and `required_params`, so an agent knows what to send before paying. + ### Categories | Category | Endpoints | What it covers | @@ -40,7 +42,7 @@ Surf unifies all of it behind one schema, billed per-call in stablecoins. An age | **project** | 3 | Project profiles, tokenomics, ecosystem maps | | **fund** | 3 | VC fund detail, portfolio, ranking | | **news** | 2 | AI-curated crypto news feed, article detail | -| **web** | 1 | General crypto web search | +| **web** | 1 | Fetch any web page as clean markdown (`web/fetch?url=`) | Full endpoint list (with `requiredParams`) is in `/openapi.json` and `/llms.txt` — search for `/api/v1/surf/`. @@ -54,7 +56,7 @@ All Surf endpoints route through: https://blockrun.ai/api/v1/surf/ ``` -Method follows the upstream — most are `GET` with **query string** params (never a request body); the two SQL/structured-query endpoints (`onchain/sql`, `onchain/query`) are `POST` with JSON bodies, while `onchain/schema` is a `GET`. The x402 discovery record (`extensions.bazaar` on the 402 response) declares GET params as `queryParams` — agents building calls from the CDP Bazaar catalog get the correct placement. Per-endpoint parameter docs live at [agents.asksurf.ai/docs](https://agents.asksurf.ai/docs). +Method follows the upstream — most are `GET` with **query string** params (never a request body); the two SQL/structured-query endpoints (`onchain/sql`, `onchain/query`) are `POST` with JSON bodies, while `onchain/schema` is a `GET`. Calling an endpoint with the wrong method returns `405 Method Not Allowed` (the body names the expected method); an unknown path returns `404` with the full `available` list. The x402 discovery record (`extensions.bazaar` on the 402 response) declares GET params as `queryParams` — agents building calls from the CDP Bazaar catalog get the correct placement. Per-endpoint parameter docs live at [agents.asksurf.ai/docs](https://agents.asksurf.ai/docs). --- @@ -171,7 +173,7 @@ The `blockrun_surf` MCP tool ships with [BlockRun MCP](../mcp/blockrun-mcp.md). ## Use Cases -### 1. Macro snapshot before every trade (~$0.013) +### 1. Macro snapshot before every trade (~$0.026) ```typescript const [price, fng, funding] = await Promise.all([ @@ -181,9 +183,9 @@ const [price, fng, funding] = await Promise.all([ ]); ``` -Total cost: $0.013 per pre-trade snapshot. Cheap enough to run every minute on a long-running bot. +Total cost: $0.0255 per pre-trade snapshot (three calls at $0.0085). Cheap enough to run every minute on a long-running bot. -### 2. Smart-money wallet monitor ($0.006/wallet) +### 2. Smart-money wallet monitor ($0.017/wallet) ```python profile = client.surf('GET', 'wallet/detail', {'address': addr}) @@ -204,7 +206,7 @@ ORDER BY unique_callers DESC LIMIT 20 ``` -A weekly research bot that runs this twice and feeds the result into Claude Opus to write a 1-page narrative costs ~$0.044 + LLM tokens. +A weekly research bot that runs this twice and feeds the result into Claude Opus to write a 1-page narrative costs $0.017 + LLM tokens. ### 4. Multi-source prediction-market aggregator (Tier 1, ~$0.0085/event) @@ -219,7 +221,7 @@ Compare odds across two venues, surface arb opportunities. ## Required Parameters -Each endpoint declares its required query/body params in the upstream OpenAPI spec. If a required param is missing, BlockRun returns `400 Bad Request` **before** settling payment — you never get charged for a malformed call. +Each endpoint declares its required query/body params (`required_params` in the unpaid 402 body, `requiredParams` in `/openapi.json`). If a required param is missing, BlockRun returns `400 Bad Request` **after** the payment header is present but **before** settling payment — you never get charged for a malformed call. The 400 body lists `missing_params` and `all_required`. (Validation runs after the payment check on purpose: an unpaid probe without params still gets the 402 discovery record instead of a 400.) Common required params: - `pair` → `BTC-USDT`, `ETH-USDT-PERP`, etc. (exchange endpoints) @@ -238,7 +240,7 @@ Common required params: | Tier 2 | $0.0085 | 36 (time-series, indicators, depth, history) | | Tier 3 | $0.0085 | 3 (`onchain/sql`, `onchain/query`, `onchain/schema`) | -Payment is in USDC on Base or Solana via x402. **Settles 1:1 directly to Surf's treasury wallet** (`0x058a5961FbE8cD8E4B47C69d3d82E159cb5d8F17` on Base) — BlockRun takes no margin. We pass through pricing as-is in exchange for being the discovery/auth/settlement layer. +Every price above is the full customer price: $0.0075 base + the flat $0.001 transaction fee. Payment is in USDC on Base or Solana via x402. On Base, settlement lands directly in Surf's treasury wallet (`0x058a5961FbE8cD8E4B47C69d3d82E159cb5d8F17`); the per-request settlement transaction hash comes back in the `X-Payment-Receipt` header, with the full receipt in `X-Payment-Response`. --- @@ -259,13 +261,16 @@ Payment is in USDC on Base or Solana via x402. **Settles 1:1 directly to Surf's | Code | Description | |------|-------------| -| 200 | Success — query result in body | -| 400 | Missing required param (pre-payment validation, **not charged**) | -| 402 | Payment required — sign and retry | -| 404 | Unknown endpoint path (typo in `surf/`) | -| 429 | Surf upstream rate limited — `Retry-After` header set, **not charged**. See [Rate Limits](rate-limits.md) | -| 502 | Surf upstream returned an error — **not charged**, error body forwarded | -| 503 | Surf integration not configured on this BlockRun deployment | +| 200 | Success — query result in body; `X-Payment-Receipt` carries the settlement tx hash | +| 400 | Missing required param (pre-settlement validation, **not charged**) — body has `missing_params` / `all_required`. Any other 4xx from Surf is forwarded with its original status, wrapped as `{ "error": "Bad Request", "status": , "details": ... }`, **not charged** | +| 402 | Payment required — sign and retry. If verification fails the body is `{ "error": "Payment verification failed", "code": "PAYMENT_INVALID" \| "PAYMENT_UNFUNDED" \| "PAYMENT_BLOCKHASH_STALE", "message"?, "details" }`; re-using an authorization returns `code: "PAYMENT_REPLAY"` — sign a fresh one per request. `Payment settlement failed` (no tx) is also a 402 | +| 404 | Unknown endpoint path (typo in `surf/`) — body lists every `available` path | +| 405 | Wrong method for a known path (e.g. `POST` to a `GET` endpoint) | +| 429 | Surf upstream rate limited — forwarded as-is, **not charged**. See [Rate Limits](rate-limits.md) | +| 502 | Surf upstream returned a 5xx — **not charged**, upstream body in `details` | +| 503 | Surf integration not configured, or the partner is temporarily paused | + +Upstream calls time out after 30 seconds. Because settlement happens **after** the upstream call succeeds, any failed call — 4xx, 5xx, or timeout — is never charged. --- diff --git a/docs/api-reference/text-to-speech.md b/docs/api-reference/text-to-speech.md index add5cff..8424704 100644 --- a/docs/api-reference/text-to-speech.md +++ b/docs/api-reference/text-to-speech.md @@ -5,9 +5,9 @@ description: ElevenLabs voice synthesis, ByteDance Seed Audio prompt-directed au # Text-to-Speech & Sound Effects API -Ultra-realistic voice synthesis (ElevenLabs), prompt-directed audio creation (ByteDance Seed Audio), and cinematic sound effects, behind x402. Pay per call in USDC — no subscriptions, no API keys. +Ultra-realistic voice synthesis (ElevenLabs models), prompt-directed audio creation (ByteDance Seed Audio), and cinematic sound effects, behind x402. Pay per call in USDC — no subscriptions, no API keys. -ElevenLabs models are billed **per input character**; ByteDance Seed Audio is billed **per second of output audio** (quoted from an estimate of your input). Either way the price is quoted up front in the 402 challenge and settlement only happens after the audio is generated. A failed generation is never charged. +ElevenLabs models are billed **per input character**; ByteDance Seed Audio is billed **per second of output audio** (quoted from an estimate of your input). Either way the price is quoted up front in the 402 challenge and settlement only happens after the audio is generated **and stored**. A failed generation is never charged. All three endpoints are **synchronous** — one paid POST returns the finished audio URL. ## Endpoints @@ -34,9 +34,9 @@ GET https://blockrun.ai/api/v1/audio/voices # list voices (free) | Parameter | Type | Required | Description | |-----------|------|----------|-------------| -| `model` | string | No | Model ID (default: `elevenlabs/flash-v2.5`) | -| `input` | string | Yes | Text to synthesize | -| `voice` | string | No | Voice alias (e.g. `sarah`, `george`) or raw ElevenLabs `voice_id` (default: `sarah`) | +| `model` | string | No | Model ID (default: `elevenlabs/flash-v2.5`). Unknown ids return `400` listing the available models. | +| `input` | string | Yes | Text to synthesize (1 char minimum; per-model maximum below — over it returns `400` before payment) | +| `voice` | string | No | Voice alias (e.g. `sarah`, `george`, case-insensitive) or a raw `voice_id` from `/v1/audio/voices` (default: `sarah`). Ignored by `bytedance/seed-audio-1.0`. | | `response_format` | string | No | `mp3` (default), `opus`, `pcm`, `wav` | | `speed` | number | No | Playback speed 0.7–1.2 | @@ -51,7 +51,7 @@ GET https://blockrun.ai/api/v1/audio/voices # list voices (free) | `bytedance/seed-audio-1.0` | $0.003 / second of audio | 3,000 | Prompt-directed audio creation (voice, emotion, staging) | :::info -ElevenLabs models: price = `(characters / 1000) × model rate`, plus a 5% platform fee, minimum **$0.003** per request. The price is quoted up front in the 402 challenge and settlement only fires after the audio is generated — a failed generation is never charged. +ElevenLabs models: price = `(characters / 1000) × model rate × 1.05` (the 5% BlockRun media margin), floored at $0.001, **plus the flat $0.001 transaction fee** charged on every paid call — so the minimum quote is **$0.002** per request and 1,000 characters on `flash-v2.5` is **$0.0535**. The price is quoted up front in the 402 challenge and settlement only fires after the audio is generated and stored — a failed generation is never charged. ::: #### Seed Audio 1.0 (ByteDance) @@ -70,16 +70,37 @@ inside `input`, and the model performs it. Example: Differences from the ElevenLabs models: -- **Billing is per second of output audio** ($0.003/second). Since the exact - duration isn't known before synthesis, the 402 quote prices an **estimated - duration** derived from your input length (CJK text estimates slower speech - than Latin text). The 402 body reports the estimate in +- **Billing is per second of output audio** ($0.003/second, a final rate — the + 5% margin is *not* added on top; only the $0.001 transaction fee is). Since + the exact duration isn't known before synthesis, the 402 quote prices an + **estimated duration** derived from your input length (CJK text estimates + slower speech than Latin text). The 402 body reports the estimate in `generation_info.estimated_seconds`, and the response reports the actual `duration_seconds`. -- Output is capped at **120 seconds** (so the maximum possible quote is $0.36). +- Output is capped at **120 seconds** (so the maximum possible quote is $0.361). - The `voice` parameter is **ignored** — direct the voice in the prompt itself. - Supports up to 3,000 input characters. +### The 402 challenge + +An unpaid POST returns `402` with the x402 requirement in the `X-Payment-Required` / `PAYMENT-REQUIRED` / `WWW-Authenticate` headers and an informational body: + +```json +{ + "error": "Payment Required", + "message": "This endpoint requires x402 payment", + "price": { "amount": "0.053500", "currency": "USD" }, + "generation_info": { + "characters": 1000, + "model": "elevenlabs/flash-v2.5", + "note": "Price scales with input character count. Synthesis is synchronous (typically <1s for Flash)." + }, + "paymentInfo": { "network": "base", "asset": "USDC", "x402Version": 2 } +} +``` + +For `bytedance/seed-audio-1.0` the `generation_info` block instead carries `estimated_seconds` and a note that billing is by output duration. `price.amount` already includes the $0.001 fee. Sign the requirement and re-send with the signature in `X-Payment` (also accepted: `Payment-Signature`). + ### Response ```json @@ -90,12 +111,15 @@ Differences from the ElevenLabs models: { "url": "https://blockrun.ai/api/media/audios/2026/06/05/....mp3", "format": "mp3", - "characters": 51 + "characters": 51, + "credits": 51 } ] } ``` +`credits` (ElevenLabs models) is the upstream-reported character cost when available; `bytedance/seed-audio-1.0` returns `duration_seconds` (the actual billed length) instead. The response carries `PAYMENT-RESPONSE` (base64 JSON `{success, transaction, network, payer}`) and `X-Payment-Receipt` (the settlement tx hash) headers. + ## Sound Effects ```json @@ -108,12 +132,23 @@ POST /api/v1/audio/sound-effects | Parameter | Type | Required | Description | |-----------|------|----------|-------------| -| `text` | string | Yes | Description of the sound effect | +| `model` | string | No | `elevenlabs/sound-effects` (the only model; default) | +| `text` | string | Yes | Description of the sound effect (1–1,000 chars) | | `duration_seconds` | number | No | 0.5–22s (auto if omitted) | | `prompt_influence` | number | No | 0–1, how strictly to follow the prompt | | `response_format` | string | No | `mp3` (default), `opus`, `pcm`, `wav` | -Flat price: **$0.0535 / generation** ($0.05 base + 5% margin + the $0.001 transaction fee). +Flat price: **$0.0535 / generation** ($0.05 base + 5% margin + the $0.001 transaction fee), regardless of duration. The 402 body is `{ error, message, price: { amount, currency }, paymentInfo }`. + +Response: + +```json +{ + "created": 1733443200, + "model": "elevenlabs/sound-effects", + "data": [{ "url": "https://blockrun.ai/api/media/audios/2026/06/05/....mp3", "format": "mp3" }] +} +``` ## Voices (free) @@ -121,13 +156,38 @@ Flat price: **$0.0535 / generation** ($0.05 base + 5% margin + the $0.001 transa GET /api/v1/audio/voices ``` -Returns the available voices with their `voice_id`, `name`, and `alias` (if mapped). Pass the `alias` or `voice_id` as the `voice` field to `/v1/audio/speech`. +Returns the available voices with their `voice_id`, `name`, `category`, `labels` (accent, gender, age, use case, language), `preview_url`, and `alias` (if mapped). Pass the `alias` or `voice_id` as the `voice` field to `/v1/audio/speech`. No payment; rate-limited to 60 requests/minute per IP (`429` with `Retry-After`) and cached for 5 minutes. + +```json +{ + "object": "list", + "endpoint": "/v1/audio/speech", + "note": "Pass a voice's `alias` (if present) or `voice_id` as the `voice` field to /v1/audio/speech.", + "data": [ + { "voice_id": "EXAVITQu4vr4xnSDxMaL", "name": "Sarah - Mature, Reassuring, Confident", "category": "premade", "labels": { "accent": "american", "gender": "female", "age": "young", "language": "en" }, "preview_url": "…", "alias": "sarah" } + ] +} +``` + +## Error Codes + +| Code | Description | +|------|-------------| +| 400 | Invalid JSON or parameters (`details` carries the schema issues), unknown / unavailable model, or `input` over the model's character cap (`Input too long: N characters (max M for )`). All before payment. | +| 402 | Payment required (challenge), or verification failed with a machine-readable `code`: `PAYMENT_INVALID` (default), `PAYMENT_UNFUNDED` (authorization not executable on-chain — usually insufficient USDC on Base, or an expired window), `PAYMENT_REPLAY` (authorization already used — sign a fresh one). Also `Payment settlement failed` when the audio was produced but the authorization could not be settled (retry with a fresh signature). | +| 429 | Upstream rate limit — `Retry-After: 30`. Not charged. | +| 502 | Upstream provider error (`Upstream provider error`). Not charged. | +| 504 | Synthesis timed out. Not charged; retry. | +| 500 | Server / storage error (including a failed upload of the finished audio — no URL, so no charge). | + +Every failure after a verified payment carries a `PAYMENT-RESPONSE` header with `success: false` so you can confirm nothing was settled. ## Notes -- Audio is stored by BlockRun and returned as a permanent hosted URL. +- Audio is stored by BlockRun and returned as a permanent hosted URL. If storage fails there is nothing to return, so the request fails **without** settling — retry for free. - The price is recomputed from the request body on the paid call, so a payment signed for short text cannot be reused to synthesize longer text. -- Settlement happens only after successful synthesis; upstream failures are not charged. +- Each signed authorization is single-use (nonce claim before synthesis); reusing one returns `402` with `code: "PAYMENT_REPLAY"`. +- Settlement happens only after successful synthesis and storage; upstream failures are not charged. ## What's next? diff --git a/docs/api-reference/video-generation.md b/docs/api-reference/video-generation.md index 44f7402..9a11d67 100644 --- a/docs/api-reference/video-generation.md +++ b/docs/api-reference/video-generation.md @@ -7,12 +7,12 @@ description: Generate short AI videos with Sora 2, Grok Imagine, or Seedance — Generate short AI videos with **OpenAI Sora 2**, **xAI Grok Imagine**, or **ByteDance Seedance**. Text (or image) prompt in, MP4 URL out — paid per call with USDC on Base via x402. -- **Endpoint:** `POST https://blockrun.ai/v1/videos/generations` -- **Poll:** `GET https://blockrun.ai/v1/videos/generations/{id}?model=…&duration=…` -- **Payment:** x402, **USDC on Base** mainnet (`network: "base"`, `x402Version: 2`). Minimum charge $0.003. +- **Endpoint:** `POST https://blockrun.ai/api/v1/videos/generations` +- **Poll:** `GET https://blockrun.ai/api/v1/videos/generations/{id}?model=…&duration=…&i2v=…&sig=…` — always use the `poll_url` the POST returns, verbatim +- **Payment:** x402, **USDC on Base** mainnet (`paymentInfo.network: "base"`, `x402Version: 2`; the signed requirement names the chain as `eip155:8453`). Every paid call carries a flat **$0.001 transaction fee** on top of the media price; the `price.amount` in the 402 body already includes it. The signed authorization window is 600s (`maxTimeoutSeconds`). :::note -The `/v1/...` and `/api/v1/...` paths are equivalent (the gateway rewrites `/v1` → `/api/v1`). Examples below use `/v1`. +The gateway serves its API under `/api/v1/...`. The bare `/v1/...` form in some examples is the **ClawRouter** local-proxy surface (`http://localhost:8402/v1/...`) — `https://blockrun.ai/v1/...` returns `404`. Examples below use `/api/v1` for direct calls. ::: --- @@ -34,7 +34,7 @@ Video generation is **asynchronous and two-step**. A clip takes ~60–180s upstr ::::steps :::step{title="Submit the job"} -**`POST /v1/videos/generations`** — verifies your x402 payment (verify only, **no charge yet**) and submits the upstream job. Returns `202` in ~3–20s with a job `id` and a `poll_url`. +**`POST /api/v1/videos/generations`** — validates the body (all per-model gates run **before** the 402, so a bad resolution or duration costs nothing), verifies your x402 payment (verify only, **no charge yet**), claims the authorization's nonce so it cannot be reused for a second job, and submits the upstream job. Returns `202` in ~3–20s with an opaque job `id` and a `poll_url`. ::: :::step{title="Poll until completed"} @@ -53,12 +53,14 @@ Key guarantees: |---|---| | **No charge on failure** | If the upstream job fails or you never poll, no USDC moves. Settlement only fires on a `completed` poll. | | **Wallet binding, not signature equality** | The poll must be signed by the wallet that submitted the POST. A *fresh* signature from that same wallet is fine — the poll endpoint returns its own 402 challenge if no header is sent, so standard x402 clients re-sign automatically. | -| **Idempotent re-polls** | Polling an already-settled job returns the same video URL again (`payment.status: "already_settled"`) — you are never double-charged. | -| **Replay-protected** | Each signed authorization can submit exactly one job (nonce claim on POST). | -| **Durable output** | The clip is mirrored to BlockRun's GCS bucket before settlement; `data[].url` is the permanent BlockRun-hosted URL, `data[].source_url` is the (often temporary) upstream URL. | +| **Idempotent re-polls** | The gateway claims a per-job settlement lock before charging, so concurrent or repeated polls of a finished job return the same video URL with `payment.status: "already_settled"` — you are never double-charged. | +| **Replay-protected** | Each signed authorization can submit exactly one job (nonce claim on POST). Re-sending a used authorization returns `402` with `code: "PAYMENT_REPLAY"`. | +| **Tamper-evident poll** | `poll_url` carries the billed `duration`, image-to-video flag, resolution and reference counts, HMAC-signed into `sig`. A hand-built poll query returns `400` — use `poll_url` verbatim. | +| **Durable output** | The clip is mirrored to BlockRun's storage before settlement; `data[].url` is the permanent BlockRun-hosted URL, `data[].source_url` is the (often temporary) upstream URL. | +| **Claimable for ~48h** | If your client times out mid-poll, re-GET the same `poll_url` later with a fresh signature from the same wallet — no resubmission needed. | :::note{title="SDKs and ClawRouter hide all of this"} -The TypeScript `VideoClient` and the local ClawRouter proxy run the submit+poll loop for you, so you make a single call and get the finished video back. The two-step contract below is only relevant if you call the raw HTTP API yourself. +The TypeScript `VideoClient`, the Python `VideoClient`, and the local ClawRouter proxy run the submit+poll loop for you, so you make a single call and get the finished video back. The two-step contract below is only relevant if you call the raw HTTP API yourself. ::: --- @@ -70,17 +72,19 @@ The TypeScript `VideoClient` and the local ClawRouter proxy run the submit+poll | `azure/sora-2` | Sora 2 | **4 / 8 / 12** (default 4 — only these three) | 720p, portrait or landscape | ✅ (non-human only) | ✅ | ❌ | | `xai/grok-imagine-video` | Grok Imagine Video | 1–15 (default 8) | 480p (default), 720p | ✅ | — | ❌ | | `xai/grok-imagine-video-1.5` | Grok Imagine Video 1.5 | 1–15 (default 8) | 480p (default), 720p, **1080p** | ✅ | ✅ | ❌ | -| `bytedance/seedance-1.5-pro` | Seedance 1.5 Pro | default 5, max 12 | 720p (default) | ✅ | ✅ (t2v) | ❌ | -| `bytedance/seedance-2.0-mini` | Seedance 2.0 Mini | default 5, max 15 | 480p, 720p (default) | ✅ | ✅ (t2v) | ✅ | -| `bytedance/seedance-2.0-fast` | Seedance 2.0 Fast | default 5, max 15 | 720p (default) | ✅ | ✅ (t2v) | ✅ | -| `bytedance/seedance-2.0` (Pro) | Seedance 2.0 Pro | default 5, max 15 | 720p (default), up to **4K** | ✅ | ✅ (t2v) | ✅ | -| `bytedance/seedance-2.5` | Seedance 2.5 | default 5, **max 30** | 720p (ceiling) | ✅ | ✅ (t2v) | ❌ | +| `bytedance/seedance-1.5-pro` | Seedance 1.5 Pro | 4–12 (default 5) | 480p, 720p (default), 1080p | ✅ | ✅ (t2v) | ❌ | +| `bytedance/seedance-2.0-mini` | Seedance 2.0 Mini | 4–15 (default 5) | 480p, 720p (default) | ✅ | ✅ (t2v) | ✅ | +| `bytedance/seedance-2.0-fast` | Seedance 2.0 Fast | 4–15 (default 5) | 480p, 720p (default) | ✅ | ✅ (t2v) | ✅ | +| `bytedance/seedance-2.0` (Pro) | Seedance 2.0 Pro | 4–15 (default 5) | 480p, 720p (default), 1080p, **4K** | ✅ | ✅ (t2v) | ✅ | +| `bytedance/seedance-2.5` | Seedance 2.5 | 4–**30** (default 5) | 480p, 720p (default) | ✅ | ✅ (t2v) | ❌ | + +The live list, with per-second rates and duration caps, is at `GET https://blockrun.ai/api/v1/video/models` (free, rate-limited). An unpaid `GET /api/v1/videos/generations` returns the same catalog inside a 402 discovery body, including each Grok SKU's `pricePerSecondByResolution`, `defaultResolution` and `perGenerationFee`. Notes: -- **Sora 2** accepts only `duration_seconds` of **4, 8, or 12** — any other value returns `400` listing the allowed set. Text-to-video only (no `image_url`). Output is 720p with synchronized audio, portrait or landscape. -- **Grok Imagine** accepts `duration_seconds` from **1 to 15** (default 8) and an optional `image_url` (first frame). `resolution` **is honored and is a billing tier** — `xai/grok-imagine-video` renders `480p`/`720p`, and `xai/grok-imagine-video-1.5` adds `1080p`; omitting it bills and renders `480p`, and anything off-tier returns a `400` before payment. `aspect_ratio` is also honored: both SKUs accept `16:9`, `9:16`, `1:1`, `4:3`, `3:4`, `3:2`, `2:3` (the Seedance-only `adaptive` and `21:9` return a `400`). Grok Imagine Video **1.5** is xAI's flagship generation — better physics and prompt adherence, with native synced audio in the same pass. The remaining Seedance-only params (`generate_audio`, `seed`, RealFace, reference media) are not sent upstream for either Grok SKU. -- **Seedance** supports a default of 5s and a per-tier maximum — **`seedance-2.5` allows up to 30s, `seedance-2.0` / `seedance-2.0-fast` / `seedance-2.0-mini` up to 15s, `seedance-1.5-pro` up to 12s** (the gateway returns `400` for a `duration_seconds` above the tier max). The gateway bumps the default to **720p** and sets `generate_audio` per the t2v/i2v split below. Only Seedance **2.0** / **2.0-fast** accept a `real_face_asset_id` (`ta_xxxx`) for character/identity consistency, multiple reference images, and reference video/audio (r2v). `seedance-2.5` is text-to-video and image-to-video only: no RealFace, no first/last-frame, no reference media. `seedance-2.0-fast` finishes in ~60–80s; `seedance-2.0` (Pro) is higher quality and slower. +- **Sora 2** accepts only `duration_seconds` of **4, 8, or 12** — any other value returns `400` listing the allowed set. Text-to-video and image-to-video (`image_url`, non-human subjects only — see below). Output is 720p with synchronized audio, portrait or landscape; `resolution` and the Seedance-only params are ignored. +- **Grok Imagine** accepts `duration_seconds` from **1 to 15** (default 8) and an optional `image_url` (first frame). `resolution` **is honored and is a billing tier** — `xai/grok-imagine-video` renders `480p`/`720p`, and `xai/grok-imagine-video-1.5` adds `1080p`; omitting it bills and renders `480p`, and anything off-tier returns a `400` before payment that lists the supported tiers and their rates. `aspect_ratio` is also honored: both SKUs accept `16:9`, `9:16`, `1:1`, `4:3`, `3:4`, `3:2`, `2:3` (the Seedance-only `adaptive` and `21:9` return a `400`). Grok Imagine Video **1.5** is xAI's flagship generation — better physics and prompt adherence, with native synced audio in the same pass. The remaining Seedance-only params (`generate_audio`, `seed`, RealFace, reference media) are not sent upstream for either Grok SKU. +- **Seedance** supports a default of 5s, a floor of **4s** (a `duration_seconds` below 4 returns `400` before payment) and a per-tier maximum — **`seedance-2.5` allows up to 30s, `seedance-2.0` / `seedance-2.0-fast` / `seedance-2.0-mini` up to 15s, `seedance-1.5-pro` up to 12s** (above the max also returns `400`). The gateway bumps the default to **720p** and sets `generate_audio` per the t2v/i2v split below. `seedance-2.0`, `seedance-2.0-fast` and `seedance-2.0-mini` accept a `real_face_asset_id` (`ta_xxxx`) for character/identity consistency and multiple reference images (omni). `seedance-2.5` is text-to-video and image-to-video only: no RealFace, no first/last-frame, no reference media. `seedance-2.0-fast` finishes in ~60–80s; `seedance-2.0` (Pro) is higher quality and slower; `seedance-2.0-mini` is the budget tier at roughly half the Fast rate. --- @@ -88,55 +92,77 @@ Notes: Whether you can seed generation from an image — and how — depends on the subject: -- **Non-human subject** (product, scene, animal, object): pass `image_url` (a public URL to the first frame) on **`azure/sora-2`**, **Grok**, or any **Seedance** model. For `azure/sora-2` the gateway resizes the seed image server-side to Sora's exact required dimensions (1280×720 / 720×1280). Seedance image-to-video is billed at the same per-token rate as text-to-video. -- **A specific real person**: you cannot upload a face to Sora (see the note below). Use **Seedance 2.0 / 2.0-fast + a RealFace `ta_xxxx` asset** — enroll the person once *with their consent* ([RealFace](realface.md), ~1-min on-phone liveness, $0.011), then pass `real_face_asset_id`. Details in [Character consistency](#character-consistency-seedance-20-fast--pro) below. +- **Non-human subject** (product, scene, animal, object): pass `image_url` (a public `https` URL to the first frame, or an inline `data:image/...;base64,` URI) on **`azure/sora-2`**, **Grok**, or any **Seedance** model. For `azure/sora-2` the gateway resizes the seed image server-side to Sora's exact required dimensions (1280×720 / 720×1280). Seedance image-to-video is billed at the same per-token rate as text-to-video. +- **A specific real person**: you cannot upload a face to Sora (see the note below), and Seedance rejects a seed image that contains a real face (`400`, `code: "INPUT_IMAGE_REAL_PERSON"`). Use **Seedance 2.0 / 2.0-fast / 2.0-mini + a RealFace `ta_xxxx` asset** — enroll the person once *with their consent* ([RealFace](realface.md), ~1-min on-phone liveness, $0.011), then pass `real_face_asset_id`. Details in [Character consistency](#character-consistency-seedance-20-mini--fast--pro) below. - **An AI character / mascot**: same flow with a [Virtual Portrait](virtual-portrait.md) asset (no KYC, $0.011). :::warning{title="Sora reference images cannot contain human faces"} `azure/sora-2` **rejects reference images that contain human faces** — a moderation pipeline blocks any recognizable person to prevent deepfakes, and there is no general human-likeness image-upload path. So on BlockRun: **`azure/sora-2` does image-to-video for non-human subjects** (`image_url`, resized server-side to Sora's exact dimensions); and **real-person video goes through Seedance 2.0 + RealFace** (the consent-based route above). ::: +:::note{title="Inline data URIs are sniffed before the 402"} +An inline `data:` image is decoded and checked for real image bytes (PNG, JPEG, GIF, BMP, WEBP, HEIC/HEIF/AVIF) **before** any payment challenge, so a malformed data URI is a free `400` rather than a paid submit that dies upstream. Remote `https` URLs pass through unchecked. +::: + --- ## Request parameters (POST body, JSON) | Parameter | Type | Required | Description | |---|---|---|---| -| `model` | string | No | Video model ID (default `xai/grok-imagine-video`). See table above. | +| `model` | string | No | Video model ID (default `xai/grok-imagine-video`). See table above. Unknown ids return `400` listing the available models. | | `prompt` | string | **Yes** | Text description of the video to generate. | -| `image_url` | string (URL) | No | Seed image for image-to-video (all video models support it). For `azure/sora-2` the image is resized server-side to Sora's exact dimensions and must not contain a human face. Mutually exclusive with `real_face_asset_id`. | -| `real_face_asset_id` | string | No | Character/face reference asset (`ta_xxxxxx`) from a [Virtual Portrait](virtual-portrait.md) (AI character) or [RealFace](realface.md) (real person). **Seedance 2.0 / 2.0-fast only.** Mutually exclusive with `image_url`. | -| `last_frame_url` | string (URL) | No | Final-frame target for first-and-last-frame interpolation. Send with `image_url` (first frame). **Seedance only** (1.5-pro, 2.0-fast, 2.0). | -| `reference_image_urls` | string[] | No | Multiple reference images for character/style consistency (cited as "image 1", "image 2"). **`seedance-2.0` / `seedance-2.0-fast` only.** | -| `reference_videos` | array of `{url}` | No | Reference videos for reference-to-video (r2v). Up to 3. **`seedance-2.0` / `seedance-2.0-fast` only.** Adds a token surcharge (see Pricing). Flat `video_url` is rejected — use this array. | -| `reference_audios` | array of `{url}` | No | Reference audios for r2v. Up to 3, each ≤15.2s. **`seedance-2.0` / `seedance-2.0-fast` only.** Adds a token surcharge. Flat `audio_url` is rejected — use this array. | -| `input_type` | string | No | Optional validated enum: `text` / `image` / `first_last_frame` / `reference`. When supplied it must match the seed fields you actually sent or the request returns a `400`. | -| `duration_seconds` | integer | No | Duration to bill for. Defaults to the model default. Must respect the model's max (Seedance: 2.0 / 2.0-fast = 15s, 1.5-pro = 12s; Sora 2: the discrete `{4,8,12}` set) or you get a `400`. | -| `resolution` | string | No | Per-model, from token360's own parameter schema: `bytedance/seedance-2.0` accepts `480p` / `720p` / `1080p` / `4K` (real 3840×2160); `seedance-1.5-pro` accepts `480p` / `720p` / `1080p`; `seedance-2.0-fast` and `seedance-2.5` accept `480p` / `720p` only. **Defaults to `720p`**; higher resolutions cost more tokens upstream. Anything else (`360p` / `540p` / `1K` / `2K` included — no model offers them) returns a `400` before payment. Seedance only. | -| `aspect_ratio` | string | No | `adaptive` / `16:9` / `9:16` / `1:1` / `4:3` / `3:4` / `21:9`. (`9:21` is not offered by any Seedance model — rejected upstream.) Seedance only — ignored by Grok. | -| `generate_audio` | boolean | No | Synced audio track. **Seedance default: `true` for text-to-video, `false` for image/face-conditioned.** Pass explicitly to override. Ignored by Grok. | +| `image_url` | string (URL or data URI) | No | Seed image for image-to-video (all video models support it). For `azure/sora-2` the image is resized server-side to Sora's exact dimensions and must not contain a human face. Mutually exclusive with `real_face_asset_id`. | +| `real_face_asset_id` | string | No | Character/face reference asset (`ta_xxxxxx`) from a [Virtual Portrait](virtual-portrait.md) (AI character) or [RealFace](realface.md) (real person). **Seedance 2.0 / 2.0-fast / 2.0-mini only.** Mutually exclusive with `image_url`. | +| `last_frame_url` | string (URL or data URI) | No | Final-frame target for first-and-last-frame interpolation. Send with `image_url` (first frame) — alone it returns `400`. **Seedance only** (1.5-pro, 2.0-mini, 2.0-fast, 2.0). Cannot be combined with `real_face_asset_id`. | +| `reference_image_urls` | string[] (1–9) | No | Multiple reference images for character/style consistency (cite them as "image 1", "image 2" in the prompt). **`seedance-2.0` / `seedance-2.0-fast` / `seedance-2.0-mini` only.** Its own mode — cannot be combined with `image_url`, `last_frame_url` or `real_face_asset_id`. No surcharge. | +| `reference_videos` | array of `{url}` (1–3) | No | Reference videos for reference-to-video (r2v). **Currently gated off** — the gateway returns `503` (`reference-to-video is temporarily unavailable`) before any payment. Flat `video_url` is rejected with a `400` pointing at this field. | +| `reference_audios` | array of `{url}` (1–3, each ≤15.2s) | No | Reference audios for r2v. **Currently gated off** (`503`, see above). Flat `audio_url` is rejected with a `400` pointing at this field. | +| `input_type` | string | No | Optional validated enum: `text` / `image` / `first_last_frame` / `reference`. When supplied it must match the seed fields you actually sent or the request returns a `400` carrying `inferred_input_type`. | +| `duration_seconds` | integer | No | Duration to bill for. Defaults to the model default. Must respect the model's range (Seedance: 4s floor; 2.5 = 30s, 2.0 / 2.0-fast / 2.0-mini = 15s, 1.5-pro = 12s max; Grok: 1–15; Sora 2: the discrete `{4,8,12}` set) or you get a `400` before payment. | +| `resolution` | string | No | `480p` / `720p` / `1080p` / `4K`, gated **per model** from the upstream parameter schema: `bytedance/seedance-2.0` accepts all four (`4K` is real 3840×2160); `seedance-1.5-pro` accepts `480p` / `720p` / `1080p`; `seedance-2.0-fast` and `seedance-2.5` accept `480p` / `720p` only. **Defaults to `720p`** on Seedance; higher resolutions cost more tokens upstream. Anything off-list returns a `400` before payment (`360p` / `540p` / `1K` / `2K` are not offered by any model). `seedance-2.0-mini` renders `480p` / `720p` — keep to those; a higher value is not rejected up front for this SKU. On Grok it selects the billing tier (see above); Sora ignores it. | +| `aspect_ratio` | string | No | Seedance: `adaptive` / `16:9` / `9:16` / `1:1` / `4:3` / `3:4` / `21:9` (`9:21` is not offered by any Seedance model). Grok: `16:9` / `9:16` / `1:1` / `4:3` / `3:4` / `3:2` / `2:3`. Off-list values return `400` before payment. Sora ignores it. | +| `generate_audio` | boolean | No | Synced audio track. **Seedance default: `true` for text-to-video, `false` for image/face/reference-conditioned.** Pass explicitly to override. Ignored by Grok and Sora. | | `seed` | integer | No | Reproducibility seed (Seedance). Same seed + prompt + params ≈ same clip. | | `watermark` | boolean | No | Embed the upstream Seedance watermark. Off by default at the gateway. | | `return_last_frame` | boolean | No | Also return the last frame as a still — useful for chaining clips. Seedance only. | +### Standard multimodal body — `POST /api/v1/videos` + +Callers migrating from a Seedance-style multimodal API can post the `content[]` shape to `POST https://blockrun.ai/api/v1/videos` instead. The gateway translates it to the flat fields above and runs the **same** validation, 402 and billing pipeline; the returned `poll_url` points at `/api/v1/videos/{id}`, which polls the same job. + +```json +{ + "model": "seedance-2.0", + "content": [ + { "type": "text", "text": "a hummingbird hovering at a red flower, ultra slow motion" }, + { "type": "image_url", "image_url": { "url": "https://example.com/flower.jpg" } } + ], + "ratio": "16:9", + "duration": 5 +} +``` + +Mapping: `ratio` → `aspect_ratio`, `duration` → `duration_seconds`, a bare `seedance-*` id → `bytedance/seedance-*`, text parts → `prompt`, the single `image_url` part → `image_url`. More than one image part, `video_url` / `audio_url` parts, or `first_frame` / `last_frame` roles return `400` before payment — use the flat fields for those. + --- ## Pricing -All prices below are the amounts quoted in the `402` challenge and actually billed in USDC. Sora and Seedance include the gateway's standard **5% margin**; Grok Imagine is billed at xAI's **official per-second rates exactly**, tiered by resolution, plus a flat **$0.001 per generation** — that flat fee, not a percentage on the rate, is the entire markup on those SKUs. +All prices below are the amounts quoted in the `402` challenge and actually billed in USDC. Sora and Seedance include the gateway's standard **5% margin**; Grok Imagine is billed at xAI's **official per-second rates exactly**, tiered by resolution, plus a flat **$0.001 per generation** — that flat fee, not a percentage on the rate, is the entire markup on those SKUs. Every model then adds the gateway-wide **$0.001 transaction fee** per paid call. There is no other minimum. -| Model | Billing basis | Effective price (720p) | +| Model | Billing basis | Effective price (720p unless noted) | |---|---|---| -| `azure/sora-2` | $0.10 / second (flat) | **4s = $0.42** · **8s = $0.84** · **12s = $1.26** | -| `xai/grok-imagine-video` | $0.05 / sec @ 480p (default) · $0.07 / sec @ 720p (official rates) + $0.001 / generation | **8s = $0.561** · **15s = $1.051** (480p: **8s = $0.401**) | -| `xai/grok-imagine-video-1.5` | $0.08 / sec @ 480p (default) · $0.14 / sec @ 720p · $0.25 / sec @ 1080p (official rates) + $0.001 / generation | **8s = $1.121** · **15s = $2.101** (480p: **8s = $0.641**; 1080p: **8s = $2.001**) | -| `bytedance/seedance-1.5-pro` | Token-metered ($3.108 / M tokens) | **5s ≈ $0.35** · **12s ≈ $0.85** | -| `bytedance/seedance-2.0-mini` | Token-metered ($3.5 / M tokens) | **5s ≈ $0.40** · **15s ≈ $1.19** | -| `bytedance/seedance-2.0-fast` | Token-metered ($7.252 / M tokens) | **5s ≈ $0.83** · **15s ≈ $2.48** | -| `bytedance/seedance-2.0` (Pro) | Token-metered ($9.9715 / M tokens) | **5s ≈ $1.14** · **15s ≈ $3.41** | -| `bytedance/seedance-2.5` | Token-metered ($13.8565 / M tokens) | **5s ≈ $1.58** · **30s ≈ $9.47** | +| `azure/sora-2` | $0.10 / second (flat) × 1.05 + $0.001 fee | **4s = $0.421** · **8s = $0.841** · **12s = $1.261** | +| `xai/grok-imagine-video` | $0.05 / sec @ 480p (default) · $0.07 / sec @ 720p (official rates) + $0.001 / generation + $0.001 fee | **8s = $0.562** · **15s = $1.052** (480p: **8s = $0.402**) | +| `xai/grok-imagine-video-1.5` | $0.08 / sec @ 480p (default) · $0.14 / sec @ 720p · $0.25 / sec @ 1080p (official rates) + $0.001 / generation + $0.001 fee | **8s = $1.122** · **15s = $2.102** (480p: **8s = $0.642**; 1080p: **8s = $2.002**) | +| `bytedance/seedance-1.5-pro` | Token-metered ($3.108 / M tokens) ≈ $0.070 / sec | **5s ≈ $0.355** · **12s ≈ $0.850** | +| `bytedance/seedance-2.0-mini` | Token-metered ($3.5 / M tokens) ≈ $0.0797 / sec | **5s ≈ $0.400** · **15s ≈ $1.197** | +| `bytedance/seedance-2.0-fast` | Token-metered ($7.252 / M tokens) ≈ $0.165 / sec | **5s ≈ $0.827** · **15s ≈ $2.478** | +| `bytedance/seedance-2.0` (Pro) | Token-metered ($9.9715 / M tokens) ≈ $0.227 / sec | **5s ≈ $1.136** · **15s ≈ $3.407** | +| `bytedance/seedance-2.5` | Token-metered ($13.8565 / M tokens) ≈ $0.315 / sec | **5s ≈ $1.579** · **30s ≈ $9.468** | -**Seedance token math:** at the 720p default a clip uses **~21,690 tokens/second** (a 5s clip ≈ 108,450 tokens). Price = `duration × 21,690 × resolution-factor × rate-per-M ÷ 1,000,000 × 1.05`. Image-to-video is billed at the **same per-token rate** as text-to-video (no i2v discount). The resolution token factor relative to 720p (=1) is: `480p ×0.5`, `1080p ×2.25`, and `4K ×9` (area-proportional) — so dropping to `480p` halves the per-clip cost and `1080p`/`4K` cost proportionally more. `4K` (real 3840×2160) is available **only on `bytedance/seedance-2.0`**; the ceiling is `1080p` on `seedance-1.5-pro` and `720p` on `seedance-2.0-fast`/`seedance-2.5`; anything above a model's ceiling returns a `400` before payment. Reference video/audio inputs (r2v) add a surcharge: `×(1 + 1.0·#refVideos + 0.3·#refAudios)`. Sora ignores resolution and reference surcharges. Grok Imagine bills per second **by resolution tier**, at xAI's official rates with no margin: on `xai/grok-imagine-video` `480p` is $0.05/sec (the default when `resolution` is omitted) and `720p` $0.07/sec, with `1080p`/`4K` rejected before payment; on `xai/grok-imagine-video-1.5` `480p` is $0.08/sec (default), `720p` $0.14/sec and `1080p` $0.25/sec, with `4K` rejected before payment. Both SKUs add a flat **$0.001 per generation** on top of rate × duration — it does not scale with length, so a 1s clip and a 15s clip carry the same one. +**Seedance token math:** at the 720p default a clip uses **~21,690 tokens/second** (a 5s clip ≈ 108,450 tokens). Price = `duration × 21,690 × resolution-factor × rate-per-M ÷ 1,000,000 × 1.05 + $0.001`. Image-to-video is billed at the **same per-token rate** as text-to-video (no i2v discount). The resolution token factor relative to 720p (=1) is: `480p ×0.5`, `1080p ×2.25`, and `4K ×9` (area-proportional) — so dropping to `480p` halves the per-clip cost and `1080p`/`4K` cost proportionally more (a 5s `seedance-2.0` clip is ≈ $2.556 at 1080p and ≈ $10.220 at 4K). `4K` (real 3840×2160) is available **only on `bytedance/seedance-2.0`**; the ceiling is `1080p` on `seedance-1.5-pro` and `720p` on `seedance-2.0-fast`/`seedance-2.0-mini`/`seedance-2.5`; anything above a model's ceiling returns a `400` before payment. On a completed poll the response carries `usage.total_tokens` with `token_source: "upstream"` when the upstream reported the metered count, or `"estimated"` when the gateway fell back to this formula. Sora ignores resolution. Grok Imagine bills per second **by resolution tier**, at xAI's official rates with no margin: on `xai/grok-imagine-video` `480p` is $0.05/sec (the default when `resolution` is omitted) and `720p` $0.07/sec, with `1080p`/`4K` rejected before payment; on `xai/grok-imagine-video-1.5` `480p` is $0.08/sec (default), `720p` $0.14/sec and `1080p` $0.25/sec, with `4K` rejected before payment. Both SKUs add a flat **$0.001 per generation** on top of rate × duration — it does not scale with length, so a 1s clip and a 15s clip carry the same one. **One-time enrollment fees (separate from per-call billing):** @@ -147,93 +173,140 @@ All prices below are the amounts quoted in the `402` challenge and actually bill --- +## The 402 challenge + +An unpaid POST returns `402` with the x402 requirement in three equivalent headers — `X-Payment-Required`, `PAYMENT-REQUIRED` (base64 JSON) and `WWW-Authenticate: X402 requirements="…"` — plus an informational JSON body: + +```json +{ + "error": "Payment Required", + "message": "This endpoint requires x402 payment", + "price": { + "amount": "1.122000", + "currency": "USD", + "pricePerSecond": 0.14, + "resolution": "720p", + "perGenerationFee": "0.001000", + "durationSeconds": 8 + }, + "generation_info": { + "output_duration": "8s video (max 15s)", + "generation_time": "~60-180s upstream", + "flow": "async", + "note": "POST verifies payment + submits the job in ~3-20s and returns { id, poll_url } …" + }, + "paymentInfo": { "network": "base", "asset": "USDC", "x402Version": 2 } +} +``` + +`price.amount` is the full amount you will be charged (media price + $0.001 fee). On the Grok SKUs `pricePerSecond` states the rate of the **tier actually billed** and `resolution` / `perGenerationFee` are present so the arithmetic reconciles; on Sora and Seedance `pricePerSecond` is the model's flat display rate. The decoded requirement's `accepts[0].amount` is the same figure in USDC base units (6 decimals). + +Sign that requirement and re-send the POST with the signature in `X-Payment` (also accepted: `Payment-Signature`). A verification failure returns `402` with a machine-readable `code`: + +| `code` | Meaning | +|---|---| +| `PAYMENT_INVALID` | Signature / payload did not verify (default when no more specific cause is known). | +| `PAYMENT_UNFUNDED` | The authorization could not be executed on-chain — usually insufficient USDC on Base, or an expired `validAfter`/`validBefore` window. | +| `PAYMENT_REPLAY` | This authorization was already claimed by an earlier POST. Sign a fresh one. | +| `PAYMENT_BLOCKHASH_STALE` | Solana gateway only — re-sign against a fresh blockhash. | + +--- + ## Responses ### 1. POST → `202 Accepted` (job submitted) ```json { - "id": "azure:vidjob_abc123", + "id": "", "object": "video.generation.job", "status": "queued", "model": "azure/sora-2", "duration_seconds": 8, - "price": { "amount": "0.840000", "currency": "USD" }, + "price": { "amount": "0.841000", "currency": "USD" }, "payment_status": "verified", "created": 1776443975, - "poll_url": "/api/v1/videos/generations/azure%3Avidjob_abc123?model=azure%2Fsora-2&duration=8" + "poll_url": "/api/v1/videos/generations/?model=azure%2Fsora-2&duration=8&i2v=0&sig=…", + "poll_instructions": "Send GET to poll_url with an x-payment header signed by the SAME wallet …" } ``` -The `id` is a composite `"{provider}:{upstreamId}"`. The `poll_url` already encodes the required `?model` and `?duration` query params — preserve them when polling. +The `id` is opaque — treat it as a token, not a string to parse. The `poll_url` already encodes the required `model`, `duration`, `i2v`, optional `resolution` and the HMAC `sig` that binds billing to this job — preserve it verbatim when polling (relative to `https://blockrun.ai`). ### 2. GET poll → `202` (still generating) ```json { - "id": "azure:vidjob_abc123", + "id": "", "object": "video.generation.job", "status": "in_progress", "model": "azure/sora-2", "payment_status": "verified", - "note": "Upstream is still generating. Poll again in 5-10s. No charge until status=completed." + "progress": 42, + "elapsed_seconds": 75, + "note": "Upstream is still generating. Poll again in 5-10s. No charge until status=completed. The job stays claimable for ~48h …" } ``` -`status` is `queued` or `in_progress`. Keep polling every 5–10s. +`status` is `queued` or `in_progress`; `progress` (percent) and `elapsed_seconds` appear when the upstream reports them. Keep polling every 5–10s. ### 3. GET poll → `200` (completed — charged here) ```json { - "id": "azure:vidjob_abc123", + "id": "", "object": "video.generation.job", "status": "completed", - "model": "azure/sora-2", + "model": "bytedance/seedance-2.0-fast", "created": 1776444180, "data": [ { "url": "https://blockrun.ai/api/media/media/videos/2026/05/27/.mp4", "source_url": "https:///.mp4", - "duration_seconds": 8, - "request_id": "vidjob_abc123", + "duration_seconds": 5, + "request_id": "", "backed_up": true } ], + "usage": { "total_tokens": 108450, "token_source": "upstream" }, + "price": { "amount": "0.826810", "currency": "USD" }, "payment": { "status": "settled", "tx_hash": "0x…", "network": "base" } } ``` -On settlement the response also carries `PAYMENT-RESPONSE` and `X-Payment-Receipt` (the on-chain tx hash) headers. A re-poll of an already-settled job returns the same body with `payment.status: "already_settled"` and no new charge. +On settlement the response also carries `PAYMENT-RESPONSE` (base64 JSON `{success, transaction, network, payer}`) and `X-Payment-Receipt` (the on-chain tx hash) headers. A re-poll of an already-settled job returns the same body with `payment.status: "already_settled"` and no new charge. ### GET poll → `200` (failed — not charged) ```json { - "id": "azure:vidjob_abc123", + "id": "", "object": "video.generation.job", "status": "failed", - "model": "azure/sora-2", + "model": "bytedance/seedance-2.0", "error": "", "payment_status": "not_charged", "note": "Upstream generation failed. No payment was taken." } ``` +One failure class is recoverable client-side: when only the **generated soundtrack** trips upstream audio-copyright moderation on a Seedance clip, the body adds `error_code: "output_audio_moderation"` and `retry_with: { "generate_audio": false }` — resubmit with `generate_audio: false` (or add "no background music, silent" to the prompt). + ### Response fields | Field | Type | Description | |---|---|---| -| `id` | string | Composite job id `"{provider}:{upstreamId}"` | +| `id` | string | Job id. Opaque on the POST; the poll echoes an internal job reference — always navigate via `poll_url`. | | `status` | string | `queued` → `in_progress` → `completed` \| `failed` | -| `data[].url` | string | Permanent BlockRun-hosted URL (GCS-backed). Falls back to upstream URL if backup fails. | +| `data[].url` | string | Permanent BlockRun-hosted URL. Falls back to the upstream URL if the mirror fails (`backed_up: false`). | | `data[].source_url` | string | Original upstream URL (may expire) | -| `data[].duration_seconds` | integer | Duration of the generated clip | +| `data[].duration_seconds` | integer | Duration of the generated clip (falls back to the billed duration when the upstream does not echo one) | | `data[].request_id` | string | Upstream request id for debugging | -| `data[].backed_up` | boolean | `true` when mirrored to BlockRun's GCS bucket | +| `data[].backed_up` | boolean | `true` when mirrored to BlockRun's storage | +| `usage.total_tokens` | integer | Seedance only: the metered token count; `usage.token_source` is `upstream` or `estimated` | +| `price.amount` | string | Amount charged in USD, fee included | | `payment.status` | string | `settled` \| `already_settled` | | `payment.tx_hash` | string | On-chain USDC settlement tx (also in `X-Payment-Receipt` header) | -| `usage` | object | On a completed Seedance poll: a `usage` block with `total_tokens` (the metered token count the price was computed from). | --- @@ -258,9 +331,26 @@ console.log(result.data[0].url); // permanent MP4 URL console.log(result.txHash); // settlement tx ``` -`VideoClient` polls internally up to its `timeout` (default 300000ms / 5 min). Options mirror the request params: `model`, `imageUrl`, `realFaceAssetId`, `durationSeconds`, `aspectRatio`, `resolution`, `generateAudio`, `seed`, `watermark`, `returnLastFrame`. +`VideoClient` polls every 5s within an overall `budgetMs` (default 900000ms / 15 min); `timeout` (default 120000ms) is the per-HTTP-call limit. Options mirror the request params: `model`, `imageUrl`, `lastFrameUrl`, `referenceImageUrls`, `realFaceAssetId`, `durationSeconds`, `aspectRatio`, `resolution`, `generateAudio`, `seed`, `watermark`, `returnLastFrame`. +::: + +:::tab{label="Python SDK"} +`blockrun_llm.VideoClient` runs the same submit + poll loop, re-signing automatically if the 600s authorization window lapses mid-poll. + +```python +from blockrun_llm import VideoClient + +client = VideoClient() # wallet from BLOCKRUN_WALLET_KEY / ~/.blockrun + +result = client.generate( + "a corgi surfing at sunset, cinematic", + model="azure/sora-2", + duration_seconds=8, +) +print(result.data[0].url) +``` -The Python SDK does not yet ship a video helper — use the raw two-step HTTP flow (sign the POST and the poll with the same wallet), or ClawRouter. +`generate()` accepts a `budget_seconds` (default 900) for the whole poll loop; if the budget runs out the job stays claimable for ~48h via the `poll_url` in the error details. ::: :::tab{label="ClawRouter"} @@ -279,17 +369,17 @@ Two steps — submit, then poll until completed. **Step 1 — submit:** ```bash -curl -X POST https://blockrun.ai/v1/videos/generations \ +curl -X POST https://blockrun.ai/api/v1/videos/generations \ -H "Content-Type: application/json" \ -H "X-Payment: $PAYMENT_HEADER" \ -d '{ "model": "azure/sora-2", "prompt": "a hummingbird hovering at a red flower, ultra slow motion", "duration_seconds": 8 }' -# → 202 { "id": "...", "poll_url": "/api/v1/videos/generations/...?model=...&duration=8", ... } +# → 202 { "id": "...", "poll_url": "/api/v1/videos/generations/...?model=...&duration=8&i2v=0&sig=...", ... } ``` -**Step 2 — poll until completed (re-sign with the SAME wallet):** +**Step 2 — poll until completed (re-sign with the SAME wallet, use `poll_url` verbatim):** ```bash -curl "https://blockrun.ai/v1/videos/generations/azure%3Avidjob_abc123?model=azure%2Fsora-2&duration=8" \ +curl "https://blockrun.ai$POLL_URL" \ -H "X-Payment: $FRESH_PAYMENT_HEADER_SAME_WALLET" # → 202 in_progress … repeat every 5–10s … → 200 completed { data:[{url}], payment:{status:"settled"} } ``` @@ -300,13 +390,13 @@ curl "https://blockrun.ai/v1/videos/generations/azure%3Avidjob_abc123?model=azur ### Image-to-video (Grok / Seedance) ```bash -curl -X POST https://blockrun.ai/v1/videos/generations \ +curl -X POST https://blockrun.ai/api/v1/videos/generations \ -H "Content-Type: application/json" \ -H "X-Payment: $PAYMENT_HEADER" \ -d '{ "model": "bytedance/seedance-2.0", "prompt": "the subject turns and smiles", "image_url": "https://example.com/portrait.jpg" }' ``` -### Character consistency (Seedance 2.0 fast / pro) +### Character consistency (Seedance 2.0 mini / fast / pro) Pass a `ta_xxxx` asset from a Virtual Portrait or RealFace enrollment to keep the same identity across clips. Mutually exclusive with `image_url`. @@ -331,9 +421,9 @@ Pass a `ta_xxxx` asset from a Virtual Portrait or RealFace enrollment to keep th |---|---| | POST → upstream job submitted (`202`) | ~3–20s | | Polling until clip ready | 60–180s (poll every 5–10s) | -| GCS backup + settle on the completed poll | ~1–30s | +| Mirror + settle on the completed poll | ~1–30s | -Set your HTTP client timeout to at least 180s per poll. The POST handler itself caps the upstream submit at ~20s (returns `504`, no charge, if upstream doesn't acknowledge). +Set your HTTP client timeout to at least 60s per request (the poll that completes has to mirror the clip and settle on-chain before it answers). The POST handler caps the upstream submit at ~20s (returns `504`, no charge, if upstream doesn't acknowledge). A job that is still `in_progress` after 5 minutes is unusual but not lost — keep polling, or come back later; the job stays claimable for ~48h. --- @@ -341,11 +431,13 @@ Set your HTTP client timeout to at least 180s per poll. The POST handler itself | Code | Where | Description | |---|---|---| -| 400 | POST / GET | Invalid request — bad/missing prompt, unsupported `image_url`/`real_face_asset_id` for the model, `duration_seconds` above max or not in the model's allowed set, model/provider mismatch on the poll. | -| 402 | POST / GET | Payment required (no header → x402 challenge), or payment verify/settle failed. On a completed-but-unsettleable poll, the clip was generated but the signed authorization could not be settled (often expired) — retry the poll. | -| 400 | POST | Content policy violation (`Content policy violation`). | -| 429 | POST / GET | Upstream rate limit. Response includes `Retry-After` (and `X-RateLimit-Source`). | +| 400 | POST | Invalid JSON; unknown / unavailable model; bad or missing prompt; `duration_seconds` outside the model's range or not in its allowed set; off-list `resolution` / `aspect_ratio` for the model; `image_url` / `real_face_asset_id` / `last_frame_url` / `reference_image_urls` on a model that lacks the capability, or in a disallowed combination; `input_type` mismatch (`inferred_input_type` tells you what you sent); malformed inline image data. All of these fire **before** the 402. | +| 400 | POST | After payment, on submit: `code: "INPUT_IMAGE_REAL_PERSON"` (Seedance rejected a seed image containing a real face — use RealFace), `Content policy violation`, or `code: "INVALID_VIDEO_REQUEST"` (upstream rejected a parameter). **No charge** — settlement only happens on a completed poll. | +| 400 | GET | Invalid job id, missing `?model` / `?duration`, model/provider mismatch, or invalid `?sig` — poll the `poll_url` verbatim. | +| 402 | POST / GET | Payment required (no header → x402 challenge), or verification failed (`code`: `PAYMENT_INVALID` / `PAYMENT_UNFUNDED` / `PAYMENT_REPLAY`). On a completed-but-unsettleable poll (`Payment settlement failed`), the clip was generated but the signed authorization could not be settled (often expired) — retry the poll with a fresh signature. | +| 429 | POST / GET | Upstream rate limit (`code: "RATE_LIMITED"`). Response includes `Retry-After` and `retry_after_seconds`. | | 500 | POST / GET | Server / provider configuration error. | +| 503 | POST | `reference_videos` / `reference_audios` sent — reference-to-video is gated off. No charge. | | 504 | POST | Upstream submit timed out (>~20s). **No payment taken** — retry. | | 504 | GET | Upstream poll timed out — retry the poll in a few seconds. | diff --git a/docs/api-reference/virtual-portrait.md b/docs/api-reference/virtual-portrait.md index 0cabb41..c1c744f 100644 --- a/docs/api-reference/virtual-portrait.md +++ b/docs/api-reference/virtual-portrait.md @@ -51,9 +51,9 @@ Images that fail the upstream content filter (NSFW, recognizable real-celebrity Standard BlockRun two-step: -1. **First request without `X-Payment`** → server returns `402 Payment Required` with x402 challenge headers -2. Sign the EIP-3009 transfer authorization for **$0.011 USDC on Base** -3. **Retry the same request with `X-Payment: `** → server verifies, registers the portrait, settles the payment after registration succeeds, returns the `ta_xxx` +1. **First request without `X-Payment`** → server returns `402 Payment Required` with x402 challenge headers (`X-Payment-Required` / `PAYMENT-REQUIRED` base64, plus `WWW-Authenticate: X402 requirements="…"`) and a body of `{ "error": "Payment Required", "message": "Enrolling a Virtual Portrait costs $0.0110 USDC. …", "price": { "amount": "0.0110", "currency": "USD" }, "paymentInfo": { "network": "base", "asset": "USDC", "x402Version": 2 } }` +2. Sign the EIP-3009 transfer authorization for **$0.011 USDC on Base** (`$0.01` enrolment + `$0.001` transaction fee — the requirements say `11000` micro-USDC) +3. **Retry the same request with `X-Payment: `** (`Payment-Signature` is accepted too) → server verifies, rejects a reused authorization (`402`, `code: "PAYMENT_REPLAY"`), registers the portrait, settles the payment after registration succeeds, returns the `ta_xxx` Settlement happens **after** the portrait is successfully enrolled. If enrollment fails (content filter, network error), no payment is taken — the route returns 502 and the caller can retry with a fresh signature. If settlement itself fails after a successful enrollment, BlockRun absorbs the cost rather than leave the user with a paid-but-unrecoverable state. @@ -67,13 +67,15 @@ If you're using `clawrouter` locally, this flow is fully automatic — just call "asset_id": "ta_abcdef1234567890", "group_id": "tg_xyz9876543210", "name": "My Spokesperson", - "image_url": "https://example.com/character.jpg", + "image_url": "https://blockrun.ai/api/media/…", + "source_image_url": "https://example.com/character.jpg", + "mirrored": true, "created_at": "2026-05-22T14:32:11.000Z", "usage": { "compatible_models": ["bytedance/seedance-2.0", "bytedance/seedance-2.0-fast"], "how_to_use": "Pass \"real_face_asset_id\": \"ta_abcdef1234567890\" on a Seedance video generation request." }, - "price": { "amount": "0.010000", "currency": "USD" }, + "price": { "amount": "0.0110", "currency": "USD" }, "settlement": { "success": true, "tx_hash": "0x9f3a…", @@ -82,10 +84,14 @@ If you're using `clawrouter` locally, this flow is fully automatic — just call } ``` +The settlement receipt is also returned in the `X-Payment-Response` / `PAYMENT-RESPONSE` headers. + | Field | Description | |-------|-------------| | `asset_id` | The `ta_…` id to pass as `real_face_asset_id` on Seedance | | `group_id` | Internal asset-group id — exposed for debugging / future delete operations | +| `image_url` | The BlockRun-hosted mirror of your image (so the listing thumbnail survives a dead source URL); falls back to the original URL if mirroring failed | +| `source_image_url` / `mirrored` | The URL you supplied, and whether the mirror succeeded | | `usage.compatible_models` | Which BlockRun video models accept this asset id | | `settlement.tx_hash` | The Base settlement transaction (verify on BaseScan) | @@ -143,7 +149,7 @@ The same `ta_xxx` can be reused across as many video generations as you want — GET https://blockrun.ai/api/v1/wallet/
/portraits ``` -Returns the list of portraits the given wallet has enrolled. Free (rate-limited to 20 requests / hour / IP, shared with the wallet-reconciliation bucket). Works for both EVM (`0x…`) and Solana (base58) addresses, though Virtual Portrait enrollment itself is currently Base-only. +Returns the list of portraits the given wallet has enrolled. Free (rate-limited to 120 requests / hour / IP, shared with the wallet-reconciliation bucket; responses cacheable for 30 s). Works for both EVM (`0x…`) and Solana (base58) addresses, though Virtual Portrait enrollment itself is currently Base-only. ```json { @@ -171,9 +177,10 @@ The video playground at `/models/bytedance-seedance-2.0-fast` reads this same li | 400 | `Invalid request body` | Missing `name`, invalid `image_url`, name > 64 chars | | 400 | `image_url must be an http(s) URL` | URL missing `https://` scheme | | 402 | `Payment Required` | First request — sign + retry with `X-Payment` header | -| 402 | `Payment verification failed` | Signature didn't match what was quoted (amount, recipient, nonce) — re-sign | +| 402 | `Payment verification failed` | `code` is `PAYMENT_INVALID` (signature/amount/recipient mismatch — re-sign), `PAYMENT_UNFUNDED` (insufficient USDC or expired window) or `PAYMENT_BLOCKHASH_STALE`; a `message` explains when known, `debug` carries the raw reason | +| 402 | `Payment authorization already used` | `code: "PAYMENT_REPLAY"` — each authorization is single-use; sign a fresh one | | 502 | varies | Enrollment failed (content filter, image too big, network) — **no payment taken**, safe to retry with a different image | -| 429 | `Rate limit exceeded` | Listing endpoint only — back off per `Retry-After` header | +| 429 | `Rate limit exceeded` | Listing endpoint only (120/hour/IP) — back off per `Retry-After` header | ## Storage and privacy diff --git a/docs/api-reference/voice-phone.md b/docs/api-reference/voice-phone.md index f0888eb..2f34b68 100644 --- a/docs/api-reference/voice-phone.md +++ b/docs/api-reference/voice-phone.md @@ -1,29 +1,29 @@ --- title: Phone & Voice -description: Outbound AI voice calls and wallet-owned phone numbers for agents — $5 per number (30 days), $0.541 flat per call, no telecom account. +description: Outbound AI voice calls, wallet-owned phone numbers and carrier lookups for agents — $5 per number (30 days), $0.541 flat per call, no telecom account. --- # Phone & Voice -Outbound AI voice calls and wallet-owned phone numbers — for AI agents. No telecom account, no Bland.ai signup, no Twilio dashboard. Your wallet *is* the phone account. +Outbound AI voice calls, wallet-owned phone numbers and carrier lookups — for AI agents. No telecom account, no voice-AI signup, no carrier dashboard. Your wallet *is* the phone account. **Default country:** US (no regulatory friction). Other countries can be requested via the `country` parameter — see [Country availability](#country-availability) below. :::info{title="Costs"} -A number is **$5 / 30 days** (renew for $5). Each outbound call is **$0.541 flat** (up to 30 min, default 5). Polling status / fetching a transcript is **free**. Failed call legs (`ended_by: ERROR`) and 429/502 upstream errors are **not charged**. All settled in USDC on Base or Solana via x402. +A number is **$5.001 / 30 days** (renew for $5.001). Each outbound call is **$0.541 flat** (up to 30 min, default 5). Polling status / fetching a transcript is **free**, and so is releasing a number. Carrier lookups are **$0.011** (or **$0.051** with fraud signals). A call that the voice provider refuses to place (502) is **not charged** — the charge settles only once the call is accepted for dialing. All settled in USDC on Base or Solana via x402. ::: -Powered by [Bland.ai](https://bland.ai) (voice AI) + [Twilio](https://twilio.com) (carrier numbers), with x402 settlement at every step. +Voice AI and carrier numbers are provisioned by BlockRun's telephony stack, with x402 settlement at every step. ## The Problem This Solves -An autonomous agent wants to call a restaurant to confirm a reservation, or call a vendor to verify a price. Traditionally that requires: a Twilio account, a Bland.ai account, KYC, a credit card, a long-lived API key, an outbound caller ID number you bought and manage, plus per-minute billing reconciliation. +An autonomous agent wants to call a restaurant to confirm a reservation, or call a vendor to verify a price. Traditionally that requires: a carrier account, a voice-AI account, KYC, a credit card, a long-lived API key, an outbound caller ID number you bought and manage, plus per-minute billing reconciliation. BlockRun collapses all of it to two endpoints and one wallet: -1. `POST /v1/phone/numbers/buy` — $5, get a US number for 30 days (default; other countries via `country` parameter). -2. `POST /v1/voice/call` — $0.541, place an outbound call with an AI voice + Bland conversational task. +1. `POST /v1/phone/numbers/buy` — $5.001, get a US number for 30 days (default; other countries via `country` parameter). +2. `POST /v1/voice/call` — $0.541, place an outbound call with an AI voice + a conversational `task`. -Wallet ownership is recorded in Firestore — only the wallet that bought a number can use it to place calls, and only that wallet can renew it. +Wallet ownership is recorded by the gateway — only the wallet that bought a number can use it to place calls, and only that wallet can renew or release it. The number is bound to the chain it was bought on (`base` on blockrun.ai, `solana` on sol.blockrun.ai). --- @@ -31,24 +31,43 @@ Wallet ownership is recorded in Firestore — only the wallet that bought a numb | Endpoint | Method | Price | Description | |----------|--------|-------|-------------| -| `/api/v1/phone/numbers/buy` | POST | **$5.001** | Provision a new number for the calling wallet (30-day lease). US default; other countries via `country` parameter (may require Twilio compliance setup — see below). | +| `/api/v1/phone/lookup` | POST | $0.011 | Carrier + line type lookup for an E.164 number | +| `/api/v1/phone/lookup/fraud` | POST | $0.051 | Carrier lookup plus fraud signals (SIM swap, call forwarding) | +| `/api/v1/phone/numbers/buy` | POST | **$5.001** | Provision a new number for the calling wallet (30-day lease). US default; other countries via `country` parameter (may require carrier regulatory approval — see below). | | `/api/v1/phone/numbers/renew` | POST | **$5.001** | Extend an active number's lease by 30 days | -| `/api/v1/phone/numbers/list` | POST | $0.003 | List the calling wallet's active numbers | +| `/api/v1/phone/numbers/list` | POST | $0.002 | List the calling wallet's numbers | +| `/api/v1/phone/numbers/release` | POST | **Free** | Release a number you own back to the pool (still signed via x402 so the gateway knows which wallet is asking) | | `/api/v1/voice/call` | POST | **$0.541** | Place an outbound AI call (max 30 min, default 5 min) | -| `/api/v1/voice/call/{id}` | GET | **Free** | Poll call status / fetch transcript | +| `/api/v1/voice/call/{call_id}` | GET | **Free** | Poll call status / fetch transcript | + +All prices include the flat $0.001 transaction fee. Every `/v1/phone/*` endpoint is `POST` with a JSON body; request bodies are validated **strictly** — an unknown key (for example `area_code` instead of `areaCode`) is a `400 Invalid request body` with the offending `keys` listed in `details`. Validation runs before the 402, so an unpaid probe still needs a well-formed body. + +--- + +## POST /api/v1/phone/lookup and /lookup/fraud + +Carrier information and line type for any phone number; the `/fraud` variant adds SIM-swap and call-forwarding signals. + +### Request Body + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `phoneNumber` | string | Yes | Number to look up, E.164 format | + +The upstream lookup result is returned as-is. If the carrier rejects the lookup, the 4xx is forwarded with its original status and you are **not charged**. --- ## POST /api/v1/phone/numbers/buy -Provisions a new phone number from Twilio's catalog in your chosen country, registers ownership against the calling wallet's address, and grants a 30-day lease. +Provisions a new phone number from the carrier catalog in your chosen country, registers ownership against the calling wallet's address, and grants a 30-day lease. The purchase happens **before** settlement, so a failed purchase is never charged. ### Request Body | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `country` | string | No | ISO 2-letter code. Default: `"US"`. See [Country availability](#country-availability) for which other countries are pre-enabled. | -| `area_code` | string | No | Preferred area code (US/CA: 3 digits). Best-effort — falls back to any in-country number if none available. | +| `areaCode` | string | No | Preferred area code (US/CA: 3 digits). Best-effort — falls back to any in-country number if none available. | ### Country availability @@ -56,77 +75,100 @@ Provisions a new phone number from Twilio's catalog in your chosen country, regi |---------|--------| | **US** | ✅ default — always works, all area codes | | **CA** | ✅ usually works without friction | -| All others (MX, BR, AR, CL, CO, EU, AU, JP, KR, IN, etc.) | ⚠️ requires Twilio **Regulatory Bundle** approval on our account first | +| All others (MX, BR, AR, CL, CO, EU, AU, JP, KR, IN, etc.) | ⚠️ requires carrier regulatory (KYC) approval on our account first | -Twilio requires per-country KYC documentation (address proof, business identity) before allowing number purchase in most non-US/CA countries. The bundle is account-level — once it's approved by Twilio for a country, all subsequent purchases work transparently. +Most non-US/CA countries require per-country KYC documentation (address proof, business identity) before a number can be purchased. The approval is account-level — once it's in place for a country, all subsequent purchases work transparently. -**If you need a number outside the US:** call the endpoint with your desired `country` code. If Twilio rejects the purchase with a regulatory error, our gateway forwards a 502 with the upstream message. Email `care@blockrun.ai` with the country you need — we'll provision the regulatory bundle (usually 1–5 business days for Twilio review) and let you know when the country is live. +**If you need a number outside the US:** call the endpoint with your desired `country` code. If the carrier rejects the purchase with a regulatory error, our gateway returns a `502 Purchase failed` with the upstream message in `details` (not charged). Email `care@blockrun.ai` with the country you need — we'll complete the regulatory registration (usually 1–5 business days for carrier review) and let you know when the country is live. -Pricing is flat **$5 / 30 days** regardless of country (we absorb the Twilio cost difference). Renewal also $5. +Pricing is flat **$5.001 / 30 days** regardless of country (we absorb the carrier cost difference). Renewal also $5.001. ### Response ```json { "phone_number": "+14155551234", - "country": "US", - "owner_wallet": "0xCC8c44AD3dc2A58D841c3EB26131E49b22665EF8", "expires_at": "2026-06-17T12:00:00Z", - "transaction": "0xabcd...1234" + "chain": "base", + "message": "Number provisioned for 30 days. Use it as 'from' in voice calls." } ``` -The returned `phone_number` can be used immediately as the `from` field on `POST /v1/voice/call`. After `expires_at`, the number is reclaimed and re-pooled (call `renew` before that to keep it). +The settlement transaction hash is in the `X-Payment-Receipt` response header (full receipt in `X-Payment-Response`). The returned `phone_number` can be used immediately as the `from` field on `POST /v1/voice/call`. After `expires_at`, the number is reclaimed and re-pooled (call `renew` before that to keep it). + +If no number is available in the requested country / area code, the response is `404 No numbers available` — not charged. --- ## POST /api/v1/phone/numbers/renew -Extends an active number's lease by 30 days. **Only the original owner wallet can renew.** +Extends an active number's lease by 30 days from its **current expiry**. **Only the original owner wallet can renew, and only before the number expires** — an expired number cannot be renewed; buy a new one. ### Request Body | Parameter | Type | Required | Description | |-----------|------|----------|-------------| -| `phone_number` | string | Yes | E.164 format (e.g. `"+14155551234"`) | +| `phoneNumber` | string | Yes | E.164 format (e.g. `"+14155551234"`) | ### Response ```json { "phone_number": "+14155551234", - "expires_at": "2026-07-17T12:00:00Z", - "transaction": "0xabcd...1234" + "expires_at": "2026-07-17T12:00:00Z" } ``` ### Errors -- `403 wrong_wallet` — the calling wallet doesn't own this number. Response body includes `actualOwner` and `your_active_numbers` (the numbers your wallet *does* own). +- `403 Forbidden` with `reason: "wrong_wallet"` — the calling wallet doesn't own this number. The body carries `payer_wallet` so you can see which wallet actually signed. +- `403 Forbidden` with `reason: "expired"` — the lease already lapsed; buy a new number. +- `403 Forbidden` with `reason: "not_found"` — the number is not in our registry. --- ## POST /api/v1/phone/numbers/list -Returns the list of active phone numbers owned by the calling wallet (identity revealed via the x402 payer field). +Returns the phone numbers owned by the calling wallet (identity revealed via the x402 payer field). Send an empty JSON object `{}` as the body. ### Response ```json { - "wallet": "0xCC8c44...665EF8", "numbers": [ { "phone_number": "+14155551234", - "country": "US", + "chain": "base", "expires_at": "2026-06-17T12:00:00Z", - "days_remaining": 28 + "active": true } ], - "transaction": "0xabcd...1234" + "count": 1 } ``` +Expired numbers still appear with `active: false` until they are reclaimed. + +--- + +## POST /api/v1/phone/numbers/release + +Releases a number you own back to the pool, immediately. Free — the request still goes through the x402 flow (a $0 authorization) so the gateway knows which wallet is asking. + +### Request Body + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `phoneNumber` | string | Yes | E.164 format | + +### Response + +```json +{ "released": true, "phone_number": "+14155551234" } +``` + +`403 Forbidden` (`reason: "wrong_wallet"`) if another wallet owns it; `404 Not Found` if it isn't in the registry. + --- ## POST /api/v1/voice/call @@ -137,42 +179,48 @@ Places an outbound AI conversation call. The AI voice agent dials the destinatio | Parameter | Type | Required | Description | |-----------|------|----------|-------------| -| `phone_number` | string | Yes | Destination in E.164 (e.g. `"+12133610872"`) | -| `task` | string | Yes | Free-text instructions for the AI — what to say, what to ask, when to hang up | -| `from` | string | No | Your wallet-owned BlockRun number (E.164). Omit if your wallet owns exactly one — it's used automatically. If you own multiple, this is required (otherwise `400 ambiguous_from`). If you own none, `403` with buy hint. | -| `voice` | string | No | Voice ID — see [Bland voice catalog](https://bland.ai/voices) | -| `language` | string | No | ISO 639-1 (`"en"`, `"es"`, `"zh"`, etc.) | -| `max_duration` | integer | No | Max call duration in minutes. Default: `5`. Cap: `30`. | -| `first_sentence` | string | No | Exact opener — overrides AI's default greeting | -| `wait_for_greeting` | boolean | No | Wait for callee to speak first before AI starts (useful for IVRs). Default: `false` | -| `interruption_threshold` | integer | No | Milliseconds of silence before AI can interrupt. Default: `100` | +| `to` | string | Yes | Destination in E.164 (e.g. `"+12133610872"`). Calls to emergency numbers (911, 112, 999, …) are refused with `403`. | +| `task` | string | Yes | Free-text instructions for the AI — what to say, what to ask, when to hang up. 10–4000 characters. | +| `from` | string | No | Your wallet-owned BlockRun number (E.164). Omit if your wallet owns exactly one active number — it's used automatically. If you own multiple, this is required (otherwise `400 ambiguous_from`). If you own none, `403 no_active_number` with a buy hint. | +| `voice` | string | No | Voice preset: `nat`, `josh`, `maya`, `june`, `paige`, `derek`, `florian` — or a voice ID from the voice catalog | +| `language` | string | No | Language tag for speech recognition + synthesis. Default: `"en-US"` | +| `max_duration` | integer | No | Max call duration in minutes. Default: `5`. Range: `1`–`30`. | +| `first_sentence` | string | No | Exact opener (≤ 500 chars) — overrides the AI's default greeting | +| `wait_for_greeting` | boolean | No | Wait for the callee to speak first before the AI starts (useful for IVRs) | +| `interruption_threshold` | integer | No | Milliseconds of silence before the AI can interrupt. Range: `50`–`500` | +| `model` | string | No | Voice model tier: `base`, `enhanced`, or `turbo` | +| `voicemail_action` | string | No | What to do if voicemail answers: `hangup`, `leave_message`, or `ignore` | +| `voicemail_message` | string | No | Message to leave (≤ 1000 chars). **Required** when `voicemail_action` is `leave_message`. | + +The body is validated strictly (unknown keys → `400`), and validation runs **before** the payment check — so the unpaid discovery request must already carry a valid `to` + `task`. ### Response ```json { "call_id": "01HXY9...", - "from": "+14155551234", - "to": "+12133610872", - "status": "started", - "expected_duration_minutes": 5, - "transaction": "0xabcd...1234" + "status": "queued", + "poll_url": "https://blockrun.ai/api/v1/voice/call/01HXY9...", + "message": "Call initiated. Poll poll_url for status, transcript, and recording." } ``` -The call is asynchronous — `status: "started"` means dialing is in progress. Poll `GET /v1/voice/call/{call_id}` to track progress. +The settlement transaction hash is in the `X-Payment-Receipt` header. The call is asynchronous — `status: "queued"` means dialing is about to start. Poll `poll_url` (`GET /v1/voice/call/{call_id}`) to track progress. ### Errors -- `403 no_owned_numbers` — your wallet hasn't bought a number yet. Buy one at `/v1/phone/numbers/buy`. -- `403 wrong_wallet` — you passed a `from` you don't own. Response includes `your_active_numbers`. -- `400 ambiguous_from` — wallet owns multiple numbers; specify `from` explicitly. +- `403 no_active_number` — your wallet hasn't bought a number yet (or every number it owns has expired). Buy one at `/v1/phone/numbers/buy`; the body includes `buy_endpoint`. +- `403 Forbidden` with `reason: "wrong_wallet"` / `"not_found"` — you passed a `from` you don't own. Response includes `your_active_numbers` (what your wallet *does* own). +- `403 Forbidden` with `reason: "expired"` — the `from` number's lease lapsed; renew or buy a new one. +- `403 Forbidden` — destination is an emergency-services number. +- `400 ambiguous_from` — wallet owns multiple active numbers; specify `from` explicitly. Response includes `your_active_numbers`. +- `502 Call initiation failed` — the voice provider refused to place the call. **Not charged**; upstream reason in `details`. --- ## GET /api/v1/voice/call/{call_id} -Poll for call status, transcript, and final outcome. **Free** — no payment required. +Poll for call status, transcript, and final outcome. **Free** — no payment required (no `X-Payment` header needed). ### Response @@ -196,7 +244,9 @@ Poll for call status, transcript, and final outcome. **Free** — no payment req } ``` -`status` values: `started`, `ringing`, `in-progress`, `completed`, `failed`, `no_answer`, `busy`. +The upstream call record is passed through as-is (field set may vary by call); BlockRun adds `ended_by` and `ended_by_reason` on top. `status` values: `queued`, `started`, `ringing`, `in-progress`, `completed`, `error`. Use `ended_by` rather than `status` to decide whether the call is finished. + +An unknown `call_id` returns `404 Call not found`. Requesting the literal `{call_id}` placeholder returns a 402-shaped discovery record (`price.free: true`) — substitute the real id. ### `ended_by` — why the call ended @@ -206,12 +256,12 @@ BlockRun synthesizes an `ended_by` field on the GET response so you can distingu |------------|---------|---------| | `USER` | **The callee hung up.** Most common outcome. After they answer your AI's question, humans typically end the call. The transcript will look "cut off" because the AI was mid-response — that's expected. Not a system error. | Yes (already settled) | | `ASSISTANT` | The AI ended the call gracefully — task complete, exit-instruction triggered, or `max_duration` approached. | Yes | -| `TIMEOUT` | Hit the `max_duration` cap. Pass a larger value next time if you expect long conversations. | Yes | +| `TIMEOUT` | Hit the `max_duration` cap (within ~5 s of it). Pass a larger value next time if you expect long conversations. | Yes | | `NO_ANSWER` | Line rang out, no human picked up. | Yes | | `BUSY` | Destination was busy. | Yes | -| `VOICEMAIL` | Voicemail picked up — the AI hung up. | Yes | -| `ERROR` | Bland/Twilio surfaced an explicit error (carrier, routing, etc.). | **No** — failed-call legs are not charged. | -| `IN_PROGRESS` | Call hasn't ended yet — keep polling. | Pending | +| `VOICEMAIL` | Voicemail picked up — the AI hung up (unless you set `voicemail_action`). | Yes | +| `ERROR` | The voice provider or carrier surfaced an explicit error after the call was accepted (routing, carrier, etc.). | The $0.541 settled when the call was accepted for dialing; there is no automatic refund. Email `care@blockrun.ai` with the `call_id` if an error leg should be credited. | +| `IN_PROGRESS` | Call hasn't ended yet — keep polling. | Already settled | If you keep seeing `ended_by: USER` and want longer conversations, **update your `task`** to give the AI a clearer exit instruction: @@ -234,12 +284,12 @@ import { LLMClient } from '@blockrun/llm'; const client = new LLMClient({ privateKey: process.env.BASE_CHAIN_WALLET_KEY }); // 1) One-time setup: buy a number -const number = await client.phoneBuy({ country: 'US', area_code: '415' }); +const number = await client.phoneBuy({ country: 'US', areaCode: '415' }); console.log('Got number:', number.phone_number); // 2) Place a call (uses the bought number automatically) const call = await client.voiceCall({ - phone_number: '+12133610872', + to: '+12133610872', task: 'Call Andy and ask him politely to take a break. Be friendly. Hang up after he confirms.', max_duration: 3, }); @@ -249,7 +299,7 @@ let status; do { await new Promise(r => setTimeout(r, 5000)); status = await client.voiceCallStatus(call.call_id); -} while (status.status === 'started' || status.status === 'in-progress'); +} while (status.ended_by === 'IN_PROGRESS'); console.log('Call ended:', status.summary); ``` @@ -262,11 +312,11 @@ from blockrun_llm import LLMClient client = LLMClient() # One-time: buy a number -number = client.phone_buy(country='US', area_code='415') +number = client.phone_buy(country='US', areaCode='415') # Place a call call = client.voice_call( - phone_number='+12133610872', + to='+12133610872', task='Confirm the 7pm reservation for two under Vicky.', max_duration=2, ) @@ -275,7 +325,7 @@ call = client.voice_call( import time while True: status = client.voice_call_status(call['call_id']) - if status['status'] in ('completed', 'failed', 'no_answer', 'busy'): + if status['ended_by'] != 'IN_PROGRESS': break time.sleep(5) @@ -285,10 +335,10 @@ print(status['summary']) :::tab{label="MCP"} ```text -Use blockrun_wallet to confirm I own a phone number, then use the BlockRun voice tool to call +14155551234 and tell them I'll be 10 minutes late. +Use blockrun_wallet to confirm I own a phone number, then use blockrun_phone to call +14155551234 and tell them I'll be 10 minutes late. ``` -(Voice MCP tools may ship under different names — check the BlockRun MCP tool list.) +The `blockrun_phone` MCP tool ships with [BlockRun MCP](../mcp/blockrun-mcp.md) and handles the x402 settlement automatically. ::: :::: @@ -313,24 +363,31 @@ The wallet-owned numbers can also receive inbound calls — feature is in develo Agent calls a family member on a schedule, has a short conversation, summarizes the call into a Slack message. +### 5. Pre-dial hygiene ($0.011) + +Run `/v1/phone/lookup` on a number before calling it: skip landlines that can't take an AI conversation, or use `/lookup/fraud` to flag recently SIM-swapped numbers before an agent reads out anything sensitive. + --- ## Pricing | Action | Price | Notes | |--------|-------|-------| -| Buy a number | $5.001 | 30-day lease, US or CA | -| Renew | $5.001 | +30 days | +| Carrier lookup | $0.011 | `/v1/phone/lookup`; `/lookup/fraud` is $0.051 | +| Buy a number | $5.001 | 30-day lease, US default (other countries on request) | +| Renew | $5.001 | +30 days from the current expiry | +| List numbers | $0.002 | `/v1/phone/numbers/list` | +| Release a number | Free | `/v1/phone/numbers/release` | | Place a call | $0.541 | Up to 30 min, default 5 min | | Poll status / fetch transcript | Free | `GET /v1/voice/call/{id}` | -All settled in USDC on Base (or Solana) via x402. +All prices include the flat $0.001 transaction fee and are settled in USDC on Base (or Solana) via x402. The exact amount is always in the `PAYMENT-REQUIRED` / `X-Payment-Required` header of the 402 (`accepts[0].amount`, in USDC micro-units — `541000` for a call). ### Why flat-rate for calls? Competitors (StablePhone et al) charge $0.54 *per minute* — a 30-min call becomes $16+. BlockRun charges $0.541 *per call* regardless of length (up to the 30-min cap). The math: - Short calls (<1 min, e.g. "is it open?"): we lose pennies, you win big. -- Long calls (15+ min): we eat the upstream Bland cost, but the call cost is bounded for you. +- Long calls (15+ min): we eat the upstream per-minute cost, but the call cost is bounded for you. This is intentional — autonomous agents need predictable per-action costs, not per-minute billing they have to model. @@ -348,11 +405,12 @@ The number is owned by **the wallet address that paid the buy x402**. Lose your ## Limitations -- **Number provisioning**: US is the default and always works. Other countries are supported via the `country` parameter but typically require Twilio Regulatory Bundle (KYC) approval first — see [Country availability](#country-availability). Request a country via `care@blockrun.ai`. -- **Call destinations**: any international destination Bland.ai supports — no geo restriction on the `phone_number` (callee) field, only on the `from` (caller-ID) number. +- **Number provisioning**: US is the default and always works. Other countries are supported via the `country` parameter but typically require carrier regulatory (KYC) approval first — see [Country availability](#country-availability). Request a country via `care@blockrun.ai`. +- **Call destinations**: any international destination the voice stack supports — no geo restriction on the `to` (callee) field, only on the `from` (caller-ID) number. Emergency-services numbers are always refused. - **Outbound only.** Inbound receive is on the roadmap. - **30-min hard cap per call.** Longer dial-and-stay use cases require a custom integration. -- **One Bland voice model per call.** Custom voice cloning requires upstream Bland account (we can't pass that through anonymously yet). +- **Preset voices only.** Custom voice cloning is not available through the anonymous x402 flow yet. +- **No SMS.** Text messaging is intentionally not exposed (it requires sender registration that can't be done per-wallet). --- @@ -360,13 +418,15 @@ The number is owned by **the wallet address that paid the buy x402**. Lose your | Code | Description | |------|-------------| -| 200 | Success | -| 400 | Bad request — missing E.164, invalid `from`, task too short, `ambiguous_from` | -| 402 | Payment required — sign and retry | -| 403 | `no_owned_numbers` / `wrong_wallet` — buy or use a different `from` | -| 404 | Unknown `call_id` on poll | -| 429 | Upstream rate limit (Bland or Twilio) — `Retry-After` header set, **not charged** | -| 502 | Upstream Bland/Twilio error — **not charged** | +| 200 | Success — `X-Payment-Receipt` carries the settlement tx hash | +| 400 | `Invalid request body` — missing/short `to` or `task`, unknown keys, `max_duration` out of range; also `ambiguous_from`. Validated before payment, **not charged** | +| 402 | Payment required — sign and retry. Verification failures carry a machine-readable `code`: `PAYMENT_INVALID`, `PAYMENT_UNFUNDED`, or `PAYMENT_BLOCKHASH_STALE` (plus `message` / `details`); re-using an authorization returns `code: "PAYMENT_REPLAY"` — sign a fresh one per request | +| 403 | `no_active_number` / `Forbidden` (`reason`: `wrong_wallet`, `expired`, `not_found`) — buy, renew, or use a different `from`; also emergency-number destinations | +| 404 | Unknown `call_id` on poll; unknown `/v1/phone/`; no numbers available for the requested country/area code; releasing a number not in the registry | +| 502 | Voice provider or carrier rejected the request (call initiation, number purchase, lookup 5xx) — **not charged** | +| 503 | Phone / voice integration not configured, or temporarily paused | + +Upstream 4xx on lookups is forwarded with its original status. Upstream calls time out after 30 seconds (15 seconds for status polls) and surface as `502` / `500`, never charged. --- @@ -375,11 +435,11 @@ The number is owned by **the wallet address that paid the buy x402**. Lose your ::::cards :::card{title="Text-to-Speech" href="text-to-speech.md" icon="Zap"} -Standalone ElevenLabs voice synthesis and sound effects, billed per character. +Standalone voice synthesis and sound effects, billed per character. ::: :::card{title="Rate Limits" href="rate-limits.md" icon="TrendingUp"} -How Bland/Twilio upstream throttling surfaces as 429 — and why you're not charged. +How upstream telephony throttling surfaces as 429 / 502 — and why you're not charged. ::: :::card{title="Error Handling" href="errors.md" icon="Code"} @@ -388,4 +448,4 @@ The gateway-wide error envelope across all paid endpoints. :::: -Also useful: [Phone & Voice service page](https://blockrun.ai/services/phone) · [Bland.ai (upstream voice)](https://bland.ai). +Also useful: [Phone & Voice service page](https://blockrun.ai/services/phone). diff --git a/docs/api-reference/zerox-swap.md b/docs/api-reference/zerox-swap.md index fbbe3ab..5b2746e 100644 --- a/docs/api-reference/zerox-swap.md +++ b/docs/api-reference/zerox-swap.md @@ -24,13 +24,13 @@ AI agents that swap tokens. Ask an agent to rebalance a wallet, take profit, dol | `/api/v1/zerox/gasless/price` | GET | 20 bps | Indicative gasless price | | `/api/v1/zerox/gasless/quote` | GET | 20 bps | Firm gasless quote — returns `trade.eip712` (+ optional `approval.eip712`) | | `/api/v1/zerox/gasless/submit` | POST | — | Submit signed gasless trade. 0x relayer pays gas; returns `tradeHash` | -| `/api/v1/zerox/gasless/status/{tradeHash}` | GET | — | Poll status of a submitted gasless trade | +| `/api/v1/zerox/gasless/status/{trade_hash}` | GET | — | Poll status of a submitted gasless trade. `{trade_hash}` must be a single path segment (no `/`) | | `/api/v1/zerox/gasless/approval-tokens` | GET | — | List tokens supporting Permit-based gasless approval | | `/api/v1/zerox/gasless/chains` | GET | — | List chains where Gasless V2 is supported | | `/api/v1/zerox/swap/chains` | GET | — | List chains where Swap V2 is supported | :::note{title="Affiliate injection"} -BlockRun force-sets `swapFeeRecipient`, `swapFeeBps=20`, and `swapFeeToken=` on every `price` and `quote` call (Swap and Gasless). Caller-supplied values for these three params are stripped before the request reaches 0x — this slot is BlockRun's. +BlockRun force-sets `swapFeeRecipient`, `swapFeeBps=20`, and `swapFeeToken=` on every `price` and `quote` call (Swap and Gasless). Caller-supplied values for these three params are stripped before the request reaches 0x — this slot is BlockRun's. Every successful passthrough response carries an `X-Affiliate: blockrun-base-treasury` header so you can see the injection happened. All other query parameters are forwarded unchanged; POST bodies are forwarded verbatim. ::: --- @@ -144,6 +144,8 @@ curl "https://blockrun.ai/api/v1/zerox/gasless/status/" ``` Status progresses `pending → submitted → confirmed`. The 0x relayer pays gas; the affiliate fee still applies and settles on-chain at fill time. + +Calling `gasless/status/` with the all-zeros placeholder hash that discovery surfaces advertise (`0x000…000`) is answered by the gateway with a descriptive stub instead of being forwarded — substitute a real `tradeHash`. ::: :::: @@ -242,15 +244,29 @@ const supported = chains.find(c => c.chainId === 8453); ## Errors -The gateway is a thin passthrough — 0x error bodies are returned verbatim with their original status code. Common cases: +The gateway is a thin passthrough — a 0x 4xx keeps its original status code, with the 0x body wrapped as `details`: + +```json +{ + "error": "Bad Request", + "message": "0x rejected the request. Check parameters.", + "status": 400, + "details": { ...0x error body... } +} +``` + +Common cases: | Code | Cause | |------|-------| -| 400 | Invalid params (bad token address, missing `taker`, etc.) — check 0x's error message | -| 404 | Unknown path — only the endpoints listed above are routed | -| 422 | Quote could not be served (no liquidity, sell amount too small, etc.) | -| 502 | 0x upstream error or timeout (we have a 30s timeout on every call) | -| 503 | `ZERO_EX_API_KEY` not configured on the gateway — should never happen in prod | +| 400 | Invalid params (bad token address, missing `taker`, etc.) — check `details` for 0x's error message | +| 404 | Unknown path — only the endpoints listed above are routed; the body lists every `available` route | +| 405 | Right path, wrong method (e.g. `POST /price`) — body names the `expected` method | +| 422 | Quote could not be served (no liquidity, sell amount too small, etc.) — forwarded from 0x | +| 502 | 0x upstream 5xx (`"Upstream provider error"`, upstream status in `status`) or no response within the 30 s timeout (`"0x did not respond in time."`) | +| 503 | 0x integration not configured on the gateway — should never happen in prod | + +These endpoints are free, so there is no 402 and no `X-Payment` header — a payment header sent by mistake is ignored. See [Error Handling](errors.md) for the gateway-wide error envelope. @@ -258,7 +274,7 @@ See [Error Handling](errors.md) for the gateway-wide error envelope. ## Internal-only endpoints -The two paths below are present on the gateway for **BlockRun reconciliation only**. They proxy 0x's Trade Analytics API under BlockRun's API key, and surface every trade routed through us — including other integrators' fields. Do not call these from end-user agents. +The two paths below are present on the gateway for **BlockRun reconciliation only**. They proxy 0x's Trade Analytics API under BlockRun's API key and surface every trade routed through us. They are deliberately left out of `/openapi.json` and the `/.well-known/x402` discovery manifest. Do not call these from end-user agents. | Endpoint | Purpose | |----------|---------| diff --git a/docs/frameworks/agentkit.md b/docs/frameworks/agentkit.md index 858e3ce..02b628f 100644 --- a/docs/frameworks/agentkit.md +++ b/docs/frameworks/agentkit.md @@ -5,7 +5,7 @@ description: Pair Coinbase AgentKit with BlockRun so your agents both hold on-ch # AgentKit Integration -Use BlockRun with Coinbase AgentKit for wallet-enabled AI agents — AgentKit holds assets and executes trades, BlockRun pays for the intelligence. +Use BlockRun with Coinbase AgentKit for wallet-enabled AI agents — AgentKit holds assets and executes on-chain actions, BlockRun pays for the intelligence. :::note{title="Community integration"} BlockRun's primary paths are [Franklin](../products/franklin.md), the [BlockRun MCP](../mcp/blockrun-mcp.md), and the [SDKs](../sdks/python.md). Framework integrations like this one are community-maintained. @@ -16,9 +16,9 @@ BlockRun's primary paths are [Franklin](../products/franklin.md), the [BlockRun ## Overview AgentKit provides: -- Wallet management (via Coinbase CDP) -- Transaction execution -- Asset management +- Wallet management (CDP server wallets, or a local key via `EthAccountWalletProvider`) +- Action providers (wallet, ERC-20, swaps, DeFi protocols, …) exposed as agent tools +- Framework extensions (`coinbase-agentkit-langchain`, …) BlockRun adds: - 84 AI model access @@ -31,21 +31,42 @@ BlockRun adds: :::step{title="Install both packages"} ```bash -pip install coinbase-agentkit blockrun-llm +pip install coinbase-agentkit blockrun-llm eth-account ``` + +`coinbase-agentkit` 0.7.x requires Python 3.10+. ::: -:::step{title="Initialize AgentKit and BlockRun"} +:::step{title="Initialize AgentKit and BlockRun on one key"} +The simplest setup shares a single Base private key: AgentKit signs transactions with it, BlockRun signs x402 payments with it. (Use a separate `BLOCKRUN_WALLET_KEY` if you want AI spend accounted apart from trading capital.) + ```python -from coinbase_agentkit import AgentKit +import os +from eth_account import Account +from coinbase_agentkit import ( + AgentKit, + AgentKitConfig, + EthAccountWalletProvider, + EthAccountWalletProviderConfig, +) from blockrun_llm import LLMClient -# Initialize AgentKit with CDP -agent_kit = AgentKit.from_cdp() +private_key = os.environ["BLOCKRUN_WALLET_KEY"] # 0x-prefixed -# Initialize BlockRun for AI -blockrun = LLMClient(private_key=agent_kit.wallet.private_key) +# AgentKit — local key on Base mainnet (chain 8453) +wallet_provider = EthAccountWalletProvider( + config=EthAccountWalletProviderConfig( + account=Account.from_key(private_key), + chain_id="8453", + ) +) +agent_kit = AgentKit(AgentKitConfig(wallet_provider=wallet_provider)) + +# BlockRun — same key pays for AI +blockrun = LLMClient(private_key=private_key) ``` + +Prefer CDP-managed keys? Construct `CdpEvmWalletProvider` with your CDP API credentials instead and keep BlockRun on its own local key — CDP server wallets do not expose a private key for the SDK to sign with. ::: :::: @@ -55,22 +76,23 @@ blockrun = LLMClient(private_key=agent_kit.wallet.private_key) ### AI-Powered Trading Agent ```python -from coinbase_agentkit import AgentKit -from blockrun_llm import LLMClient - -# Setup -agent_kit = AgentKit.from_cdp() -blockrun = LLMClient() +# Get AI analysis of what the wallet holds +address = wallet_provider.get_address() +balance = wallet_provider.get_balance() # native balance, in wei -# Get AI analysis analysis = blockrun.chat( "openai/gpt-5.4", - f"Analyze this portfolio: {agent_kit.get_balances()}" + f"Wallet {address} holds {balance} wei of ETH on Base. " + "Should it rotate into USDC? Answer BUY, SELL or HOLD with one reason." ) -# Execute based on analysis -if "buy" in analysis.lower(): - agent_kit.swap("USDC", "ETH", amount=100) +# Execute through AgentKit's action providers +actions = {a.name: a for a in agent_kit.get_actions()} +print(sorted(actions)) # e.g. WalletActionProvider_native_transfer, ERC20ActionProvider_transfer, ... + +if "SELL" in analysis.upper(): + # pick the swap/transfer action you have enabled and invoke it with its schema + ... ``` ### Multi-Model Decision Making @@ -88,6 +110,29 @@ final_decision = blockrun.chat( ) ``` +### AgentKit tools + BlockRun as the model (LangChain) + +AgentKit's LangChain extension turns every action provider into a tool. Run the [BlockRun LiteLLM sidecar](langchain.md) (Path 1 on the LangChain page) and point `ChatOpenAI` at it, and the whole ReAct loop — reasoning and on-chain execution — pays per request with no OpenAI key: + +```bash +pip install coinbase-agentkit-langchain langchain-openai langgraph 'blockrun-litellm[proxy]' +export BLOCKRUN_WALLET_KEY=0x... +blockrun-litellm-proxy --port 4001 & +``` + +```python +from coinbase_agentkit_langchain import get_langchain_tools +from langgraph.prebuilt import create_react_agent +from langchain_openai import ChatOpenAI + +tools = get_langchain_tools(agent_kit) +llm = ChatOpenAI(model="openai/gpt-5.4", base_url="http://127.0.0.1:4001/v1", api_key="dummy") + +agent = create_react_agent(llm, tools) +result = agent.invoke({"messages": [("human", "What is my wallet balance?")]}) +print(result["messages"][-1].content) +``` + ## Wallet Architecture ``` @@ -97,7 +142,7 @@ final_decision = blockrun.chat( │ AgentKit Wallet │ BlockRun Wallet │ │ (Trading/Assets) │ (AI Payments) │ │ │ │ -│ • Hold ETH, USDC │ • Pay for GPT-4o │ +│ • Hold ETH, USDC │ • Pay for GPT-5.4 │ │ • Execute swaps │ • Pay for Claude │ │ • Transfer assets │ • Pay for images │ └─────────────────────┴───────────────────────────┘ @@ -109,13 +154,22 @@ You can use the same wallet for both, or separate wallets for accounting. ```python import asyncio -from coinbase_agentkit import AgentKit +import os +from eth_account import Account +from coinbase_agentkit import ( + AgentKit, AgentKitConfig, EthAccountWalletProvider, EthAccountWalletProviderConfig, +) from blockrun_llm import LLMClient class TradingBot: def __init__(self): - self.agent_kit = AgentKit.from_cdp() - self.blockrun = LLMClient() + key = os.environ["BLOCKRUN_WALLET_KEY"] + self.wallet = EthAccountWalletProvider( + config=EthAccountWalletProviderConfig(account=Account.from_key(key), chain_id="8453") + ) + self.agent_kit = AgentKit(AgentKitConfig(wallet_provider=self.wallet)) + self.actions = {a.name: a for a in self.agent_kit.get_actions()} + self.blockrun = LLMClient(private_key=key) async def analyze_market(self, asset: str) -> dict: """Get AI analysis of an asset.""" @@ -131,17 +185,16 @@ class TradingBot: return {"analysis": response, "asset": asset} async def execute_trade(self, decision: dict): - """Execute trade based on AI decision.""" - if decision.get("action") == "buy": - self.agent_kit.swap("USDC", decision["asset"], decision["amount"]) - elif decision.get("action") == "sell": - self.agent_kit.swap(decision["asset"], "USDC", decision["amount"]) + """Execute trade based on AI decision via an AgentKit action.""" + action = self.actions.get(decision["action_name"]) + if action: + action.invoke(decision["args"]) async def run(self): """Main trading loop.""" while True: analysis = await self.analyze_market("ETH") - # Parse analysis and execute + # Parse analysis into {"action_name": ..., "args": {...}} and execute await asyncio.sleep(3600) # Check hourly # Run the bot @@ -156,24 +209,27 @@ AgentKit handles gas fees for transactions. BlockRun handles AI costs. ```python # Use cheap models for routine analysis routine_analysis = blockrun.chat( - "deepseek/deepseek-chat", # ~$0.14/M tokens + "deepseek/deepseek-chat", # $0.14/M input tokens "Quick market check..." ) # Use premium models for important decisions important_decision = blockrun.chat( - "openai/gpt-5.4", # ~$2.50/M tokens + "openai/gpt-5.4", # $2.50/M input tokens "Should I execute this $10k trade?" ) + +# Or let the bundled router decide per request +routed = blockrun.smart_chat("Quick market check...") ``` ## Security | Aspect | AgentKit | BlockRun | |--------|----------|----------| -| Key storage | CDP MPC or local | Local (~/.blockrun/) | -| Transactions | On-chain signed | EIP-712 signatures | -| Verification | Etherscan | Basescan | +| Key storage | CDP server wallet or local `eth_account` key | Local (`BLOCKRUN_WALLET_KEY` or `~/.blockrun/.session`) | +| Transactions | On-chain signed | EIP-712 x402 signatures | +| Verification | Basescan | Basescan | ## Links diff --git a/docs/frameworks/elizaos.md b/docs/frameworks/elizaos.md index 4848174..c656908 100644 --- a/docs/frameworks/elizaos.md +++ b/docs/frameworks/elizaos.md @@ -21,12 +21,16 @@ BlockRun's primary paths are [Franklin](../products/franklin.md), the [BlockRun ```bash npm install @blockrun/elizaos-plugin ``` + +`@blockrun/elizaos-plugin` 1.0.0 has a peer dependency on `@elizaos/core >= 1.0.0`. ::: :::step{title="Set your wallet key"} ```bash -export BLOCKRUN_WALLET_KEY=0x...your_private_key... +export BASE_CHAIN_WALLET_KEY=0x...your_private_key... ``` + +The plugin reads `BASE_CHAIN_WALLET_KEY` (from agent settings first, then the environment) — not `BLOCKRUN_WALLET_KEY`. ::: :::step{title="Register the plugin"} @@ -39,49 +43,64 @@ export const character = { name: 'MyAgent', // ...other character config plugins: [blockrunPlugin], + settings: { + BASE_CHAIN_WALLET_KEY: process.env.BASE_CHAIN_WALLET_KEY, + BLOCKRUN_DEFAULT_MODEL: 'openai/gpt-5.4', // optional; default openai/gpt-4o-mini + }, }; ``` ::: :::: -Once registered, the agent uses BlockRun models through ElizaOS's normal model calls — no per-provider API keys, paid per request from the wallet key you set. The plugin's actions and providers are the source of truth; see the [repo](https://github.com/BlockRunAI/elizaos-plugin-blockrun) for the current action/provider list. +Once registered, the agent pays per request from the wallet key you set — no per-provider API keys. The plugin registers one action and one provider (the [repo](https://github.com/BlockRunAI/elizaos-plugin-blockrun) is the source of truth): + +| Component | Name | What it does | +|-----------|------|--------------| +| Action | `BLOCKRUN_CHAT` | Makes a pay-per-request chat call through the BlockRun gateway; the model comes from the action's `options.model`, then `BLOCKRUN_DEFAULT_MODEL`, then `openai/gpt-4o-mini` | +| Provider | `BLOCKRUN_WALLET` | Injects the wallet address and USDC balance on Base into agent context, so the agent knows its payment capacity | + +### Configuration + +| Setting / env var | Required | Description | +|-------------------|----------|-------------| +| `BASE_CHAIN_WALLET_KEY` | Yes | Base wallet private key that signs x402 payments | +| `BLOCKRUN_API_URL` | No | Gateway URL (default `https://blockrun.ai/api`) | +| `BLOCKRUN_DEFAULT_MODEL` | No | Model for `BLOCKRUN_CHAT` when none is passed (default `openai/gpt-4o-mini`) | ## Usage -With `blockrunPlugin` registered, your agent reaches BlockRun models through ElizaOS's normal model interface — chat, plus image/video/music when you call those actions. Model selection follows your ElizaOS character/runtime settings; BlockRun model ids look like `openai/gpt-5.5`, `anthropic/claude-opus-5`, `deepseek/deepseek-chat`. The plugin's exact actions and providers live in the [plugin repo](https://github.com/BlockRunAI/elizaos-plugin-blockrun) — treat it as the source of truth for action names and options. +With `blockrunPlugin` registered, `BLOCKRUN_CHAT` triggers when the agent needs to query a model; payments are signed locally (EIP-712) and the request is retried with the payment proof automatically. BlockRun model ids look like `openai/gpt-5.5`, `anthropic/claude-opus-5`, `deepseek/deepseek-chat`. -For full media/data control (image, video, search, RPC, etc.) outside the ElizaOS model flow, call the [TypeScript SDK](../sdks/typescript.md) clients directly from your actions — same wallet, same x402 settlement. +The plugin is chat-only and Base-only. For image, video, music, search, RPC and Solana payments, call the [TypeScript SDK](../sdks/typescript.md) clients directly from your own actions — same wallet, same x402 settlement. ## Available Models -All BlockRun models are available: +All BlockRun chat models are available. A sample of what the live catalog lists today: | Provider | Models | |----------|--------| -| OpenAI | gpt-5.4, gpt-5.2, o1, o1-mini | +| OpenAI | gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.2, o3, o1 | | Anthropic | claude-fable-5, claude-opus-5, claude-opus-4.8, claude-sonnet-5, claude-sonnet-4.6, claude-haiku-4.5 | -| Google | gemini-3.1-pro, gemini-3-flash-preview, gemini-2.5-flash-lite | -| DeepSeek | deepseek-chat, deepseek-reasoner | -| xAI | grok-4.3, grok-4.5 | -| Meta | llama-3.3-70b, llama-3.1-405b | +| Google | gemini-3.1-pro, gemini-3.5-flash, gemini-3-flash-preview, gemini-2.5-flash-lite | +| DeepSeek | deepseek-v4-pro, deepseek-chat, deepseek-reasoner | +| xAI | grok-4.3, grok-4.5, grok-build-0.1 | +| NVIDIA (free) | step-3.7-flash, mistral-nemotron, nemotron-nano-9b-v2 | -See [Models Reference](../api-reference/models.md) for full list. +See [Models Reference](../api-reference/models.md) for the full list, or `curl https://blockrun.ai/api/v1/models`. ## Pricing -Same as BlockRun: provider cost + 5%. - -Your agent pays per request via x402. No API keys needed for individual providers. +Same as the BlockRun API — the plugin adds no markup. Your agent pays per request via x402; no API keys needed for individual providers. See [Intelligence Pricing](../products/intelligence/pricing.md). ## Wallet & budgets -The plugin pays from one wallet — set `BLOCKRUN_WALLET_KEY`, or let BlockRun create and use a local key at `~/.blockrun/.session`. Fund it with USDC on Base (or Solana) and manage spend caps the same way as any BlockRun client. +The plugin pays from one Base wallet — set `BASE_CHAIN_WALLET_KEY` in agent settings or the environment. Fund it with USDC on Base and manage spend caps the same way as any BlockRun client; the `BLOCKRUN_WALLET` provider surfaces the live balance to the agent. ```bash -export BLOCKRUN_WALLET_KEY=0x... # the wallet your agent spends from +export BASE_CHAIN_WALLET_KEY=0x... # the wallet your agent spends from ``` See [Wallet Setup](../getting-started/wallet-setup.md) for funding, chain switching, and per-agent budget delegation. diff --git a/docs/frameworks/goat.md b/docs/frameworks/goat.md index 8d2e914..e5d97cb 100644 --- a/docs/frameworks/goat.md +++ b/docs/frameworks/goat.md @@ -23,29 +23,29 @@ npm install @goat-sdk/core @blockrun/llm :::step{title="Set your wallet key"} ```bash -export BLOCKRUN_WALLET_KEY=0x... # Your Base wallet private key +export BASE_CHAIN_WALLET_KEY=0x... # Your Base wallet private key ``` ::: :::step{title="Use BlockRun for AI + GOAT for on-chain execution"} ```typescript import { getOnChainTools } from '@goat-sdk/adapter-vercel-ai'; -import { BlockRunLLM } from '@blockrun/llm'; +import { LLMClient } from '@blockrun/llm'; -const blockrun = new BlockRunLLM({ - privateKey: process.env.BLOCKRUN_WALLET_KEY, +const blockrun = new LLMClient({ + privateKey: process.env.BASE_CHAIN_WALLET_KEY as `0x${string}`, }); -const response = await blockrun.chat({ - model: 'anthropic/claude-sonnet-4.6', - messages: [ - { role: 'user', content: 'Analyze ETH/USDC liquidity on Uniswap V3 on Base' } - ], - max_tokens: 1024, -}); +const response = await blockrun.chatCompletion( + 'anthropic/claude-sonnet-4.6', + [{ role: 'user', content: 'Analyze ETH/USDC liquidity on Uniswap V3 on Base' }], + { maxTokens: 1024 }, +); console.log(response.choices[0].message.content); ``` + +`chatCompletion(model, messages, options?)` takes camelCase options (`maxTokens`, `temperature`, `tools`, `toolChoice`, …) and returns an OpenAI-shaped response. Prefer the exact OpenAI shape? `import { OpenAI } from '@blockrun/llm'` gives you `chat.completions.create({ model, messages, max_tokens })`. ::: :::: @@ -74,18 +74,15 @@ console.log(response.choices[0].message.content); ```typescript // Analyze yield opportunities across chains -const yieldAnalysis = await agent.blockrun.chat({ - model: 'openai/gpt-5.4', - messages: [{ - role: 'user', - content: ` - Compare yield opportunities: - - Aave on Ethereum: ${await agent.aave.getAPY('USDC')} - - Uniswap LP on Base: ${await agent.uniswap.getLPYield('USDC/ETH')} - Which offers better risk-adjusted returns? - ` - }] -}); +const yieldAnalysis = await agent.blockrun.chat( + 'openai/gpt-5.4', + ` + Compare yield opportunities: + - Aave on Ethereum: ${await agent.aave.getAPY('USDC')} + - Uniswap LP on Base: ${await agent.uniswap.getLPYield('USDC/ETH')} + Which offers better risk-adjusted returns? + `, +); ``` ### Cross-Chain Arbitrage @@ -94,20 +91,39 @@ const yieldAnalysis = await agent.blockrun.chat({ // Spot and execute arbitrage with AI verification const arbitrageOpp = await agent.findArbitrage('ETH'); -// AI validates the opportunity -const validation = await agent.blockrun.chat({ - model: 'anthropic/claude-sonnet-4.6', - messages: [{ - role: 'user', - content: `Validate this arbitrage: ${JSON.stringify(arbitrageOpp)}` - }] -}); +// AI validates the opportunity — chat() returns the reply text directly +const validation = await agent.blockrun.chat( + 'anthropic/claude-sonnet-4.6', + `Validate this arbitrage: ${JSON.stringify(arbitrageOpp)}`, +); if (validation.includes('valid')) { await agent.executeArbitrage(arbitrageOpp); } ``` +### Let the model drive GOAT's tools + +GOAT's Vercel AI adapter returns AI SDK tools; BlockRun's gateway is OpenAI-compatible, so any AI SDK provider can call it once payments are signed. The [BlockRun LiteLLM sidecar](https://github.com/BlockRunAI/blockrun-litellm) does that signing on `http://127.0.0.1:4001/v1` and forwards `tools` / `tool_choice` verbatim, which closes the loop: + +```typescript +import { generateText, stepCountIs } from 'ai'; +import { createOpenAI } from '@ai-sdk/openai'; +import { getOnChainTools } from '@goat-sdk/adapter-vercel-ai'; +import { viem } from '@goat-sdk/wallet-viem'; + +const blockrun = createOpenAI({ baseURL: 'http://127.0.0.1:4001/v1', apiKey: 'dummy' }); + +const tools = await getOnChainTools({ wallet: viem(walletClient) }); + +const { text } = await generateText({ + model: blockrun('openai/gpt-5.4'), + tools, + stopWhen: stepCountIs(5), // AI SDK v5+; `maxSteps: 5` on v4 + prompt: 'Check my USDC balance on Base and tell me if I can afford a $50 swap.', +}); +``` + ## Why BlockRun + GOAT? | GOAT Provides | BlockRun Adds | @@ -122,23 +138,27 @@ if (validation.includes('valid')) { Use BlockRun's TypeScript SDK alongside GOAT until the official plugin is released: ```typescript -import { BlockRunLLM } from '@blockrun/llm'; +import { LLMClient } from '@blockrun/llm'; -const blockrun = new BlockRunLLM({ - privateKey: process.env.BLOCKRUN_WALLET_KEY, +const blockrun = new LLMClient({ + privateKey: process.env.BASE_CHAIN_WALLET_KEY as `0x${string}`, }); async function analyzeAndAct() { - const analysis = await blockrun.chat({ - model: 'openai/gpt-5.4', - messages: [{ + const analysis = await blockrun.chatCompletion( + 'openai/gpt-5.4', + [{ role: 'user', content: 'Compare Aave USDC yield on Ethereum vs Base. Which is better risk-adjusted?' }], - max_tokens: 2048, - }); + { maxTokens: 2048 }, + ); console.log('AI Analysis:', analysis.choices[0].message.content); + + // Or let the bundled router pick the cheapest capable model + const routed = await blockrun.smartChat('Summarize the last 24h of Base DEX volume'); + console.log(routed.model, routed.response); } analyzeAndAct(); @@ -156,7 +176,7 @@ analyzeAndAct(); ::::cards :::card{title="BlockRun TypeScript SDK" href="../sdks/typescript.md" icon="Code"} -Full reference for `BlockRunLLM` and the payment flow it wraps. +Full reference for `LLMClient` and the payment flow it wraps. ::: :::card{title="Agent Developer Guide" href="../getting-started/agent-developers.md" icon="Brain"} diff --git a/docs/frameworks/langchain.md b/docs/frameworks/langchain.md index 715be81..29b2013 100644 --- a/docs/frameworks/langchain.md +++ b/docs/frameworks/langchain.md @@ -5,28 +5,69 @@ description: Wrap BlockRun in a custom LangChain LLM class that handles x402 pay # LangChain Integration -Use BlockRun as an LLM provider in LangChain — a custom LLM class handles x402 payments automatically across chains, agents, and RAG. +Use BlockRun as an LLM provider in LangChain — either through the OpenAI-compatible [LiteLLM adapter](https://github.com/BlockRunAI/blockrun-litellm) (full chat model: tools, streaming, async) or a custom LLM class over the Python SDK. Both handle x402 payments automatically across chains, agents, and RAG. -[LangChain](https://github.com/langchain-ai/langchain) is the most popular framework for building LLM applications. BlockRun provides a custom LLM class that handles x402 payments automatically. +[LangChain](https://github.com/langchain-ai/langchain) is the most popular framework for building LLM applications. BlockRun's `/v1/chat/completions` is already OpenAI-compatible at the protocol level; the only thing that differs is authentication — a per-request wallet signature instead of a Bearer key — and the two paths below bridge exactly that gap. -:::note{title="Community integration — planned"} -No official LangChain package yet; use the custom provider below or the BlockRun [SDK](../sdks/python.md) directly. BlockRun's primary paths are [Franklin](../products/franklin.md), the [MCP](../mcp/blockrun-mcp.md), and the SDKs. +:::note{title="Community integration"} +No official `langchain-blockrun` package yet; use the two paths below or the BlockRun [SDK](../sdks/python.md) directly. BlockRun's primary paths are [Franklin](../products/franklin.md), the [MCP](../mcp/blockrun-mcp.md), and the SDKs. ::: -## Setup +## Path 1 — `ChatOpenAI` via the BlockRun LiteLLM sidecar (recommended) + +[`blockrun-litellm`](https://pypi.org/project/blockrun-litellm/) (v0.9.1) ships a local OpenAI-compatible proxy that signs x402 payments with your wallet. Any LangChain chat model that speaks the OpenAI protocol — `ChatOpenAI` — then works unchanged, including tool calling, streaming and async, which the string-in/string-out custom class in Path 2 cannot offer. + +::::steps + +:::step{title="Install"} +```bash +pip install 'blockrun-litellm[proxy]' langchain langchain-openai +``` +::: + +:::step{title="Start the sidecar"} +```bash +export BLOCKRUN_WALLET_KEY=0x... # Base wallet; never leaves your machine +blockrun-litellm-proxy --port 4001 # → http://127.0.0.1:4001/v1 +``` + +Add `--api-url https://sol.blockrun.ai/api` with a `SOLANA_WALLET_KEY` to pay on Solana. Keep the bind on loopback unless you also set `BLOCKRUN_PROXY_TOKEN`. +::: + +:::step{title="Point ChatOpenAI at it"} +```python +from langchain_openai import ChatOpenAI + +llm = ChatOpenAI( + model="openai/gpt-5.4", # any BlockRun chat model id + base_url="http://127.0.0.1:4001/v1", + api_key="dummy", # ignored unless BLOCKRUN_PROXY_TOKEN is set +) + +print(llm.invoke("Explain x402 in one sentence").content) +``` +::: + +:::: + +Prefer to stay in-process? `pip install blockrun-litellm langchain-litellm`, call `from blockrun_litellm import register; register()` once, and use `ChatLiteLLM(model="blockrun/openai/gpt-5.4")`. + +## Path 2 — Custom LLM class over the Python SDK + +No sidecar, no LiteLLM: a minimal `LLM` subclass over `blockrun_llm.LLMClient`. Text in, text out — fine for chains and RAG, not for tool-calling agents. ::::steps :::step{title="Install LangChain and the BlockRun SDK"} ```bash -pip install langchain blockrun-llm +pip install langchain langchain-core blockrun-llm ``` ::: :::step{title="Define a custom LLM provider"} ```python from typing import Any, List, Optional -from langchain.llms.base import LLM +from langchain_core.language_models.llms import LLM from blockrun_llm import LLMClient class BlockRunLLM(LLM): @@ -38,7 +79,7 @@ class BlockRunLLM(LLM): def __init__(self, model: str = "openai/gpt-5.4", **kwargs): super().__init__(**kwargs) self.model = model - self.client = LLMClient() + self.client = LLMClient() # BLOCKRUN_WALLET_KEY or ~/.blockrun/.session @property def _llm_type(self) -> str: @@ -50,12 +91,13 @@ class BlockRunLLM(LLM): stop: Optional[List[str]] = None, **kwargs ) -> str: - response = self.client.chat(self.model, prompt) - return response + return self.client.chat(self.model, prompt, stop=stop) # Usage llm = BlockRunLLM(model="openai/gpt-5.4") ``` + +`langchain_core.language_models.llms.LLM` is the import that works on both LangChain 0.3 and 1.x; the old `langchain.llms.base` path was removed in 1.0. ::: :::: @@ -64,6 +106,8 @@ llm = BlockRunLLM(model="openai/gpt-5.4") ### Basic Chain (LCEL) +Works with either path — swap `llm` for the `ChatOpenAI` instance above if you are using the sidecar. + ```python from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser @@ -79,35 +123,33 @@ print(result) ### Agent with Tools +Tool calling needs a chat model, so this uses Path 1. `create_agent` is LangChain 1.x; the sidecar forwards `tools` / `tool_choice` verbatim to the gateway. + ```python -from langchain.agents import create_tool_calling_agent, AgentExecutor -from langchain_core.prompts import ChatPromptTemplate +from langchain.agents import create_agent +from langchain_openai import ChatOpenAI from langchain_community.tools import DuckDuckGoSearchRun -llm = BlockRunLLM(model="openai/gpt-5.4") - -tools = [DuckDuckGoSearchRun()] +llm = ChatOpenAI(model="openai/gpt-5.4", base_url="http://127.0.0.1:4001/v1", api_key="dummy") -prompt = ChatPromptTemplate.from_messages([ - ("system", "You are a helpful assistant."), - ("human", "{input}"), -]) - -agent = create_tool_calling_agent(llm, tools, prompt) -executor = AgentExecutor(agent=agent, tools=tools, verbose=True) +agent = create_agent( + model=llm, + tools=[DuckDuckGoSearchRun()], + system_prompt="You are a helpful assistant.", +) -result = executor.invoke({"input": "What's the current price of ETH?"}) -print(result["output"]) +result = agent.invoke({"messages": [("human", "What's the current price of ETH?")]}) +print(result["messages"][-1].content) ``` ### RAG Pipeline ```python -from langchain_community.vectorstores import Chroma -from langchain_community.embeddings import HuggingFaceEmbeddings -from langchain.chains import create_retrieval_chain -from langchain.chains.combine_documents import create_stuff_documents_chain +from langchain_chroma import Chroma +from langchain_huggingface import HuggingFaceEmbeddings from langchain_core.prompts import ChatPromptTemplate +from langchain_core.output_parsers import StrOutputParser +from langchain_core.runnables import RunnablePassthrough llm = BlockRunLLM(model="anthropic/claude-sonnet-4.6") embeddings = HuggingFaceEmbeddings() @@ -118,11 +160,18 @@ retriever = vectorstore.as_retriever() prompt = ChatPromptTemplate.from_template( "Answer based on context:\n{context}\n\nQuestion: {input}" ) -combine_chain = create_stuff_documents_chain(llm, prompt) -rag_chain = create_retrieval_chain(retriever, combine_chain) -result = rag_chain.invoke({"input": "How does x402 payment work?"}) -print(result["answer"]) +def format_docs(docs): + return "\n\n".join(d.page_content for d in docs) + +rag_chain = ( + {"context": retriever | format_docs, "input": RunnablePassthrough()} + | prompt + | llm + | StrOutputParser() +) + +print(rag_chain.invoke("How does x402 payment work?")) ``` ## Multi-Model Chains @@ -152,37 +201,48 @@ analysis = analysis_chain.invoke({"text": summary}) # Use model routing based on task complexity def get_model_for_task(task_type: str) -> str: if task_type == "simple": - return "deepseek/deepseek-chat" # $0.14/M tokens + return "deepseek/deepseek-chat" # $0.14/M input tokens elif task_type == "complex": - return "openai/gpt-5.4" # $2.50/M tokens + return "openai/gpt-5.4" # $2.50/M input tokens elif task_type == "reasoning": - return "openai/o1" # $15/M tokens + return "openai/o1" # $15/M input tokens return "openai/gpt-5.4" # Dynamic model selection llm = BlockRunLLM(model=get_model_for_task("simple")) ``` +Or let the SDK decide: the Python SDK's `smart_chat()` (bundled Router Core V3) picks the cheapest capable model per request — see [Smart Routing](../sdks/python.md#smart-routing) — and the sidecar accepts `blockrun/auto`, `blockrun/eco` and `blockrun/premium` as model ids. + ## Async Support +The Python SDK ships an `AsyncLLMClient` with the same `chat()` signature: + ```python import asyncio +from blockrun_llm import AsyncLLMClient class AsyncBlockRunLLM(BlockRunLLM): - async def _acall(self, prompt: str, **kwargs) -> str: - # BlockRun SDK supports async - response = await self.client.achat(self.model, prompt) - return response + aclient: Any = None + + def __init__(self, model: str = "openai/gpt-5.4", **kwargs): + super().__init__(model=model, **kwargs) + self.aclient = AsyncLLMClient() + + async def _acall(self, prompt: str, stop: Optional[List[str]] = None, **kwargs) -> str: + return await self.aclient.chat(self.model, prompt, stop=stop) # Usage async def main(): llm = AsyncBlockRunLLM() - result = await llm.agenerate(["Hello!"]) + result = await llm.ainvoke("Hello!") print(result) asyncio.run(main()) ``` +With Path 1, `ChatOpenAI` already supports `ainvoke` / `astream`. + ## Wallet Setup BlockRun LLM uses your configured wallet: @@ -197,26 +257,27 @@ Or create programmatically: from blockrun_llm import LLMClient client = LLMClient() # Auto-creates wallet -print(f"Fund this address: {client.get_address()}") +print(f"Fund this address: {client.get_wallet_address()}") ``` See [Wallet Setup](../getting-started/wallet-setup.md). ## Pricing -Same as BlockRun API: provider cost + 5%. +Same as the BlockRun API — no markup on top of the gateway price. Live prices come from `https://blockrun.ai/api/v1/models`; a few examples (per 1M tokens, input/output): | Model | Cost | |-------|------| -| GPT-4o | $2.63/$10.50 per 1M tokens | -| DeepSeek V3 | $0.15/$0.29 per 1M tokens | -| Claude Sonnet | $3.15/$15.75 per 1M tokens | +| `openai/gpt-4o` | $2.50 / $10.00 | +| `deepseek/deepseek-chat` | $0.14 / $0.28 | +| `anthropic/claude-sonnet-4.6` | $3.00 / $15.00 | See [Intelligence Pricing](../products/intelligence/pricing.md). ## Links - [LangChain Documentation](https://python.langchain.com) +- [blockrun-litellm](https://github.com/BlockRunAI/blockrun-litellm) — the LiteLLM adapter and sidecar - [BlockRun Python SDK](../sdks/python.md) - [Agent Developer Guide](../getting-started/agent-developers.md) diff --git a/docs/getting-started/agent-developers.md b/docs/getting-started/agent-developers.md index d463861..e4e3d0f 100644 --- a/docs/getting-started/agent-developers.md +++ b/docs/getting-started/agent-developers.md @@ -45,13 +45,13 @@ Then set up a wallet and make your first call: :::step{title="Set up the wallet"} ```python -from blockrun_llm import LLMClient +from blockrun_llm import setup_agent_wallet -client = LLMClient() # Creates wallet if none exists -print(f"Wallet address: {client.get_address()}") +client = setup_agent_wallet() # Creates ~/.blockrun/.session if none exists, prints a funding QR +print(f"Wallet address: {client.get_wallet_address()}") ``` -Fund this address with USDC on Base network. +Fund this address with USDC on Base network. (Solana: `pip install "blockrun-llm[solana]"` and `setup_agent_solana_wallet()`.) ::: :::step{title="Use any model"} @@ -138,16 +138,28 @@ def smart_route(task: str, importance: str) -> str: return client.chat(model, task) ``` -### Session Budgets +Or let the SDK's built-in router classify each request locally and pick the cheapest capable model, with a fallback chain walked on 429/5xx: -Limit spending per agent session: +```python +result = client.smart_chat(task) # routing_profile: "auto" | "eco" | "premium" | "free" +print(result.model, result.routing.tier, result.routing.savings) + +# Same thing from any chat call — one string change +client.chat("blockrun/auto", task) +``` + +### Spend Limits + +Cap what an agent session can sign for. A quote above the ceiling is refused *before* payment, so nothing settles: ```python -client = LLMClient(session_budget=10.00) # Max $10 +from blockrun_llm import LLMClient, SpendLimitError + +client = LLMClient(max_cost_per_call=0.50, max_session_cost=10.00) # or BLOCKRUN_MAX_* env vars try: response = client.chat("openai/o1", expensive_prompt) -except InsufficientBudgetError: +except SpendLimitError: # Fallback to cheaper model response = client.chat("deepseek/deepseek-chat", expensive_prompt) ``` @@ -158,13 +170,15 @@ For high-throughput agents: ```python import asyncio +from blockrun_llm import AsyncLLMClient async def process_batch(items: list) -> list: - tasks = [ - client.achat("deepseek/deepseek-chat", f"Process: {item}") - for item in items - ] - return await asyncio.gather(*tasks) + async with AsyncLLMClient() as client: + tasks = [ + client.chat("deepseek/deepseek-chat", f"Process: {item}") + for item in items + ] + return await asyncio.gather(*tasks) results = asyncio.run(process_batch(my_items)) ``` @@ -194,19 +208,15 @@ Full list: [Models Reference](../api-reference/models.md) ## Pricing -Pay only for what you use: - -``` -Your cost = Provider cost + 5% -``` +Pay only for what you use, at the per-token price in the live catalog (`client.list_models()` or `GET https://blockrun.ai/api/v1/models`). Example costs per 1M tokens: | Model | Input | Output | |-------|-------|--------| -| DeepSeek Chat | $0.29 | $0.44 | -| GPT-5.4 | $2.63 | $15.75 | -| Claude Opus 4.6 | $5.25 | $26.25 | +| `deepseek/deepseek-chat` | $0.14 | $0.28 | +| `openai/gpt-5.4` | $2.50 | $15.00 | +| `anthropic/claude-opus-5` | $5.00 | $25.00 | Full pricing: [Intelligence Pricing](../products/intelligence/pricing.md) @@ -221,25 +231,28 @@ export BLOCKRUN_WALLET_KEY=0x... ### Programmatic ```python -# Create new -client = LLMClient() # Auto-generates if none exists +from blockrun_llm import LLMClient, setup_agent_wallet + +# Create new (or load the existing ~/.blockrun/.session) +client = setup_agent_wallet() -# Use existing -client = LLMClient(private_key="0x...") +# Use existing key +client = LLMClient(private_key="0x...") # LLMClient() alone raises ValueError if no wallet is configured # Check balance balance = client.get_balance() print(f"${balance} USDC") # Get address to fund -print(client.get_address()) +print(client.get_wallet_address()) ``` ### Security -- Private key stored locally (`~/.blockrun/wallet.json`) +- Private key stored locally (`~/.blockrun/.session`, mode 0600; Solana: `~/.blockrun/.solana-session`) - Only signatures sent to API - All payments verifiable on [Basescan](https://basescan.org) +- A failed paid request is never retried with a second payment — the SDK refuses to advance its fallback chain once a signature has gone out :::warning Never commit `BLOCKRUN_WALLET_KEY` to git or share your private key. Use a dedicated agent wallet funded with only what the session needs. @@ -250,19 +263,22 @@ Never commit `BLOCKRUN_WALLET_KEY` to git or share your private key. Use a dedic ```python from blockrun_llm import ( LLMClient, - InsufficientBalanceError, - ModelNotFoundError, - RateLimitError + PaymentError, + SpendLimitError, + APIError, ) try: response = client.chat(model, prompt) -except InsufficientBalanceError: +except SpendLimitError: + print("Over the agent's spend limit — nothing was charged") +except PaymentError: print("Need to fund wallet") -except ModelNotFoundError: - print("Invalid model ID") -except RateLimitError: - print("Too many requests, backing off") +except APIError as e: + if e.status_code == 429: + print("Too many requests, backing off") + else: + print(f"API error {e.status_code}: {e}") # e.g. unknown model id ``` ## Best Practices diff --git a/docs/getting-started/claude-code.md b/docs/getting-started/claude-code.md index 47117dc..d001fec 100644 --- a/docs/getting-started/claude-code.md +++ b/docs/getting-started/claude-code.md @@ -8,7 +8,7 @@ description: Install BlockRun MCP in Claude Code, fund a wallet with USDC on Bas Get started with BlockRun in 60 seconds. Give your Claude agent superpowers. :::tip{title="In a hurry?"} -The whole setup is three steps below — install, fund, go. No API keys, no subscription. +The whole setup is three steps below — install, fund, go. No API keys, no subscription, no signup: the wallet is created for you on first run. ::: ## Install, fund, and go @@ -20,19 +20,21 @@ The whole setup is three steps below — install, fund, go. No API keys, no subs claude mcp add blockrun -s user -- npx -y @blockrun/mcp@latest ``` -That's it. The MCP server is now available to Claude Code. +That's it. Restart Claude Code and the MCP server is available. Needs Node.js ≥ 20.19. + +Homebrew or nvm user and the server won't connect? Pass your shell PATH through: `claude mcp add blockrun -s user -e PATH="$PATH" -- npx -y @blockrun/mcp@latest`. ::: -:::step{title="Set up your wallet"} -In Claude Code, say: +:::step{title="See your wallet"} +A wallet was created automatically at `~/.blockrun/.session` when the server first started. In Claude Code, say: ``` -blockrun setup +Set up my BlockRun wallet ``` -Claude will: -1. Generate a new wallet (or use an existing one) -2. Show you your wallet address +Claude calls `blockrun_wallet action:"setup"` and will: +1. Show you your wallet address (creating one if it's somehow missing) +2. Print a funding QR code 3. Explain how to fund it ::: @@ -43,9 +45,10 @@ Send USDC to your wallet address on **Base network**: - **Minimum:** $1 to get started Get USDC on Base: -- [Coinbase](https://coinbase.com) - withdraw directly to Base +- [Coinbase](https://coinbase.com) - Send → USDC → Base network → paste address - [Base Bridge](https://bridge.base.org) - bridge from Ethereum - [Uniswap](https://app.uniswap.org) - swap on Base +- **Card:** ask Claude to run `blockrun_wallet action:"deposit"` — it opens a one-time Coinbase Onramp link that settles into your own wallet ::: :::: @@ -62,37 +65,92 @@ blockrun_wallet action:"setup" Then send USDC (SPL) on the **Solana** network (Coinbase → pick "Solana", or Phantom/Solflare/Backpack). Switch back with `blockrun_wallet action:"chain" chain:"base"`. :::info -Image, music, speech, video, paid stock prices, smart routing, and native Anthropic (`claude-*`) settle on Base only. +Music, speech, video, the Modal sandbox, DeFi data, paid RealFace, paid stock prices, and native Anthropic (`claude-*`) settle on Base only. Image generation pays on either chain. ::: ## What You Can Do Now +### Try it for free first + +``` +blockrun_chat mode:"free" message:"Draft a commit message for this diff" +``` + +The free tier costs $0 and needs no funded wallet — a good smoke test before you send USDC. DEX data, crypto/FX/commodity prices, and the model list are free too. + +### Access Other AI Models + +``` +Use GPT-5.5 to get a second opinion on this code +``` + +``` +Ask Kimi K3 to review this function +``` + +Claude routes to the model you name — or picks one by intent with `mode:"fast"` / `"balanced"` / `"powerful"` / `"cheap"` / `"reasoning"` / `"coding"` / `"glm"` — and pays via x402. Run `blockrun_models` for the live list with pricing. + ### Image Generation ``` Generate a logo for a crypto trading bot ``` -Claude uses nano-banana to generate images via Google's Nano Banana, OpenAI GPT Image, CogView-4, or xAI Grok Imagine. ~$0.015-0.15 per image. +Claude calls `blockrun_image` — models include `google/nano-banana`, `google/nano-banana-pro`, `openai/gpt-image-2`, `xai/grok-imagine-image`, `zai/cogview-4`, and `bytedance/seedream-5-pro`; edits (img2img, inpaint, fusion) too. ~$0.015-0.15 per image. Install the `image-prompting` skill for text-accurate prompts (see [Skills](../mcp/skills.md)). -### Access Other AI Models +### Video, Music, and Speech + +``` +Generate a 5-second video of a sunset over Tokyo +Compose a 60-second lo-fi hip-hop loop +Speak this paragraph with the sarah voice +``` + +`blockrun_video` and `blockrun_music` are async and payment-on-completion — if a job fails or times out you are not charged. + +### Live Web and X Search + +``` +What are people saying about @sama on X right now? +Find me the top 5 papers on speculative decoding from the last 90 days +``` + +`blockrun_search` covers live web, X/Twitter, and news with citations; `blockrun_exa` does neural research, cited answers, and page reading. + +### Prediction Markets — read and trade + +``` +What's Polymarket saying about the next Fed decision? +If "hold" is above 70%, put $2 on it +``` + +`blockrun_markets` reads odds across Polymarket, Kalshi and more. `blockrun_polymarket` places real, USDC-settled orders from a gasless deposit wallet — every order requires your explicit confirmation and is capped per order (default $25). See [Polymarket funding](../api-reference/polymarket-funding.md). + +### Crypto and On-chain Data ``` -Use GPT-5 to get a second opinion on this code +What's this wallet labeled, and what does it hold? +Top 10 tokens by DEX volume on Base, last 24h +Call eth_getBalance on Arbitrum for 0x... ``` +`blockrun_surf` (exchange, on-chain SQL, wallet labels, social mindshare), `blockrun_price` (Pyth-backed quotes), `blockrun_dex`, `blockrun_defi` (TVL, yields), and `blockrun_rpc` (raw JSON-RPC on 40+ chains). The `crypto-data` skill says which one to use. + +### Phone Calls and Sandboxed Compute + ``` -Ask Grok what's trending on X right now +Call +1-415-555-0100 and confirm Friday at 3pm +Run this benchmark on an H100 in a disposable sandbox ``` -Claude automatically routes to 96 AI models and pays via x402. +`blockrun_phone` makes outbound AI voice calls and leases US/CA numbers to your wallet; `blockrun_modal` runs code in an isolated container with optional GPU. ### Trading (alpha-mcp) -For trading, install the trading-specific MCP: +For strategy-driven crypto trading, install the separate trading MCP: ```bash -claude mcp add @blockrun/alpha +claude mcp add alpha -s user -- npx -y @blockrun/alpha@latest ``` Then: @@ -106,7 +164,7 @@ See [Trading Overview](../products/trading/overview.md) for full details. ## Check Your Balance ``` -blockrun balance +blockrun_wallet ``` or @@ -115,19 +173,46 @@ or What's my BlockRun wallet balance? ``` +Shows both wallet addresses, USDC balances, the active chain, and what this session has spent. + +## Spend Controls + +- **Session cap:** `blockrun_wallet action:"budget" budget_action:"set" budget_amount:5.00` (or `BLOCKRUN_BUDGET_LIMIT=5` in the environment) +- **Sub-agent budgets:** `blockrun_wallet action:"delegate" agent_id:"researcher" agent_limit:2.00`, then pass `agent_id:"researcher"` on every downstream call — the agent is hard-stopped at zero +- **Confirm before every paid call:** install the `blockrun-media` plugin, which adds a spend-confirmation prompt, a per-call receipt, and a status-line balance meter: + +``` +/plugin marketplace add BlockRunAI/blockrun-claude-plugin +/plugin install blockrun-media@blockrun +``` + +Codex users get the same via [blockrun-codex-plugin](https://github.com/BlockRunAI/blockrun-codex-plugin). + ## Available Skills -BlockRun provides Claude Code skills that extend what Claude can do: +BlockRun ships prompt-based skills that teach Claude how to use each tool family. Add the marketplace once, then install what you need: + +``` +/plugin marketplace add BlockRunAI/blockrun-mcp +/plugin install blockrun@blockrun-mcp +``` | Skill | What It Does | |-------|-------------| -| [nano-banana](../products/creation/nano-banana.md) | Generate images via micropayments | -| [alpha-mcp](../products/trading/overview.md) | AI crypto trading with risk management | +| `blockrun` | Start here — which tool answers what, and how to make a first call free | +| `image-prompting` | Turns a vague image request into a text-accurate prompt | +| `search`, `exa-research` | Live search and cited research workflows | +| `crypto-data`, `surf`, `rpc` | Routes crypto questions to the right (often free) tool | +| `prediction-markets`, `polymarket-trading` | Read odds, then place confirm-gated bets | +| `phone`, `modal` | Voice calls and sandboxed compute | + +Full list in [Skills](../mcp/skills.md). [alpha-mcp](../products/trading/overview.md) is a separate MCP server, installed with `claude mcp add`. ## Pricing -- **Intelligence:** Provider cost + 5% (no subscriptions) +- **Intelligence:** Provider cost, no platform margin on chat tokens, plus $0.001 per request (no subscriptions) - **Images:** $0.015-0.15 per image +- **Free tier:** `mode:"free"` chat, DEX data, crypto/FX/commodity prices, model list, Polymarket reads — $0 - **Trading tools:** Free (open source) $1 gets you approximately: @@ -137,10 +222,11 @@ $1 gets you approximately: ## Security -- Your private key stays on your machine (`~/.blockrun/`) +- Your private key stays on your machine (`~/.blockrun/.session`, mode `0600`; optionally the OS keychain with `BLOCKRUN_KEYCHAIN=strict`) - Only cryptographic signatures are sent to servers -- All payments are verifiable on [Basescan](https://basescan.org) +- All payments are verifiable on [Basescan](https://basescan.org) (or Solscan on Solana) - You control your wallet — withdraw anytime +- Back up `~/.blockrun/.session`: it is the only key to both the payment wallet and the Polymarket deposit wallet ## Troubleshooting @@ -153,18 +239,22 @@ Restart Claude Code after installation: claude mcp list ``` +`spawn npx ENOENT`? Reinstall with `-e PATH="$PATH"` (see step 1). + ### Wallet not found? ```bash # Check wallet location -ls ~/.blockrun/ +ls -la ~/.blockrun/ ``` -### Transaction failed? +Then `blockrun_wallet action:"setup"` to (re)create and print the address. + +### Transaction failed / 402? -Check your USDC balance on Base: +Check your USDC balance on the active chain: ``` -blockrun balance +blockrun_wallet ``` For more help, see [MCP Troubleshooting](../mcp/troubleshooting.md). @@ -174,11 +264,11 @@ For more help, see [MCP Troubleshooting](../mcp/troubleshooting.md). ::::cards :::card{title="Browse the MCP tools" href="../mcp/blockrun-mcp.md" icon="Boxes"} -The full list of 20 `blockrun_*` tools — chat, image, video, search, markets, RPC, and more. +The full list of 20 `blockrun_*` tools — chat, image, video, search, markets, Polymarket trading, RPC, and more. ::: :::card{title="Generate images" href="../products/creation/nano-banana.md" icon="Image"} -Create images via micropayments with the nano-banana skill. +Create images via micropayments with `blockrun_image`. ::: :::card{title="Explore all models" href="../products/intelligence/overview.md" icon="Brain"} diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md index 3a4788e..7fb02d7 100644 --- a/docs/getting-started/quickstart.md +++ b/docs/getting-started/quickstart.md @@ -46,13 +46,18 @@ npm install @blockrun/llm ::::steps :::step{title="Create a wallet"} -A non-custodial wallet is created automatically on first run. In Claude Code, just ask for your wallet, or run the SDK once. +A non-custodial wallet is created on first run. In Claude Code, just ask for your wallet; with the Python SDK, call `setup_agent_wallet()` once. ``` blockrun_wallet action:"setup" ``` -This prints your address and opens a funding QR code. The private key is stored locally at `~/.blockrun/.session` and never leaves your machine. +```python +from blockrun_llm import setup_agent_wallet +client = setup_agent_wallet() # creates the wallet if missing, prints the address + QR +``` + +This prints your address and opens a funding QR code. The private key is stored locally at `~/.blockrun/.session` (mode 0600) and never leaves your machine — the MCP and both SDKs read the same file, so one funded wallet serves all of them. ::: :::step{title="Fund it with USDC"} @@ -81,12 +86,17 @@ Confirm the charge — each call costs a fraction of a cent. ``` blockrun_wallet action:"status" ``` + +```python +print(client.get_balance()) # USDC on Base +print(client.get_spending()) # {"total_usd": ..., "calls": ...} for this session +``` ::: :::: :::warning -If a call returns `402 Payment Required` after retrying, your wallet is empty or on the wrong chain. Run `blockrun_wallet action:"setup"` and confirm you funded the **active** chain. +If a call returns `402 Payment Required` after retrying, your wallet is empty or on the wrong chain. Run `blockrun_wallet action:"setup"` and confirm you funded the **active** chain. In Python, `LLMClient()` raising `ValueError: No wallet configured` means no key was found — run `setup_agent_wallet()` or set `BLOCKRUN_WALLET_KEY`. Solana users install `pip install "blockrun-llm[solana]"` and use `SolanaLLMClient`. ::: ## What's next? diff --git a/docs/getting-started/sdk-developers.md b/docs/getting-started/sdk-developers.md index 2c7de82..7f14af6 100644 --- a/docs/getting-started/sdk-developers.md +++ b/docs/getting-started/sdk-developers.md @@ -10,7 +10,7 @@ Direct API integration with Python, TypeScript, or Go. This guide is for developers who want to integrate BlockRun directly into their applications using our SDKs. :::note{title="What you need"} -A wallet private key in `BLOCKRUN_WALLET_KEY` (or let the SDK auto-generate one), funded with a few dollars of USDC on Base. See [Wallet Setup](wallet-setup.md). +A wallet private key in `BLOCKRUN_WALLET_KEY`, a wallet file at `~/.blockrun/.session`, or one created for you by `setup_agent_wallet()` — funded with a few dollars of USDC on Base (or Solana). See [Wallet Setup](wallet-setup.md). ::: ## Quick Start @@ -19,13 +19,13 @@ A wallet private key in `BLOCKRUN_WALLET_KEY` (or let the SDK auto-generate one) :::tab{label="Python"} ```bash -pip install blockrun-llm +pip install blockrun-llm # add [solana] for USDC on Solana ``` ```python from blockrun_llm import LLMClient -client = LLMClient() # Uses BLOCKRUN_WALLET_KEY env var +client = LLMClient() # BLOCKRUN_WALLET_KEY env var or ~/.blockrun/.session response = client.chat("openai/gpt-5.4", "Hello!") print(response) ``` @@ -73,23 +73,32 @@ func main() { ### Environment Variables ```bash -# Required: Your wallet private key +# Your Base wallet private key (falls back to ~/.blockrun/.session if unset) export BLOCKRUN_WALLET_KEY=0x... -# Optional: Custom wallet path -export BLOCKRUN_WALLET_PATH=~/.blockrun/wallet.json +# Solana instead: bs58 key for SolanaLLMClient (falls back to ~/.blockrun/.solana-session) +export SOLANA_WALLET_KEY=... # Optional: API endpoint (default: https://blockrun.ai/api) export BLOCKRUN_API_URL=https://blockrun.ai/api + +# Optional: client-side spend limits in USD (refused before any payment is signed) +export BLOCKRUN_MAX_COST_PER_CALL=0.25 +export BLOCKRUN_MAX_SESSION_COST=10 + +# Optional: chat timeout in seconds (default 600) and a per-call transaction log +export BLOCKRUN_CHAT_TIMEOUT=600 +export BLOCKRUN_TX_LOG=1 ``` ### Programmatic Configuration ```python client = LLMClient( - private_key="0x...", # Or use env var + private_key="0x...", # Or use env var / wallet file api_url="https://blockrun.ai/api", - session_budget=10.00 # Optional spending limit + max_cost_per_call=0.25, # Optional USD ceiling per request + max_session_cost=10.00, # Optional USD ceiling per client session ) ``` @@ -118,32 +127,50 @@ messages = [ {"role": "user", "content": "What is x402?"} ] -response = client.chat_messages("openai/gpt-5.4", messages) +result = client.chat_completion("openai/gpt-5.4", messages) +print(result.choices[0].message.content) +``` + +`chat_completion()` also takes `tools` / `tool_choice`, `response_format`, `stop` and `fallback_models`. + +### Smart Routing + +Let Router Core pick the cheapest capable model and keep the rest as a fallback chain — or opt in from any chat call with the `blockrun/auto` virtual model id: + +```python +result = client.smart_chat("Summarize this changelog in one line") +print(result.model, result.routing.savings) + +result = client.chat_completion("blockrun/auto", messages) # also blockrun/eco, blockrun/premium ``` ### Image Generation +Media lives in dedicated clients that share the same wallet resolution: + ```python -image_url = client.generate_image( - prompt="A futuristic city at sunset", - model="google/nano-banana", - size="1024x1024" -) +from blockrun_llm import ImageClient + +img = ImageClient() +res = img.generate("A futuristic city at sunset", model="google/nano-banana", size="1024x1024") +print(res.data[0].url) ``` +`VideoClient`, `MusicClient`, `SpeechClient`, `SearchClient`, `PriceClient`, `RpcClient` and more follow the same pattern — see the [Python SDK reference](../sdks/python.md#specialized-clients). + ### Wallet Operations ```python # Get address -address = client.get_address() +address = client.get_wallet_address() # Check balance balance = client.get_balance() print(f"${balance} USDC") -# Get usage stats -usage = client.get_usage() -print(f"Spent: ${usage['total_spent']}") +# Session spend +spent = client.get_spending() +print(f"Spent: ${spent['total_usd']:.4f} across {spent['calls']} calls") ``` ## Available Models @@ -189,26 +216,27 @@ client.chat("moonshot/kimi-k3", prompt) ```python from blockrun_llm import ( LLMClient, - InsufficientBalanceError, - ModelNotFoundError, - RateLimitError, - APIError + PaymentError, + SpendLimitError, + APIError, ) try: response = client.chat("openai/gpt-5.4", prompt) -except InsufficientBalanceError: - print("Need to fund wallet") - print(f"Address: {client.get_address()}") -except ModelNotFoundError as e: - print(f"Invalid model: {e.model}") -except RateLimitError: - print("Too many requests, waiting...") - time.sleep(60) +except SpendLimitError as e: + print(f"Quote ${e.quoted_usd} over your {e.scope} limit ${e.limit_usd} — nothing was charged") +except PaymentError as e: + print(f"Payment failed: {e}") # e.g. insufficient USDC + print(f"Fund: {client.get_wallet_address()}") except APIError as e: - print(f"API error: {e.message}") + if e.status_code == 429: + print("Too many requests, backing off") + else: + print(f"API error {e.status_code}: {e}") ``` +`SpendLimitError` subclasses `PaymentError`; every SDK exception derives from `BlockrunError`. A 429 or 5xx walks `fallback_models` automatically when you pass one. + ## Async Support ::::tabs @@ -216,10 +244,12 @@ except APIError as e: :::tab{label="Python"} ```python import asyncio +from blockrun_llm import AsyncLLMClient async def main(): - response = await client.achat("openai/gpt-5.4", "Hello!") - print(response) + async with AsyncLLMClient() as client: + response = await client.chat("openai/gpt-5.4", "Hello!") + print(response) asyncio.run(main()) ``` @@ -233,79 +263,90 @@ const response = await client.chat('openai/gpt-5.4', 'Hello!'); :::: -## Streaming (Coming Soon) +## Streaming ```python -# Planned API -for chunk in client.chat_stream("openai/gpt-5.4", prompt): - print(chunk, end="", flush=True) +for chunk in client.chat_completion_stream("openai/gpt-5.4", [{"role": "user", "content": prompt}]): + delta = chunk.choices[0].delta + if delta.content: + print(delta.content, end="", flush=True) ``` -## Session Budgets +Payment is signed once before the stream opens; `AsyncLLMClient` exposes the same method for `async for`. + +## Spend Limits -Limit spending per session: +Cap spending per request or per client session — a quote above the ceiling is refused before anything is signed, so nothing settles: ```python -client = LLMClient(session_budget=10.00) # Max $10 +from blockrun_llm import LLMClient, SpendLimitError -# Will raise InsufficientBudgetError if exceeded -response = client.chat("openai/gpt-5.4", prompt) +client = LLMClient(max_cost_per_call=0.25, max_session_cost=10.00) + +try: + response = client.chat("openai/gpt-5.4", prompt) +except SpendLimitError as e: + print(e.scope, e.quoted_usd, e.limit_usd) ``` ## Batch Processing ```python import asyncio +from blockrun_llm import AsyncLLMClient async def process_batch(items: list) -> list: - tasks = [ - client.achat("deepseek/deepseek-chat", f"Process: {item}") - for item in items - ] - return await asyncio.gather(*tasks) + async with AsyncLLMClient() as client: + tasks = [ + client.chat("deepseek/deepseek-chat", f"Process: {item}") + for item in items + ] + return await asyncio.gather(*tasks) results = asyncio.run(process_batch(my_items)) ``` ## OpenAI-Compatible API -BlockRun's API is OpenAI-compatible. You can use the OpenAI SDK: +BlockRun's request and response shapes are OpenAI-compatible, but authentication is an x402 payment, not a bearer token — a paid model answers `402 Payment Required` until a signed USDC authorization is attached, and your private key must never be sent as an API key. Two ways to keep the official SDK surface: -```python -from openai import OpenAI +- **Free models** need no payment, so the plain `openai` SDK works against `https://blockrun.ai/api/v1` with any placeholder `api_key` for `nvidia/*` free models (per-IP rate limits apply). +- **Paid models**: use `blockrun-llm` (`AnthropicClient` wraps the official `anthropic` SDK; `pip install "blockrun-llm[anthropic]"`), or [`blockrun-llm-vip`](https://pypi.org/project/blockrun-llm-vip/), which subclasses the official `anthropic` and `openai` SDKs and only swaps the transport to add x402 signing. -client = OpenAI( - base_url="https://blockrun.ai/api/v1", - api_key=os.environ["BLOCKRUN_WALLET_KEY"] -) +```python +from blockrun_llm_vip import OpenAI # pip install blockrun-llm-vip +client = OpenAI() # wallet from BLOCKRUN_WALLET_KEY / ~/.blockrun/.session response = client.chat.completions.create( - model="openai/gpt-5.4", + model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}] ) ``` ## Direct HTTP +A free model can be called with plain HTTP and no credentials: + ```bash curl https://blockrun.ai/api/v1/chat/completions \ -H "Content-Type: application/json" \ - -H "Authorization: Bearer $BLOCKRUN_WALLET_KEY" \ -d '{ - "model": "openai/gpt-5.4", + "model": "nvidia/step-3.7-flash", "messages": [{"role": "user", "content": "Hello!"}] }' ``` +A paid model returns `402` with the price and a `payment-required` header describing what to sign; see [How payment works](../x402/how-it-works.md) if you want to implement the x402 handshake yourself rather than use an SDK. + ## Pricing -Pay per request: provider cost + 5%. +Pay per request at the per-token price listed in the live catalog (`GET https://blockrun.ai/api/v1/models`, or `client.list_models()`). | Model | Input/1M | Output/1M | |-------|----------|-----------| -| GPT-5.4 | $2.63 | $15.75 | -| DeepSeek Chat | $0.29 | $0.44 | -| Gemini Flash | $0.32 | $2.63 | +| `openai/gpt-5.4` | $2.50 | $15.00 | +| `deepseek/deepseek-chat` | $0.14 | $0.28 | +| `google/gemini-2.5-flash` | $0.30 | $2.50 | Full pricing: [Intelligence Pricing](../products/intelligence/pricing.md) @@ -335,7 +376,7 @@ Every model ID, context window, and live price. ::: :::card{title="Pricing" href="../products/intelligence/pricing.md" icon="TrendingUp"} -Provider cost + 5%, no subscriptions or minimums. +Per-token prices from the live catalog, no subscriptions or minimums. ::: :::: diff --git a/docs/getting-started/wallet-setup.md b/docs/getting-started/wallet-setup.md index 682b9df..27ecfa9 100644 --- a/docs/getting-started/wallet-setup.md +++ b/docs/getting-started/wallet-setup.md @@ -34,19 +34,21 @@ Switch to Solana with `blockrun_wallet action:"chain" chain:"solana"` then `bloc BlockRun SDKs can generate a wallet automatically: -**Claude Code:** +**Claude Code (MCP):** ``` -blockrun setup +blockrun_wallet action:"setup" ``` **Python SDK:** ```python -from blockrun_llm import LLMClient +from blockrun_llm import setup_agent_wallet -client = LLMClient() # Creates wallet at ~/.blockrun/wallet.json if none exists -print(client.get_address()) +client = setup_agent_wallet() # Creates ~/.blockrun/.session if none exists and prints a funding QR +print(client.get_wallet_address()) ``` +`LLMClient()` on its own does not create a wallet — it raises `ValueError` if no key is found in the environment or at `~/.blockrun/.session`. + **TypeScript SDK:** ```typescript import { LLMClient } from '@blockrun/llm'; @@ -69,6 +71,32 @@ Or pass it directly: client = LLMClient(private_key="0x...") ``` +**Solana (Python SDK):** install the extra and use the Solana client. The key can be a bs58 keypair or seed, the Solana CLI's `~/.config/solana/id.json` byte array, or 64-byte hex: + +```bash +pip install "blockrun-llm[solana]" +export SOLANA_WALLET_KEY=... +``` + +```python +from blockrun_llm import SolanaLLMClient, setup_agent_solana_wallet + +client = SolanaLLMClient() # SOLANA_WALLET_KEY, else ~/.blockrun/.solana-session +client = setup_agent_solana_wallet() # creates ~/.blockrun/.solana-session if none exists +``` + +Base and Solana keys are not interchangeable (a Base key is `0x` + 64 hex); pass each to its own client. The payer must already hold a USDC token account on Solana. + +**Adopt a wallet another application created:** the SDK never switches wallets on its own. List what it found and import one deliberately — your current key is backed up to `~/.blockrun/.session.backup-` first: + +```python +from blockrun_llm import list_discovered_wallets, import_wallet + +for w in list_discovered_wallets(): + print(w["address"], "from", w["source"]) +import_wallet("0x...") +``` + ### Option 3: BlockRun MCP session wallet Installing [BlockRun MCP](/docs/mcp/blockrun-mcp) auto-creates a wallet at `~/.blockrun/.session` on first use — the Python and TypeScript SDKs read the same file automatically, so one funded wallet serves your MCP tools and your scripts: @@ -99,15 +127,18 @@ claude mcp add blockrun -s user -- npx -y @blockrun/mcp@latest ### Check Balance -**Claude Code:** +**Claude Code (MCP):** ``` -blockrun balance +blockrun_wallet action:"status" ``` **Python:** ```python balance = client.get_balance() print(f"Balance: ${balance} USDC") + +# or, one-liner that also creates the wallet if needed: +python3 -c "from blockrun_llm import status; status()" ``` **MCP:** ask your agent — the `blockrun_wallet` tool reports the address and USDC balance. @@ -120,9 +151,23 @@ View your wallet on [Basescan](https://basescan.org) by searching your address. | Platform | Location | |----------|----------| -| Claude Code / MCP | `~/.blockrun/wallet.json` | -| Python SDK | `~/.blockrun/wallet.json` or env var | -| TypeScript SDK | `~/.blockrun/wallet.json` or env var | +| Claude Code / MCP | `~/.blockrun/.session` | +| Python SDK | `BLOCKRUN_WALLET_KEY` env var, else `~/.blockrun/.session` (legacy `~/.blockrun/wallet.key` still read) | +| Python SDK (Solana) | `SOLANA_WALLET_KEY` env var, else `~/.blockrun/.solana-session` | +| TypeScript SDK | `~/.blockrun/.session` or env var | + +Every file is written with mode `0600`. The MCP and both SDKs share the Base file, so one funded wallet serves all of them. + +## Spend Limits + +The Python SDK can refuse any quote above a ceiling **before** signing, so an agent can never spend more than you allowed — nothing settles on a refusal: + +```python +client = LLMClient(max_cost_per_call=0.25, max_session_cost=10.00) +# or per deployment: BLOCKRUN_MAX_COST_PER_CALL / BLOCKRUN_MAX_SESSION_COST +``` + +A refused quote raises `SpendLimitError` (a `PaymentError` subclass). Both limits are unset by default. ## Security Best Practices @@ -191,8 +236,10 @@ https://testnet.blockrun.ai/api ### Available Testnet Models -- `openai/gpt-oss-20b` - $0.003/request -- `openai/gpt-oss-120b` - $0.004/request +- `openai/gpt-oss-20b` - $0.001/request +- `openai/gpt-oss-120b` - $0.002/request + +Testnet also lists the image models and `minimax/music-2.5+`; see `https://testnet.blockrun.ai/api/v1/models`. ## Troubleshooting @@ -206,16 +253,18 @@ Check your USDC balance on Base network. ETH for gas is handled by the x402 faci 2. Check if the network is congested on [Basescan](https://basescan.org) 3. Try again in a few seconds -### "Wallet not found" +### "No wallet configured" ```bash -# Check if wallet file exists -ls ~/.blockrun/ +# Check if the wallet file exists +ls -la ~/.blockrun/.session -# Create new wallet -blockrun setup +# Create a new wallet (Python) +python3 -c "from blockrun_llm import setup_agent_wallet; setup_agent_wallet()" ``` +Or in Claude Code: `blockrun_wallet action:"setup"`. If you hold a Solana key, remember it belongs to `SolanaLLMClient`, not `LLMClient`. + ## What's next? ::::cards diff --git a/docs/mcp/blockrun-mcp.md b/docs/mcp/blockrun-mcp.md index ceddebe..7acbc39 100644 --- a/docs/mcp/blockrun-mcp.md +++ b/docs/mcp/blockrun-mcp.md @@ -5,7 +5,7 @@ description: A Model Context Protocol server that gives Claude Code 72 models, c # BlockRun MCP -Give Claude Code access to 96 AI models, 80+ crypto data endpoints, voice calls, image/video/music generation, prediction markets, and a sandbox runtime — all with zero API keys. +Give Claude Code access to 96 AI models, 80+ crypto data endpoints, voice calls, image/video/music generation, prediction markets (read *and* trade), multi-chain RPC, and a sandbox runtime — all with zero API keys. BlockRun MCP is a Model Context Protocol server that connects Claude Code to BlockRun's intelligence, trading, and creation capabilities. @@ -17,20 +17,116 @@ Always installs the latest version — see [npm](https://www.npmjs.com/package/@ claude mcp add blockrun -s user -- npx -y @blockrun/mcp@latest ``` -That's it. Restart Claude Code and the MCP is available. +That's it. Restart Claude Code and the MCP is available. A wallet is auto-created on first run — no signup. + +`-s user` installs globally (available in every project). The `--` separator ensures `-y` is passed to `npx`, not parsed by `claude mcp add`. Requires Node.js ≥ 20.19. + +:::tip{title="Homebrew / nvm users"} +If the server doesn't connect, Claude Code likely can't find `node`/`npx` on its launcher PATH. Pass your shell PATH through — works on CLI and desktop: + +```bash +claude mcp add blockrun -s user -e PATH="$PATH" -- npx -y @blockrun/mcp@latest +``` +::: :::tip Failed calls aren't charged. You only pay when a request succeeds and settles on-chain. ::: +### Other clients + +The same server runs in any MCP-compatible client. + +::::tabs + +:::tab{label="Claude Desktop"} +Add to `claude_desktop_config.json`: + +```json +{ + "mcpServers": { + "blockrun": { "command": "npx", "args": ["-y", "@blockrun/mcp@latest"] } + } +} +``` +::: + +:::tab{label="Cursor"} +Add to `~/.cursor/mcp.json` (macOS/Linux) or `%APPDATA%\Cursor\mcp.json` (Windows): + +```json +{ + "mcpServers": { + "blockrun": { "command": "npx", "args": ["-y", "@blockrun/mcp@latest"] } + } +} +``` +::: + +:::tab{label="Windsurf"} +Same JSON, in: +- macOS: `~/.codeium/windsurf/mcp_config.json` +- Linux: `~/.config/.codeium/windsurf/mcp_config.json` +- Windows: `%APPDATA%\Codeium\windsurf\mcp_config.json` +::: + +:::tab{label="Codex CLI"} +```bash +codex mcp add blockrun -- npx -y @blockrun/mcp@latest +``` + +Or add to `~/.codex/config.toml`: + +```toml +[mcp_servers.blockrun] +command = "npx" +args = ["-y", "@blockrun/mcp@latest"] +``` +::: + +:::: + +### Tool profiles (optional) + +Expose a trimmed tool set so the client loads fewer schemas into context. Pass `--profile ` (or set `BLOCKRUN_MCP_PROFILE`); omit for the full set. + +| Profile | Tools | +|---------|-------| +| `full` *(default)* | everything | +| `media` | `wallet` `models` `image` `video` `realface` `music` `speech` | +| `trading` | `wallet` `price` `dex` `markets` `surf` `defi` `rpc` `polymarket_read` `polymarket` | +| `research` | `wallet` `models` `chat` `search` `exa` `surf` | +| `chat` | `wallet` `models` `chat` | + +```bash +claude mcp add blockrun-trading -s user -- npx -y @blockrun/mcp@latest --profile trading +``` + +An unknown profile name falls back to `full`. `modal` and `phone` are `full`-profile only. + +### Plugins (spend controls) + +The bare MCP works everywhere. For Claude Code specifically, the **blockrun-media** plugin wraps the `media` profile with a spend-confirmation prompt before every paid call, a running cost meter, a status-line balance, and `/blockrun-media:balance` · `/blockrun-media:report` · `/blockrun-media:insights` commands: + +``` +/plugin marketplace add BlockRunAI/blockrun-claude-plugin +/plugin install blockrun-media@blockrun +``` + +The Codex port is [blockrun-codex-plugin](https://github.com/BlockRunAI/blockrun-codex-plugin) (`codex plugin marketplace add BlockRunAI/blockrun-codex-plugin`, then `codex plugin install blockrun-media`). Both read real settled spend from `~/.blockrun/cost_log.jsonl`. Knobs: `BLOCKRUN_ASK_THRESHOLD` (auto-run paid calls at or under this USD amount), `BLOCKRUN_SESSION_CAP` (soft per-session budget). + +Prompt-based skills for every tool family install from a separate marketplace — see [Skills](skills.md). + ## What It Enables | Capability | Example | |------------|---------| -| **Multi-model access** | "Use GPT-5 to review this code" | -| **Image generation** | "Generate a logo for my app" | -| **Real-time data** | "What's trending on X?" | -| **Cost optimization** | "Use DeepSeek for bulk processing" | +| **Multi-model access** | "Use GPT-5.5 to review this code" | +| **Image, video, music, speech** | "Generate a logo for my app" · "A 5-second clip of a sunset over Tokyo" | +| **Real-time data** | "What's trending on X?" · "Polymarket odds on the next Fed decision?" | +| **Trading** | "If 'hold' is above 70%, put $2 on it" (confirm-gated) | +| **On-chain & crypto** | "What's this wallet labeled, and what does it hold?" | +| **Cost optimization** | "Use DeepSeek for bulk processing" · `mode:"free"` for drafts | ## How It Works @@ -52,29 +148,30 @@ claude mcp add blockrun -s user -- npx -y @blockrun/mcp@latest ``` ::: -:::step{title="Set up the wallet"} -In Claude Code: +:::step{title="See your wallet"} +The wallet is created automatically the first time the server starts. In Claude Code: ``` -blockrun setup +blockrun_wallet action:"setup" ``` -This creates or displays your wallet address. +Or just ask: "Set up my BlockRun wallet." This prints the address and a funding QR code. ::: :::step{title="Fund the wallet"} Send USDC to your wallet on Base network: -- [Coinbase](https://coinbase.com) — withdraw directly to Base +- [Coinbase](https://coinbase.com) — Send → USDC → Base network → paste address - [Base Bridge](https://bridge.base.org) — bridge from Ethereum +- Card: `blockrun_wallet action:"deposit"` mints a one-time Coinbase Onramp link and opens it (Base only; funds land in your own wallet) Recommended: $5-20 to start. ::: :::step{title="Verify"} ``` -blockrun balance +blockrun_wallet ``` -Or ask: "What's my BlockRun wallet balance?" +Or ask: "What's my BlockRun wallet balance?" The default `status` action shows both wallet addresses, USDC balances, the active chain, and session spend. ::: :::: @@ -88,99 +185,165 @@ blockrun_wallet action:"chain" chain:"solana" # provisions + activates the Sol blockrun_wallet action:"setup" # shows the Solana address + funding QR ``` -Then send USDC (SPL) on the **Solana** network — from Coinbase (pick "Solana"), Phantom, Solflare, or Backpack. Switch back anytime with `blockrun_wallet action:"chain" chain:"base"`. +Then send USDC (SPL) on the **Solana** network — from Coinbase (pick "Solana"), Phantom, Solflare, or Backpack. Switch back anytime with `blockrun_wallet action:"chain" chain:"base"`. The server keeps both wallets; switching just changes which one pays. :::info -**Base-only** (these need Base regardless of active chain): `blockrun_image`, `blockrun_music`, `blockrun_speech`, `blockrun_video`, paid stock `blockrun_price`, `blockrun_chat routing:"smart"`, and native Anthropic (`claude-*`) models. On Solana, pass `model:` or `mode:` to `blockrun_chat` explicitly. +**Base-only** (these fall back to Base regardless of active chain): `blockrun_music`, `blockrun_speech`, `blockrun_video`, `blockrun_modal`, `blockrun_defi`, paid `blockrun_realface`, paid stock `blockrun_price`, and native Anthropic (`claude-*`) passthrough. In Solana mode they return a "switch to Base" message instead of charging. `blockrun_image` pays on either chain. ::: ## Available Tools -The MCP exposes 20 tools to Claude: +The MCP exposes 20 tools to Claude, grouped below by what they do. Every tool description carries its current price — prices are generated from the live catalog, not typed, so read the tool description (or the `402` response) rather than a remembered figure. + +### Intelligence -### `blockrun_chat` +#### `blockrun_chat` -Call any supported LLM model — plus image, video, and music generation when smart-routed. +Get a second opinion from another model, or use a specialised model for a task. Pick by `model` id or by `mode`. ``` -Use GPT-5.4 to explain this error -Generate an image of a futuristic city -Generate a 5-second video of a sunset over Tokyo -Compose a 60-second lo-fi hip-hop loop +Use GPT-5.5 to explain this error +blockrun_chat mode:"reasoning" message:"Prove there are infinitely many primes" +blockrun_chat mode:"free" message:"Draft a commit message for this diff" ``` **Parameters** | Param | Type | Description | |-------|------|-------------| -| `message` | string | The prompt (or use `messages` for multi-turn). | -| `model` | string | Explicit model id, e.g. `openai/gpt-5.5`. Overrides `mode`. | -| `mode` | enum | Pick a model by intent instead of by name: `fast`, `balanced`, `powerful`, `cheap`, `reasoning`, `free`, `coding`, `glm`. | -| `routing` | `"smart"` | Run ClawRouter's local scorer to choose the cheapest capable model. Pair with `routing_profile`. | -| `routing_profile` | enum | `free` · `eco` · `auto` (default) · `premium`. Only used when `routing:"smart"`. | +| `message` | string | The prompt (appended as the final user turn when `messages` is also given). | +| `model` | string | Explicit model id, e.g. `openai/gpt-5.5`, `moonshot/kimi-k3`, `zai/glm-5`. Overrides `mode`. | +| `mode` | enum | Pick a model by intent: `fast`, `balanced` (default), `powerful`, `cheap`, `reasoning`, `free`, `coding`, `glm`. Ignored when `model` is set. | | `system` | string | System prompt. | | `max_tokens` | number | Default `1024`. | -| `temperature` | number | Default `1`. | -| `messages` | array | `[{role:"user"\|"assistant"\|"system", content}]` for multi-turn. | +| `temperature` | number | 0–2, default `1`. | +| `response_format` | enum | `text` or `json_object` (forces valid JSON, no fences). | +| `stop` | string[] | Up to 4 stop sequences. | +| `thinking` | object | `{type:"enabled", budget_tokens}` — extended thinking, honoured for `anthropic/claude-*` only. | +| `messages` | array | `[{role:"user"\|"assistant"\|"system", content}]` for multi-turn; `content` may be text or `text`/`image_url` parts. | | `agent_id` | string | Attribute the spend to a sub-agent budget (see `blockrun_wallet`). | +#### `blockrun_models` + +List every available LLM, image, video, music and speech model with live pricing, context windows, and categories. Free. + +### Search & research + +#### `blockrun_search` + +Live web + X/Twitter + news search with AI-summarised results and citations. Body: `{ query, sources: ["web","x","news"], max_results, from_date, to_date }`. `sources` accepts any subset (default all three; `["x"]` for tweets only). `max_results` is 1–50 and drives the price — pass a small value to cap spend. + ``` -blockrun_chat mode:"reasoning" message:"Prove there are infinitely many primes" -blockrun_chat routing:"smart" routing_profile:"eco" message:"Summarize this thread" +blockrun_search body:{query:"what are people saying about @sama", sources:["x"], max_results:5} ``` -### `blockrun_search` +#### `blockrun_exa` + +Neural semantic web search — `search`, `answer` (cited), `contents` (read URLs), `find-similar`. Best for research, papers, competitors. + +### Markets & trading + +#### `blockrun_markets` + +Prediction-market and derivatives data via the Predexon aggregator — Polymarket (markets, candles, trades, orderbooks, leaderboards, smart-wallet PnL), Kalshi, Limitless, Opinion, Predict.Fun, dFlow, Binance Futures, sports, and cross-venue search. See [Prediction Markets reference](../api-reference/prediction-markets.md). -Live web and news search (Grok-grounded). Params: `query` (required), `sources` (`web` · `news`), `max_results` (1–50, default 10), `from_date` / `to_date` (`YYYY-MM-DD`). +#### `blockrun_polymarket_read` -### `blockrun_exa` +Read or preview Polymarket state without signing anything — `positions`, open `orders`, and a live order `preview` from the CLOB book. Free; never accepts `confirm`. -Neural semantic web search — find URLs, read pages, get cited answers, find similar. +#### `blockrun_polymarket` -### `blockrun_markets` +**Trade on Polymarket** (CLOB V2, Polygon) — `setup`, `fund`, `buy`, `sell`, `cancel`, `redeem`, `withdraw`. Orders are signed locally by your BlockRun key and settle in pUSD from a gasless deposit wallet. Real money: every order needs `confirm:true`, capped per order by `POLYMARKET_MAX_BET_USD` (default $25). Without `confirm` you get a dry-run. Discover markets and token ids with `blockrun_markets` first. See [Polymarket funding](../api-reference/polymarket-funding.md). -Predexon prediction markets — Polymarket, Kalshi, sports markets. +``` +blockrun_polymarket action:"setup" +blockrun_polymarket action:"fund" amount_usd:5 confirm:true +blockrun_polymarket_read action:"preview" side:"buy" token_id:"" amount_usd:5 order_type:"FOK" +blockrun_polymarket action:"buy" token_id:"" amount_usd:5 order_type:"FOK" confirm:true +``` + +### Crypto & on-chain data -### `blockrun_surf` +#### `blockrun_surf` -Crypto data via Surf — 83 endpoints across exchanges, on-chain analytics, prediction markets, wallet labels, social mindshare, news, and search. See [Surf API reference](../api-reference/surf.md). +Unified crypto data — CEX data, on-chain SQL, labeled wallets, social mindshare, news, prediction markets, and unified search. See [Surf API reference](../api-reference/surf.md). ``` What's the BTC funding rate on Binance perps right now, and how does it compare to the 7-day average? ``` -### `blockrun_price` +#### `blockrun_price` + +Pyth-backed realtime quotes and OHLC history — crypto, FX, commodities (free) and 12 global stock markets (paid). Actions: `price`, `history`, `list`. + +#### `blockrun_dex` + +Real-time DEX pair data — token prices, liquidity, volume, pair and contract lookup across chains. Free. -Pyth-grounded real-time quotes — crypto, FX, commodities, stocks. +#### `blockrun_defi` -### `blockrun_dex` +DeFi fundamentals — protocol TVL, chain TVL, yield pools (APY), token prices. See [DefiLlama reference](../api-reference/defillama.md). -0x Swap — Permit2 + Gasless V2 aggregation across 100+ venues (free passthrough). +#### `blockrun_rpc` -### `blockrun_phone` +Raw JSON-RPC against 40+ blockchains through one endpoint — contract reads, balances, blocks, transactions, logs, gas estimates. No node, no API key. See [Multi-chain RPC](../api-reference/multi-chain-rpc.md). -AI voice calls + wallet-owned US/CA phone numbers. See [Phone & Voice reference](../api-reference/voice-phone.md). +### Media + +#### `blockrun_image` + +Generate or edit images (img2img, inpaint, fusion). Models include `openai/gpt-image-2`, `openai/gpt-image-1`, `google/nano-banana`, `google/nano-banana-2`, `google/nano-banana-pro`, `xai/grok-imagine-image`, `xai/grok-imagine-image-pro`, `zai/cogview-4`, `bytedance/seedream-5-pro`. Pays on either chain. Pair with the `image-prompting` skill for text-accurate prompts. + +``` +Generate a poster announcing our launch, retro-futuristic, headline "NOW LIVE" +``` + +#### `blockrun_video` + +Short AI video from a text prompt and optional seed image — `azure/sora-2`, `xai/grok-imagine-video`, `xai/grok-imagine-video-1.5`, and `bytedance/seedance-1.5-pro` / `2.0-mini` / `2.0-fast` / `2.0` / `2.5`. Async and client-polled (typically 60–180s, 9-minute hard cap); you are charged only when a finished video comes back. + +#### `blockrun_realface` + +Enroll a real person (phone liveness check) or an AI character as a `ta_xxxx` asset, then drive Seedance 2.0 / 2.0-fast / 2.0-mini video of that specific person via `blockrun_video real_face_asset_id`. Not supported on Seedance 2.5 or 1.5-pro. See [RealFace reference](../api-reference/realface.md). + +#### `blockrun_music` + +Generate a full-length (~3 minute) MP3 track. Async; payment settles only when a finished track is returned. + +#### `blockrun_speech` + +Text-to-speech (`speak`, default), cinematic `sound_effect` clips up to 22s, and a free `voices` list. Price is quoted before payment. See [Text-to-Speech reference](../api-reference/text-to-speech.md). + +### Actions & compute + +#### `blockrun_phone` + +Phone-number intelligence (carrier, line type, SIM-swap and call-forwarding fraud signals), wallet-owned US/CA number leases, and outbound AI voice calls. See [Phone & Voice reference](../api-reference/voice-phone.md). ``` Call +14155551234 and ask if Tuesday at 7pm is available for a reservation for two. ``` -### `blockrun_models` +#### `blockrun_modal` -List all available models with live pricing, context windows, and categories. +Run isolated code in a BlockRun-hosted sandbox — a disposable remote container with optional GPU (T4 / L4 / A10G / A100 / H100). The `timeout` you ask for is the billed lifetime, charged up front and never refunded, so request what you need. See [Modal sandbox reference](../api-reference/modal-sandbox.md). -### `blockrun_wallet` +### Wallet -USDC balance, wallet setup, chain switching, and per-agent budget delegation. +#### `blockrun_wallet` + +USDC balance, wallet setup, card on-ramp, chain switching, and per-agent budget delegation. **`action`** (default `status`): | action | What it does | Extra params | |--------|--------------|--------------| -| `status` | Address, USDC balance, session spend | — | -| `setup` | Print address + funding QR (creates wallet on first run) | — | -| `chain` | Switch active chain | `chain:"base"` \| `"solana"` | -| `budget` | Set/clear a global session spend cap | `budget_action:"set"\|"clear"`, `budget_amount` | +| `status` | Both addresses + USDC balances, active chain, session spend | — | +| `setup` | Print address + funding QR (creates the wallet if missing) | — | +| `deposit` | Buy USDC with a card via a one-time Coinbase Onramp link (Base only) | — | +| `qr` | Open the funding QR in a viewer | — | +| `chain` | Switch active chain (omit `chain` to view current) | `chain:"base"` \| `"solana"` | +| `budget` | Set / check / clear a global session spend cap | `budget_action:"set"\|"check"\|"clear"`, `budget_amount` | | `delegate` | Allocate a spend limit to a sub-agent | `agent_id`, `agent_limit` | | `revoke` | Remove a sub-agent's budget | `agent_id` | | `report` | Per-agent spending breakdown | — | @@ -191,34 +354,48 @@ blockrun_wallet action:"budget" budget_action:"set" budget_amount:5.00 blockrun_wallet action:"delegate" agent_id:"researcher" agent_limit:2.00 ``` -### Smart Routing +Call this tool **first** whenever another `blockrun_*` tool returns a payment or balance error. Paid tools auto-open the card on-ramp when a call fails for lack of funds. + +### Mode Routing -The MCP includes built-in smart routing that selects the best model based on your intent: +`mode` picks a model by intent in one hop. Each mode is an ordered list; the first entry is tried first and the rest are fallbacks. -| Mode | Models | Best For | -|------|--------|----------| -| `fast` | Gemini Flash, GPT-5 Mini | Quick responses | -| `balanced` | GPT-5.4, Claude Sonnet 4.6 | General use | -| `powerful` | GPT-5.4, Claude Opus 5 | Complex tasks | -| `cheap` | NVIDIA free models, DeepSeek, Gemini Flash | Cost savings | -| `reasoning` | o3, DeepSeek Reasoner | Logic and math | +| Mode | Tries first | Best For | +|------|-------------|----------| +| `fast` | `google/gemini-3.5-flash`, `google/gemini-2.5-flash`, `openai/gpt-5.6-luna` | Quick responses | +| `balanced` *(default)* | `openai/gpt-5.6-terra`, `anthropic/claude-sonnet-5`, `moonshot/kimi-k3` | General use | +| `powerful` | `anthropic/claude-opus-5`, `anthropic/claude-opus-4.8`, `openai/gpt-5.6-sol` | Complex tasks, 1M context | +| `cheap` | `deepseek/deepseek-v4-pro`, `qwen/qwen3.7-flash`, `minimax/minimax-m3` | Cost savings | +| `reasoning` | `anthropic/claude-opus-5`, `anthropic/claude-opus-4.8`, `openai/gpt-5.6-sol` | Logic and math | | `free` | NVIDIA-hosted open models only | Development, zero-cost | -| `coding` | Coding-tuned models | Code generation/review | -| `glm` | GLM-5.2 / GLM-5 family | Strong general + coding at low token cost | +| `coding` | Coding-tuned models (`openai/gpt-5.3-codex`, `xai/grok-build-0.1`, `zai/glm-5.2`, …) | Code generation/review | +| `glm` | `zai/glm-5` / `5.2` / `5.1` / `5-turbo` | Strong general + coding at low token cost | -`mode` picks a model by intent in one hop. For prompt-aware selection (the scorer reads the prompt and picks the cheapest capable model), use `routing:"smart"` with a `routing_profile` instead — see [ClawRouter / Smart Routing](../products/routing/clawrouter.md). +An explicit `model` always wins over `mode`. There is no prompt-aware "smart" router inside the MCP any more — that lived in the standalone [ClawRouter](../products/routing/clawrouter.md) proxy and was dropped from `blockrun_chat` because every caller here is already a frontier model reaching for something specific. ## Configuration ### Environment Variables -```bash -# Optional: Custom wallet location -export BLOCKRUN_WALLET_PATH=~/.blockrun/custom-wallet.json +All optional. The wallet file is created for you; nothing here is required for a first call. -# Optional: Session budget limit -export BLOCKRUN_SESSION_BUDGET=10.00 -``` +| Variable / File | Default | Effect | +|---|---|---| +| `~/.blockrun/.session` | auto-created on first run | EVM private key (`0600`). Also the Polymarket signer. | +| `~/.blockrun/.solana-session` | created by `action:"chain"` | Solana private key. | +| `~/.blockrun/.chain` | unset | Explicit chain preference, written only by `blockrun_wallet action:"chain"`. | +| `BLOCKRUN_WALLET_KEY` | unset | Env override of the EVM key — outranks the session file. | +| `SOLANA_WALLET_KEY` | unset | Env override of the Solana key. Set → pay on Solana (unless a stored `.chain` preference says `base`). | +| `BLOCKRUN_KEYCHAIN` | `auto` | `auto` mirrors the key into the OS keychain (macOS Keychain / Linux `secret-tool`) and keeps the file; `off` is file only; `strict` also deletes `~/.blockrun/.session` once the keychain read-back matches — this breaks other tools that read that file. | +| `BLOCKRUN_BUDGET_LIMIT` | unset | Default global session spend cap in USD (same as `action:"budget"`). | +| `BLOCKRUN_MCP_PROFILE` | `full` | Tool profile (`media` / `trading` / `research` / `chat`). | +| `BLOCKRUN_CONFIRM_SPEND` | off | `on` asks for confirmation before paid calls on clients that support MCP elicitation. `BLOCKRUN_CONFIRM_THRESHOLD` (USD) only confirms calls above that estimate. | +| `BLOCKRUN_INLINE_IMAGES` | off | `1` returns a small inline preview alongside the image URL (rich clients render it). | +| `POLYMARKET_MAX_BET_USD` | `25` | Hard per-order cap for `blockrun_polymarket`. `POLYMARKET_MAX_SESSION_USD` adds an optional session cap. | + +Chain selection priority: `.chain` preference → `SOLANA_WALLET_KEY` → first-run auto-pin → `.solana-session` exists → otherwise Base. The full Polymarket variable list is in the [repo README](https://github.com/BlockRunAI/blockrun-mcp#configuration). + +The server checks npm at startup and prints an `Update available` notice to stderr when a newer `@blockrun/mcp` exists — re-run the install command to upgrade. ### Claude Code Settings @@ -231,13 +408,14 @@ claude mcp list Remove if needed: ```bash -claude mcp remove blockrun +claude mcp remove blockrun -s user ``` ## Pricing -- **Intelligence:** Provider cost + 5% +- **Intelligence:** Provider cost, no platform margin on chat tokens, plus $0.001 per request - **Images:** $0.015-0.15 per image +- **Free tier:** `blockrun_chat mode:"free"`, `blockrun_dex`, crypto/FX/commodity `blockrun_price`, `blockrun_models`, `blockrun_polymarket_read`, and `blockrun_speech action:"voices"` cost $0 - **No subscriptions, no minimums** See [Intelligence Pricing](../products/intelligence/pricing.md) for details. @@ -246,28 +424,36 @@ See [Intelligence Pricing](../products/intelligence/pricing.md) for details. | Aspect | How It's Protected | |--------|-------------------| -| Private key | Stored locally at `~/.blockrun/` | +| Private key | Stored locally at `~/.blockrun/.session` (`0600`), or in the OS keychain with `BLOCKRUN_KEYCHAIN=strict` | | Payments | Only signatures sent, key never transmitted | -| Verification | All transactions viewable on Basescan | +| Trading | Polymarket orders are EIP-712-signed locally, `confirm:true` required, per-order cap | +| Verification | All transactions viewable on Basescan / Solscan | | Control | You control your wallet, withdraw anytime | +:::warning +Back up `~/.blockrun/.session`. It is the only key to both the payment wallet and the Polymarket deposit wallet. +::: + ## Common Commands ``` # Check balance -blockrun balance +blockrun_wallet -# Setup/view wallet -blockrun setup +# Setup/view wallet + funding QR +blockrun_wallet action:"setup" # Use a specific model -Use GPT-5 to analyze this +Use GPT-5.5 to analyze this # Generate image Create an image of [description] # Get second opinion Ask Claude Opus what it thinks about this approach + +# Draft for free +blockrun_chat mode:"free" message:"..." ``` ## Troubleshooting @@ -286,22 +472,26 @@ claude claude mcp list ``` +**`spawn npx ENOENT` / server won't connect:** reinstall with your shell PATH passed through (`-e PATH="$PATH"`, see [Installation](#installation)). + **Wallet not found:** ``` -blockrun setup +blockrun_wallet action:"setup" ``` -**Insufficient balance:** +**Insufficient balance / 402:** ``` -blockrun balance +blockrun_wallet ``` -Fund your wallet if needed. +Fund your wallet on the active chain if needed. ## Links - [GitHub: blockrun-mcp](https://github.com/BlockRunAI/blockrun-mcp) - [npm: @blockrun/mcp](https://www.npmjs.com/package/@blockrun/mcp) +- [Claude Code plugin: blockrun-claude-plugin](https://github.com/BlockRunAI/blockrun-claude-plugin) +- [Codex plugin: blockrun-codex-plugin](https://github.com/BlockRunAI/blockrun-codex-plugin) - [Claude Code Users Guide](../getting-started/claude-code.md) ## What's next? diff --git a/docs/mcp/skills.md b/docs/mcp/skills.md index fd50e7e..674576d 100644 --- a/docs/mcp/skills.md +++ b/docs/mcp/skills.md @@ -8,15 +8,39 @@ description: BlockRun skills extend Claude Code with prompt-based workflows — Skills extend what Claude Code can do. They're like plugins that add specific capabilities. :::note -A **skill** teaches Claude *how* to do a task; an **MCP server** gives Claude the *tools* to call. They often work together — the nano-banana skill uses the BlockRun MCP to generate images. +A **skill** teaches Claude *how* to do a task; an **MCP server** gives Claude the *tools* to call. They work together — the `image-prompting` skill turns "make me a cool poster" into a text-accurate prompt, then hands it to the BlockRun MCP's `blockrun_image` tool. ::: ## Available Skills -| Skill | Description | Install | -|-------|-------------|---------| -| [nano-banana](../products/creation/nano-banana.md) | Image generation | `claude skill add nano-banana` | -| [alpha-mcp](../products/trading/overview.md) | Crypto trading | `claude mcp add @blockrun/alpha` | +Every skill ships inside the [blockrun-mcp](https://github.com/BlockRunAI/blockrun-mcp) repo and is published through its Claude Code plugin marketplace. Add the marketplace once, then install whichever you need: + +``` +/plugin marketplace add BlockRunAI/blockrun-mcp +/plugin install blockrun@blockrun-mcp +``` + +| Skill | Use when | Tools it drives | +|-------|----------|-----------------| +| `blockrun` | **Start here.** Which tool answers a question, how the wallet works, how to make a first call for free. | all | +| `search` | Real-time web or news results with citations — "what just happened" questions. | `blockrun_search` | +| `exa-research` | Researching products, papers, competitors, reading pages, cited answers. | `blockrun_exa` | +| `crypto-data` | Any crypto data question — prices, FX, OHLC, DEX pairs, TVL, on-chain SQL, wallet labels. Says which of the five overlapping tools to use and which are free. | `blockrun_price` `blockrun_dex` `blockrun_defi` `blockrun_surf` `blockrun_rpc` | +| `surf` | Deep crypto data — on-chain SQL, CEX order books, wallet net worth, social mindshare, news. | `blockrun_surf` | +| `rpc` | Raw blockchain JSON-RPC — contract reads, balances, blocks, logs, gas — across 40 chains. | `blockrun_rpc` | +| `prediction-markets` | Event probabilities, Polymarket / Kalshi odds, finding markets on a topic. | `blockrun_markets` | +| `polymarket-trading` | Actually placing, managing, or redeeming real bets — setup, funding, confirm-gated orders. | `blockrun_polymarket` `blockrun_polymarket_read` | +| `signal-to-trade-demo` | A presentation-ready live signal (price, probability history, smart money, liquidity) ending in a safe order preview. | `blockrun_markets` `blockrun_polymarket_read` | +| `image-prompting` | Generating or editing images — structured, text-accurate prompts. | `blockrun_image` | +| `phone` | Number intelligence (carrier, SIM-swap, call-forwarding), US/CA number leases, outbound AI calls. | `blockrun_phone` | +| `modal` | Running code in a disposable remote container, optional GPU. | `blockrun_modal` | +| `gentech-blockrun` | GenTech Labs' daily-usage and multi-tool pipeline patterns from Hermes Agent. | all | + +Install any of them the same way: `/plugin install @blockrun-mcp`, e.g. `/plugin install polymarket-trading@blockrun-mcp`. The same commands work from the shell as `claude plugin marketplace add BlockRunAI/blockrun-mcp` and `claude plugin install @blockrun-mcp`. + +:::info{title="Trading with alpha-mcp"} +[alpha-mcp](../products/trading/overview.md) is a separate MCP server, not a skill: `claude mcp add alpha -s user -- npx -y @blockrun/alpha@latest`. See [Trading installation](../products/trading/installation.md). +::: ## What Are Skills? @@ -27,23 +51,30 @@ Skills are prompt-based extensions that teach Claude new workflows. Unlike MCP s ## Installing Skills -### From BlockRun +### From the BlockRun marketplace -```bash -# Add nano-banana skill -claude skill add nano-banana ``` +# Once per machine +/plugin marketplace add BlockRunAI/blockrun-mcp + +# Then per skill +/plugin install blockrun@blockrun-mcp +/plugin install image-prompting@blockrun-mcp +``` + +The skills only describe workflows — the tools they call come from the MCP server, so install [BlockRun MCP](blockrun-mcp.md) first. ### Manual Installation -Skills are markdown files in your Claude Code skills directory: +A skill is a directory containing a `SKILL.md`. Copy one into your Claude Code skills directory: ```bash # Find skills directory ls ~/.claude/skills/ -# Add a skill manually -cp my-skill.md ~/.claude/skills/ +# Add a skill manually (directory, not a single file) +git clone https://github.com/BlockRunAI/blockrun-mcp +cp -r blockrun-mcp/skills/search ~/.claude/skills/search ``` ## Using Skills @@ -51,7 +82,7 @@ cp my-skill.md ~/.claude/skills/ Skills are invoked with slash commands: ``` -/nano-banana "a minimalist logo for a crypto trading bot" +/image-prompting "a minimalist logo for a crypto trading bot" ``` Or by natural language when Claude recognizes the task: @@ -60,19 +91,19 @@ Or by natural language when Claude recognizes the task: Generate an image of a futuristic AI agent ``` -Claude will use the nano-banana skill automatically. +Claude will pick up the `image-prompting` skill automatically, because its description lists the phrases that should trigger it. ## Creating Custom Skills -Skills are markdown files with a specific format: +A skill is a `SKILL.md` with YAML frontmatter followed by instructions: ```markdown --- name: my-skill -description: What this skill does -triggers: - - "create a post" - - "write a tweet" +description: | + What this skill does, and when Claude should reach for it. + TOOLS: blockrun_search, blockrun_chat. + TRIGGERS: create a post, write a tweet --- # My Skill @@ -95,13 +126,15 @@ Instructions for Claude on how to perform this task... | Field | Required | Description | |-------|----------|-------------| -| `name` | Yes | Skill identifier | -| `description` | Yes | Brief description | -| `triggers` | No | Phrases that activate the skill | +| `name` | Yes | Skill identifier (matches the directory name) | +| `description` | Yes | What it does and when to use it. Put trigger phrases here — Claude matches on the description, and this is the only field it reads before deciding to load the skill. | ### Skill Content -The body of the skill file is instructions for Claude. Write it like you're teaching someone the task. +The body of the skill file is instructions for Claude. Write it like you're teaching someone the task. Two rules the BlockRun skills follow that are worth copying: + +- **Never put a price in a skill.** Read it from `blockrun_models` or the `402` response — every typed price in the repo drifted the last time the transaction fee changed. +- **Say which tools don't exist on the other chain.** `blockrun_defi` and `blockrun_modal` are Base-only; a skill that sends a Solana user there without saying so produces a confusing error. ## Skills vs MCP Servers @@ -109,8 +142,8 @@ The body of the skill file is instructions for Claude. Write it like you're teac |---------|--------|-------------| | What they provide | Instructions | Tools | | Written in | Markdown | Code (JS/Python) | -| Installation | `skill add` | `mcp add` | -| Example | nano-banana | blockrun-mcp | +| Installation | `/plugin install` | `claude mcp add` | +| Example | image-prompting | blockrun-mcp | **Use skills when:** You want to teach Claude a workflow **Use MCP when:** You need Claude to call external services @@ -121,61 +154,48 @@ Skills often work alongside MCP servers: | Skill | Uses MCP | |-------|----------| -| nano-banana | blockrun-mcp (for generation) | -| alpha-mcp | alpha-mcp (for trading tools) | +| image-prompting | blockrun-mcp (`blockrun_image`) | +| polymarket-trading | blockrun-mcp (`blockrun_markets` → `blockrun_polymarket`) | +| crypto-data | blockrun-mcp (five data tools) | ## Managing Skills -### List Installed Skills +Open the plugin manager in Claude Code to see what's installed, update, or remove: -```bash -claude skill list ``` - -### Remove a Skill - -```bash -claude skill remove nano-banana +/plugin ``` -### Update a Skill - -```bash -claude skill update nano-banana -``` +Skills installed by hand live under `~/.claude/skills//SKILL.md`; delete the directory to remove one. ## Troubleshooting ### Skill Not Triggering -Check if the skill is installed: -```bash -claude skill list -``` - -Try explicit invocation: +Check that the marketplace and skill are installed (`/plugin`), then try explicit invocation: ``` /skill-name command ``` +If the skill loads but the tool call fails, the problem is the MCP, not the skill — run `claude mcp list` and see [Troubleshooting](troubleshooting.md). + ### Skill Conflicts -If multiple skills handle similar tasks, use explicit invocation to specify which one. +If multiple skills handle similar tasks, use explicit invocation to specify which one. `crypto-data` exists for exactly this: it routes across the five overlapping crypto tools. ### Skill Not Found -```bash -# Reinstall -claude skill remove skill-name -claude skill add skill-name +``` +/plugin marketplace add BlockRunAI/blockrun-mcp +/plugin install skill-name@blockrun-mcp ``` ## What's next? ::::cards -:::card{title="nano-banana skill" href="../products/creation/nano-banana.md" icon="Image"} -Generate images via micropayments with the bundled skill. +:::card{title="Image generation" href="../products/creation/nano-banana.md" icon="Image"} +Generate images via micropayments with `blockrun_image` and the image-prompting skill. ::: :::card{title="BlockRun MCP" href="blockrun-mcp.md" icon="Boxes"} diff --git a/docs/mcp/troubleshooting.md b/docs/mcp/troubleshooting.md index 665b5f7..bf96127 100644 --- a/docs/mcp/troubleshooting.md +++ b/docs/mcp/troubleshooting.md @@ -29,6 +29,29 @@ claude claude mcp list ``` +### `blockrun` doesn't connect / "MCP server failed" / `spawn npx ENOENT` + +**Cause:** Almost always a PATH issue — Claude Code can't find `node`/`npx` on its launcher PATH. Common with Homebrew and nvm installs, on the CLI *and* the desktop app. + +**Solution:** Reinstall with your shell PATH passed through: +```bash +claude mcp remove blockrun -s user +claude mcp add blockrun -s user -e PATH="$PATH" -- npx -y @blockrun/mcp@latest +``` +Then restart Claude Code. Or pin absolute paths (`which npx`). + +### `claude mcp list` doesn't show `blockrun` + +**Solution:** +```bash +# Node must be 20.19 or newer +node --version + +# Clear the npx cache, then re-run the install +rm -rf ~/.npm/_npx +claude mcp add blockrun -s user -- npx -y @blockrun/mcp@latest +``` + ### "Permission denied" during install **Cause:** npm doesn't have write permission. @@ -55,65 +78,77 @@ brew install node # Or download from https://nodejs.org # Verify -node --version # Should be 18+ +node --version # Must be 20.19+ ``` +### "Update available" on startup + +Not an error. The server checks npm at startup and prints this to stderr when a newer `@blockrun/mcp` exists. `npx -y @blockrun/mcp@latest` picks up the new version on the next restart; clear `~/.npm/_npx` if it keeps starting an old one. + ## Wallet Issues ### "Wallet not found" -**Cause:** No wallet has been created yet. +**Cause:** The wallet is created automatically the first time the server starts, so this usually means the server never started (see [Installation Issues](#installation-issues)) or the key file was moved. **Solution:** ``` # In Claude Code -blockrun setup +blockrun_wallet action:"setup" ``` -This creates a wallet at `~/.blockrun/wallet.json`. +This creates the wallet if it is missing and prints the address plus a funding QR. The key lives at `~/.blockrun/.session` (mode `0600`). -### "Insufficient balance" +### "Insufficient balance" / HTTP 402 after retry -**Cause:** Not enough USDC in your wallet. +**Cause:** Not enough USDC on the **active** chain. The server holds a Base wallet and a Solana wallet but pays from one at a time. **Solution:** -1. Check balance: `blockrun balance` -2. Fund wallet with USDC on Base network -3. Verify balance updated +1. Check balance and active chain: `blockrun_wallet` +2. Fund that wallet — USDC on Base, or USDC (SPL) on Solana — or buy with a card via `blockrun_wallet action:"deposit"` (Base) +3. Verify balance updated, then retry the original call. Don't retry-loop the failing tool: the wallet is empty until funded. -### "Transaction failed" +### Balance funded but still shows zero -**Causes:** -- Network congestion -- Insufficient balance -- RPC endpoint issues +Wait for network confirmation and re-check. Base finality is seconds, not instant; a card on-ramp can take a few minutes. -**Solutions:** -```bash -# Check balance -blockrun balance +### "402 Payment Required" came back twice -# Check Base network status -curl https://mainnet.base.org -X POST \ - -H "Content-Type: application/json" \ - -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' +**Cause:** The payment signature did not verify. Usually the wallet has no key, or the active chain doesn't match the chain the request was priced on. -# Retry after a few seconds -``` +**Solution:** `blockrun_wallet` to confirm the active chain has a funded wallet, then retry once. + +### `fetch failed` / balance-check timeout + +**Cause:** A transient Base RPC outage. The tool falls through several public RPCs on its own. + +**Solution:** Retry after 30 seconds. If it persists, a local proxy or firewall is blocking outbound RPC traffic. + +### `~/.blockrun/.session no longer exists because BLOCKRUN_KEYCHAIN=strict retired it` + +**Cause:** You opted into `BLOCKRUN_KEYCHAIN=strict`, so the key lives only in the OS keychain, and the keychain could not be read (locked, ACL denial, timeout). The server stops rather than silently minting a new empty wallet. + +**Solution:** Unlock the keychain and restart, or export the key as `BLOCKRUN_WALLET_KEY`. Do not run `setup` expecting it to recover funds — the funded wallet is still in the keychain. + +### `SOLANA_WALLET_KEY is set but the active chain is BASE` + +**Cause:** A stored chain preference (`~/.blockrun/.chain`) outranks the env var. -### "Invalid private key" +**Solution:** `blockrun_wallet action:"chain" chain:"solana"` — switching explicitly also clears the stored preference. -**Cause:** Corrupted wallet file or incorrect format. +### Want a different key + +**Cause:** Replacing `~/.blockrun/.session` is how a wallet gets rotated or restored from backup; a `BLOCKRUN_WALLET_KEY` env var outranks the file. **Solution:** ```bash -# Backup existing wallet -mv ~/.blockrun/wallet.json ~/.blockrun/wallet.json.backup +# Back up the current key first — it is the only key to the payment wallet AND the Polymarket deposit wallet +cp ~/.blockrun/.session ~/.blockrun/.session.backup -# Create new wallet -# In Claude Code: -blockrun setup +# Then either drop in the replacement key, or export it +export BLOCKRUN_WALLET_KEY=0x... ``` +`blockrun_wallet action:"setup"` prints the address the server is actually signing with. ## Connection Issues @@ -128,9 +163,15 @@ blockrun setup ### "Rate limited" -**Cause:** Too many requests in a short period. +**Cause:** Too many requests in a short period. Only the free tier is rate limited; paid calls are not. + +**Solution:** Wait 60 seconds and retry, or pass a paid `mode` / explicit `model`. Consider session budgets to pace unattended agents. + +### "The free tier did not answer within …s" + +**Cause:** Free capacity is saturated. `mode:"free"` tries each free model in turn and gives up after a deadline rather than hanging. -**Solution:** Wait 60 seconds and retry. Consider using session budgets to pace requests. +**Solution:** Retry shortly, or pass an explicit model (or a paid `mode`) to skip the free tier. Nothing was charged. ### "API error: 500" @@ -145,7 +186,7 @@ blockrun setup ### "Tool not available" -**Cause:** MCP not properly initialized. +**Cause:** MCP not properly initialized, or the server was installed with a trimmed `--profile` that doesn't include the tool (`modal` and `phone` are `full`-profile only). **Solution:** ```bash @@ -154,15 +195,15 @@ claude mcp list # Should show "blockrun" in the list # If not, reinstall: -claude mcp remove blockrun +claude mcp remove blockrun -s user claude mcp add blockrun -s user -- npx -y @blockrun/mcp@latest ``` -### "Invalid model" +### "Invalid model" / a model id 404s -**Cause:** Requested model doesn't exist or isn't supported. +**Cause:** Requested model doesn't exist, or was delisted. Some ids redirect, some are gone. -**Solution:** Check available models at [Models Reference](../api-reference/models.md). +**Solution:** Run `blockrun_models` for the live list, or check [Models Reference](../api-reference/models.md). ### "Image generation failed" @@ -176,34 +217,60 @@ claude mcp add blockrun -s user -- npx -y @blockrun/mcp@latest 2. Simplify prompt (remove potentially flagged content) 3. Try again with shorter prompt +### "Video generation timed out" / "Music generation timed out" + +**Cause:** Upstream queue congestion. Both tools are async and payment-on-completion; a typical clip takes 60–180s and the video poll budget is 9 minutes. + +**Solution:** **No charge** was made. Retry, or pick a faster model. Don't retry-loop — let one job finish. + +### Modal sandbox charged more than expected + +**Cause:** `timeout` is the billed lifetime, charged up front and never refunded — you pay for the time you ask for, not the time you use, and terminating early refunds nothing. + +**Solution:** Request the lifetime you actually need rather than a safe-looking ceiling. + +### Polymarket: buy on a "winner"-style (neg-risk) market fails, or `redeem` reverts, though `setup` shows ready + +**Cause:** A deposit wallet provisioned before an upgrade may lack some on-chain approvals. + +**Solution:** Re-run `blockrun_polymarket action:"setup" confirm:true` once. See the [setup guide](https://github.com/BlockRunAI/blockrun-mcp/blob/main/docs/polymarket-trading-setup.md). + +### Polymarket order refused without a network call + +**Cause:** Server-side safety rails — every order, approval and redeem needs `confirm:true`, and each order is capped by `POLYMARKET_MAX_BET_USD` (default $25). Without `confirm` you get a dry-run. + +**Solution:** Preview with `blockrun_polymarket_read action:"preview"`, get the user's explicit approval for that exact trade, then place it with `confirm:true`. + +### "Per-call cap refused to sign" + +That is a client-side spending hook (a plugin `PreToolUse` gate or `BLOCKRUN_CONFIRM_SPEND`), not BlockRun rejecting the call. Raise `BLOCKRUN_ASK_THRESHOLD` / `BLOCKRUN_SESSION_CAP` (plugin) or `BLOCKRUN_CONFIRM_THRESHOLD` (MCP), or approve the prompt. + ## Environment Issues ### Wrong wallet being used -**Cause:** Environment variable overriding default location. +**Cause:** An env var outranks the key file. Precedence: `BLOCKRUN_WALLET_KEY` → `~/.blockrun/.session` → OS keychain (only once the file is gone). **Solution:** ```bash # Check environment echo $BLOCKRUN_WALLET_KEY -echo $BLOCKRUN_WALLET_PATH +echo $SOLANA_WALLET_KEY +echo $BLOCKRUN_KEYCHAIN # Unset if needed unset BLOCKRUN_WALLET_KEY -unset BLOCKRUN_WALLET_PATH +unset SOLANA_WALLET_KEY ``` +`blockrun_wallet action:"setup"` always prints the address actually in use. -### Using wrong network +### Wrong chain being used -**Cause:** Custom RPC configured for wrong network. - -**Solution:** -```bash -# Verify using Base mainnet -echo $BASE_RPC_URL +**Cause:** Chain selection priority is `~/.blockrun/.chain` (explicit preference) → `SOLANA_WALLET_KEY` → first-run auto-pin → `~/.blockrun/.solana-session` exists → otherwise Base. -# Should be empty (uses default) or: -# https://mainnet.base.org +**Solution:** Set it explicitly — that clears the auto-pin: +``` +blockrun_wallet action:"chain" chain:"base" # or "solana" ``` ### Want to pay on Solana instead of Base @@ -218,28 +285,21 @@ blockrun_wallet action:"setup" # Solana address + funding QR Applies instantly — no env vars, no file editing, no restart. Switch back with `chain:"base"`. :::info -`blockrun_image`, `blockrun_music`, `blockrun_speech`, `blockrun_video`, paid stock `blockrun_price`, `blockrun_chat routing:"smart"`, and native Anthropic (`claude-*`) settle on Base only. +`blockrun_music`, `blockrun_speech`, `blockrun_video`, `blockrun_modal`, `blockrun_defi`, paid `blockrun_realface`, paid stock `blockrun_price`, and native Anthropic (`claude-*`) settle on Base only. In Solana mode they return a "switch to Base" message instead of charging. `blockrun_image` pays on either chain. ::: ## Getting Help ### Check Logs -```bash -# Claude Code logs -cat ~/.claude/logs/latest.log - -# Look for MCP-related errors -grep -i "blockrun\|mcp" ~/.claude/logs/latest.log -``` - -### Debug Mode +The MCP server writes its own diagnostics (startup, profile, update notices, keychain fallbacks) to stderr, which Claude Code captures. Start Claude Code with debug output to see them: ```bash -# Run with verbose logging -DEBUG=* claude +claude --debug ``` +`claude mcp list` shows whether the `blockrun` server connected. + ### Report Issues If you can't resolve an issue: @@ -251,11 +311,12 @@ If you can't resolve an issue: - Steps to reproduce - Claude Code version: `claude --version` - Node version: `node --version` +- `@blockrun/mcp` version: `npm view @blockrun/mcp version` - OS: `uname -a` ::: :::step{title="Open an issue"} -Report at [GitHub Issues](https://github.com/BlockRunAI/blockrun-mcp/issues). +Report at [GitHub Issues](https://github.com/BlockRunAI/blockrun-mcp/issues). Security issues: see the repo's [SECURITY.md](https://github.com/BlockRunAI/blockrun-mcp/blob/main/SECURITY.md) for private reporting. ::: :::: @@ -265,8 +326,11 @@ Report at [GitHub Issues](https://github.com/BlockRunAI/blockrun-mcp/issues). | Issue | Quick Fix | |-------|-----------| | MCP not loading | Restart Claude Code | -| No wallet | `blockrun setup` | -| No balance | Fund wallet on Base | +| `spawn npx ENOENT` | Reinstall with `-e PATH="$PATH"` | +| No wallet | `blockrun_wallet action:"setup"` | +| No balance / 402 | `blockrun_wallet`, fund on the active chain | +| Wrong chain | `blockrun_wallet action:"chain" chain:"base"` | +| Video / music timed out | Not charged — retry | | Network error | Check internet, retry | | Tool error | `claude mcp list` to verify | diff --git a/docs/products/franklin.md b/docs/products/franklin.md index c33a953..b02d7cf 100644 --- a/docs/products/franklin.md +++ b/docs/products/franklin.md @@ -5,12 +5,12 @@ description: Franklin is the AI agent with a wallet — it writes code and spend # Franklin Agent -**The AI agent with a wallet.** Other coding agents write code; Franklin writes code *and spends money to get the job done* — picking the best model per task, buying data, generating media, paying for search, all autonomously from one USDC wallet. +**The AI agent with a wallet.** Other coding agents write code; Franklin writes code *and spends money to get the job done* — picking the best model per task, buying data, generating media, paying for search, proposing trades you approve, all autonomously from one USDC wallet. -**Source:** [github.com/BlockRunAI/Franklin](https://github.com/BlockRunAI/Franklin) · [npm: @blockrun/franklin](https://www.npmjs.com/package/@blockrun/franklin) · Apache-2.0 +**Source:** [github.com/BlockRunAI/Franklin](https://github.com/BlockRunAI/Franklin) · [npm: @blockrun/franklin](https://www.npmjs.com/package/@blockrun/franklin) · Apache-2.0 · current release **3.42.0** (2026-08-29) :::tip{title="YOPO — You Only Pay Outcome"} -Not a subscription (pay for access), not generic pay-per-call (pay for trying). You set an outcome and a budget; Franklin decides what to call, what to pay for, and when to stop. Provider cost + 5%, settled per action in USDC — no monthly fees, no rate limits. +Not a subscription (pay for access), not generic pay-per-call (pay for trying). You set an outcome and a budget; Franklin decides what to call, what to pay for, and when to stop. Provider cost + 5%, settled per action in USDC — no monthly fees, no rate limits, and no overdraft: when the wallet is empty, Franklin stops. ::: ## Quick start @@ -18,10 +18,11 @@ Not a subscription (pay for access), not generic pay-per-call (pay for trying). ::::steps :::step{title="Install"} -Requires Node.js 20.19+ (Node 22 LTS recommended). +Requires Node.js 20.19+ (Node 22 LTS recommended). Older Node crashes at startup with `ERR_REQUIRE_ESM`. ```bash npm install -g @blockrun/franklin +franklin --version ``` ::: @@ -34,30 +35,99 @@ franklin ::: :::step{title="Fund a wallet to unlock everything"} -Add USDC to unlock Claude, GPT, Gemini, Grok, and every paid API (trading data, image/video, web search). +Add USDC to unlock Claude, GPT, Gemini, Grok, and every paid API (market data, image/video/music, web search, prediction markets, RPC). Since 3.41.0 a fresh install defaults to **Solana**; pass `base` to create a Base wallet instead. ```bash -franklin setup base # or: franklin setup solana +franklin setup # Solana wallet (default) +franklin setup base # or: a Base wallet franklin balance # show address + USDC balance ``` + +You can also buy USDC with a card from inside Franklin — the onramp link targets whichever chain your wallet is on. Switch chains any time with `franklin solana` or `franklin base`; an existing Base wallet stays on Base until you choose otherwise. ::: :::: -No install? Run it directly with `npx @blockrun/franklin`. +No install? Run it directly with `npx @blockrun/franklin`. If a global install fails with `EACCES`, don't use `sudo` — use `npx`, or install Node through `nvm`/`fnm` so global packages live in your home directory. + +## What Franklin can do + +Franklin is chat-first: you state an outcome, and it decides what to read, search, fetch, call, and pay for. Every tool call is itemized and priced; run `/cost` any time to see where the USDC went. + +| Area | Built-in tools | +|---|---| +| Code & files | `Read`, `Write`, `Edit`, `Bash`, `Glob`, `Grep`, `Task` (sub-agents), detached background tasks (`franklin task`) | +| Research | `WebSearch`, `WebFetch`, Exa neural search / answer / read-URLs, `MemoryRecall` | +| Trading & markets | `TradingSignal`, `TradingMarket`, paper-trading portfolio (`TradingPortfolio` / `TradingOpenPosition` / `TradingClosePosition` / `TradingHistory`), `TradePlan`, DEX quotes on Solana (Jupiter) and Base (0x), `PolymarketBet`, `PredictionMarket`, DeFi protocol / chain / yield / price lookups, `MultiChainRPC` (read-only, 40+ chains), `Wallet` — see [Trading](trading/overview.md) | +| Media | `ImageGen`, `VideoGen`, `MusicGen`, `RealFace` avatars, a content library (`franklin content`) | +| Social & comms | `SearchX`, `PostToX`, `BrowserX`, phone numbers + voice calls, webhooks | +| Compute | GPU sandbox (`ModalCreate` / `ModalExec` / `ModalStatus` / `ModalTerminate`) | +| Autonomy | `Monitor` (watch long-running commands), `Scheduler` (durable `/loop`), `UpdateGoal` (`/goal` mode), lifecycle hooks | +| Extensibility | MCP servers (auto-discovered, `/mcp`), Plugin SDK, skills (`franklin skills`), paid skills from the BlockRun agent marketplace | + +Every paid call goes through the BlockRun gateway over x402 — Franklin never holds an API key for any provider. + +## Guardrails on money + +Autonomy over a wallet needs architecture, not vibes. Every spending path in Franklin has a hard bound and an off switch: + +- **The wallet balance is the hard limit.** `--max-spend ` adds a per-session cap on gateway spend; the session stops when it's exceeded. +- **Real-money trades need an approved plan.** Before any swap or prediction-market order, Franklin proposes a `TradePlan` (venue, asset, size, slippage, stop condition, rationale, total spend). Nothing executes until you approve; approved budgets draw down per trade and expire after 15 minutes. This holds in every permission mode, `--trust` included. Headless runs fail closed unless you pass `--approve-trades`, and even then the plan must fit inside what's left of `--max-spend`. +- **Lifecycle hooks veto before money moves.** Drop JSON hooks in `~/.blockrun/hooks/`; `PreSpend` fires for any money-moving tool with the estimated USD on stdin and can deny it. Examples ship for a daily spend cap, a token blacklist, and a spend ledger. +- **Every approval and denial is recorded** in `~/.blockrun/approvals.jsonl`. + +Details in [Risk Management](trading/risk-management.md). + +## CLI reference + +```bash +franklin # interactive session (default chain) +franklin solana | franklin base # start on a specific chain (and remember it) +franklin -p "prompt" # one-shot, non-interactive (add --approve-trades to allow plans within --max-spend) +franklin --max-spend 5 # hard USD cap on this session's spend +franklin --trust # skip permission prompts for tools (trade plans still require approval) +franklin --model # pin a model; or a router profile: auto | eco | premium | free +franklin -c / -r [id] # continue the last session / resume by id +franklin --from claude|codex # start from another agent's session context + +franklin setup [base|solana] # create a wallet +franklin balance # address + USDC balance +franklin models # model catalog +franklin doctor # health check: node, wallet, chain, gateway, MCP, telemetry +franklin insights / stats / search # cost analytics, usage stats, full-text session search +franklin predict -m -q "" # headless forecast with a research-only toolset +franklin proxy / init / daemon # payment proxy for Anthropic-compatible CLIs (port 8402) +franklin telegram / slack # remote control from chat (owner-locked) +franklin serve / panel # local agent server + browser mission control (many agents, remote approvals) +franklin skills / plugins / mcp / migrate / task / content +``` + +Inside a session: `/model`, `/plan` / `/execute`, `/ultrathink`, `/compact`, `/cost`, `/goal ` (autonomous goal with adversarially verified completion), `/loop `, `/remember` / `/flush` / `/dream` (memory), `/session-search`, `/mcp`, `/insights`, `/help`. + +## Surfaces + +- **CLI** — the reference surface, above. +- **Franklin Desktop (beta)** — native macOS and Windows workspace built on the same runtime, wallet, tools, and session history as the CLI (visual chat, history, gallery, wallet, tools, skills, and CLI panels). Local-first: it works in `Documents/Franklin` and asks before touching files outside the workspace or running shell commands. Beta builds are unsigned test installers — macOS Apple-silicon `.dmg` pre-releases on the [Franklin releases page](https://github.com/BlockRunAI/Franklin/releases), Windows x64 `.exe` from Desktop CI; signed downloads and auto-update are not live yet. Source lives in `apps/desktop` of the Franklin repo. +- **Franklin for VS Code** — [marketplace extension](https://marketplace.visualstudio.com/items?itemName=blockrun.franklin-vscode) (publisher `blockrun`): chat panel, model picker, wallet balance, image/video generation, inline diff cards. Shares `~/.blockrun/` config and sessions with the CLI. +- **Telegram / Slack** — `franklin telegram` (set `TELEGRAM_BOT_TOKEN` + `TELEGRAM_OWNER_ID`) or `franklin slack` on an always-on machine; sessions resume across restarts. +- **Mission control** — `franklin serve` + `franklin panel`: dispatch one agent per strategy or market, watch them stream, and approve their trade plans and permission requests from one browser page. ## How Franklin fits Franklin is the autonomous agent on top of the BlockRun stack — it uses the same pieces you can use directly: -- **Models & routing** — picks the best model per task via [ClawRouter](routing/clawrouter.md)'s scoring, across 72 chat models. -- **Paid APIs** — search, market data, media, RPC, and more, paid per call over [x402](../x402/how-it-works.md). -- **One wallet** — the wallet is the identity; fund it on Base or Solana ([Wallet Setup](../getting-started/wallet-setup.md)). +- **Models & routing** — picks the best model per task via [ClawRouter](routing/clawrouter.md)'s scoring, across 72 chat models. Four profiles: `auto`, `eco`, `premium`, `free`. +- **Paid APIs** — search, market data, media, RPC, prediction markets and more, paid per call over [x402](../x402/how-it-works.md). +- **One wallet** — the wallet is the identity; fund it on Solana or Base ([Wallet Setup](../getting-started/wallet-setup.md)). ## What's next? ::::cards +:::card{title="Trading with Franklin" href="trading/overview.md" icon="TrendingUp"} +Signals, paper trading, prediction markets, and the trade-plan gate that protects real money. +::: + :::card{title="ClawRouter" href="routing/clawrouter.md" icon="Route"} The router Franklin uses — 15-dimension scoring plus portfolio ranking picks the cheapest capable model. ::: @@ -67,7 +137,7 @@ Prefer Claude Code / Cursor? Get the same tools as MCP commands. ::: :::card{title="Wallet Setup" href="../getting-started/wallet-setup.md" icon="Wallet"} -Fund on Base or Solana and set budgets for autonomous spend. +Fund on Solana or Base and set budgets for autonomous spend. ::: :::: diff --git a/docs/products/intelligence/overview.md b/docs/products/intelligence/overview.md index 9ef60d0..c9dbffd 100644 --- a/docs/products/intelligence/overview.md +++ b/docs/products/intelligence/overview.md @@ -89,7 +89,7 @@ See [Pricing](pricing.md) for complete list and calculator. ## Pricing Model ``` -Your cost = Provider cost + 5% +Your cost = Provider cost (no platform margin on chat tokens) + $0.001 per request ``` The 5% covers: diff --git a/docs/products/intelligence/pricing.md b/docs/products/intelligence/pricing.md index 13b7f10..cbbe794 100644 --- a/docs/products/intelligence/pricing.md +++ b/docs/products/intelligence/pricing.md @@ -28,7 +28,7 @@ Media generation and Live Search still carry a 5% platform margin, which covers: | DeepSeek V4 Flash Chat | ~5M input tokens | | Gemini 3.5 Flash | ~635K input tokens | | Image generation | ~10–65 images | -| **Free tier** (8 models — reasoning, coding, and vision) | **Unlimited (FREE)** | +| **Free tier** (5 models — reasoning, coding, and vision) | **Unlimited (FREE)** | :::tip{title="Start with the free tier"} The free tier costs $0 — 10 reasoning, coding, and vision models with no per-token charge. You still need a funded wallet for the x402 handshake, but these calls don't draw it down. diff --git a/docs/products/routing/benchmarks.md b/docs/products/routing/benchmarks.md index 0b74734..261bcfb 100644 --- a/docs/products/routing/benchmarks.md +++ b/docs/products/routing/benchmarks.md @@ -60,6 +60,17 @@ number. tier absorbs the majority of traffic. `auto` trades some of that for headroom on quality, which is why it is the default. +These are the **priced** picks, and they are not always the router's actual +primary. ClawRouter's `auto` MEDIUM primary is `moonshot/kimi-k2.7` and its +REASONING primary (both profiles) is `xai/grok-4-1-fast-reasoning`; both are +withheld from `GET /api/v1/models`, and a public claim priced on a model you +cannot look up is not defensible. Where the primary is hidden, the mix prices +the cheapest *visible* model in the same tier's fallback chain instead. That +substitution only ever moves the published figure down, so the number is +conservative. The `eco` SIMPLE row moved the same way when NVIDIA retired +`deepseek-v4-flash` on 2026-08-12 — its replacement is also free, so the `eco` +figure did not change. + ## Reproduce it Prices are **not** frozen into the figure. They are read from the live model @@ -75,6 +86,56 @@ The assumptions live in `src/brand/savings-mix.json`, and the published result is served at [`/brand/numbers.json`](https://blockrun.ai/brand/numbers.json). If your arithmetic disagrees with ours, the inputs are all there to find out why. +## Does the router pick *better*, not just cheaper? + +The savings figure above is a price calculation. Whether the router's choices +actually complete tasks is a separate question, and it is measured separately — +on full agent sessions, in [router-core](https://github.com/BlockRunAI/router-core), +the engine that makes the decision for ClawRouter, the SDKs, Franklin and +ClawRouter-Hermes. + +Three public benchmark families, run through one host framework with their own +validators, three arms per task — the previous rules router, the constraint-first +V3.4 router, and a pinned flagship — sharing a frozen model catalog, pricing +snapshot, tool surface and scorer: + +| Source family | Share of strict cohort | What it measures | +|---|---:|---| +| τ-bench family | 55% | Stateful tool use under domain policies | +| BrowseComp | 25% | Multi-hop web research ending in an exact answer | +| Terminal-Bench | 20% | End-to-end terminal and repository work | + +**V3.4 checkpoint:** verified task success **49% → 57%** (+8 points) over the +previous rules router. Normalized cost per *successful* task fell **6.4%**. +Against pinning the flagship on every task, the router used **8.9%** of the +normalized token cost while giving up 10 points of success. + +And the limits, because a benchmark that only publishes wins is not evidence: +the paired 95% interval on the quality gain is **−1.9 to +15.5 points** and +crosses zero; the router is *not* statistically proven better across the +production distribution; it does not match flagship quality; p95 session +latency regressed. The machine-readable scorecard records +`releaseEligible: false`. Method, figures and the four failure classes are in +the [constraint-first router report](https://blockrun.ai/signal/router-v3-4-constraint-first-auto-routing). + +The decision itself is cheap: **~0.05 ms warm, ~0.15 ms including JIT warm-up** +on mixed prompt shapes up to 33KB (Node 22, M-series laptop). Feature extraction +is bounded, so a 400KB prompt still routes in under 0.1 ms. No network call is +made to decide. + +## Per-model latency is a dated snapshot + +ClawRouter's repo also carries a gateway latency run — every model at the time, +two coding prompts each, 256 max tokens, non-streaming, measured end-to-end +through payment verification. It is dated **March 16, 2026**, deliberately not +refreshed, and should be read as a measurement of that day rather than a current +ranking: Grok 4 Fast answered in 1,143 ms while GPT-5.4 took 6,213 ms for the +same request. The write-up is +[LLM Router Benchmark: 39 models](https://github.com/BlockRunAI/ClawRouter/blob/main/docs/llm-router-benchmark-46-models-sub-1ms-routing.md); +for current per-model latency, p95, uptime and error rate, use the +[Observatory](https://blockrun.ai/observatory), which is also the feed +router-core's `speed` and `reliability` terms are designed to consume. + ## What this does not claim Three things this page is **not** saying: @@ -88,13 +149,16 @@ call to a frontier model costs marginally *more* here than buying that model's tokens directly. The saving comes from the 80% of requests that never needed the frontier model, not from a better rate on the ones that did. -**Not a quality benchmark.** These are cost figures. A profile that routes a -COMPLEX request to a smaller model saves money and may answer worse. `auto` -exists because that trade is real; measure it on your own traffic before -adopting `eco`. +**Not a quality benchmark.** The savings figures are cost figures. A profile that +routes a COMPLEX request to a smaller model saves money and may answer worse. +`auto` exists because that trade is real; the agent checkpoint above is the +closest thing to a quality measurement, and it is published with its confidence +interval for a reason. Measure it on your own traffic before adopting `eco`. ## Related - [ClawRouter](/docs/products/routing/clawrouter) — how constraints pick a model +- [router-core](https://github.com/BlockRunAI/router-core) — the shared routing + engine, its benchmark method and the decision-snapshot corpus - [Pricing](/docs/products/intelligence/pricing) — per-model rates and the per-request fee diff --git a/docs/products/routing/clawrouter.md b/docs/products/routing/clawrouter.md index acd9a9b..063bbd4 100644 --- a/docs/products/routing/clawrouter.md +++ b/docs/products/routing/clawrouter.md @@ -10,17 +10,18 @@ description: ClawRouter is a smart LLM router for OpenClaw that picks the optima ClawRouter is a smart LLM router for OpenClaw that routes every request to the cheapest model that can handle it. One wallet, 72 models, zero API keys. :::tip{title="In a hurry?"} -Install, fund a wallet, then run `/model blockrun/auto` in any OpenClaw conversation — that's it. +Install, fund a wallet, then run `/model blockrun/auto` in any OpenClaw conversation — that's it. Current release: **v0.12.250** (August 29, 2026). ::: ## Overview ClawRouter analyzes your prompt and automatically picks the right model tier: -- **Simple questions** → Cheap models (DeepSeek, Gemini Flash) -- **Medium complexity** → Balanced models (Gemini 3.5 Flash, Claude Haiku) -- **Complex reasoning** → Premium models (Claude Opus 5, GPT-5.5) -- **Code generation** → Specialized models (Claude Sonnet 4.6, GLM-5.2) +- **Simple questions** → Cheap models (Gemini 2.5 Flash on `auto`; a free NVIDIA-hosted model on `eco`) +- **Medium complexity** → Balanced models (Kimi K2.7) +- **Complex reasoning** → Premium models (Gemini 3.1 Pro on `auto`; Claude Fable 5 on `premium`) +- **Math, logic, proofs** → Reasoning models (Grok 4.1 Fast Reasoning) +- **Turns that need their tools** → Agent-tuned models (Kimi K2.7, Claude Sonnet 4.6) — selected automatically in any profile **Result:** 78% average cost savings with no quality loss. @@ -29,15 +30,25 @@ ClawRouter analyzes your prompt and automatically picks the right model tier: ### Installation (2 minutes) ```bash -# 1. Install ClawRouter -curl -fsSL https://raw.githubusercontent.com/BlockRunAI/ClawRouter/main/scripts/reinstall.sh | bash +# 1. Install ClawRouter (registers the plugin, syncs the model list, writes the auth profile, sets up a wallet) +curl -fsSL https://blockrun.ai/ClawRouter-update | bash -# 2. Fund wallet with USDC on Base ($5 is enough for thousands of requests) +# 2. Fund wallet with USDC on Solana or Base ($5 is enough for thousands of requests) — optional, skip for the free tier # 3. Restart OpenClaw openclaw gateway restart ``` +Prefer plain npm? Install the package, then **run `clawrouter setup`** — a bare `npm install -g` only puts the package on disk and leaves OpenClaw unregistered: + +```bash +npm install -g @blockrun/clawrouter +clawrouter setup # finishes OpenClaw integration — REQUIRED +openclaw gateway restart +``` + +New installs default to the **Solana** payment chain; existing installs stay on **Base**, where their USDC already lives. Switch any time with `/wallet solana` or `/wallet base`. + ### Enable Smart Routing In any OpenClaw conversation: @@ -48,28 +59,47 @@ In any OpenClaw conversation: ClawRouter will now automatically route all requests to the optimal model. +### Standalone (no OpenClaw) + +ClawRouter also runs as a local OpenAI-compatible proxy on port 8402 for continue.dev, Cursor, VS Code, ElizaOS, or any OpenAI SDK: + +```bash +npx @blockrun/clawrouter +``` + +Then point your client at `http://localhost:8402` with model `blockrun/auto` and any API key (e.g. `x402`). For continue.dev, `apiBase` must end with `/v1/`. + +```python +from openai import OpenAI +client = OpenAI(base_url="http://localhost:8402", api_key="x402") +response = client.chat.completions.create(model="blockrun/auto", messages=[...]) +``` + ## How It Works -ClawRouter does **not** pick "the cheapest model that can handle the prompt" — that framing was tested in v0.12.47 and reverted within 24 hours when fast/cheap models started giving shallow answers on hard tasks. The real architecture is **tier-first, model-second**, with a multi-objective scoring system optimized across quality, cost, and latency simultaneously. +ClawRouter does **not** pick "the cheapest model that can handle the prompt" — that framing was tested in v0.12.47 and reverted within 24 hours when fast/cheap models started giving shallow answers on hard tasks. The real architecture is **constraint-first**: hard requirements decide which models may compete, and a multi-objective ranker optimized across quality, cost, speed and reliability decides which one wins. -For the full technical deep-dive, see [Inside ClawRouter's Decision Layer](https://blockrun.ai/signal/clawrouter-quality-vs-cost-real-time-routing). The summary: +Since v0.12.242 the decision itself is made by [Router Core](https://github.com/BlockRunAI/router-core) (`@blockrun/router-core`, **V3.4**) — the routing engine extracted from ClawRouter and now shared by every BlockRun product (ClawRouter, Franklin, the `@blockrun/llm` SDKs, ClawRouter-Hermes). ClawRouter inlines it at build time, pinned to an exact commit, and injects its live model catalog at startup. + +For the full technical deep-dive, see [Inside ClawRouter's Decision Layer](https://blockrun.ai/signal/clawrouter-quality-vs-cost-real-time-routing) and the [constraint-first router report](https://blockrun.ai/signal/router-v3-4-constraint-first-auto-routing). The summary: ### The Decision Pipeline (<1ms, fully local) ``` -1. Lexical scoring → 15 weighted dimensions, score ∈ [-1, 1] each -2. Tier mapping → SIMPLE / MEDIUM / COMPLEX / REASONING -3. Confidence calibration → sigmoid; below 0.7 → AMBIGUOUS → defaults to MEDIUM -4. Task classification → chat / code_edit / code_agent / tool_agent / - reasoning_math / long_context / vision / … -5. Profile resolution → auto / eco / premium → primary + ordered fallback -6. Capability filtering → context window, output length, tool calling, vision -7. Portfolio ranking → task affinity × cost × speed × reliability +1. Classify → 15-dimension lexical scorer → tier (SIMPLE / MEDIUM / COMPLEX / REASONING) + → task classifier → shape (chat / extraction / code_edit / code_agent / + tool_agent / tool_agent_parallel / debug / reasoning / reasoning_math / + long_context / vision / …) +2. Filter → hard constraints (tools · vision · context · max-output · structured output · + present in the live catalog) remove every model that cannot serve the request +3. Rank → survivors scored on task quality × capability × cost × speed × reliability, + with per-profile weights +4. Recover → winner + the whole ranked list as an ordered fallback chain ``` -No external API calls. No LLM inference in the classification step. Pure keyword matching and arithmetic. +No external API calls. No LLM inference in the classification step. Pure keyword matching and arithmetic — a warm decision costs about 0.05 ms. -Steps 4 and 7 are the V3 portfolio layer: the tier says how much capability the request needs, the task type says what *kind* of work it is, and the portfolio ranks the eligible models against calibrated per-task evidence. The tier primary is a starting point, not the answer — a code-agent turn and a multiple-choice question in the same tier get different models. +Steps 1 and 3 are the V3 portfolio layer: the tier says how much capability the request needs, the task type says what *kind* of work it is, and the portfolio ranks the eligible models against calibrated per-task evidence. The tier primary is a starting point, not the answer — a code-agent turn and a multiple-choice question in the same tier get different models. ### 15-Dimension Scoring @@ -103,7 +133,7 @@ The weighted score lands on a single axis with three boundaries: SIMPLE < 0.0 < MEDIUM < 0.3 < COMPLEX < 0.5 < REASONING ``` -We classify the *request*, not the model. Tier first, model second. +We classify the *request*, not the model. Tier first, model second. Two overrides apply on top of the scorer: conversations above 100K tokens are forced to COMPLEX, and structured-output requests (JSON/YAML) are lifted to at least MEDIUM. ### Sigmoid Confidence Calibration @@ -137,14 +167,18 @@ openai/gpt-5.4 ← last resort GPT-5.4 sits last despite the highest IQ — its 6.2s latency creates a worse compounded experience across multi-step workflows than a slightly-lower-IQ model that completes in 1.4s. +Full chains for every profile, plus the per-profile ranking weights, live in the repo's [routing-profiles.md](https://github.com/BlockRunAI/ClawRouter/blob/main/docs/routing-profiles.md). + ### Runtime Capability Filtering -Before any model is dispatched, the candidate set is filtered against four hard constraints: +Before any model is scored, the candidate set is filtered against six hard constraints: 1. **Context window fit** — must hold (input + estimated output) × 1.10 safety buffer, measured against the *whole* conversation, not the last message 2. **Output length** — must be able to emit the requested `max_tokens` -3. **Tool calling** — if request includes tools, only function-calling models stay +3. **Tool calling** — if the turn actually needs its tools, only function-calling models stay (`tool_choice: "none"` is authoritative; host tool *descriptions* alone do not trigger it) 4. **Vision** — if request includes images, only vision-capable models stay +5. **Structured output** — an incompatible JSON/structured-output path disqualifies the model +6. **Catalog presence** — a model absent from the live catalog, or one the proxy has observed dead at the gateway (400/404/410), is removed from every chain before selection A "cheaper" model lacking a required capability is removed from the candidate set, **never silently substituted.** This prevents the classic multi-step failure mode where a tool-call step gets routed to a model that can't actually call tools. @@ -154,26 +188,37 @@ Every request is its own settled x402 transaction. There is no session state to ### 4-Tier Model Selection -Default `auto` profile primaries (cost-balanced; switch to `free` profile for $0 routing across the free tier): +Curated primaries per tier and profile (prices are input/output $/M tokens, as published in the ClawRouter README): + +| Tier | ECO Model | AUTO Model | PREMIUM Model | AGENTIC Model ‡ | +|---|---|---|---|---| +| **SIMPLE** | step-3.7-flash (**FREE**) | gemini-2.5-flash ($0.30/$2.50) | kimi-k2.7 † ($0.95/$4.00) | gpt-4o-mini ($0.15/$0.60) | +| **MEDIUM** | gemini-3.1-flash-lite ($0.25/$1.50) | kimi-k2.7 † ($0.95/$4.00) | gpt-5.3-codex ($1.75/$14.00) | kimi-k2.7 † ($0.95/$4.00) | +| **COMPLEX** | gemini-3.1-flash-lite ($0.25/$1.50) | gemini-3.1-pro ($2/$12) | claude-fable-5 ($10/$50) | claude-sonnet-4.6 ($3/$15) | +| **REASONING** | grok-4-1-fast-reasoning † ($0.20/$0.50) | grok-4-1-fast-reasoning † ($0.20/$0.50) | claude-sonnet-4.6 ($3/$15) | claude-sonnet-4.6 ($3/$15) | + +† Withheld from `/v1/models` — the router still calls it by direct ID, but you will not find it on the public pricing page. The published savings claim is priced on visible models only, which makes it conservative. +‡ Not a profile you pick — auto-selected in any profile when the turn actually needs its attached tools; prefers models that keep going instead of stopping to ask. Force or disable with `routing.overrides.agenticMode`. + +The primary is where the tier starts, not where the request necessarily lands: the portfolio ranks every capability-eligible candidate for the detected task, so a tool-calling turn and a proof in the same tier resolve to different models. `/model free` is an alias rather than a routed profile: it pins the free default (`free/step-3.7-flash`, the same model that opens ECO SIMPLE) and walks the other free models as fallbacks, for $0 routing across the 5 free models. -| Tier | Primary (auto) | Use Case | -|------|-------|----------| -| **SIMPLE** | google/gemini-2.5-flash | Q&A, summaries, simple tasks | -| **MEDIUM** | moonshot/kimi-k2.7 | Analysis, writing, coding | -| **COMPLEX** | google/gemini-3.1-pro | Advanced reasoning, research, long documents | -| **REASONING** | xai/grok-4-1-fast-reasoning | Math, logic, proofs | +### Router Core V3.4: measured, with limits -The primary is where the tier starts, not where the request necessarily lands: the portfolio ranks every capability-eligible candidate for the detected task, so a tool-calling turn and a proof in the same tier resolve to different models. Switch to the `free` profile for $0 routing across the 5 free models. +On router-core's frozen three-arm agent benchmark (τ-bench, BrowseComp, Terminal-Bench — three arms per task: the previous rules router, the constraint-first router, and a pinned flagship) the V3.4 policy completed **57%** of tasks vs **49%** for the previous rules router, at **6.4%** lower cost per successful task, and spent **8.9%** of the tokens a pinned flagship would have — while giving up 10 points of success against that flagship. The paired 95% interval on the quality gain is −1.9 to +15.5 points and crosses zero, so the router is not statistically proven better across the production distribution; the scorecard records `releaseEligible: false`. See [Routing Benchmarks](./benchmarks.md). + +Two levers exist if you disagree with the new policy: `routing.strategy: "rules"` restores the V2 primary-first selector, and `routing.shadow` compares both strategies locally on a sample of requests without a second paid call. ## Smart Routing Examples -| Prompt | Routed To | Cost | Savings | -|--------|-----------|------|---------| -| "What is 2+2?" | DeepSeek | $0.28/M | 99% | -| "Summarize this article" | Gemini 3.5 Flash | $9.00/M | 70% | -| "Build a React component" | Claude Sonnet 4.6 | $15.00/M | 80% | -| "Prove this theorem" | DeepSeek Reasoner | $0.28/M | 99% | -| "Run 50 parallel searches" | Kimi K2.7 | $4.00/M | 87% | +Real decisions from Router Core's bundled defaults on `auto` (prices are output $/M from the ClawRouter README): + +| Prompt | Tier / task | Routed To | Output $/M | +|--------|-------------|-----------|------------| +| "What is the capital of France?" | SIMPLE / chat | google/gemini-2.5-flash | $2.50 | +| "Prove that the sum of two odd integers is even, step by step." | REASONING / reasoning | deepseek/deepseek-v4-pro | $0.87 | +| "Cancel order B-42 and book the 9am flight to SFO." (tools attached) | AGENTIC / tool_agent_parallel | anthropic/claude-opus-4.8 | $25.00 | + +Note the second row: the REASONING primary is Grok 4.1 Fast Reasoning, but the portfolio ranked DeepSeek V4 Pro higher for this task shape. The tier primary is a starting point. Add `/model eco` and the SIMPLE row becomes a free NVIDIA-hosted model at $0. ## Features @@ -187,21 +232,23 @@ The primary is where the tier starts, not where the request necessarily lands: t Access all major providers through one wallet: -- **OpenAI**: GPT-5.5, GPT-5.4, GPT-5.4 Pro, GPT-5.2 -- **Anthropic**: Claude Fable 5, Claude Opus 5, Claude Opus 4.8, Opus 4.7, Sonnet 5, Sonnet 4.6, Haiku 4.5 -- **Google**: Gemini 3.1 Pro, Gemini 3.5 Flash -- **DeepSeek**: DeepSeek V4 Flash Chat, DeepSeek V4 Pro, DeepSeek Reasoner -- **xAI**: Grok 4.3, Grok 4 Fast (2M context) +- **OpenAI**: GPT-5.6 (Terra, Luna, Sol, and their Pro modes), GPT-5.5, GPT-5.5 Pro, GPT-5.4, GPT-5.4 Pro, GPT-5.3, GPT-5.3 Codex, GPT-5.2, GPT-5.2 Pro, GPT-4.1, GPT-4o, o3, o4-mini +- **Anthropic**: Claude Fable 5, Claude Opus 5, Claude Opus 4.8, Sonnet 5, Sonnet 4.6, Haiku 4.5 +- **Google**: Gemini 3.1 Pro, Gemini 3.6 Flash, Gemini 3.5 Flash, Gemini 3.5 Flash Lite, Gemini 2.5 Pro, Gemini 2.5 Flash +- **DeepSeek**: DeepSeek V4 Pro, DeepSeek V4 Flash Chat, DeepSeek Reasoner +- **xAI**: Grok 4.5, Grok 4.3, Grok Build 0.1, Grok 4 Fast (reasoning and non-reasoning) - **Z.AI**: GLM-5.2 (flagship, 1M context), GLM-5.1, GLM-5, GLM-5 Turbo -- **Moonshot**: Kimi K3 (flagship, 1M context, image + text), Kimi K2.7 (256K, image + video) -- **MiniMax**: MiniMax M3 +- **Moonshot**: Kimi K3 (flagship, 1M context), Kimi K2.7, Kimi K2.5 +- **Qwen**: Qwen3.7 Max, Qwen3.7 Plus, Qwen3.7 Flash +- **MiniMax**: MiniMax M3, MiniMax M2.7, MiniMax M2.5 +- **Tencent / Xiaomi**: Hy3, MiMo-V2.5 Pro - **Free tier (all FREE)**: 5 NVIDIA-hosted chat, reasoning and vision models with no per-token charge [View all models →](../intelligence/pricing.md) ### x402 Micropayments -- Pay per request with USDC on Base +- Pay per request with USDC on Solana or Base — both wallets derive from one BIP-39 mnemonic - Non-custodial - you control your wallet - No API keys needed - No subscriptions or prepaid credits @@ -209,7 +256,7 @@ Access all major providers through one wallet: ### Open Source - MIT licensed -- Fully inspectable routing logic +- Fully inspectable routing logic — the engine is its own repo, [router-core](https://github.com/BlockRunAI/router-core), pinned by commit - No black boxes - [View on GitHub →](https://github.com/BlockRunAI/ClawRouter) @@ -222,7 +269,7 @@ Once installed and enabled with `/model blockrun/auto`, ClawRouter works automat ``` You: Explain quantum computing in simple terms -ClawRouter: [Routes to a free-tier model - FREE] +ClawRouter: [Routes to google/gemini-2.5-flash - SIMPLE tier] Response: Quantum computing uses quantum mechanics... ``` @@ -234,15 +281,49 @@ You can still override routing for specific requests: /model openai/gpt-5.5 ``` +Shortcuts also work: `/model grok`, `/model br-sonnet`, `/model gpt5`, `/model o3`, `/model free`. + +### Slash Commands + +``` +/wallet # Balance and address (both chains) +/wallet export # Export mnemonic + keys for backup +/wallet recover # Restore wallet from mnemonic on a new machine +/wallet solana # Switch to Solana USDC payments +/wallet base # Switch to Base (EVM) USDC payments +/stats # View usage and savings +/exclude add # Block a model from routing (aliases work: "grok-4", "free") +/exclude remove # Unblock a model +``` + +Exclusions persist across restarts (`~/.openclaw/blockrun/exclude-models.json`). If every model in a tier is excluded, the safety net ignores the filter so routing never breaks. + ### View Routing Decision -ClawRouter logs show which model was selected and why: +OpenClaw's log shows the tier, the model and the cost of every request: +```bash +openclaw logs --follow ``` -[ClawRouter] Prompt complexity: LOW → Tier: SIMPLE → Model: free-tier model (free profile) -[ClawRouter] Cost: $0.0000 (saved $0.0150 vs. Claude Opus) + +``` +[plugins] [SIMPLE] google/gemini-2.5-flash $0.0012 (saved 99%) +[plugins] [MEDIUM] deepseek/deepseek-chat $0.0003 (saved 99%) +[plugins] [REASONING] deepseek/deepseek-reasoner $0.0005 (saved 99%) ``` +Non-streaming responses also carry `x-clawrouter-profile`, `x-clawrouter-tier`, `x-clawrouter-model`, `x-clawrouter-confidence` and `x-clawrouter-reasoning` headers (set `CLAWROUTER_DEBUG_HEADERS=off` to suppress them). + +## Beyond Chat + +The same wallet pays for the rest of the gateway through the proxy. Prices as published in the ClawRouter README: + +- **Image generation** — `/cr-imagegen a dog dancing on the beach` (`--model`, `--size`). Nine models from $0.015/image (CogView-4) to $0.10/image (Nano Banana Pro); default `gpt-image` at $0.02/image. +- **Image editing** — `/img2img --image ~/photo.png change the background to a starry sky`, with optional `--mask`. +- **Video generation** — `/videogen a red apple slowly spinning` (`--model`, `--duration`), or `POST /v1/videos/generations`. Seedance 1.5 Pro / 2.0 Fast / 2.0 / 2.5, Sora 2, and Grok Imagine; the MP4 is downloaded to local disk so it survives the upstream's temporary bucket. +- **Phone and voice** — `/cr-call +14155552671 "Confirm tomorrow's 3pm meeting"` places a real outbound AI voice call ($0.54 flat, up to 30 minutes); `clawrouter phone lookup|fraud|numbers ...` handles carrier lookup ($0.01), fraud signals ($0.05) and 30-day number leases ($5). +- **Crypto data (Surf)** — `/v1/surf/*` is whitelisted through the proxy: 84 endpoints across 13 domains at $0.001 / $0.005 / $0.020 per call, including ad-hoc on-chain SQL. + ## Why ClawRouter? ### vs. OpenRouter / LiteLLM @@ -279,7 +360,7 @@ Smart routing to appropriate models: ``` 70 simple requests → DeepSeek ($0.28/M) = $0.02 -20 medium requests → Claude Haiku ($4.00/M) = $0.08 +20 medium requests → Kimi K2.7 ($4.00/M) = $0.08 10 complex requests → Claude Opus 5 ($25.00/M) = $0.25 Total: $0.36 (saved $2.14 = 86% savings) @@ -291,28 +372,31 @@ ClawRouter has access to all models available through BlockRun Intelligence: ### Chat Models -- **OpenAI**: GPT-5.5, GPT-5.4, GPT-5.4 Pro, GPT-5.2 -- **Anthropic Claude**: Fable 5, Opus 5, Opus 4.8, Opus 4.7, Opus 4.5, Sonnet 5, Sonnet 4.6, Haiku 4.5 -- **Google Gemini**: 3.1 Pro, 3.5 Flash -- **DeepSeek**: V4 Flash Chat, V4 Pro, Reasoner -- **xAI Grok**: Grok 4.3, Grok 4 Fast (2M context) +- **OpenAI**: GPT-5.6 Terra / Luna / Sol (+ Pro), GPT-5.5, GPT-5.5 Pro, GPT-5.4, GPT-5.4 Pro, GPT-5.3, GPT-5.3 Codex, GPT-5.2, GPT-5.2 Pro, GPT-4.1, GPT-4o, o1, o3, o4-mini +- **Anthropic Claude**: Fable 5, Opus 5, Opus 4.8, Sonnet 5, Sonnet 4.6, Haiku 4.5 +- **Google Gemini**: 3.1 Pro, 3.6 Flash, 3.5 Flash, 3.5 Flash Lite, 2.5 Pro, 2.5 Flash, 2.5 Flash Lite +- **DeepSeek**: V4 Pro, V4 Flash Chat, Reasoner +- **xAI Grok**: Grok 4.5, Grok 4.3, Grok Build 0.1, Grok 4 Fast, Grok 4.1 Fast - **Z.AI**: GLM-5.2 (1M context), GLM-5.1, GLM-5, GLM-5 Turbo -- **Moonshot**: Kimi K3 flagship (1M context, image + text), Kimi K2.7 (256K, image + video) -- **MiniMax**: MiniMax M3 -- **Free tier**: 8 models, no per-token charge +- **Moonshot**: Kimi K3 flagship (1M context), Kimi K2.7, Kimi K2.5 +- **Qwen**: Qwen3.7 Max, Plus, Flash +- **MiniMax**: MiniMax M3, M2.7, M2.5 +- **Tencent / Xiaomi**: Hy3, MiMo-V2.5 Pro +- **Free tier**: 5 models, no per-token charge ### Image Generation -- GPT Image 1 / ChatGPT Images 2.0 -- Nano Banana / Nano Banana Pro +- GPT Image 1 / GPT Image 2 +- Nano Banana / Nano Banana 2 / Nano Banana Pro +- Seedream 5 +- Grok Imagine / Grok Imagine Pro - CogView-4 -- Grok Imagine [View full pricing →](../intelligence/pricing.md) ## SDK Integration -Use ClawRouter's smart routing directly in your code with the `smart_chat()` method: +Use ClawRouter's smart routing directly in your code with the `smart_chat()` method. The SDKs run the same Router Core engine — the Python SDK carries a line-by-line port, pinned to the same upstream commit, so an identical request routes identically in both languages: ::::tabs @@ -325,7 +409,7 @@ client = LLMClient() # Auto-route to optimal model result = client.smart_chat("What is 2+2?") print(result.response) # "4" -print(result.model) # "deepseek/deepseek-chat" +print(result.model) # "google/gemini-2.5-flash" print(result.routing.tier) # "SIMPLE" print(result.routing.savings) # 0.94 (94% savings) @@ -345,7 +429,7 @@ const client = new LLMClient({ privateKey: '0x...' }); // Auto-route to optimal model const result = await client.smartChat('What is 2+2?'); console.log(result.response); // "4" -console.log(result.model); // "deepseek/deepseek-chat" +console.log(result.model); // "google/gemini-2.5-flash" console.log(result.routing.tier); // "SIMPLE" console.log(result.routing.savings); // 0.94 @@ -366,80 +450,117 @@ All SDKs support the same routing profiles: | Profile | Behavior | Best For | |---------|----------|----------| -| `free` | Always uses free-tier models | Development, testing | -| `eco` | Maximizes cost savings | Bulk processing | +| `free` | Always uses free-tier models (an alias that pins the free default and walks the free cascade) | Development, testing | +| `eco` | Maximizes cost savings — opens on the free tier | Bulk processing | | `auto` | Balances quality and cost (default) | Production workloads | | `premium` | Always uses top-tier models | Critical tasks | +## Other Harnesses + +- **OpenClaw** — the plugin above (slash commands, usage reports, image/video/voice commands). +- **Any OpenAI-compatible client** — continue.dev, Cursor, VS Code extensions, ElizaOS, custom agents — via the standalone proxy on `http://localhost:8402`. +- **NousResearch Hermes** — [ClawRouter-Hermes](https://github.com/BlockRunAI/ClawRouter-Hermes) (`pip install hermes-plugin-clawrouter`) supervises a local ClawRouter proxy for `hermes-agent`; same wallet, same routing engine. +- **Claude Code** — use [BRCC](https://github.com/BlockRunAI/brcc), which is purpose-built for Claude Code with the same smart routing and x402 payments. +- **OKX Agentic Wallet** — [XClawRouter](https://github.com/BlockRunAI/XClawRouter) is the same router with the wallet held in OKX's TEE instead of a local key: `curl -fsSL https://blockrun.ai/XClawRouter-update | bash`, then `npx @blockrun/xclawrouter setup` for an email + OTP login. No local private key is stored. + ## Configuration ### Environment Variables -ClawRouter reads your wallet configuration from OpenClaw: +For basic usage, no configuration is needed. For advanced options: -```bash -# Set in OpenClaw settings -BASE_CHAIN_WALLET_KEY=0x... -``` +| Variable | Default | Description | +|---|---|---| +| `BLOCKRUN_WALLET_KEY` | auto-generated | Your wallet private key (a saved `~/.openclaw/blockrun/wallet.key` takes priority) | +| `BLOCKRUN_PROXY_PORT` | `8402` | Local proxy port | +| `CLAWROUTER_PAYMENT_CHAIN` | persisted selection | `solana` or `base`; overrides the saved payment chain | +| `CLAWROUTER_SOLANA_RPC_URL` | `https://api.mainnet-beta.solana.com` | Solana RPC endpoint for balance checks | +| `CLAWROUTER_DISABLED` | `false` | Disable smart routing (pass requests through as-is) | +| `CLAWROUTER_DEBUG_HEADERS` | `on` | Set to `off` to suppress `x-clawrouter-*` debug response headers | +| `CLAWROUTER_TOOL_CALL_PROSE` | `on` | Set to `off` to blank assistant prose on tool-calling turns (the pre-v0.12.248 behavior) | +| `BLOCKRUN_WEB_SEARCH` | auto-enabled | Set to `off` to skip registering BlockRun's Exa web search provider with OpenClaw | ### Advanced Settings -Edit `~/.openclaw/clawrouter.config.json`: - -```json -{ - "costWeight": 0.7, // How much to prioritize cost (0-1) - "qualityWeight": 0.3, // How much to prioritize quality (0-1) - "enableFreeModels": true, // Use free-tier models when appropriate - "maxCostPerRequest": 0.10 // Maximum cost per request in USD -} +Routing overrides live in the plugin block of `~/.openclaw/openclaw.yaml` and are merged over Router Core's defaults — override just one tier without redefining the other three: + +```yaml +plugins: + - id: "@blockrun/clawrouter" + config: + maxCostPerRun: 0.50 # USD per session; default: no limit + maxCostPerRunMode: graceful # graceful = downgrade premium → auto → eco → free; strict = 429 at the cap + routing: + strategy: portfolio # "rules" rolls back to the V2 primary-first selector + # shadow: { strategy: rules, sampleRate: 0.1 } # compare locally, no second paid call + tiers: + COMPLEX: + primary: "anthropic/claude-sonnet-5" + fallback: ["google/gemini-3.1-pro", "openai/gpt-5.6-terra"] + # ecoTiers / premiumTiers / agenticTiers take the same shape; `agenticTiers: null` disables agentic switching + classifier: + confidenceThreshold: 0.7 + overrides: + maxTokensForceComplex: 100000 + structuredOutputMinTier: MEDIUM + ambiguousDefaultTier: MEDIUM + # agenticMode: true | false # force / disable the agentic tier set ``` +Image generation bypasses `maxCostPerRun` — it is charged per x402 payment and not tracked per session. Full reference: [configuration.md](https://github.com/BlockRunAI/ClawRouter/blob/main/docs/configuration.md). + ## Troubleshooting ### ClawRouter not routing -Check installation: +Run the checklist: ```bash -openclaw gateway status +cat ~/.openclaw/extensions/clawrouter/package.json | grep version # should be 0.12+ +curl http://localhost:8402/health # proxy up, wallet, chain, balance ``` -Should show: +Then, in a conversation, `/wallet` for both addresses and the balance. If you installed with `npm install -g` and `/models` shows only OpenClaw's handful of defaults, run `clawrouter setup`. -``` -✓ ClawRouter plugin loaded -✓ Connected to BlockRun Intelligence -✓ Wallet: 0x123...789 (funded) +For anything else, the doctor collects diagnostics and asks a model to analyze them: + +```bash +npx @blockrun/clawrouter doctor # Sonnet, ~$0.003 +npx @blockrun/clawrouter doctor opus "why is my request failing?" # Opus, ~$0.01 ``` ### Wrong model selected -View routing logs: +Watch the decision per request: ```bash -tail -f ~/.openclaw/logs/clawrouter.log +openclaw logs --follow ``` +Then either pin a model (`/model `), block one (`/exclude add `), or override the tier's chain in `openclaw.yaml` (see above). `x-clawrouter-reasoning` on a non-streaming response explains the pick. + ### High costs -Check your settings: +Check what you spent and on what: -```bash -openclaw gateway config show ``` +/stats +``` + +Set a session cap with `maxCostPerRun`, switch to `/model eco`, or `/exclude add gpt-5.4` to keep expensive models out of the chains. -Adjust cost weight: +### Updating ```bash -openclaw gateway config set costWeight 0.9 +npx @blockrun/clawrouter@latest +openclaw gateway restart ``` ## FAQ ### Do I need API keys? -No. ClawRouter uses x402 micropayments with USDC on Base. Just fund your wallet. +No. ClawRouter uses x402 micropayments with USDC on Solana or Base. Just fund your wallet — or stay on the 5 free models with no wallet at all. ### How much should I fund my wallet? @@ -451,11 +572,11 @@ Yes. Use `/model ` to override smart routing for specific requests. ### Is my data sent to BlockRun? -Only your prompts are sent to the AI provider you're routed to. Routing decisions happen locally. +Routing decisions happen locally. The prompt itself is sent through the BlockRun gateway to the model you're routed to; nothing is sent anywhere to decide the route. ### Can I see the routing algorithm? -Yes. ClawRouter is [fully open source](https://github.com/BlockRunAI/ClawRouter) under MIT license. +Yes. ClawRouter is [fully open source](https://github.com/BlockRunAI/ClawRouter) under MIT license, and the routing engine is its own MIT repo, [router-core](https://github.com/BlockRunAI/router-core). ### Does it work offline? @@ -482,5 +603,5 @@ Fund on Base or Solana and start routing in minutes. ## Support - **GitHub Issues**: [Report a bug](https://github.com/BlockRunAI/ClawRouter/issues) -- **Telegram**: [Join community](https://t.me/+mroQv4-4hGgzOGUx) +- **Telegram**: [Join community](https://t.me/blockrunAI) - **Documentation**: [BlockRun Docs](https://blockrun.ai/docs) diff --git a/docs/products/trading/installation.md b/docs/products/trading/installation.md index 7897c2a..7f7b4c0 100644 --- a/docs/products/trading/installation.md +++ b/docs/products/trading/installation.md @@ -1,14 +1,14 @@ --- -title: Installing alpha-mcp -description: Install the alpha-mcp trading server in Claude Code, fund a wallet on Base, and confirm Claude can pull live technical signals. +title: Setting Up Trading +description: Install Franklin, fund a wallet on Solana or Base, and confirm the agent can pull live technical signals before you let it near real money. --- -# Installing alpha-mcp +# Setting Up Trading -Add the alpha-mcp trading tools to Claude Code, fund a wallet, and verify it works — a few minutes start to finish. +Franklin's trading tools are built into the agent — install Franklin, fund a wallet, and verify signals work. A few minutes start to finish. :::note{title="Requirements"} -Claude Code CLI, Node.js 18+, and a funded wallet on Base (for paid features). +Node.js 20.19+ (Node 22 LTS recommended) and, for paid features, a wallet funded with USDC on Solana or Base. Signals and paper trading work on the free tier with no wallet. ::: ## Install @@ -17,29 +17,29 @@ Pick one of the three install methods. ::::tabs -:::tab{label="Direct install"} +:::tab{label="Global install"} ```bash -claude mcp add alpha -s user -- npx -y @blockrun/alpha@latest +npm install -g @blockrun/franklin +franklin --version # 3.42.0 or later ``` ::: :::tab{label="Via npx"} ```bash +npx @blockrun/franklin ``` +No global install and no `sudo` needed. ::: -:::tab{label="Manual config"} -Add to your Claude Code settings (`~/.claude/settings.json`): +:::tab{label="User-owned Node (fixes EACCES)"} +```bash +# nvm +nvm install 22 && nvm use 22 +npm install -g @blockrun/franklin -```json -{ - "mcpServers": { - "alpha": { - "command": "npx", - "args": ["@blockrun/alpha"] - } - } -} +# or fnm +fnm install 22 && fnm use 22 +npm install -g @blockrun/franklin ``` ::: @@ -49,91 +49,106 @@ Add to your Claude Code settings (`~/.claude/settings.json`): ::::steps -:::step{title="Verify installation"} -Restart Claude Code, then: +:::step{title="Create a wallet"} +A fresh install defaults to Solana. Pass `base` for a Base wallet. ```bash -claude mcp list +franklin setup # Solana +franklin setup base # Base +franklin balance # prints the address and USDC balance ``` -You should see `alpha` or `@blockrun/alpha` in the list. +Fund the address with USDC, or use the card onramp from inside Franklin. See [Wallet Setup](../../getting-started/wallet-setup.md). Switch chains later with `franklin solana` / `franklin base`. ::: -:::step{title="Set up a wallet"} -alpha-mcp needs a funded wallet for sentiment analysis queries (paid via x402) and trade execution (USDC for swaps). - -``` -# In Claude Code -blockrun setup +:::step{title="Health check"} +```bash +franklin doctor ``` -Then fund with USDC on Base. See [Wallet Setup](../../getting-started/wallet-setup.md). +Checks Node, wallet, chain, gateway reachability, MCP servers, and telemetry in one command. ::: :::step{title="Configure (optional)"} -alpha-mcp uses environment variables for configuration: +Trading settings live in `~/.blockrun/trading-config.json`, written with defaults on first run: + +```json +{ + "version": 1, + "watchlist": ["BTC", "ETH", "SOL"], + "signals": { "rsi_oversold": 30, "rsi_overbought": 70 }, + "model_tier": "cheap" +} +``` + +Real-money controls are environment variables and flags: ```bash -# Optional: Custom RPC endpoint -export BASE_RPC_URL=https://mainnet.base.org +# Polymarket per-order cap (default $25) and optional cumulative session cap +export POLYMARKET_MAX_BET_USD=25 +export POLYMARKET_MAX_SESSION_USD=100 -# Optional: Custom wallet location -export BLOCKRUN_WALLET_PATH=~/.blockrun/trading-wallet.json +# Hard cap on gateway spend for a session +franklin --max-spend 5 + +# Headless runs reject all trade plans unless this is passed (still bounded by --max-spend) +franklin -p "review my SOL thesis" --max-spend 2 --approve-trades ``` + +Your own guardrails go in `~/.blockrun/hooks/` — see [Risk Management](risk-management.md). ::: :::step{title="Test it works"} -In Claude Code: +In a Franklin session: ``` What's the current RSI for ETH? ``` -Claude should call `alpha_signal` and return technical indicators. +Franklin should call `TradingSignal` and return the indicators plus a verdict. Then try a paper trade: + +``` +Open a $100 paper position in ETH and tell me my exposure +``` + +`TradingOpenPosition` fills at the live price and `TradingPortfolio` reports cash, positions, and how close you are to the exposure caps. ::: :::: ## Troubleshooting -### "MCP not found" +### `ERR_REQUIRE_ESM` on first run -Restart Claude Code after installation: +Node is older than 20.19. Upgrade to Node 20.19+ or 22 LTS. -```bash -# Kill any running instances -pkill -f "claude" +### `EACCES` during `npm install -g` -# Start fresh -claude -``` +Your global npm folder is root-owned. Don't use `sudo` — run `npx @blockrun/franklin`, or install Node via `nvm`/`fnm` so global packages live in your home directory. -### "Wallet not configured" +### "Wallet not configured" / free models only -Run wallet setup: +Create and fund a wallet: -``` -blockrun setup +```bash +franklin setup +franklin balance ``` -### "Permission denied" +Paid models and paid tools activate as soon as the wallet holds USDC. -Check npm permissions: +### Trade plan rejected in a scripted run -```bash -npm config set prefix ~/.npm-global -export PATH=~/.npm-global/bin:$PATH -``` +Non-interactive runs (`-p`) fail closed. Add `--approve-trades` and make sure the plan's total fits inside the remaining `--max-spend`. + +### Swap tool says execution is unavailable -### "Network error" +Expected in 3.42.0: `JupiterSwap`, `Base0xSwap`, and `Base0xGaslessSwap` are paused while local transaction validation lands. Use `JupiterQuote` / `Base0xQuote` for routes and prices. -Verify Base RPC is accessible: +### MCP servers or gateway not reachable ```bash -curl https://mainnet.base.org \ - -X POST \ - -H "Content-Type: application/json" \ - -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' +franklin doctor --json ``` ## What's next? @@ -141,15 +156,15 @@ curl https://mainnet.base.org \ ::::cards :::card{title="Tools reference" href="tools.md" icon="Boxes"} -Learn what each of the 7 alpha-mcp tools does. +Learn what each trading tool does and what it costs. ::: :::card{title="Risk management" href="risk-management.md" icon="TrendingUp"} -Understand the hardcoded safety limits before you trade. +Understand the caps and the trade-plan gate before you trade. ::: :::card{title="Wallet setup" href="../../getting-started/wallet-setup.md" icon="Wallet"} -Fund your agent with USDC on Base. +Fund your agent with USDC on Solana or Base. ::: :::: diff --git a/docs/products/trading/overview.md b/docs/products/trading/overview.md index b0ebb03..0bd46b1 100644 --- a/docs/products/trading/overview.md +++ b/docs/products/trading/overview.md @@ -1,102 +1,120 @@ --- -title: Trading with alpha-mcp -description: alpha-mcp gives Claude the tools to analyze markets, execute swaps on Base, and manage risk autonomously — with hardcoded safety limits. +title: Trading with Franklin +description: Franklin's built-in trading surface — live signals, paper trading with hard exposure caps, prediction-market bets, and a trade-plan gate that keeps real money behind your approval. --- -# Trading with alpha-mcp +# Trading with Franklin -Your AI just became a trader. +Your AI just became a trader — one that has to ask before it spends. -alpha-mcp gives Claude the tools to analyze markets, execute trades, and manage risk. It pays for its own intelligence and acts autonomously. +Trading is Franklin's flagship arena. The [Franklin agent](../franklin.md) ships with the trading tools built in: it buys live market data, computes signals locally, keeps a wallet-bound trading journal, paper-trades against real prices, and proposes real-money trade plans that nothing executes until you approve. There is no separate trading package to install. + +:::note{title="alpha-mcp is retired"} +Earlier versions of these pages documented `alpha-mcp`, a standalone MCP server. That repo has not shipped since January 2026. The maintained trading surface is Franklin itself — this section documents Franklin 3.42.0. +::: ## What It Does -- **Technical Analysis** — RSI, MACD, EMA from Binance data -- **DEX Data** — Market data via DexScreener -- **Sentiment Analysis** — Social signals (paid via BlockRun) -- **Trade Execution** — Token swaps on Base via 0x Protocol -- **Portfolio Tracking** — Manage and monitor positions -- **Risk Management** — Hardcoded safety limits -- **Trade Memory** — Semantic search across trade history +- **Technical Analysis** — `TradingSignal`: price, RSI, MACD, Bollinger Bands, volatility, and a bullish / bearish / neutral verdict, computed locally from live market data +- **Multi-asset market data** — `TradingMarket`: crypto spot, trending coins, market overview, FX pairs, commodities, and equities across 12 markets via the BlockRun gateway +- **Paper trading** — open and close simulated positions at live prices, with a persistent portfolio and trade log across sessions +- **Prediction markets** — research odds across Polymarket, Kalshi and others (`PredictionMarket`), and place real bets on Polymarket (`PolymarketBet`) +- **DEX routes** — read-only quotes on Solana (Jupiter) and Base (0x) +- **DeFi & on-chain reads** — protocol TVL, chains, yields, token prices, and read-only JSON-RPC across 40+ chains +- **Risk Management** — code-level exposure caps on paper trades, a mandatory trade plan for real money, per-order bet caps, and your own veto hooks +- **Trade Memory** — every trade journals its thesis on open and P&L on close into a journal keyed by your wallet address, recallable from any directory ## Pricing -**alpha-mcp is free and open source.** +**Franklin is free and open source (Apache-2.0).** You only pay, from your own wallet over x402, for the intelligence and data the agent actually uses: -You only pay for the intelligence your agent uses: -- Technical signals: Free (Binance public API) -- DEX data: Free (DexScreener public API) -- Sentiment analysis: ~$0.01 per query (paid via x402) -- Trade execution: Network gas only +- Signals and crypto / FX / commodity prices: free +- Equity prices, prediction-market data, on-chain RPC, web search: paid per call from the agent wallet — the tool descriptions show the per-call price before Franklin calls them +- Model calls: provider cost, no platform margin, plus $0.001 per request +- Polymarket bets: your own funds on Polygon, plus network fees — bets do **not** draw from the `--max-spend` AI budget ## Quick Start ```bash -# Install alpha-mcp -claude mcp add @blockrun/alpha - -# Or via npm -npx @anthropic-ai/claude-code install @blockrun/alpha +npm install -g @blockrun/franklin +franklin setup # Solana wallet (default); `franklin setup base` for Base +franklin balance # fund the address with USDC +franklin --max-spend 5 # start with a hard cap on this session's spend ``` -Then in Claude Code: +Then, in the session: ``` -Analyze BTC for trading signals +what's BTC looking like today? ``` +Franklin calls `TradingSignal` and returns a signal report with a verdict. Signals need no wallet at all — the free-tier models can run them. + ## Data Sources | Source | Data Type | Pricing | |--------|-----------|---------| -| Binance | Price & technical indicators | Free | -| DexScreener | DEX market data | Free | -| 0x Protocol | Swap execution on Base | Gas only | -| BlockRun Sentiment | Social signals | Pay-per-query | +| Live market data | Crypto spot, trending, market overview | Free | +| Local indicators | RSI, MACD, Bollinger, volatility | Free (computed on your machine) | +| BlockRun gateway | FX pairs, commodities | Free | +| BlockRun gateway | Equity prices (12 markets) | Pay-per-call | +| BlockRun gateway | Prediction-market search, wallet profiles, smart money | Pay-per-call | +| BlockRun gateway | Read-only JSON-RPC across 40+ chains | Pay-per-call | +| Jupiter (Solana) / 0x (Base) | DEX quotes | Free (quote only) | +| Polymarket (Polygon) | Bet execution | Your funds + fees | ## How It Works -1. **Discover** — Claude identifies what analysis is needed -2. **Analyze** — Calls alpha-mcp tools for signals and data -3. **Decide** — Claude evaluates signals against risk rules -4. **Execute** — Performs swap via 0x if conditions are met -5. **Record** — Logs trade to memory for future analysis +1. **Discover** — Franklin decides what analysis the question needs +2. **Analyze** — calls `TradingSignal`, `TradingMarket`, `PredictionMarket`, or DeFi tools +3. **Decide** — evaluates signals against the portfolio and the risk caps +4. **Plan** — for real money, proposes a `TradePlan`; for paper trades, opens a simulated position +5. **Approve** — you approve, revise, or reject the plan (a paper trade needs no approval) +6. **Execute** — places the order within the approved budget +7. **Record** — journals thesis and outcome to the wallet-keyed trading journal ## Safety First -alpha-mcp has hardcoded risk limits that **cannot be overridden**: +Franklin's money guardrails are layered, and each has a hard bound: -| Limit | Value | -|-------|-------| -| Max position size | 15% of portfolio | -| Total exposure cap | 50% of portfolio | -| Daily loss threshold | 5% | -| Min cash reserve | 50% | -| Stop-loss trigger | 15% | +| Guardrail | Value | +|-----------|-------| +| Paper-trading per-position cap | $400 | +| Paper-trading total exposure cap | $900 (of a $1,000 paper bankroll) | +| Real-money trades | Require an approved `TradePlan`; approvals expire after 15 minutes | +| Headless runs | Reject every plan unless `--approve-trades`, and only within `--max-spend` | +| Polymarket per-order cap | $25 by default (`POLYMARKET_MAX_BET_USD`) | +| Session spend | `--max-spend ` — the session stops when exceeded | +| Your own rules | `PreSpend` lifecycle hooks can veto any money-moving tool | -:::warning{title="Limits are hardcoded"} -These guardrails live in code, not in a prompt — neither you nor the AI can override them. See [Risk Management](risk-management.md) for the full enforcement model. +:::warning{title="DEX execution is currently paused"} +In 3.42.0 the live swap tools (`JupiterSwap`, `Base0xSwap`, `Base0xGaslessSwap`) are temporarily disabled while Franklin adds complete local transaction validation — Franklin will not sign opaque upstream transaction bytes. Quotes still work. Polymarket bets and paper trading are unaffected. See [Risk Management](risk-management.md) for the full enforcement model. ::: +## Franklin Trading (specialized fork) + +[Franklin-Trading](https://github.com/BlockRunAI/Franklin-Trading) (`@blockrun/franklin-trading`) is a fork of Franklin specialized as a wallet-native trading agent: it strips the media, social, phone, and browser tools, adds a `defineStrategy` DSL, trading skills (`/trade-signal`, `/trade-strategy`, `/trade-discussion`), and a Base MCP connector. Its unified backtest → paper → live strategy runner (`franklin-trading run --mode ...`) is still roadmap work, and the fork predates Franklin's trade-plan gate and lifecycle hooks. Unless you specifically want the strategy DSL, use Franklin's built-in trading documented here. + ## What's next? ::::cards -:::card{title="Install alpha-mcp" href="installation.md" icon="Terminal"} -Add the MCP to Claude Code and fund a wallet on Base. +:::card{title="Set up trading" href="installation.md" icon="Terminal"} +Install Franklin, fund a wallet, and confirm signals work. ::: :::card{title="Tools reference" href="tools.md" icon="Boxes"} -The 7 tools — signals, DEX data, sentiment, swaps, portfolio, risk, memory. +Signals, market data, paper trading, trade plans, prediction markets, DEX quotes, DeFi, RPC, wallet. ::: :::card{title="Risk management" href="risk-management.md" icon="TrendingUp"} -How the hardcoded safety limits protect your capital. +How the exposure caps, trade-plan gate, bet caps, and hooks protect your capital. ::: :::: ## Links -- [GitHub: alpha-mcp](https://github.com/BlockRunAI/alpha-mcp) -- [GitHub: Agent Wallet](https://github.com/BlockRunAI/blockrun-agent-wallet) +- [GitHub: Franklin](https://github.com/BlockRunAI/Franklin) +- [GitHub: Franklin-Trading](https://github.com/BlockRunAI/Franklin-Trading) +- [Franklin hook examples](https://github.com/BlockRunAI/Franklin/tree/main/docs/examples/hooks) diff --git a/docs/products/trading/risk-management.md b/docs/products/trading/risk-management.md index b5e7cec..a71e1f7 100644 --- a/docs/products/trading/risk-management.md +++ b/docs/products/trading/risk-management.md @@ -1,183 +1,202 @@ --- title: Risk Management -description: alpha-mcp enforces five hardcoded trading limits — position size, exposure, daily loss, cash reserve, and stop-loss — that neither the AI nor the user can override. +description: Franklin layers five money guardrails — a hard wallet/session cap, paper-trading exposure caps, a mandatory trade-plan approval for real money, per-order bet caps, and your own veto hooks. --- # Risk Management -alpha-mcp has hardcoded safety limits to protect your capital. These limits **cannot be overridden** by the AI or by users. +Franklin treats money differently from file edits. A `--trust` session can skip permission prompts for tools, but it can never skip the gate on real money. The guardrails below are layered; every one has a hard bound, and the ones you can loosen require an explicit flag or environment variable, never a prompt. -:::danger{title="No override mechanism exists"} -The limits below live in code, not in a prompt. There is no flag, setting, or instruction — to Claude or otherwise — that can bypass them. Asking to "ignore the risk limits" or "override safety for this one trade" will always be rejected. +:::danger{title="Prompts cannot open the gate"} +The trade-plan gate and the exposure caps live in code, not in a system prompt. Asking Franklin to "ignore the risk limits" or "skip the plan for this one trade" does nothing — the tool refuses to execute without an approved plan, in every permission mode. ::: -## Built-in Limits +## The five layers -| Limit | Value | Purpose | -|-------|-------|---------| -| **Max position size** | 15% | No single asset can exceed 15% of portfolio | -| **Total exposure cap** | 50% | Maximum 50% can be in risk assets | -| **Daily loss threshold** | 5% | Trading pauses if daily loss exceeds 5% | -| **Min cash reserve** | 50% | Always keep 50% in stablecoins | -| **Stop-loss trigger** | 15% | Auto-exit positions down 15% | - -:::warning{title="The numbers are fixed"} -15% max position, 50% exposure cap, 5% daily loss, 50% cash reserve, 15% stop-loss. These values are not user-configurable in the current version. -::: +| Layer | What it bounds | Value (Franklin 3.42.0) | Adjustable? | +|-------|----------------|-------------------------|-------------| +| **Wallet + session cap** | Total gateway spend | Wallet balance is the hard limit; `--max-spend ` caps a session | Flag | +| **Paper-trading caps** | Simulated positions | $400 per position · $900 total exposure · cash sufficiency · $1,000 starting bankroll | No (code defaults) | +| **Trade-plan gate** | Every real-money trade | Approval required; plan expires after 15 minutes; budget draws down per trade | No — approval is always required | +| **Polymarket caps** | Bet sizes | $25 per order by default; optional cumulative session cap | `POLYMARKET_MAX_BET_USD`, `POLYMARKET_MAX_SESSION_USD` | +| **Lifecycle hooks** | Anything you define | Your command decides; `PreSpend` can veto with the estimated USD | Your JSON in `~/.blockrun/hooks/` | ## How Enforcement Works -Every trade goes through `alpha_risk` before execution: +### Paper trades + +Every `TradingOpenPosition` goes through the risk engine before a simulated fill: ``` -1. Claude wants to buy $500 ETH -2. alpha_risk checks: - - Would this exceed 15% position size? - - Would total exposure exceed 50%? - - Would cash reserve drop below 50%? - - Are we in daily loss lockout? -3. If any check fails → trade rejected with reason -4. If all checks pass → trade proceeds to alpha_swap +1. Franklin wants to buy $500 of ETH (paper) +2. RiskEngine checks: + - Is there enough paper cash for the order? + - Would ETH's projected position exceed the $400 per-position cap? + - Would total exposure (other positions at cost + this one) exceed $900? +3. Any check fails → order blocked with the reason; the agent can retry smaller +4. All pass → simulated fill at the live price, 10 bps fee, portfolio saved ``` +Sells of an existing position always pass — exit orders bypass the exposure caps so a cap can never trap the agent in a losing position. Selling more than you hold is rejected by the portfolio itself. + +### Real money + +Real-money tools — Polymarket orders and, when re-enabled, the Jupiter / 0x swaps — refuse to run without an approved `TradePlan`: + +``` +1. Franklin proposes a plan: venue, asset, size, slippage, stop condition, + rationale, total spend +2. The proposal blocks until you decide — in the terminal, in Franklin Desktop, + or from the mission-control panel +3. Approved → the plan's budget draws down as trades execute, and expires + after 15 minutes +4. Rejected or expired → nothing executes; revise and re-propose +5. Every decision is appended to ~/.blockrun/approvals.jsonl +``` + +Headless runs (`franklin -p ...`) have no approval surface, so they fail closed: every plan is rejected unless you pass `--approve-trades`, and even then the plan must fit inside what is left of `--max-spend`. + +Polymarket adds its own layer inside the tool: every placement is a dry-run preview unless `confirm:true`, a confirmed placement is shown to you before signing (bypass only via `auto_approve` / `FRANKLIN_POLYMARKET_AUTO_APPROVE=1`), and orders are capped per order and optionally per session. + +### Your own hooks + +Drop a JSON hook (and its script) in `~/.blockrun/hooks/`, or in `/.franklin/hooks/` for a trusted project. Eight lifecycle events; two are blocking: + +| Event | Blocking | Fires | +|---|---|---| +| `PreToolUse` | yes | before any tool executes (`matcher` regex on the tool name) | +| `PreSpend` | yes | before a tool that moves real money, with `spend: {estimatedUsd, tool, params}` on stdin | +| `PostSpend` | no | after a successful spend | +| `SessionStart`, `UserPromptSubmit`, `PostToolUse`, `Stop`, `SessionEnd` | no | lifecycle | + +Exit `2` or print `{"decision":"deny","reason":"..."}` to block. A hook that crashes or times out (5s default) fails **open** — only an explicit deny blocks. Every deny is recorded in `~/.blockrun/approvals.jsonl`; disable all hooks with `FRANKLIN_HOOKS=0`. + +Franklin ships three examples: a **daily spend cap** (`PreSpend`), a **token blacklist** (`PreToolUse` on swap tools), and a **session spend ledger** (`PostSpend`). + ## Example Scenarios -### Scenario 1: Position Size Limit +### Scenario 1: Paper per-position cap ``` -Portfolio: $1000 total -Current ETH: $100 (10%) -Requested: Buy $100 more ETH +Paper cash: $1,000 +Current ETH: $300 +Requested: buy $150 more ETH -Check: Would ETH be 20%? > 15% limit -Result: REJECTED - "Would exceed 15% position limit" +Check: projected ETH $450 > $400 per-position cap +Result: BLOCKED — "Exceeds per-position cap: projected $450.00 > cap $400.00" ``` -### Scenario 2: Cash Reserve +### Scenario 2: Paper total exposure ``` -Portfolio: $1000 total -Current cash: $600 (60%) -Requested: Buy $200 of tokens +Open: BTC $400, SOL $350 (at cost) +Requested: buy $200 ETH -Check: Would cash be 40%? < 50% minimum -Result: REJECTED - "Would violate 50% cash reserve" +Check: projected total $950 > $900 exposure cap +Result: BLOCKED — "Exceeds total exposure cap" ``` -### Scenario 3: Daily Loss Lockout +### Scenario 3: Exit is always allowed ``` -Portfolio started day: $1000 -Current value: $940 (-6%) +Open: BTC $400 (down 6%) +Requested: sell all BTC -Check: Daily loss > 5% threshold -Result: ALL TRADES REJECTED until next day +Check: existing position → exposure caps skipped +Result: FILLED ``` -### Scenario 4: Approved Trade +### Scenario 4: Real-money plan in a scripted run ``` -Portfolio: $1000 total -Current cash: $700 (70%) -Current ETH: $50 (5%) -Requested: Buy $100 ETH - -Checks: -- ETH would be 15% ✓ (at limit, not over) -- Cash would be 60% ✓ (above 50% minimum) -- Total exposure would be 40% ✓ (below 50% cap) -- No daily loss lockout ✓ +franklin -p "buy $20 of YES on the rate-cut market" --max-spend 1 -Result: APPROVED +TradePlan propose → no approval surface, --approve-trades not set +Result: REJECTED — recorded in ~/.blockrun/approvals.jsonl ``` -## Why These Limits? +### Scenario 5: Approved plan -### 15% Max Position +``` +Plan: polymarket · bet · "Fed cuts in September" YES · $20 · stop: exit if price < 0.35 +You: approve +PolymarketBet buy (confirm:true) → within $25 per-order cap → preview → signed +Remaining plan budget: $0 · plan expires in 15 minutes +``` -Prevents overconcentration. Even if AI is confident, diversification protects against being wrong. +## Why These Limits? -### 50% Cash Reserve +### $400 / $900 paper caps -Ensures you always have capital to: -- Average down on good opportunities -- Cover unexpected losses -- Exit positions gradually +Two-and-a-half fully loaded positions and a 10% cash buffer on a $1,000 paper bankroll. Concentration is bounded, and the agent learns to size before it ever touches real funds. -### 50% Exposure Cap +### Approval, not autonomy, for real money -Combined with cash reserve, this means: -- Max 50% in risk assets -- Min 50% in stablecoins -- Balanced exposure regardless of market conditions +A signed micropayment for a model call is cheap and reversible in effect; a swap or a bet is not. The plan makes the agent write down venue, size, slippage, stop, and rationale before it asks — so what you approve is a thesis, not a button. -### 5% Daily Loss Threshold +### 15-minute expiry -Circuit breaker to prevent catastrophic losses. If the day is going badly, stop trading and reassess. +Markets move. An approval given for one price should not be spendable an hour later. -### 15% Stop-Loss +### Fail closed headless, fail open hooks -Automatic exit to prevent holding losing positions indefinitely. Losses are cut, not hoped away. +A scripted run with nobody watching must not trade unless you explicitly said so. A guardrail script that crashes must not silently freeze the agent — so hooks fail open and log, and only an explicit deny blocks. ## What You Cannot Do -Even if you ask Claude to: - -❌ "Buy 50% of my portfolio in PEPE" → Rejected (exceeds position limit) +❌ "Skip the plan and just buy" → real-money tools refuse without an approved `TradePlan` -❌ "Go all-in on ETH" → Rejected (violates cash reserve) +❌ "Ignore the exposure caps" → the paper risk engine is code, not a prompt -❌ "Ignore the risk limits" → Limits are hardcoded, not prompts +❌ "Approve it yourself" in `--trust` mode → trade plans always prompt, even with `--trust` -❌ "Override safety for this one trade" → No override mechanism exists +❌ Trade from a `-p` script without `--approve-trades` → every plan is rejected ## What You Can Do -✅ Trade within limits freely +✅ Paper-trade freely within the caps -✅ Ask Claude to optimize within constraints +✅ Approve, revise, or reject each real-money plan — from the terminal, Desktop, or the panel -✅ Use multiple smaller positions +✅ Cap a session with `--max-spend`, and cap bets with `POLYMARKET_MAX_BET_USD` / `POLYMARKET_MAX_SESSION_USD` -✅ Let Claude manage risk automatically +✅ Add your own vetoes with `PreSpend` / `PreToolUse` hooks ## Viewing Current Risk Status ``` -What's my current risk status? +What's my current exposure? ``` -Claude will show: -- Current positions as % of portfolio -- Remaining room in each limit -- Whether any limits are close -- Daily P&L status +`TradingPortfolio` shows cash, each position with unrealized P&L, total exposure against the $900 cap, and the journal-discipline trend. `TradePlan status` shows the active plan and its remaining budget. ## Best Practices -1. **Start small** — Test with amounts you can afford to lose -2. **Trust the limits** — They exist to protect you -3. **Review trades** — Check alpha_memory for decision history -4. **Monitor daily** — Check portfolio and P&L regularly +1. **Start on paper** — the paper engine uses live prices, so the P&L is real even if the money isn't +2. **Set `--max-spend`** on every session that can reach real-money tools +3. **Write the hook you wish you had** — a daily cap and a token blacklist take five minutes +4. **Read the journal** — `TradingHistory` and the wallet-keyed journal tell you whether your theses hold up -## Customization +## Current limitations -Currently, risk limits are fixed. Future versions may support user-configurable limits with minimum safety floors. +- Paper-trading caps ($400 / $900 / $1,000) are code defaults, not user-configurable +- Live DEX execution (`JupiterSwap`, `Base0xSwap`, `Base0xGaslessSwap`) is paused in 3.42.0 pending complete local transaction validation; quotes work, and the tools stay behind `TradePlan` when they return +- Trade plans initiated from surfaces without an approval UI are rejected by the gate ## What's next? ::::cards :::card{title="Tools reference" href="tools.md" icon="Boxes"} -See how `alpha_risk` gates every swap. +See how `TradePlan` and the risk engine gate each tool. ::: -:::card{title="Installation" href="installation.md" icon="Terminal"} -Get alpha-mcp running in Claude Code. +:::card{title="Setup" href="installation.md" icon="Terminal"} +Get Franklin running with a funded wallet. ::: :::card{title="Overview" href="overview.md" icon="Book"} -What alpha-mcp does and how it trades autonomously. +What Franklin's trading surface does and how it trades. ::: :::: diff --git a/docs/products/trading/tools.md b/docs/products/trading/tools.md index fe22c7f..6dea302 100644 --- a/docs/products/trading/tools.md +++ b/docs/products/trading/tools.md @@ -1,238 +1,212 @@ --- title: Trading Tools Reference -description: The 7 alpha-mcp tools for autonomous crypto trading — signals, DEX data, sentiment, swaps, portfolio, risk checks, and trade memory. +description: Franklin's trading tools — signals, market data, paper trading, trade plans, prediction markets, DEX quotes, DeFi data, on-chain RPC, and the wallet. --- # Trading Tools Reference -alpha-mcp provides 7 tools for autonomous crypto trading. +Franklin's trading surface is a set of built-in tools the agent calls on its own. Tool names below are the exact names you will see in the session's tool activity. -## alpha_signal +## TradingSignal -Technical indicators from Binance. +Price, technical indicators, and a verdict for a cryptocurrency, computed locally from live market data. **What it returns:** -- RSI (Relative Strength Index) -- MACD (Moving Average Convergence Divergence) -- EMA (Exponential Moving Averages) -- Volume analysis -- Price action data +- Current price, market cap, 24h volume +- RSI, MACD (signal / histogram), Bollinger Bands, annualized volatility +- A Verdict section: bullish / bearish / neutral with confidence and bull/bear signal lists +- A dual-listing note for tickers that also trade as equities (COIN, MSTR, CRCL, …), so the agent can fetch the spot equity in parallel + +**Parameters:** `ticker` (required), `days` (lookback; default 90 — below 35 leaves MACD undefined) **Example usage:** ``` -Get technical signals for BTC/USDT +Get technical signals for BTC ``` ``` What's the RSI and MACD for ETH? ``` -**Pricing:** Free (Binance public API) +**Pricing:** Free --- -## alpha_dex +## TradingMarket -DEX market data via DexScreener. +Market data across asset classes. -**What it returns:** -- Token prices across DEXes -- Liquidity depth -- 24h volume -- Price changes -- Pool information +**Actions:** +- `price` — crypto spot (free) +- `trending` — top trending coins (free) +- `overview` — top 20 by market cap (free) +- `fxPrice` — FX pairs like `EUR-USD` via the BlockRun gateway (free) +- `commodityPrice` — `XAU-USD` (gold), `XAG-USD` (silver), … (free) +- `stockPrice` — equities across `us`, `hk`, `jp`, `kr`, `gb`, `de`, `fr`, `nl`, `ie`, `lu`, `cn`, `ca` (paid per call from the agent wallet; `market` is required) **Example usage:** ``` -Check the liquidity for PEPE on Uniswap +What's gold doing today? ``` ``` -What DEXes have the best price for swapping 1000 USDC to ETH? +Price of 7203 on the Tokyo exchange ``` -**Pricing:** Free (DexScreener public API) - --- -## alpha_sentiment +## TradingPortfolio · TradingOpenPosition · TradingClosePosition · TradingHistory -Social sentiment analysis powered by BlockRun. +Paper trading against live prices. Fills are simulated (10 bps fee); no real assets move and no USDC is spent on exchange fees. -**What it returns:** -- Social media sentiment score -- Trending topics -- Influencer activity -- News sentiment -- Community metrics +**TradingPortfolio** — cash, open positions with unrealized P&L, realized P&L, and a readout of exposure against the caps. Includes a journal-discipline footer scored on rationale completeness. + +**TradingOpenPosition** — buy into a position. Pre-trade risk checks enforce the $400 per-position cap, the $900 total exposure cap, and cash sufficiency; a blocked order returns the reason so the agent can retry smaller. Optional `rationale` (direction, price target, stop, time horizon, conviction, evidence, tags, thesis) is journaled. + +**TradingClosePosition** — sell part or all of a position. Exits always bypass the exposure caps so the agent is never trapped in a losing position. + +**TradingHistory** — recent trades and realized P&L over a time window, read from the persistent trade log so it spans every prior session on the machine. + +**State on disk:** `~/.blockrun/portfolio.json` (portfolio, $1,000 starting paper bankroll), `~/.blockrun/trades.jsonl` (trade log), `~/.blockrun/memory/trading-*/` (wallet-keyed journal). **Example usage:** ``` -What's the social sentiment around SOL right now? +Open a $200 paper position in SOL — thesis: funding flipped negative ``` ``` -Is there any negative news about BTC today? +Am I up this week? What was my worst trade? ``` -**Pricing:** ~$0.01 per query (paid via x402) +**Pricing:** Free --- -## alpha_swap - -Execute token swaps on Base via 0x Protocol. +## TradePlan -**What it does:** -- Finds best swap route -- Executes trade on-chain -- Returns transaction hash +The agent's only path to real-money authorization. **Required before any swap or Polymarket order.** -**Parameters:** -- `tokenIn`: Token to sell -- `tokenOut`: Token to buy -- `amount`: Amount to swap -- `slippage`: Max slippage (default 0.5%) +**Actions:** +- `propose` — validates the intended trades, persists a pending plan, and blocks until you decide +- `status` — show the active plan and its remaining budget +- `cancel` — cancel a plan by id -**Example usage:** - -``` -Swap 100 USDC for ETH on Base -``` +**Each trade:** `venue` (`jupiter` | `zerox` | `polymarket`), `action` (`buy` | `sell` | `swap` | `bet`), `asset`, `amountUsd`, optional `direction`, `maxSlippageBps`, `stopCondition`, plus a one-paragraph `rationale` for the plan. -``` -Buy $50 worth of PEPE with USDC -``` - -**Pricing:** Network gas only (no BlockRun fee) - -:::warning{title="Subject to risk limits"} -Every swap is validated by `alpha_risk` first. Trades that breach a hardcoded limit are rejected — see [Risk Management](risk-management.md). -::: +Approved plans expire after 15 minutes and their budget draws down as trades execute. Without an approval surface (a scripted `-p` run) the proposal fails closed unless `--approve-trades` was granted and the total fits the remaining `--max-spend`. Every decision is appended to `~/.blockrun/approvals.jsonl`. --- -## alpha_portfolio +## PredictionMarket -Track and manage trading positions. +Prediction-market research via the BlockRun gateway, paid per call. -**What it returns:** -- Current holdings -- Position sizes (% of portfolio) -- Unrealized P&L -- Entry prices -- Portfolio value +**Actions:** `searchAll` (Polymarket, Kalshi, Limitless, Opinion, Predict.Fun in one call), `searchPolymarket`, `searchKalshi`, `leaderboard`, `walletProfile`, `walletPnl`, `walletPositions`, `smartActivity`, `smartMoney`. **Example usage:** ``` -What's my current portfolio? +Is there a market anywhere on the Fed cutting in September? ``` ``` -How much ETH do I hold? +What are the top Polymarket wallets by P&L positioning in right now? ``` -**Pricing:** Free - --- -## alpha_risk +## PolymarketBet + +Real-money bets on Polymarket (CLOB V2, Polygon), signed locally by your BlockRun key. Orders spend pUSD in a Polymarket deposit wallet funded from your own Base USDC. + +**Actions:** `setup` (create / inspect the deposit wallet and approvals; reports region status), `fund`, `buy` / `sell` (limit or market), `orders`, `cancel`, `positions`, `redeem`, `withdraw`. -Enforce risk management constraints. +Every placement is a dry-run preview unless `confirm:true`; a confirmed placement is shown to you for approval before signing (bypass only with `auto_approve` / `FRANKLIN_POLYMARKET_AUTO_APPROVE=1` for headless runs). Per-order cap `POLYMARKET_MAX_BET_USD` (default $25) and optional `POLYMARKET_MAX_SESSION_USD`. Order placement is geoblocked in some regions. -**What it does:** -- Validates trades against risk limits -- Returns approval/rejection with reason -- Cannot be overridden +:::warning{title="Behind the trade-plan gate"} +`PolymarketBet` orders also require an approved `TradePlan`. Bets are your own funds on Polygon and do not draw from the `--max-spend` AI budget; the small funding fee is metered as x402 spend. +::: + +--- -**Risk limits enforced:** +## JupiterQuote · Base0xQuote (and the paused swap tools) -| Limit | Value | -|-------|-------| -| Max position size | 15% | -| Total exposure cap | 50% | -| Daily loss threshold | 5% | -| Min cash reserve | 50% | -| Stop-loss trigger | 15% | +Read-only DEX routes and prices: Jupiter on Solana, 0x on Base. + +`JupiterSwap`, `Base0xSwap`, and `Base0xGaslessSwap` exist but are **temporarily disabled in 3.42.0** while Franklin adds complete local transaction, Permit2, and EIP-712 validation — Franklin will not sign opaque transaction bytes handed back by an upstream. When re-enabled they remain behind `TradePlan`. **Example usage:** ``` -Can I buy $500 worth of PEPE? +What's the best route to swap 100 USDC to SOL right now? ``` -Claude will automatically check risk limits before executing. +--- -**Pricing:** Free +## DeFiLlamaProtocols · DeFiLlamaProtocol · DeFiLlamaChains · DeFiLlamaYields · DeFiLlamaPrice ---- +Protocol TVL and rankings, per-protocol detail, chain TVL, yield pools, and token prices. -## alpha_memory +``` +Which Base protocols grew TVL the most this month? +``` -Semantic search across trade history. +--- -**What it does:** -- Stores all trade decisions and outcomes -- Enables learning from past trades -- Retrieves relevant historical context +## MultiChainRPC -**Example usage:** +Read-only JSON-RPC across 40+ chains through one gateway endpoint (no per-chain key), paid per call. EVM chains speak `eth_*`, Solana speaks `getSlot` / `getBalance` / `getTransaction`, Bitcoin-family speaks `getblockcount`. Signing and send-transaction methods are rejected. ``` -What happened last time I traded PEPE? +Did my last Base transaction land? Check the receipt. ``` -``` -Show me my most profitable trades this week -``` +--- + +## Wallet + +Franklin's own wallet status — chain, address, USDC balance. Never costs USDC. ``` -Why did I sell ETH on Monday? +What's my balance? ``` -**Pricing:** Free (local storage) - --- ## Tool Interaction Flow -Typical trading session: +Typical session: -1. **alpha_signal** — Get technical indicators -2. **alpha_dex** — Check liquidity and prices -3. **alpha_sentiment** — (optional) Check social signals -4. **alpha_risk** — Validate trade against limits -5. **alpha_swap** — Execute if approved -6. **alpha_portfolio** — Verify position -7. **alpha_memory** — Log for future reference +1. **TradingSignal** / **TradingMarket** — indicators and prices +2. **PredictionMarket** / **DeFiLlama\*** — (optional) odds, TVL, yields +3. **TradingOpenPosition** — paper trade, checked against the exposure caps +4. **TradePlan** — for real money: propose, wait for your approval +5. **PolymarketBet** — execute within the approved budget +6. **TradingPortfolio** / **TradingHistory** — verify position and P&L +7. Journal entry written automatically; recall later with **MemoryRecall** ## Example Session ``` -User: Analyze ETH and execute a trade if signals are good - -Claude: -1. Calls alpha_signal for ETH/USDT - → RSI: 45 (neutral), MACD: bullish crossover +User: Analyze ETH and open a paper position if signals are good -2. Calls alpha_dex for ETH liquidity - → Sufficient liquidity on Uniswap, best price +Franklin: +1. TradingSignal ETH + → RSI 45 (neutral), MACD bullish crossover · Verdict: bullish (medium confidence) -3. Calls alpha_risk to validate $100 buy - → Approved: within position limits +2. TradingPortfolio + → Cash $1,000 · exposure $0 / $900 cap -4. Calls alpha_swap to buy $100 ETH - → Executed, tx: 0x... +3. TradingOpenPosition ETH $150, rationale: bullish MACD crossover, stop −4% + → Filled at live price (10 bps simulated fee) -5. Calls alpha_portfolio - → Updated holdings: 0.042 ETH +4. TradingPortfolio + → ETH $150 (unrealized 0.0%) · exposure $150 / $900 -6. Calls alpha_memory - → Logged: "Bought ETH on bullish MACD crossover" +Journal: thesis recorded under your wallet's trading journal ``` ## What's next? @@ -240,15 +214,15 @@ Claude: ::::cards :::card{title="Risk management" href="risk-management.md" icon="TrendingUp"} -Understand the safety limits enforced before every swap. +The caps, the trade-plan gate, and the hooks that gate every spend. ::: -:::card{title="Installation" href="installation.md" icon="Terminal"} -Get alpha-mcp running in Claude Code. +:::card{title="Setup" href="installation.md" icon="Terminal"} +Get Franklin running with a funded wallet. ::: :::card{title="Overview" href="overview.md" icon="Book"} -What alpha-mcp does and how it pays for its own intelligence. +What Franklin's trading surface does and how it pays for its own intelligence. ::: :::: diff --git a/docs/rate-limits.md b/docs/rate-limits.md index 44563ab..7eb1233 100644 --- a/docs/rate-limits.md +++ b/docs/rate-limits.md @@ -13,7 +13,7 @@ request, BlockRun surfaces it to you transparently as an HTTP `429` so you can back off or fail over. :::info{title="No platform throttle on paid calls"} -For paid model calls there is no BlockRun-side per-request limit — your only ceiling is the upstream provider's TPM/RPM. Some non-LLM endpoints (image generation, async job submission, wallet reconciliation, RealFace init) carry small per-IP limits to bound abuse and real upstream cost. +For paid model calls there is no BlockRun-side per-request limit — your only ceiling is the upstream provider's TPM/RPM. Free chat models are limited to **30 requests/minute and 300 requests/hour per IP**, and a few non-LLM endpoints (metadata catalogs, wallet reconciliation, RealFace init, onramp link minting) carry small per-IP limits to bound abuse and real upstream cost. ::: ## The `429` response @@ -28,16 +28,22 @@ Content-Type: application/json ```json { - "error": "Rate limited", - "message": "Upstream provider rate limit hit — retry after 60s, or fail over to a same-tier model from a different family.", + "error": { + "message": "Rate limited — … Upstream provider rate limit hit — retry after 60s, or fail over to a same-tier model on a different provider.", + "type": "rate_limit_error", + "code": "RATE_LIMITED", + "param": null + }, "code": "RATE_LIMITED", + "source": "anthropic", "retry_after_seconds": 60 } ``` | Field / Header | Meaning | |----------------|---------| -| `Retry-After` (header) | Seconds to wait before retrying. Honor this. | +| `Retry-After` (header) | Seconds to wait before retrying. Honor this. Taken from the upstream when it says; otherwise 60. | +| `X-RateLimit-Source` (header) / `source` | The model family whose capacity is exhausted — a failover hint. | | `code` | Always `RATE_LIMITED` for this case. | | `retry_after_seconds` | Same value as `Retry-After`, in the body for convenience. | @@ -61,9 +67,13 @@ if resp.status_code == 429: BlockRun serves each model through its own gateway and may route a single model id across multiple backing capacity pools, failing over automatically and internally. You only ever see a `429` when **every** backing pool for that model is exhausted — at which point backing off or failing over to another model family is the fastest path through. +## Free models + +Free chat models (`"billing_mode": "free"` in `GET /v1/models`) take no payment, so they are throttled per source IP: **30 requests/minute** and **300 requests/hour**. Over the limit you get `429` with `code: "FREE_TIER_RATE_LIMITED"`, `Retry-After`, and `X-RateLimit-Limit` / `X-RateLimit-Remaining` / `X-RateLimit-Reset` (unix ms). Paid requests from the same IP are never counted. When the free pool itself is out of capacity the code is `STREAM_FAILED` / `FREE_MODEL_FAILED` with `Retry-After: 30`. + ## Other endpoints -Some non-LLM endpoints (image generation, async job submission, wallet reconciliation, RealFace init) carry small per-IP limits to bound abuse and real upstream cost. When exceeded they also return `429`; the same `Retry-After` guidance applies. +The metadata catalogs (`GET /v1/models` and the image/video/audio model lists, `/api/pricing`: 100/hour per IP; `/api/health`: 60/minute), per-wallet lookups (reconciliation, portraits, RealFace status: 120/hour), `POST /v1/realface/init` (10/hour) and `POST /v1/onramp/token` (30/hour per IP, 10/hour per wallet) carry small per-IP limits to bound abuse and real upstream cost. When exceeded they return `429` with `{"error":"Rate limit exceeded"}` and an `X-RateLimit-Reset` header (unix ms). The full table is in the [API reference](api-reference/rate-limits.md). ## What's next? diff --git a/docs/resources/changelog.md b/docs/resources/changelog.md index 460bb16..1b51d50 100644 --- a/docs/resources/changelog.md +++ b/docs/resources/changelog.md @@ -16,6 +16,68 @@ All notable changes to BlockRun, newest first — gateway endpoints, model lineu - **`zai/glm-5.3`** ($1.40/$4.40, 1M context), launched 2026-08-19, is now listed in the model reference table as well. - Visible chat models are now **72** (was 71); total catalog **96** across chat, image, video, music, speech, and sound effects. +### Changed — Reasoning rides in `reasoning_content`, never as tool-call `content` +- **`/v1/chat/completions`** non-streaming responses now carry a model's chain of thought on **`choices[].message.reasoning_content`**, the same field streaming has always used for `delta.reasoning_content`. It is optional and absent when the model produced no reasoning. Claude thinking, DeepSeek, GLM, MiniMax, Kimi and open-weight reasoning models all populate it. ([#423](https://github.com/BlockRunAI/blockrun/pull/423)) +- An assistant turn that carries **`tool_calls` now has `content: ""`**, as the OpenAI spec intends. Previously the gateway rescued the empty content with the model's raw reasoning, so Anthropic-format clients rendered the chain of thought as a visible text block ahead of the `tool_use`, and clients echoing history fed reasoning back as content. Turns with no tool calls and no answer still receive the reasoning text as `content` so a paid call never returns a blank string. ([#422](https://github.com/BlockRunAI/blockrun/pull/422)) +- `/v1/messages` builds a leading `thinking` block from the same field, so streaming and non-streaming Anthropic-format responses finally have the same shape. + +--- + +## [2026-08-26] + +### Changed — Payment-verification 402s carry a machine-readable `code` +- Every BlockRun-native paid endpoint now answers a failed `PAYMENT-SIGNATURE` with `402` and a **`code`** a client can branch on instead of parsing prose: **`PAYMENT_UNFUNDED`** (the on-chain simulation reverted — usually an empty wallet, or an authorization outside its validity window), **`PAYMENT_BLOCKHASH_STALE`** (a Solana-signed transaction pinned to a blockhash that expired — re-sign against a fresh one and resend; nothing was charged), **`PAYMENT_REPLAY`** (nonce already used), and **`PAYMENT_INVALID`** for everything else. Previously only `/v1/chat/completions` classified failures; the other paid routes returned a bare `{error, details}`. +- The facilitator's own explanation is now folded into `debug` rather than only logged, so a rejection says *which* failure it was. Verification always runs before settlement, so any of these codes means nothing was charged. +- The vendor-compatible `/v1/messages`, `/v1/responses` and Gemini-native endpoints keep their vendor error schemas and do not carry the field. ([commit ac50282](https://github.com/BlockRunAI/blockrun/commit/ac50282)) + +--- + +## [2026-08-19] + +### Added — Z.AI GLM-5.3 +- Added **`zai/glm-5.3`** ($1.40/$4.40 per 1M, **1M context**, 131,072 max output). Thinking is always on for this generation and cannot be disabled — unlike GLM-5/5.1/5.2 it returns content alongside its reasoning, so no gateway workaround is applied. ([#397](https://github.com/BlockRunAI/blockrun/pull/397)) + +--- + +## [2026-08-13] + +### Added — xAI Grok Imagine Video 1.5, and official resolution tiers on both Grok video SKUs +- Added **`xai/grok-imagine-video-1.5`** (image/text-to-video, 480p/720p/**1080p**, up to 15s, native audio) at xAI's official per-second rates: **$0.08/s @480p** (default), **$0.14/s @720p**, **$0.25/s @1080p**. ([#380](https://github.com/BlockRunAI/blockrun/pull/380)) +- **`xai/grok-imagine-video`** moved to the same tiered model: **$0.05/s @480p** (default) and **$0.07/s @720p**; 1080p/4K are rejected with a `400` before payment. `resolution` is now a billing tier and is pinned in the quote, so the price you sign is the tier you get. ([#378](https://github.com/BlockRunAI/blockrun/pull/378)) +- Both Grok SKUs charge the official rate **exactly** (no 5% media margin) plus a flat **$0.001 per generation**, disclosed as `perGenerationFee` in the 402 price block alongside `pricePerSecond` for the billed tier; the flat $0.001 transaction fee applies on top. `aspect_ratio` (incl. `3:2` / `2:3`) is now validated and forwarded — it was previously accepted and silently dropped. ([#379](https://github.com/BlockRunAI/blockrun/pull/379), [#381](https://github.com/BlockRunAI/blockrun/pull/381)) + +--- + +## [2026-08-12] + +### Added — Seedance 2.0 Mini +- Added **`bytedance/seedance-2.0-mini`** at **$0.0797/sec** (480p/720p with audio, 4–15s) — roughly half the rate of `seedance-2.0-fast` for the same 720p output, the sensible default for volume. Supports the same image, first/last-frame and RealFace inputs as the rest of the 2.0 family. ([#366](https://github.com/BlockRunAI/blockrun/pull/366), [#369](https://github.com/BlockRunAI/blockrun/pull/369)) + +--- + +## [2026-08-08] + +### Changed — Per-token chat now bills at list price, with no platform margin +- Every per-token chat model on `/v1/chat/completions`, `/v1/messages` and `/v1/responses` is now priced at the maker's list rate with **0% platform margin** (was 5%). The flat **$0.001 per-request transaction fee** is unchanged and is the only thing BlockRun adds to a chat call. Image, video, music, speech and Live Search keep their 5% margin. ([#354](https://github.com/BlockRunAI/blockrun/pull/354)) +- Three prices were corrected in the same pass: **`deepseek/deepseek-chat`** and **`deepseek/deepseek-reasoner`** to **$0.14/$0.28** (from $0.20/$0.40, per DeepSeek's current list), and **`zai/glm-5`** to **$1.00/$3.20** (from $0.60/$1.92 — Z.AI had raised its standard rate). +- **Images**: a client that loses the response to an inline image generation can now retry with the **same** signed authorization and get the job it already paid for back (`402` / `PAYMENT_REPLAY` now carries `job_id` and `poll_url`) instead of being told to sign — and pay — again. ([#346](https://github.com/BlockRunAI/blockrun/pull/346)) + +--- + +## [2026-08-07] + +### Added — Seedance 2.5, and the Seedance family repriced +- Added **`bytedance/seedance-2.5`** — **4–30s** clips (double the 2.0 ceiling) at a **720p** ceiling, $0.315/sec. It is not a straight upgrade over 2.0, which keeps the 4K ceiling at 4–15s; both stay listed. Duration always defaults to 5s (the model's own "-1, pick for me" default is never forwarded, since the price is signed before generation). ([#351](https://github.com/BlockRunAI/blockrun/pull/351)) +- The rest of the family moved to current per-second rates: `seedance-1.5-pro` $0.070/sec, `seedance-2.0-fast` $0.165/sec, `seedance-2.0` $0.227/sec (720p, 5s default; 1080p and 4K scale by area). Per-model resolution and duration limits are now derived from the model's own parameter schema and enforced before the 402. ([#353](https://github.com/BlockRunAI/blockrun/pull/353)) + +--- + +## [2026-08-05] + +### Changed — Discovery now lists every live endpoint, with derived prices +- `/.well-known/x402` and `openapi.json` now publish the full live surface — Surf, 0x, DefiLlama, the templated Predexon paths, every RPC chain, and the first-party phone, voice, music, RealFace, Portrait, Polymarket-funding, onramp and catalog endpoints — instead of a hand-maintained subset. Every published price is derived from the same constant the 402 signs, which corrected five stale literals (Predexon, phone buy/lookup/list, DefiLlama). ([#336](https://github.com/BlockRunAI/blockrun/pull/336)) +- Six dead prediction-market endpoints were de-registered (`matching-markets`, `matching-markets/pairs`, `markets`, `listings`, `outcomes/{id}`, `dflow/*`). ([#332](https://github.com/BlockRunAI/blockrun/pull/332), [#336](https://github.com/BlockRunAI/blockrun/pull/336)) + --- ## [2026-08-03] @@ -26,6 +88,31 @@ All notable changes to BlockRun, newest first — gateway endpoints, model lineu - We passed the cut through rather than keeping the spread. Every other OpenAI model in the catalog was re-checked against the published rates in the same pass and already matched. - Fallbacks moved with the prices: Terra now falls back to `gpt-5.4-mini` and Luna to `gpt-4o-mini`. The previous targets were not repriced by OpenAI, so they now cost more than the primaries bill and would settle below cost on every failover. +### Added — Eight models: Gemini 3.6 Flash, GPT-5.6 Pro tiers, Qwen3.7 Plus/Flash, Nano Banana 2 +- **`google/gemini-3.6-flash`** ($1.50/$7.50 per 1M) and **`google/gemini-3.5-flash-lite`** ($0.30/$2.50). Thinking is always on for both — they reject a zero thinking budget upstream, which the gateway no longer sends. +- **`openai/gpt-5.6-sol-pro`** ($5.00/$30.00), **`openai/gpt-5.6-terra-pro`** ($1.00/$6.00), **`openai/gpt-5.6-luna-pro`** ($0.10/$0.60) — the pro reasoning modes behind ChatGPT Pro. Served on `/v1/chat/completions`; `/v1/responses` now gates on the OpenAI model family rather than on the id prefix. +- **`qwen/qwen3.7-plus`** ($0.32/$1.28, 131K max output) and **`qwen/qwen3.7-flash`** ($0.03/$0.13) complete the Qwen3.7 line; both are reasoning models. +- **`google/nano-banana-2`** ($0.09/image, 1024×1024) on both `/v1/images/generations` and `/v1/images/image2image`. ([#329](https://github.com/BlockRunAI/blockrun/pull/329)) + +### Changed — RPC upstream faults are ours, not yours +- `/v1/rpc/{network}`: an upstream timeout, credential failure or 5xx now returns **`502` with `Retry-After: 30`** and releases your payment nonce, so an SDK retry with the same signed header succeeds instead of dying as a false `PAYMENT_REPLAY`. A known chain's upstream `404` is reported as our stale slug, not a caller error. ([#324](https://github.com/BlockRunAI/blockrun/pull/324), [#327](https://github.com/BlockRunAI/blockrun/pull/327), [#328](https://github.com/BlockRunAI/blockrun/pull/328)) + +--- + +## [2026-08-02] + +### Changed — The 402 tells you what to fix +- **`/v1/chat/completions`**: an unfunded wallet now gets `402` / **`PAYMENT_UNFUNDED`** with a plain-language explanation instead of "message a human on Telegram". One empty wallet had retried 292 times against the old message. ([#322](https://github.com/BlockRunAI/blockrun/pull/322)) +- The chat `402` body's `price.amount` now equals the amount the header signs, **transaction fee included**. It had been quoting the pre-fee price, under-stating a floored call by the whole $0.001 fee. ([#321](https://github.com/BlockRunAI/blockrun/pull/321)) +- When the gateway serves a smaller `max_tokens` than you asked for (model ceiling or context headroom), the response now says so: **`X-Max-Tokens-Capped: true`**, `X-Max-Tokens-Requested`, `X-Max-Tokens-Effective`. Headers only, only when the clamp bit; bodies are unchanged. ([#271](https://github.com/BlockRunAI/blockrun/pull/271)) + +--- + +## [2026-07-29] + +### Changed — Transaction fee back to $0.001 +- The flat per-request transaction fee added to every paid call returns to **$0.001** (it had been $0.002 since 2026-07-11). The smallest possible paid chat call is therefore $0.002 all-in. Every published minimum was swept to match. ([#319](https://github.com/BlockRunAI/blockrun/pull/319), [#320](https://github.com/BlockRunAI/blockrun/pull/320)) + --- ## [2026-07-24] diff --git a/docs/resources/ecosystem.md b/docs/resources/ecosystem.md index 5680808..c6e2b95 100644 --- a/docs/resources/ecosystem.md +++ b/docs/resources/ecosystem.md @@ -1,29 +1,95 @@ --- title: Ecosystem -description: Products, SDKs, framework integrations, networks, x402 facilitators, partners, AI providers, and community projects building with BlockRun. +description: Every public BlockRun repo — gateway entry points, routers and agent-runtime plugins, SDKs, payment rails, Franklin apps, skills, curated lists — plus API products, networks, x402 facilitators, partners, and community projects. --- # Ecosystem -Projects, integrations, and partners building with BlockRun and x402. - -## Official Products - -| Product | Description | Link | -|---------|-------------|------| -| [alpha-mcp](https://github.com/BlockRunAI/alpha-mcp) | AI crypto trading for Claude | [GitHub](https://github.com/BlockRunAI/alpha-mcp) | -| [blockrun-mcp](https://github.com/BlockRunAI/blockrun-mcp) | MCP server for 96 AI models, images, video, music, voice, prediction markets, crypto data, sandbox runtime, smart routing | [GitHub](https://github.com/BlockRunAI/blockrun-mcp) | -| [nano-banana](https://github.com/BlockRunAI/nano-banana-blockrun) | Image generation skill | [GitHub](https://github.com/BlockRunAI/nano-banana-blockrun) | +Projects, integrations, and partners building with BlockRun and x402. Every row under **Official projects** is a public, actively maintained repository in the [BlockRunAI GitHub org](https://github.com/BlockRunAI); archived repos are not listed. + +## Official projects + +### Entry points + +| Project | What it is | Install | +|---------|------------|---------| +| [blockrun-mcp](https://github.com/BlockRunAI/blockrun-mcp) | MCP server for Claude Code and any MCP client — 20 tools: chat across the full model catalog, image / video / music / speech, web + neural search, prediction markets (read **and** trade Polymarket), crypto data, Pyth prices, multi-chain RPC, DeFi data, sandboxed code exec, phone calls, wallet | `claude mcp add blockrun -s user -- npx -y @blockrun/mcp@latest` | +| [blockrun-cli](https://github.com/BlockRunAI/blockrun-cli) | Umbrella CLI (`blockrun`) + `@blockrun/core` shared kernel — one wallet, one x402 payment path, one `{ok,data\|error}` output contract. ~40 commands: wallet, inference, multimodal, data, generic `api` / `pay` for any x402 endpoint, spending guardrails, agent skills; prefix discovery routes to ClawRouter, Franklin, MCP and the Codex bridge | `npm install -g @blockrun/cli` | +| [ClawRouter](https://github.com/BlockRunAI/ClawRouter) | The agent-native LLM router — every frontier model behind one wallet, <1ms local routing, USDC on Base & Solana via x402. OpenClaw plugin; free models need no wallet | `npm install -g @blockrun/clawrouter` | +| [router-core](https://github.com/BlockRunAI/router-core) | The routing engine underneath ClawRouter, Franklin, ClawRouter-Hermes and dsh-clawrouter. Deterministic, constraint-first model routing — classify, hard-filter, rank — locally in <1ms, with no inference call. Product-neutral: no wallet, gateway, or telemetry | Library (`@blockrun/router-core`, consumed from GitHub) | +| [Franklin](https://github.com/BlockRunAI/Franklin) | The AI agent with a wallet — spends USDC autonomously to get real work done. Apache-2.0, TypeScript | `npm install -g @blockrun/franklin` | + +### Routers and agent-runtime plugins + +| Project | What it is | Install | +|---------|------------|---------| +| [XClawRouter](https://github.com/BlockRunAI/XClawRouter) | ClawRouter powered by the OKX OnchainOS wallet — full model catalog, wallet-based auth, USDC micropayments via x402 on Base & Solana. OpenClaw plugin | `curl -fsSL https://blockrun.ai/XClawRouter-update \| bash` then `npx @blockrun/xclawrouter setup` | +| [ClawRouter-Hermes](https://github.com/BlockRunAI/ClawRouter-Hermes) | ClawRouter for [NousResearch Hermes](https://github.com/NousResearch/hermes-agent) — Python plugin wrapping the ClawRouter proxy; one Hermes provider for the whole catalog, paid per request in USDC on Base & Solana | `pip install hermes-plugin-clawrouter` then `hermes plugins enable clawrouter` | +| [dsh-clawrouter](https://github.com/BlockRunAI/dsh-clawrouter) | Safety gate for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): a stronger model reviews dangerous tool calls before they run. Adds vision and the full model catalog from one wallet, paid per request over x402 | `dsh plugin --profile web add dsh-clawrouter` | +| [clawrouter-codex](https://github.com/BlockRunAI/clawrouter-codex) | OpenAI Codex (CLI, IDE, Desktop) ↔ BlockRun bridge — translates the Responses API to Chat Completions, pays per request from a local wallet, `blockrun/auto` smart routing, zero API keys | `npx @blockrun/clawrouter-codex up` then `codex --profile clawrouter` | +| [blockrun-claude-plugin](https://github.com/BlockRunAI/blockrun-claude-plugin) | Claude Code media plugin (prototype) — wraps the MCP media profile with spend confirmation before each paid call, a running cost meter, a status-line balance, and `/blockrun-media:*` slash commands | `claude --plugin-dir /path/to/blockrun-plugin` | +| [blockrun-codex-plugin](https://github.com/BlockRunAI/blockrun-codex-plugin) | Codex port of the media plugin — image / video / audio with spend gate and real-ledger cost meter | `codex plugin marketplace add BlockRunAI/blockrun-codex-plugin` then `codex plugin install blockrun-media` | +| [@blockrun/opencode](https://www.npmjs.com/package/@blockrun/opencode) | OpenCode plugin — chat, multi-turn messages, image generation and an auto-generated wallet for OpenCode agents (npm only, no public repo) | add `"@blockrun/opencode"` to the `plugin` list in `opencode.json` | +| [blockrun-litellm](https://github.com/BlockRunAI/blockrun-litellm) | LiteLLM adapter — call x402-paid models through LiteLLM as an in-process custom provider (`blockrun/`) or a local OpenAI-compatible proxy. Base and Solana | `pip install blockrun-litellm` (`[proxy]`, `[solana]` extras) | +| [lobstercash-blockrun-skill](https://github.com/BlockRunAI/lobstercash-blockrun-skill) | BlockRun skill for the [lobster.cash](https://lobster.cash) OpenClaw plugin — chat, image generation and model browsing, paid with Solana USDC from the lobster.cash smart wallet | `curl -sSL https://raw.githubusercontent.com/BlockRunAI/lobstercash-blockrun-skill/main/install.sh \| bash` | +| [elizaos-plugin-blockrun](https://github.com/BlockRunAI/elizaos-plugin-blockrun) | ElizaOS plugin for x402 pay-per-request AI on Base | `npm install @blockrun/elizaos-plugin` | +| [alpha-mcp](https://github.com/BlockRunAI/alpha-mcp) | AI crypto-trading MCP server — technical analysis, DEX data, sentiment, swap execution on Base, portfolio and risk tools | `claude mcp add alpha npx @blockrun/alpha` | + +### SDKs + +| Project | What it is | Install | +|---------|------------|---------| +| [blockrun-llm](https://github.com/BlockRunAI/blockrun-llm) | Python SDK — every model, pay-per-call with USDC, OpenAI-compatible, zero rate limits. Chat, images, video, music, search, prediction markets, smart routing, Base + Solana | `pip install blockrun-llm` (`[solana]` extra) | +| [blockrun-llm-ts](https://github.com/BlockRunAI/blockrun-llm-ts) | TypeScript SDK — smart routing built in, OpenAI drop-in, Base + Solana | `npm install @blockrun/llm` | +| [blockrun-llm-go](https://github.com/BlockRunAI/blockrun-llm-go) | Go SDK — chat, image, video, music, speech, voice, web search, market data, prediction markets, DeFi/DEX and multi-chain RPC. Base or Solana | `go get github.com/BlockRunAI/blockrun-llm-go` | +| [blockrun-llm-vip](https://github.com/BlockRunAI/blockrun-llm-vip) | Native passthrough for the Anthropic and OpenAI APIs (Python) — subclasses the official SDKs and swaps only transport and base URL, so responses come back verbatim: real thinking-block signatures, native `content[]`, cache-token usage. Zero model substitution. Base or Solana | `pip install blockrun-llm-vip` | +| [blockrun-llm-go-vip](https://github.com/BlockRunAI/blockrun-llm-go-vip) | Go counterpart — returns the official `anthropic-sdk-go` and `openai-go` client types with x402 transport, zero response reshaping | `go get github.com/BlockRunAI/blockrun-llm-go-vip` | +| [blockrun-nano-client](https://github.com/BlockRunAI/blockrun-nano-client) | TypeScript SDK for `nano.blockrun.ai` — the full model catalog paid with gas-free, batched USDC via Circle Gateway on Polygon, Arbitrum, Optimism and Unichain mainnet (Base buyers use native x402 on `blockrun.ai`) | `npm install @blockrun/nano-client` | +| [blockrun-llm-xrpl](https://github.com/BlockRunAI/blockrun-llm-xrpl) | XRPL SDK (RLUSD) — **deprecated**, see the [XRPL notice](../sdks/xrpl.md) | `pip install blockrun-llm-xrpl` | + +### Franklin apps + +| Project | What it is | Install | +|---------|------------|---------| +| [Franklin-Trading](https://github.com/BlockRunAI/Franklin-Trading) | Wallet-native trading agent forked from Franklin — multi-persona debate, Backtest → Paper → Live lifecycle, multi-venue on-chain execution, x402 USDC receipt per fill | `npm install -g @blockrun/franklin-trading` | +| [franklin-canvas](https://github.com/BlockRunAI/franklin-canvas) | Open-source node-based AI media studio — image, video and music on an infinite canvas, or let an agent build a whole short film from one prompt. Pay-per-use in USDC | `git clone` + `npm start` | +| [franklin-bet](https://github.com/BlockRunAI/franklin-bet) | AI World Cup match predictions — a council of frontier models researches the form, reads live odds, and bets on every 2026 World Cup match. Static, reproducible pipeline built on Franklin × BlockRun | `npm run generate -- --agent` | +| [franklin-run](https://github.com/BlockRunAI/franklin-run) | Source of [franklin.run](https://franklin.run), Franklin's landing site, blog and docs (13 locales) | — | + +### Skills, agents, and samples + +| Project | What it is | Install | +|---------|------------|---------| +| [Claude-Code-GPT-IMAGE2-SeeDance-BlockRun](https://github.com/BlockRunAI/Claude-Code-GPT-IMAGE2-SeeDance-BlockRun) | Run any awesome-gpt-image-2 or Seedance prompt as a one-line Claude Code command — `/headshot`, `/dance`, `/poster`, `/launch-film` + an 848-case library. Pay per image with x402 USDC on Base | `curl -fsSL https://raw.githubusercontent.com/BlockRunAI/Claude-Code-GPT-IMAGE2-SeeDance-BlockRun/main/install.sh \| bash` | +| [polymarket-agent](https://github.com/BlockRunAI/polymarket-agent) | Autonomous AI-powered prediction-market trading agent using x402 micropayments — owns its wallet, pays for its own inference | `git clone` + `pip install -r requirements.txt` | +| [circle-nanopayment-sample](https://github.com/BlockRunAI/circle-nanopayment-sample) | Circle Gateway nanopayment sample — an AI agent pays for API access with gas-free, batched USDC micropayments | `npm run setup` | + +### Curated lists and org infrastructure + +| Project | What it is | +|---------|------------| +| [awesome-OpenClaw-Money-Maker](https://github.com/BlockRunAI/awesome-OpenClaw-Money-Maker) | Curated list of ways to make money with OpenClaw — automations, skills, services, and strategies | +| [awesome-finance-mcp](https://github.com/BlockRunAI/awesome-finance-mcp) | A curated list of MCP servers for AI finance agents | +| [awesome-healthcare-mcp](https://github.com/BlockRunAI/awesome-healthcare-mcp) | A curated list of MCP servers for healthcare and medical | +| [awesome-mcp-servers](https://github.com/BlockRunAI/awesome-mcp-servers) | A collection of MCP servers | +| [awesome-blockrun](https://github.com/BlockRunAI/awesome-blockrun) | This repo — docs, SDK index, research, and community | +| [branding](https://github.com/BlockRunAI/branding) | Official brand kit — logos, colors, and usage guidelines ([live preview](https://blockrunai.github.io/branding/)) | +| [renovate-config](https://github.com/BlockRunAI/renovate-config) | Shared Renovate preset for every BlockRun repo | + +### Enterprise (coming soon) + +**user.blockrun.ai** — enterprise access for teams that cannot run wallets: authenticate with an API key (`brk_live_…`), pay by wire (prepaid credit), and get billed post-hoc at exact actual usage — no per-call minimum, no per-call fee. It is a standard x402-paying client of the main gateway, so the gateway itself stays wallet-native. **Sign-in and self-serve API keys are not yet open**; contact the team via [Telegram](https://t.me/+mroQv4-4hGgzOGUx) for early access. ## API Products | Product | Endpoint | Pricing | Status | |---------|----------|---------|--------| -| **LLM Chat** | `/v1/chat/completions` | Per token | ✅ Live | -| **Anthropic-Compat** | `/v1/messages` | Per token | ✅ Live | +| **LLM Chat** | `/v1/chat/completions` | Per token (provider cost, no platform margin) + $0.001/request | ✅ Live | +| **Anthropic-Compat** | `/v1/messages` | Per token + $0.001/request | ✅ Live | +| **Responses API** | `/v1/responses` | Per token + $0.001/request | ✅ Live | | **Image Generation** | `/v1/images/generations` | $0.015–0.10/image | ✅ Live | | **Image Editing** | `/v1/images/image2image` | Per request | ✅ Live | -| **Video Generation** | `/v1/videos/generations` | Per M tokens | ✅ Live | +| **Video Generation** | `/v1/videos/generations` | Per second / per M tokens | ✅ Live | | **Music Generation** | `/v1/audio/generations` | $0.151/track | ✅ Live | | **Text-to-Speech** | `/v1/audio/speech` | $0.05–0.10/1k chars | ✅ Live | | **Sound Effects** | `/v1/audio/sound-effects` | $0.0535/generation | ✅ Live | @@ -37,30 +103,31 @@ Projects, integrations, and partners building with BlockRun and x402. | **Prediction Markets** | `/v1/pm/*` | $0.0085 | ✅ Live | | **DefiLlama** | `/api/v1/defillama/*` | $0.002–0.006 | ✅ Live | | **Market Data (Pyth)** | `/v1/{crypto,fx,commodity}/*` | Free | ✅ Live | -| **Equity Prices (Pyth)** | `/v1/{usstock,stocks}/*` | $0.0010 | ✅ Live | +| **Equity Prices (Pyth)** | `/v1/{usstock,stocks}/*` | $0.002 | ✅ Live | | **RealFace Enrollment** | `/v1/realface/enroll` | $0.011 | ✅ Live | | **Virtual Portrait** | `/v1/portrait/enroll` | $0.011 | ✅ Live | | **Polymarket Funding** | `/v1/polymarket/fund` | $0.011 fee | ✅ Live | +| **Coinbase Onramp** | `/v1/onramp/token` | Free (Base only) | ✅ Live | | **Modal Sandbox** | `/v1/modal/*` | $0.002–0.011 | ✅ Live | | **Models** | `/v1/models` | Free | ✅ Live | | **Pricing** | `/v1/pricing` | Free | ✅ Live | | **Balance** | `/v1/balance` | Free | ✅ Live | -## SDKs - -| Language | Repository | Features | Status | -|----------|------------|----------|--------| -| Python | [blockrun-llm](https://github.com/blockrunai/blockrun-llm) | Chat, Images, Search, Prediction Markets, Smart Routing, Solana | Stable | -| TypeScript | [blockrun-llm-ts](https://github.com/blockrunai/blockrun-llm-ts) | Chat, Images, Search, OpenAI drop-in, Smart Routing, Solana | Stable | -| Go | [blockrun-llm-go](https://github.com/blockrunai/blockrun-llm-go) | Chat | Stable | +Per-token chat carries **no platform margin** — only the flat $0.001 transaction fee. Media generation and Live Search carry 5%. See [Pricing](../products/intelligence/pricing.md) and the full [x402 endpoint catalog](../x402/endpoints.md). ## Framework Integrations -| Framework | Status | Link | -|-----------|--------|------| -| OpenClaw | Released | [ClawRouter](https://github.com/BlockRunAI/ClawRouter) - Smart LLM router | +| Framework / runtime | Status | Link | +|---------|--------|------| +| Claude Code, Cursor, any MCP client | Released | [blockrun-mcp](https://github.com/BlockRunAI/blockrun-mcp) · [blockrun-claude-plugin](https://github.com/BlockRunAI/blockrun-claude-plugin) | +| OpenClaw | Released | [ClawRouter](https://github.com/BlockRunAI/ClawRouter) · [XClawRouter](https://github.com/BlockRunAI/XClawRouter) (OKX OnchainOS wallet) · [lobstercash-blockrun-skill](https://github.com/BlockRunAI/lobstercash-blockrun-skill) | +| OpenAI Codex | Released | [clawrouter-codex](https://github.com/BlockRunAI/clawrouter-codex) · [blockrun-codex-plugin](https://github.com/BlockRunAI/blockrun-codex-plugin) | +| NousResearch Hermes | Released | [ClawRouter-Hermes](https://github.com/BlockRunAI/ClawRouter-Hermes) | +| DeepSeek Harness | Released | [dsh-clawrouter](https://github.com/BlockRunAI/dsh-clawrouter) | +| OpenCode | Released | [@blockrun/opencode](https://www.npmjs.com/package/@blockrun/opencode) | +| LiteLLM | Released | [blockrun-litellm](https://github.com/BlockRunAI/blockrun-litellm) | +| Continue | Released | [Native provider](https://github.com/continuedev/continue/pull/11751) — ClawRouter as a built-in LLM provider | | ElizaOS | Released | [elizaos-plugin-blockrun](https://github.com/BlockRunAI/elizaos-plugin-blockrun) | -| Claude Code | Released | [blockrun-mcp](https://github.com/BlockRunAI/blockrun-mcp) | | GOAT SDK | In Review | [GitHub Issue](https://github.com/crossmint/goat) | | AgentKit | Planned | [Integration Guide](../frameworks/agentkit.md) | | LangChain | Planned | [Custom LLM Guide](../frameworks/langchain.md) | @@ -72,7 +139,6 @@ Projects built by the community using BlockRun. | Project | Category | Description | |---------|----------|-------------| | [PredictOS](https://github.com/PredictionXBT/PredictOS) | Prediction Markets | Prediction market analysis with multi-AI provider support | -| [Polymarket AI Agent](https://github.com/BlockRunAI/polymarket-agent) | Prediction Markets | Autonomous trading agent using 3-model LLM consensus | | [LLM_trader](https://github.com/qrak/LLM_trader) | Trading Bot | Autonomous crypto trading with Visual Cortex chart analysis | | [Spraay](https://github.com/plagtech/spraay-x402-gateway) | x402 Gateway | Multi-chain x402 payment gateway with dual-provider AI inference | | [NoFx](https://github.com/NoFxAiOS/nofx) | Crypto Trading | Personal AI trading assistant - any market, any model, pay with USDC | @@ -80,12 +146,13 @@ Projects built by the community using BlockRun. ## Networks -| Network | Gateway | Asset | Status | -|---------|---------|-------|--------| -| Base | `blockrun.ai` | USDC | ✅ Live | -| Solana | `sol.blockrun.ai` | USDC | ✅ Live | -| Base Sepolia | `testnet.blockrun.ai` | USDC (testnet) | ✅ Testnet | -| Solana Devnet | `devnet-sol.blockrun.ai` | USDC (devnet) | ✅ Testnet | +| Network | Gateway | Asset | Settlement | Status | +|---------|---------|-------|------------|--------| +| Base | `blockrun.ai` | USDC | Per-call x402 | ✅ Live | +| Solana | `sol.blockrun.ai` | USDC (SPL) | Per-call x402 | ✅ Live | +| Polygon / Arbitrum / Optimism / Unichain | `nano.blockrun.ai` | USDC via Circle Gateway | Gas-free, batched nanopayments | ✅ Live | +| Base Sepolia | `testnet.blockrun.ai` | USDC (testnet) | Per-call x402 | ✅ Testnet | +| XRP Ledger | `xrpl.blockrun.ai` | RLUSD | — | ⛔ Sunset (gateway offline) | ## x402 Facilitators @@ -94,6 +161,7 @@ BlockRun works with the x402 facilitator network: | Facilitator | Network | Link | |-------------|---------|------| | Coinbase CDP | Base, Ethereum | [coinbase.com/cloud](https://coinbase.com/cloud) | +| Circle Gateway | Polygon, Arbitrum, Optimism, Unichain (batched nanopayments) | [developers.circle.com/gateway](https://developers.circle.com/gateway/nanopayments) | | PayAI | Base, Solana | [payai.network](https://payai.network) | | QuestFlow | Base | [questflow.ai](https://questflow.ai) | | AnySpend | Base | [anyspend.com](https://anyspend.com) | @@ -104,10 +172,11 @@ BlockRun works with the x402 facilitator network: | Partner | Relationship | |---------|--------------| -| [Circle](https://partners.circle.com/partner/blockrunai) | Alliance Partner — USDC payments on Base | +| [Circle](https://partners.circle.com/partner/blockrunai) | Alliance Partner — USDC payments on Base; Circle Gateway nanopayments on `nano.blockrun.ai` | | [Coinbase CDP](https://coinbase.com/cloud) | x402 facilitator infrastructure | | [x402 Foundation](https://x402.org) | Protocol development | | [thirdweb](https://thirdweb.com) | Wallet & payment infrastructure | +| [OKX OnchainOS](https://web3.okx.com) | Agentic wallet behind XClawRouter | | [Predexon](https://predexon.com) | Prediction market data | | [Modal](https://modal.com) | Sandbox compute (isolated code execution) | | [Surf (asksurf.ai)](https://asksurf.ai) | Crypto data — 83 endpoints (CEX, on-chain SQL, prediction markets, wallet labels, social, news) | @@ -115,6 +184,8 @@ BlockRun works with the x402 facilitator network: | [Twilio](https://twilio.com) | Phone-number provisioning (wallet-owned US/CA numbers) | | [Exa](https://exa.ai) | Neural web search | | [0x](https://0x.org) | DEX aggregation (Swap V2 + Gasless V2) | +| [Tatum](https://tatum.io) | Multi-chain JSON-RPC (40 chains) | +| [Pyth](https://pyth.network) | Crypto, FX, commodity and equity prices | ## AI Providers diff --git a/docs/resources/examples.md b/docs/resources/examples.md index 5404a1d..f0efa37 100644 --- a/docs/resources/examples.md +++ b/docs/resources/examples.md @@ -13,18 +13,42 @@ Drop-in skills that turn a BlockRun capability into a one-line Claude Code comma ::::cards -:::card{title="nano-banana-blockrun" href="https://github.com/BlockRunAI/nano-banana-blockrun" icon="Image"} -Image generation as a Claude Code skill, paid via x402 micropayments. -::: - :::card{title="GPT-Image-2 / SeeDance" href="https://github.com/BlockRunAI/Claude-Code-GPT-IMAGE2-SeeDance-BlockRun" icon="Image"} -Run any GPT-Image-2 or Seedance prompt as a one-liner — `/headshot`, `/dance`, `/poster` + a 1010-case library. +Run any GPT-Image-2 or Seedance prompt as a one-liner — `/headshot`, `/dance`, `/poster`, `/launch-film` + an 848-case library. ::: :::card{title="lobster.cash skill" href="https://github.com/BlockRunAI/lobstercash-blockrun-skill" icon="Zap"} BlockRun skill for the lobster.cash OpenClaw plugin — 72 models paid with Solana USDC. ::: +:::card{title="blockrun-claude-plugin" href="https://github.com/BlockRunAI/blockrun-claude-plugin" icon="Wallet"} +Claude Code media plugin — spend confirmation before each paid image/video/audio call, a running cost meter, and a status-line balance. +::: + +:::: + +## Bridges to other agent runtimes + +Reference implementations for putting the whole catalog behind a runtime that speaks a different API. + +::::cards + +:::card{title="clawrouter-codex" href="https://github.com/BlockRunAI/clawrouter-codex" icon="Code"} +Responses API ↔ Chat Completions bridge so OpenAI Codex (CLI, IDE, Desktop) can use any BlockRun model, wallet-signed. +::: + +:::card{title="ClawRouter-Hermes" href="https://github.com/BlockRunAI/ClawRouter-Hermes" icon="Code"} +Python plugin wrapping the ClawRouter proxy as a Hermes provider. +::: + +:::card{title="dsh-clawrouter" href="https://github.com/BlockRunAI/dsh-clawrouter" icon="Code"} +DeepSeek Harness plugin — a stronger model reviews dangerous tool calls before they run. +::: + +:::card{title="blockrun-litellm" href="https://github.com/BlockRunAI/blockrun-litellm" icon="Code"} +LiteLLM custom provider and local OpenAI-compatible proxy, Base and Solana. +::: + :::: ## Sample agents & apps @@ -39,6 +63,14 @@ Node-based AI media studio — generate image/video/music on an infinite canvas, Autonomous prediction-market trading agent using x402 micropayments. ::: +:::card{title="franklin-bet" href="https://github.com/BlockRunAI/franklin-bet" icon="TrendingUp"} +A council of frontier models researches the form, reads live odds, and bets on every 2026 World Cup match — open-source, reproducible pipeline. +::: + +:::card{title="Franklin-Trading" href="https://github.com/BlockRunAI/Franklin-Trading" icon="TrendingUp"} +Wallet-native trading agent — persona debate, Backtest → Paper → Live, an x402 USDC receipt per fill. +::: + :::card{title="circle-nanopayment-sample" href="https://github.com/BlockRunAI/circle-nanopayment-sample" icon="Wallet"} Gas-free USDC micropayments for API access via Circle Gateway. ::: diff --git a/docs/resources/faq.md b/docs/resources/faq.md index 5f9a6a4..76e0def 100644 --- a/docs/resources/faq.md +++ b/docs/resources/faq.md @@ -14,7 +14,8 @@ Frequently asked questions about BlockRun — payments, products, models, wallet BlockRun is economic infrastructure for AI agents. It provides: - **Trading** — AI that analyzes markets and executes trades (alpha-mcp) - **Creation** — generate images, video, music, and speech, paid per output -- **Intelligence** — Access to 66 chat/LLM models via x402 micropayments +- **Intelligence** — Access to 72 chat/LLM models via x402 micropayments +- **Routing** — ClawRouter picks the cheapest capable model locally, in under 1ms ### What makes BlockRun different? @@ -29,7 +30,7 @@ BlockRun is economic infrastructure for AI agents. It provides: - **Trading (alpha-mcp):** Free and open source - **Creation:** Pay-per-use — images $0.015–0.15, video from $0.05/sec, music $0.15/track, text-to-speech $0.05–0.10 per 1k chars -- **Intelligence:** Provider cost + 5% +- **Intelligence:** Provider cost with **no platform margin** on per-token chat (since 2026-08-07) — only a flat $0.001 transaction fee per request. Media generation and Live Search carry 5%. - **Free tier:** 10 chat/reasoning/vision models with no per-token charge ## Products @@ -45,7 +46,19 @@ It's free and open source. See [Trading Overview](../products/trading/overview.m ### What is nano-banana? -nano-banana is a Claude Code skill for image generation. Generate images using Nano Banana, GPT Image, CogView-4, or Grok Imagine via micropayments. See [nano-banana](../products/creation/nano-banana.md). +nano-banana is the image-generation capability — Nano Banana, GPT Image, CogView-4, or Grok Imagine via micropayments. See [nano-banana](../products/creation/nano-banana.md). The original `nano-banana-blockrun` Claude Code skill repo is archived; use the `blockrun_image` tool in [blockrun-mcp](https://github.com/BlockRunAI/blockrun-mcp) or the [GPT-Image-2 / SeeDance skill](https://github.com/BlockRunAI/Claude-Code-GPT-IMAGE2-SeeDance-BlockRun) instead. + +### What is ClawRouter? + +ClawRouter is the open-source LLM router for autonomous agents — it classifies each request locally and routes it to the cheapest capable model in under 1ms, paying per request in USDC on Base or Solana. It ships as an OpenClaw plugin and as ports for Hermes, DeepSeek Harness, Codex and the OKX OnchainOS wallet. See [ClawRouter](../products/routing/clawrouter.md) and the [Ecosystem](ecosystem.md). + +### What is Franklin? + +Franklin is the AI agent with a wallet — it holds USDC and spends it autonomously across every model and paid API to get work done, with budgets and guardrails. See [Franklin](../products/franklin.md). + +### Can I use an API key instead of a wallet? + +Enterprise access at **user.blockrun.ai** is coming soon: API keys (`brk_live_…`), wire/prepaid billing, and post-hoc billing at exact usage with no per-call minimum. Sign-in is not yet open — reach out on [Telegram](https://t.me/+mroQv4-4hGgzOGUx) for early access. Everyone else pays per call from a wallet; no API key is ever required. ## Payments @@ -59,7 +72,7 @@ BlockRun uses the x402 protocol: ### What currency is accepted? -USDC on Base network. +USDC — on **Base** (`blockrun.ai`) or **Solana** (`sol.blockrun.ai`), both live. `nano.blockrun.ai` also accepts gas-free, batched USDC via Circle Gateway on Polygon, Arbitrum, Optimism and Unichain. ### What's the minimum to get started? @@ -67,10 +80,11 @@ $1 is enough for testing. Recommended: $5-20 for regular usage. ### How do I fund my wallet? -Send USDC to your wallet address on Base: -- [Coinbase](https://coinbase.com) — Direct withdrawal to Base +Send USDC to your wallet address on Base (or Solana): +- [Coinbase](https://coinbase.com) — Direct withdrawal to Base or Solana - [Base Bridge](https://bridge.base.org) — Bridge from Ethereum - [Uniswap](https://app.uniswap.org) — Swap on Base +- `POST /v1/onramp/token` — a free Coinbase Onramp link for a Base wallet (`blockrun fund` in the CLI) ### What if a request fails? @@ -98,6 +112,10 @@ Tokens on Base via 0x Protocol. Common pairs: ETH/USDC, popular tokens with liqu No. alpha-mcp is free. You only pay for intelligence (sentiment analysis) and network gas. +### Is there a per-request fee? + +Yes — a flat $0.001 transaction fee on every paid call, which covers on-chain settlement. That is the only thing added on top of provider cost for chat; there is no percentage margin on tokens. + ## Technical ### Which AI models are available? @@ -130,12 +148,24 @@ Then run `blockrun setup` in Claude Code. ### What frameworks are supported? -- Claude Code (via MCP) +- Claude Code, Cursor and any MCP client (blockrun-mcp; blockrun-claude-plugin for media spend gating) +- OpenClaw (ClawRouter, XClawRouter, lobster.cash skill) +- OpenAI Codex (clawrouter-codex bridge, blockrun-codex-plugin) +- NousResearch Hermes (ClawRouter-Hermes) +- DeepSeek Harness (dsh-clawrouter) +- OpenCode (@blockrun/opencode) +- LiteLLM (blockrun-litellm) +- Continue (native provider) - ElizaOS (plugin) -- AgentKit (SDK integration) -- LangChain (custom LLM class) +- AgentKit (SDK integration) and LangChain (custom LLM class) — planned - GOAT SDK (in review) +Install commands for each are in the [Ecosystem](ecosystem.md). + +### Can I get the provider's response verbatim? + +Yes. `blockrun-llm-vip` (Python) and `blockrun-llm-go-vip` (Go) subclass the official Anthropic and OpenAI SDKs and only swap the transport, so thinking-block signatures, native `content[]`, cache-token usage and streaming events come back exactly as the provider sent them — no model substitution. + ## Security ### Is my private key safe? @@ -144,7 +174,7 @@ Your private key never leaves your machine. Only cryptographic signatures are se ### Where is my wallet stored? -`~/.blockrun/wallet.json` by default. +`~/.blockrun/.session` (Base key) and `~/.blockrun/.solana-session` (Solana key) by default — shared by the MCP, the CLI, ClawRouter and the SDKs, so one wallet covers every tool. ### Can BlockRun steal my funds? @@ -180,7 +210,7 @@ print(client.get_address()) ### Can I withdraw my funds? -Yes. Your wallet is a standard Ethereum wallet. Import the private key into any Web3 wallet to withdraw. +Yes. Your wallet is a standard Ethereum (Base) or Solana wallet. Import the private key into any Web3 wallet to withdraw. ## Troubleshooting @@ -222,10 +252,14 @@ Check internet connection and retry. If persistent, check [BlockRun status](http ### How do I report bugs? Open an issue on the relevant GitHub repository: -- General: [blockrun-mcp](https://github.com/BlockRunAI/blockrun-mcp) +- General / MCP: [blockrun-mcp](https://github.com/BlockRunAI/blockrun-mcp) +- Routing: [ClawRouter](https://github.com/BlockRunAI/ClawRouter) +- Franklin agent: [Franklin](https://github.com/BlockRunAI/Franklin) +- CLI: [blockrun-cli](https://github.com/BlockRunAI/blockrun-cli) - Trading: [alpha-mcp](https://github.com/BlockRunAI/alpha-mcp) - Python SDK: [blockrun-llm](https://github.com/blockrunai/blockrun-llm) - TypeScript SDK: [blockrun-llm-ts](https://github.com/blockrunai/blockrun-llm-ts) +- Go SDK: [blockrun-llm-go](https://github.com/blockrunai/blockrun-llm-go) ### Where can I follow updates? diff --git a/docs/sdks/go.md b/docs/sdks/go.md index 41c6c92..2769b06 100644 --- a/docs/sdks/go.md +++ b/docs/sdks/go.md @@ -1,13 +1,13 @@ --- title: Go SDK -description: The Go SDK for BlockRun — call 96 AI models, generate images, and manage wallets over x402 micropayments with no API keys. +description: The Go SDK for BlockRun — call 96 AI models, generate images, video, music and speech, search the web, read market data and multi-chain RPC, and manage wallets over x402 micropayments with no API keys. --- # Go SDK -The Go SDK for BlockRun provides access to 96 AI models via x402 micropayments — pay per call in USDC, no API keys. +The Go SDK for BlockRun provides access to 96 AI models via x402 micropayments — pay per call in USDC on **Base** or **Solana**, no API keys. Beyond chat it covers image, video, music and speech generation, web search, market data, prediction markets, DeFi and DEX data, and multi-chain JSON-RPC. -**Source:** [github.com/BlockRunAI/blockrun-llm-go](https://github.com/BlockRunAI/blockrun-llm-go) · `go get github.com/BlockRunAI/blockrun-llm-go` · MIT +**Source:** [github.com/BlockRunAI/blockrun-llm-go](https://github.com/BlockRunAI/blockrun-llm-go) · `go get github.com/BlockRunAI/blockrun-llm-go@v0.20.0` · Go 1.22+ · MIT :::tip{title="In a hurry?"} New to BlockRun? Run the [5-Minute Quickstart](../getting-started/quickstart.md) first to fund a wallet, then come back for the full SDK reference. @@ -17,7 +17,7 @@ New to BlockRun? Run the [5-Minute Quickstart](../getting-started/quickstart.md) :::step{title="Install"} ```bash -go get github.com/BlockRunAI/blockrun-llm-go +go get github.com/BlockRunAI/blockrun-llm-go@v0.20.0 ``` ::: @@ -26,18 +26,25 @@ go get github.com/BlockRunAI/blockrun-llm-go package main import ( + "context" "fmt" - "os" + "log" + blockrun "github.com/BlockRunAI/blockrun-llm-go" ) func main() { - // Uses BLOCKRUN_WALLET_KEY env var - client := blockrun.NewClient("") + ctx := context.Background() + + // Empty key → reads BLOCKRUN_WALLET_KEY (or BASE_CHAIN_WALLET_KEY) + client, err := blockrun.NewLLMClient("") + if err != nil { + log.Fatal(err) + } - response, err := client.Chat("openai/gpt-5.5", "Hello!") + response, err := client.Chat(ctx, "openai/gpt-5.5", "Hello!") if err != nil { - panic(err) + log.Fatal(err) } fmt.Println(response) } @@ -46,92 +53,326 @@ func main() { :::: +Every method takes a `context.Context` first, and every constructor returns `(client, error)`. + ## Configuration -### Environment Variable +### Environment Variables + +| Variable | Description | Required | +|----------|-------------|----------| +| `BLOCKRUN_WALLET_KEY` | Base wallet private key (`0x...`) | Yes for Base (or pass to constructor) | +| `BASE_CHAIN_WALLET_KEY` | Alias for `BLOCKRUN_WALLET_KEY` | No | +| `SOLANA_WALLET_KEY` | bs58 Solana private key, for the `*Solana` constructors | Yes for Solana (or pass to constructor) | +| `SOLANA_RPC_URL` | Solana RPC used for mint info / blockhash when the 402 does not carry one | No | +| `BLOCKRUN_API_URL` | Custom API endpoint | No (default: `https://blockrun.ai/api`) | ```bash export BLOCKRUN_WALLET_KEY=0x...your_private_key... ``` :::warning{title="Keep your key safe"} -Never commit private keys to version control. Use a dedicated payment wallet funded with only small amounts, and load the key from an environment variable. +Never commit private keys to version control. Use a dedicated payment wallet funded with only small amounts, and load the key from an environment variable. The key only signs payments locally — it is never transmitted. ::: ### Programmatic ```go -client := blockrun.NewClient("0x...private_key...") +client, err := blockrun.NewLLMClient("0x...private_key...") // With options -client := blockrun.NewClientWithOptions(blockrun.Options{ - PrivateKey: "0x...", - APIURL: "https://blockrun.ai/api", - SessionBudget: 10.00, -}) +client, err := blockrun.NewLLMClient("0x...", + blockrun.WithAPIURL("https://blockrun.ai/api"), + blockrun.WithTimeout(120*time.Second), + blockrun.WithCache(true), // local response cache for search / prediction-market calls + blockrun.WithHTTPClient(httpClient), +) ``` +### Pay on Solana + +Every client has a `New…Solana` counterpart that pays USDC on Solana via `sol.blockrun.ai` — same API, same responses. The second argument is an optional Solana RPC URL (empty → `SOLANA_RPC_URL` → BlockRun's free proxy). + +```go +client, err := blockrun.NewLLMClientSolana("", "") // key from SOLANA_WALLET_KEY +img, err := blockrun.NewImageClientSolana("", "") +``` + +Payments on Solana are gasless: the SDK signs a USDC `TransferChecked` transaction locally and BlockRun's facilitator co-signs as fee payer, so the wallet needs no SOL. Solana wallets have the same helpers as EVM ones (`GetOrCreateSolanaWallet`, `ScanSolanaWallets`, `GetSolanaPayURI`, …). Buying USDC with a card via `Onramp` is Base-only; fund a Solana wallet by transfer. + ## API Reference ### Chat Completion ```go // Simple chat -response, err := client.Chat("openai/gpt-5.5", "Hello!") +response, err := client.Chat(ctx, "openai/gpt-5.5", "Hello!") -// With options -response, err := client.ChatWithOptions("openai/gpt-5.5", "Explain x402", blockrun.ChatOptions{ +// With a system prompt +response, err := client.ChatWithSystem(ctx, "openai/gpt-5.5", "Explain x402", "You are a concise teacher.") + +// Full completion with options +result, err := client.ChatCompletion(ctx, "openai/gpt-5.5", messages, &blockrun.ChatCompletionOptions{ Temperature: 0.7, MaxTokens: 1000, }) +fmt.Println(result.Choices[0].Message.Content) +fmt.Println(result.Usage.TotalTokens) ``` +`ChatCompletionOptions` also accepts `TopP`, `Stop`, `ResponseFormat` (e.g. `map[string]string{"type": "json_object"}`), `Tools` / `ToolChoice`, and `Search` / `SearchParameters` for live web search inside a chat call. + ### Chat with Messages ```go -messages := []blockrun.Message{ +messages := []blockrun.ChatMessage{ {Role: "system", Content: "You are a helpful assistant."}, {Role: "user", Content: "What is x402?"}, } -response, err := client.ChatMessages("openai/gpt-5.5", messages) +result, err := client.ChatCompletion(ctx, "openai/gpt-5.5", messages, nil) +fmt.Println(result.Choices[0].Message.Content) +``` + +### Streaming + +```go +stream, err := client.ChatCompletionStream(ctx, "openai/gpt-5.5", []blockrun.ChatMessage{ + {Role: "user", Content: "Write a poem about Go"}, +}, nil) +if err != nil { + log.Fatal(err) +} +defer stream.Close() + +for { + chunk, err := stream.Next() + if err != nil { + log.Fatal(err) + } + if chunk == nil { + break // stream complete + } + fmt.Print(chunk.Choices[0].Delta.Content) +} +``` + +### Tool Calling + +```go +result, err := client.ChatCompletion(ctx, "openai/gpt-5.5", messages, &blockrun.ChatCompletionOptions{ + Tools: []blockrun.Tool{{ + Type: "function", + Function: blockrun.ToolFunction{ + Name: "get_weather", + Description: "Get current weather for a location", + Parameters: map[string]any{ + "type": "object", + "properties": map[string]any{ + "location": map[string]any{"type": "string"}, + }, + "required": []string{"location"}, + }, + }, + }}, + ToolChoice: "auto", +}) + +if calls := result.Choices[0].Message.ToolCalls; len(calls) > 0 { + fmt.Printf("Tool: %s(%s)\n", calls[0].Function.Name, calls[0].Function.Arguments) +} +``` + +### Smart Routing + +`SmartChat` picks a model per request from a local classifier (<1ms, no extra model call). Profiles: `RoutingFree`, `RoutingEco`, `RoutingAuto` (default), `RoutingPremium`. + +```go +resp, err := client.SmartChat(ctx, "What is 2+2?", &blockrun.SmartChatOptions{ + RoutingProfile: blockrun.RoutingFree, // free models only — no USDC needed +}) +fmt.Println(resp.Model) // model that answered +fmt.Println(resp.Response) +fmt.Println(resp.Routing.Tier) // SIMPLE / MEDIUM / COMPLEX / REASONING ``` +### Anthropic Messages API + +`NewAnthropicClient` speaks the native Messages format (content blocks, `StopReason`, input/output token usage) for Claude models and any other BlockRun model. + +```go +ac, err := blockrun.NewAnthropicClient("") +resp, err := ac.Messages.Create(ctx, blockrun.AnthropicCreateParams{ + Model: "claude-sonnet-4-6", // native Anthropic id form; "openai/gpt-5.5" etc. also accepted + MaxTokens: 1024, + Messages: []blockrun.AnthropicMessage{{Role: "user", Content: "Hello!"}}, +}) +fmt.Println(resp.Text(), resp.StopReason) +``` + +If you need the **official** `anthropic-sdk-go` / `openai-go` client types with verbatim upstream responses (thinking-block signatures, native streaming), use the companion module [`blockrun-llm-go-vip`](https://github.com/BlockRunAI/blockrun-llm-go-vip): `vip.NewAnthropic()` / `vip.NewOpenAI()` return the official clients with x402 payment on the transport, with `vip.WithChain("solana")` for Solana. + ### Image Generation ```go -imageURL, err := client.GenerateImage(blockrun.ImageRequest{ - Prompt: "A futuristic city", - Model: "google/nano-banana", - Size: "1024x1024", +imageClient, err := blockrun.NewImageClient("") + +result, err := imageClient.Generate(ctx, "A futuristic city", &blockrun.ImageGenerateOptions{ + Model: "google/nano-banana", + Size: "1024x1024", +}) +fmt.Println(result.Data[0].URL) // permanent BlockRun-hosted URL +fmt.Println(result.TxHash) // on-chain settlement tx + +// Edit or fuse images (base64 data URIs) +result, err = imageClient.Edit(ctx, "make the sky purple", + []string{"data:image/png;base64,..."}, nil) +``` + +Current image models: `openai/gpt-image-1`, `openai/gpt-image-2`, `google/nano-banana`, `google/nano-banana-2`, `google/nano-banana-pro`, `bytedance/seedream-5-pro`, `zai/cogview-4`, `xai/grok-imagine-image`, `xai/grok-imagine-image-pro`. `client.ListImageModels(ctx)` returns the live list with pricing. + +### Video, Music and Speech + +```go +videoClient, err := blockrun.NewVideoClient("") +video, err := videoClient.Generate(ctx, "a red apple spinning on a wooden table", &blockrun.VideoGenerateOptions{ + Model: "bytedance/seedance-1.5-pro", + ImageURL: "https://example.com/portrait.jpg", // optional image-to-video +}) +fmt.Println(video.Data[0].URL) // blocks until the MP4 is ready + +musicClient, err := blockrun.NewMusicClient("") +track, err := musicClient.Generate(ctx, "upbeat synthwave with neon pads", nil) +fmt.Println(track.Data[0].URL) // download promptly — URLs expire + +speechClient, err := blockrun.NewSpeechClient("") +audio, err := speechClient.Generate(ctx, "Welcome to BlockRun.", &blockrun.SpeechGenerateOptions{ + Voice: "george", +}) +fmt.Println(audio.Data[0].URL) +fx, err := speechClient.SoundEffect(ctx, "rain on a tin roof", nil) +``` + +Video options also support `LastFrameURL` (first/last-frame interpolation), `ReferenceImageURLs` (multi-reference, Seedance 2.0), `RealFaceAssetID` (character consistency via `NewPortraitClient` / `NewRealFaceClient`), `Resolution` and `GenerateAudio`. + +### Web Search + +```go +result, err := client.Search(ctx, "latest AI news", &blockrun.SearchOptions{ + Sources: []string{"web", "x", "news"}, + MaxResults: 5, + FromDate: "2026-01-01", +}) +fmt.Println(result.Summary) +fmt.Println(result.Citations) + +// Neural search, similarity, content extraction, grounded answers +hits, err := client.ExaSearch(ctx, "latest AI safety research", map[string]any{"numResults": 5}) +answer, err := client.ExaAnswer(ctx, "What is x402?", nil) +``` + +### Market Data + +Realtime quotes and OHLC history for crypto, FX, commodities and equities. Crypto / FX / commodity reads are free; equities are paid. + +```go +btc, err := client.Price(ctx, blockrun.CategoryCrypto, "BTC-USD", nil) +fmt.Println(btc.Price) + +aapl, err := client.Price(ctx, blockrun.CategoryStocks, "AAPL", &blockrun.PriceOptions{Market: "us"}) + +bars, err := client.History(ctx, blockrun.CategoryCrypto, "BTC-USD", &blockrun.HistoryOptions{ + Resolution: "D", From: 1700000000, To: 1710000000, +}) +symbols, err := client.ListSymbols(ctx, blockrun.CategoryCrypto, &blockrun.ListOptions{Query: "sol"}) +``` + +### Prediction Markets + +```go +events, err := client.PM(ctx, "polymarket/events", nil) +markets, err := client.PM(ctx, "polymarket/search", map[string]string{"q": "bitcoin"}) +result, err := client.PMQuery(ctx, "polymarket/query", map[string]any{"filter": "active", "limit": 10}) +``` + +### DeFi and DEX + +```go +// DeFi data — protocols, TVL, yields, token prices +protocols, err := client.DefiProtocols(ctx) +pools, err := client.DefiYields(ctx, map[string]string{"chain": "Base"}) +prices, err := client.DefiPrices(ctx, []string{"coingecko:bitcoin"}) + +// DEX swaps — free quotes, no x402 payment (affiliate fee on executed swaps) +price, err := client.DexPrice(ctx, map[string]string{ + "chainId": "8453", "sellToken": "0x...", "buyToken": "0x...", "sellAmount": "1000000", +}) +quote, err := client.DexQuote(ctx, params) // + "taker" +gq, err := client.DexGaslessQuote(ctx, params) +``` + +### Multi-chain RPC + +`RPCClient` wraps `POST /v1/rpc/{network}` — JSON-RPC 2.0 to every [supported chain](../api-reference/multi-chain-rpc.md) through one endpoint, $0.002 per call (a batch charges per element). + +```go +rpcClient, err := blockrun.NewRPCClient("") + +block, err := rpcClient.Call(ctx, "ethereum", "eth_blockNumber", nil) +fmt.Println(string(block.Result)) + +slot, err := rpcClient.Call(ctx, "solana", "getSlot", nil) +tip, err := rpcClient.Call(ctx, "bitcoin", "getblockcount", nil) + +out, err := rpcClient.Batch(ctx, "polygon", []blockrun.RPCBatchRequest{ + {Method: "eth_blockNumber"}, + {Method: "eth_gasPrice"}, }) ``` +Supported slugs are exported as `blockrun.RPCSupportedNetworks`; aliases (`eth`, `arb`, `sol`, `btc`, `xrp`, …) resolve server-side. + +### Other Clients + +`NewVoiceClient` (outbound AI phone calls), `NewPhoneClient` (number lookup and caller-ID provisioning), `NewSurfClient` (crypto intelligence endpoints), `NewPortraitClient` / `NewRealFaceClient` (reusable face assets for video), and `client.ModalSandboxCreate` / `ModalSandboxExec` (sandboxed compute). See the [README](https://github.com/BlockRunAI/blockrun-llm-go#features) for each. + ### Wallet Operations ```go // Get address -address := client.GetAddress() +address := client.GetWalletAddress() fmt.Printf("Address: %s\n", address) -// Check balance -balance, err := client.GetBalance() +// Check USDC balance on the chain this client pays from +balance, err := client.GetBalance(ctx) fmt.Printf("Balance: $%.2f USDC\n", balance) + +// Fund with a card — one-time Coinbase Onramp link (Base only, free) +res, err := client.Onramp(ctx, client.GetWalletAddress()) +fmt.Println("Buy USDC:", res.URL) + +// Autonomous agents: create + persist a wallet on first run +client, err := blockrun.SetupAgentWallet() +address, balance, err := client.Status(ctx) ``` +`ListModels(ctx)`, `ListImageModels(ctx)` and `ListAllModels(ctx)` return the live catalog with current pricing. + ## Error Handling ```go -response, err := client.Chat("openai/gpt-5.5", prompt) +response, err := client.Chat(ctx, "openai/gpt-5.5", prompt) if err != nil { switch e := err.(type) { - case *blockrun.InsufficientBalanceError: - fmt.Println("Need to fund wallet") - case *blockrun.ModelNotFoundError: - fmt.Printf("Invalid model: %s\n", e.Model) - case *blockrun.RateLimitError: - fmt.Println("Rate limited, waiting...") - time.Sleep(60 * time.Second) + case *blockrun.PaymentError: + fmt.Printf("Payment failed (fund the wallet?): %s\n", e.Message) + case *blockrun.ValidationError: + fmt.Printf("Invalid input: %s - %s\n", e.Field, e.Message) + case *blockrun.APIError: + fmt.Printf("API error %d: %s\n", e.StatusCode, e.Message) + if e.StatusCode == 429 { + time.Sleep(60 * time.Second) + } default: fmt.Printf("Error: %v\n", err) } @@ -142,24 +383,27 @@ if err != nil { ```go // OpenAI -client.Chat("openai/gpt-5.2", prompt) -client.Chat("openai/gpt-5.5", prompt) -client.Chat("openai/o1", prompt) +client.Chat(ctx, "openai/gpt-5.2", prompt) +client.Chat(ctx, "openai/gpt-5.5", prompt) +client.Chat(ctx, "openai/o1", prompt) // Anthropic -client.Chat("anthropic/claude-opus-5", prompt) -client.Chat("anthropic/claude-sonnet-4.6", prompt) +client.Chat(ctx, "anthropic/claude-opus-5", prompt) +client.Chat(ctx, "anthropic/claude-sonnet-4.6", prompt) // Google -client.Chat("google/gemini-3.1-pro", prompt) -client.Chat("google/gemini-3-flash-preview", prompt) -client.Chat("google/gemini-2.5-flash-lite", prompt) +client.Chat(ctx, "google/gemini-3.1-pro", prompt) +client.Chat(ctx, "google/gemini-3-flash-preview", prompt) +client.Chat(ctx, "google/gemini-2.5-flash-lite", prompt) // DeepSeek -client.Chat("deepseek/deepseek-chat", prompt) +client.Chat(ctx, "deepseek/deepseek-chat", prompt) // xAI -client.Chat("xai/grok-4.3", prompt) +client.Chat(ctx, "xai/grok-4.3", prompt) + +// Free (no USDC needed) +client.Chat(ctx, "nvidia/step-3.7-flash", prompt) ``` ## Concurrent Requests @@ -167,14 +411,14 @@ client.Chat("xai/grok-4.3", prompt) ```go import "golang.org/x/sync/errgroup" -func processItems(client *blockrun.Client, items []string) ([]string, error) { +func processItems(ctx context.Context, client *blockrun.LLMClient, items []string) ([]string, error) { results := make([]string, len(items)) - g := new(errgroup.Group) + g, ctx := errgroup.WithContext(ctx) for i, item := range items { i, item := i, item g.Go(func() error { - response, err := client.Chat("deepseek/deepseek-chat", item) + response, err := client.Chat(ctx, "deepseek/deepseek-chat", item) if err != nil { return err } @@ -190,19 +434,18 @@ func processItems(client *blockrun.Client, items []string) ([]string, error) { } ``` -## Session Budgets +## Cost Tracking ```go -client := blockrun.NewClientWithOptions(blockrun.Options{ - SessionBudget: 10.00, // Max $10 per session -}) - -// Will return error if budget exceeded -response, err := client.Chat("openai/gpt-5.5", prompt) -if err != nil { - if _, ok := err.(*blockrun.BudgetExceededError); ok { - fmt.Println("Session budget exceeded") - } +// This session +spending := client.GetSpending() +fmt.Printf("Session: %d calls, $%.6f\n", spending.Calls, spending.TotalUSD) + +// Persistent JSONL log across runs +summary, err := client.GetCostSummary() +fmt.Printf("Total: $%.4f across %d calls\n", summary.TotalUSD, summary.Calls) +for endpoint, cost := range summary.ByEndpoint { + fmt.Printf(" %s: $%.4f\n", endpoint, cost) } ``` @@ -212,23 +455,30 @@ if err != nil { package main import ( + "context" "fmt" + "log" "time" + blockrun "github.com/BlockRunAI/blockrun-llm-go" ) type TradingBot struct { - client *blockrun.Client + client *blockrun.LLMClient } -func (b *TradingBot) AnalyzeMarket(asset string) (string, error) { - prompt := fmt.Sprintf("Analyze %s for trading: technicals, sentiment, recommendation", asset) - return b.client.Chat("openai/gpt-5.5", prompt) +func (b *TradingBot) AnalyzeMarket(ctx context.Context, asset string) (string, error) { + btc, err := b.client.Price(ctx, blockrun.CategoryCrypto, asset+"-USD", nil) // free quote + if err != nil { + return "", err + } + prompt := fmt.Sprintf("%s is trading at %.2f. Analyze technicals, sentiment, recommendation.", asset, btc.Price) + return b.client.Chat(ctx, "openai/gpt-5.5", prompt) } -func (b *TradingBot) Run() { +func (b *TradingBot) Run(ctx context.Context) { for { - analysis, err := b.AnalyzeMarket("ETH") + analysis, err := b.AnalyzeMarket(ctx, "ETH") if err != nil { fmt.Printf("Error: %v\n", err) } else { @@ -239,17 +489,22 @@ func (b *TradingBot) Run() { } func main() { - bot := &TradingBot{ - client: blockrun.NewClient(""), + client, err := blockrun.NewLLMClient("") + if err != nil { + log.Fatal(err) } - bot.Run() + bot := &TradingBot{client: client} + bot.Run(context.Background()) } ``` ## Links - [GitHub: blockrun-llm-go](https://github.com/BlockRunAI/blockrun-llm-go) +- [GitHub: blockrun-llm-go-vip](https://github.com/BlockRunAI/blockrun-llm-go-vip) — official Anthropic / OpenAI Go clients over x402 +- [pkg.go.dev reference](https://pkg.go.dev/github.com/BlockRunAI/blockrun-llm-go) - [Models Reference](../api-reference/models.md) +- [Multi-chain RPC](../api-reference/multi-chain-rpc.md) - [SDK Developer Guide](../getting-started/sdk-developers.md) ## What's next? diff --git a/docs/sdks/python.md b/docs/sdks/python.md index b32910c..ce63fcc 100644 --- a/docs/sdks/python.md +++ b/docs/sdks/python.md @@ -7,7 +7,7 @@ description: The official BlockRun Python SDK — call 72 LLMs, smart routing, a The official Python SDK for BlockRun — pay per call in USDC, no API keys or subscriptions. -**Source:** [github.com/BlockRunAI/blockrun-llm](https://github.com/BlockRunAI/blockrun-llm) · [PyPI: blockrun-llm](https://pypi.org/project/blockrun-llm/) · MIT +**Source:** [github.com/BlockRunAI/blockrun-llm](https://github.com/BlockRunAI/blockrun-llm) · [PyPI: blockrun-llm](https://pypi.org/project/blockrun-llm/) · MIT · current release **1.13.0** · Python 3.9+ :::tip{title="In a hurry?"} New to BlockRun? Run the [5-Minute Quickstart](../getting-started/quickstart.md) first to fund a wallet, then come back for the full SDK reference. @@ -17,7 +17,9 @@ New to BlockRun? Run the [5-Minute Quickstart](../getting-started/quickstart.md) :::step{title="Install"} ```bash -pip install blockrun-llm +pip install blockrun-llm # Base (USDC on Base) — all core clients +pip install "blockrun-llm[solana]" # + SolanaLLMClient (USDC on Solana) +pip install "blockrun-llm[anthropic]" # + AnthropicClient (official anthropic SDK over x402) ``` ::: @@ -39,8 +41,16 @@ print(response) | Variable | Description | |----------|-------------| -| `BLOCKRUN_WALLET_KEY` | Your Base chain wallet private key | +| `BLOCKRUN_WALLET_KEY` | Your Base chain wallet private key (`0x` + 64 hex). `BASE_CHAIN_WALLET_KEY` is accepted as an alias | | `BLOCKRUN_API_URL` | API endpoint (default: https://blockrun.ai/api) | +| `BLOCKRUN_CHAT_TIMEOUT` | Default chat request timeout in seconds (default: 600) | +| `BLOCKRUN_MAX_COST_PER_CALL` | Refuse any single quote above this USD amount (see [Spend limits](#spend-limits)) | +| `BLOCKRUN_MAX_SESSION_COST` | Refuse quotes once the session total would exceed this USD amount | +| `BLOCKRUN_TX_LOG` | `1` or a directory path — write a per-call transaction log (see [Transaction log](#transaction-log-and-cost-tracking)) | +| `SOLANA_WALLET_KEY` | Solana secret key for `SolanaLLMClient` (bs58 keypair/seed, Solana CLI JSON array, or 64-byte hex) | +| `SOLANA_RPC_URL` | Optional Solana RPC used to fetch a blockhash when signing (defaults to BlockRun's proxy) | + +If no key is passed or set, every client falls back to the wallet file at `~/.blockrun/.session` (legacy `~/.blockrun/wallet.key` is still honoured); Solana clients use `~/.blockrun/.solana-session`. `LLMClient()` raises `ValueError` when none of these exist — call `setup_agent_wallet()` to create one (see [Wallet helpers](#wallet-helpers)). ### Client Options @@ -48,12 +58,18 @@ print(response) from blockrun_llm import LLMClient client = LLMClient( - private_key="0x...", # Wallet key (or use env var) + private_key="0x...", # Wallet key (or use env var / ~/.blockrun/.session) api_url="https://blockrun.ai/api", # Optional - timeout=60.0 # Request timeout in seconds + timeout=600.0, # Chat request timeout in seconds (default 600) + search_timeout=300.0, # Timeout when Live Search is enabled (default 300) + transaction_log=None, # True → ./log/, or a directory path (default: BLOCKRUN_TX_LOG) + max_cost_per_call=None, # USD ceiling per quote (default: unset) + max_session_cost=None, # USD ceiling per client session (default: unset) ) ``` +`AsyncLLMClient` takes the same arguments. `SolanaLLMClient` / `AsyncSolanaLLMClient` add `rpc_url`, `rpc_headers` and `image_timeout` (default 200s) and default `api_url` to `https://sol.blockrun.ai/api`. + ## Methods ### `chat(model, prompt, **options)` @@ -66,12 +82,18 @@ response = client.chat( "Explain quantum computing", system="You are a physics teacher.", # Optional system prompt max_tokens=500, # Optional max output - temperature=0.7 # Optional temperature + temperature=0.7, # Optional temperature + response_format={"type": "json_object"}, # Optional JSON mode (honoured on every model) + stop=["###"], # Optional stop sequence(s), str or list of ≤4 + fallback_models=["openai/gpt-5.4"], # Optional chain walked on timeout / 429 / 5xx + search=True, # Optional Live Search grounding (uses search_timeout) ) ``` **Returns:** `str` - The assistant's response text +`max_tokens` above a model's ceiling is not rejected: the gateway clamps it to the model's ceiling and quotes payment for the clamped value, and the SDK warns you before signing. Values over 1,000,000 raise `ValueError` as an obvious typo guard. + ### `chat_completion(model, messages, **options)` Full OpenAI-compatible chat completion. @@ -87,14 +109,29 @@ result = client.chat_completion( messages, max_tokens=100, temperature=0.7, - top_p=0.9 + top_p=0.9, + tools=None, # OpenAI-format tool definitions + tool_choice=None, # "auto" | "required" | {"type": "function", ...} ) print(result.choices[0].message.content) print(f"Tokens used: {result.usage.total_tokens}") ``` -**Returns:** `ChatResponse` object +**Returns:** `ChatResponse` object. Also accepts `response_format`, `stop`, `search` / `search_parameters` and `fallback_models`, as `chat()` does. + +### `chat_completion_stream(model, messages, **options)` + +Server-sent-events streaming. Same arguments as `chat_completion()`; yields one `ChatCompletionChunk` per SSE line until `[DONE]`. The x402 payment is signed once, before the stream opens. + +```python +for chunk in client.chat_completion_stream("openai/gpt-5.5", messages): + delta = chunk.choices[0].delta + if delta.content: + print(delta.content, end="", flush=True) +``` + +`AsyncLLMClient.chat_completion_stream()` is the `async for` counterpart. ### `list_models()` @@ -103,18 +140,41 @@ Get available models with pricing. ```python models = client.list_models() for model in models: - print(f"{model['id']}: ${model['inputPrice']}/M") + print(f"{model['id']}: ${model['pricing']['input']}/M in, ${model['pricing']['output']}/M out") ``` -### `get_wallet_address()` +Each row is the raw `/v1/models` entry: `id`, `name`, `context_window`, `max_output`, `categories`, `billing_mode` (`paid` / `free` / `per_image` / ...) and `pricing`. `list_image_models()` returns the image catalog the same way. -Get the wallet address being used. +### `get_wallet_address()`, `get_balance()`, `get_spending()` ```python address = client.get_wallet_address() print(f"Paying from: {address}") + +print(f"USDC balance: ${client.get_balance():.2f}") # on-chain balance of the active chain + +spent = client.get_spending() # this client session only +print(f"Spent ${spent['total_usd']:.4f} across {spent['calls']} calls") +``` + +### Spend limits + +Both limits are opt-in and unset by default. A quote above either ceiling is refused **before** the paid request is sent, so nothing settles. + +```python +from blockrun_llm import LLMClient, SpendLimitError + +client = LLMClient(max_cost_per_call=0.25, max_session_cost=10.00) +# or per-deployment: BLOCKRUN_MAX_COST_PER_CALL / BLOCKRUN_MAX_SESSION_COST + +try: + client.chat("openai/gpt-5.5", "...") +except SpendLimitError as e: + print(e.scope, e.quoted_usd, e.limit_usd) # "call" | "session" ``` +`SpendLimitError` subclasses `PaymentError`, so existing handlers keep working, and the model fallback chain will not shop for a cheaper model after a refusal. + ## Smart Routing (Router Core) **Save 88% on LLM costs automatically.** @@ -209,14 +269,14 @@ result = client.smart_chat("Review this contract for legal issues...", routing_p The classifier places every request in one of 4 tiers. Under `auto`, the tier primary is the starting point — the portfolio then ranks the eligible candidates and may promote a better-suited model for the task. -| Tier | Auto primary | Use Case | -|------|--------------|----------| -| **SIMPLE** | `google/gemini-2.5-flash` | Q&A, summaries, simple tasks | -| **MEDIUM** | `moonshot/kimi-k2.7` | Analysis, writing, coding | -| **COMPLEX** | `google/gemini-3.1-pro` | Advanced reasoning, research, long documents | -| **REASONING** | `xai/grok-4-1-fast-reasoning` | Math, logic, proofs | +| Tier | Use Case | +|------|----------| +| **SIMPLE** | Q&A, summaries, simple tasks | +| **MEDIUM** | Analysis, writing, coding | +| **COMPLEX** | Advanced reasoning, research, long documents | +| **REASONING** | Math, logic, proofs | -Under uncertainty the router fails **upward**: a score too close to a tier boundary is treated as ambiguous and defaults to MEDIUM, never SIMPLE. +The per-tier candidate chains live in Router Core's shared config and are resolved against the live `/v1/models` catalog at call time — a rung the catalog does not list (or marks unavailable) is skipped, so `route()` is the reliable way to see what a request would pick today. Under uncertainty the router fails **upward**: a score too close to a tier boundary is treated as ambiguous and defaults to MEDIUM, never SIMPLE. ### Routing Decision Details @@ -272,12 +332,41 @@ solana = SolanaLLMClient() print(solana.route("Prove this theorem").model) ``` +Hosts that drive the engine directly (`blockrun_llm.router_core`) can pass `options["unavailable_models"]` — or call the exported `apply_unavailable_models` on a tier map — to hard-remove a model that has started answering 400/404/410 from every chain on the next request, without waiting for an SDK release (1.13.0). The `LLMClient` methods above do not take this option. + +## Solana + +Pay in USDC on Solana instead of Base. The Solana clients talk to `https://sol.blockrun.ai/api`, sign an SVM transfer instead of an EIP-712 authorization, and expose the same chat, routing, prediction-market, DeFi/DEX, Exa, Modal, RPC and media surface — media lives directly on the client (`image`, `image_edit`, `video`, `video_from_content`, `music`, `speech`, `sound_effect`, `search`, `price`, `rpc`, `portrait_enroll`, `realface_*`) rather than in separate classes. + +```bash +pip install "blockrun-llm[solana]" +export SOLANA_WALLET_KEY="..." # bs58 keypair or seed, ~/.config/solana/id.json array, or 64-byte hex +``` + +```python +from blockrun_llm import SolanaLLMClient, AsyncSolanaLLMClient, setup_agent_solana_wallet + +client = SolanaLLMClient() # SOLANA_WALLET_KEY → ~/.blockrun/.solana-session +client = SolanaLLMClient(private_key="...") # or pass the key +client = setup_agent_solana_wallet() # creates ~/.blockrun/.solana-session if missing + +print(client.chat("openai/gpt-5.5", "gm Solana")) +img = client.image("a fox in snow", model="openai/gpt-image-2", quality="low") # quality is Solana-only +print(img.data[0].url) +``` + +:::warning{title="Base and Solana keys are not interchangeable"} +A Base key is `0x` + 64 hex characters; a Solana key is base58 (or the CLI JSON array). Pass a Solana key to `SolanaLLMClient`, never to `LLMClient`. Since 1.10.0 the SDK names the key's source and what it looks like when the format is wrong, instead of failing on a character-set error. +::: + +The payer must already hold a USDC token account on Solana — the SDK fails fast (1.6.1) rather than signing a transfer that cannot settle. A settlement failure after the signed transaction went out is terminal on every Solana path: the SDK never re-signs a second payment for one request (1.13.0). Pre-broadcast rejections (`PAYMENT_UNDERPAID`, `PAYMENT_REPLAY`, expired signatures, facilitator timeouts) are retried with a fresh signature automatically. + ## Specialized clients `LLMClient` covers chat and routing. Everything else — image, video, music, speech, voice, search, prices, RPC, and more — lives in a dedicated client class. Each is imported from `blockrun_llm` and constructed independently. :::note{title="Every client shares one constructor"} -`Client(private_key=None, api_url=None, timeout=...)`. The key is resolved in order: the `private_key` argument → `BLOCKRUN_WALLET_KEY` → `BASE_CHAIN_WALLET_KEY` → `~/.blockrun/.session`. So if you've run `blockrun_wallet setup`, no argument is needed. Every client also exposes `get_wallet_address()` and `close()`. +`Client(private_key=None, api_url=None, timeout=...)`. The key is resolved in order: the `private_key` argument → `BLOCKRUN_WALLET_KEY` → `BASE_CHAIN_WALLET_KEY` → `~/.blockrun/.session`. So if you've run `setup_agent_wallet()` or the MCP's `blockrun_wallet action:"setup"`, no argument is needed. Every client also exposes `get_wallet_address()` and `close()`. ::: ### Media generation @@ -320,6 +409,8 @@ print(res.data[0].url) :::note{title="Image-to-video inputs are mutually exclusive"} Pass exactly one of `image_url` (first-frame), `real_face_asset_id` (a `ta_…` Virtual Portrait / RealFace asset), or `reference_image_urls` (≤9). `last_frame_url` seeds the final frame. `generate_from_content(content=[...])` accepts the Seedance `content[]` array. + +Declare the seed mode you intend with `input_type="text" | "image" | "first_last_frame" | "reference"` (1.7.0). The gateway infers the mode from the seed fields and returns 400 **before charging** if your declared value disagrees — so a dynamically built `image_url` that comes back empty fails loudly instead of quietly producing a text-to-video clip you still pay for. ::: #### `MusicClient` @@ -489,10 +580,12 @@ print(client.onramp()) # Coinbase on-ramp link Access real-time prediction market data from Polymarket, Kalshi, Limitless, Opinion, Predict.Fun and Binance via [Predexon](https://predexon.com). No API keys needed — pay-per-request via x402. > **Retired upstream.** `pm_markets` / `pm_listings` / `pm_outcome` (and -> `matching-markets`) hit endpoints Predexon sunset on 2026-07-20 — they return -> `410`. The dFlow endpoints return `404`; that category is gone. Use -> `markets/search` for cross-venue lookups. `sports/*` is returning an upstream -> `500` as of 2026-08-04 and is withheld from discovery until it recovers. +> `matching-markets`) hit endpoints Predexon sunset on 2026-07-20 — the +> endpoints return `410`, and since 1.10.1 the helpers raise +> `RetiredEndpointError` locally instead of making a paid round trip. The dFlow +> endpoints return `404`; that category is gone. Use `markets/search` for +> cross-venue lookups. `sports/*` is returning an upstream `500` as of +> 2026-08-04 and is withheld from discovery until it recovers. ### `pm(path, **params)` @@ -617,13 +710,13 @@ asyncio.run(main()) ### Solana Usage ```python -from blockrun_llm.solana_client import SolanaLLMClient +from blockrun_llm import SolanaLLMClient client = SolanaLLMClient() markets = client.pm("polymarket/markets") ``` -Works on all clients: `LLMClient` (Base), `AsyncLLMClient`, and `SolanaLLMClient`. +Works on all clients: `LLMClient` (Base), `AsyncLLMClient`, `SolanaLLMClient` and `AsyncSolanaLLMClient`. ## Testnet Usage @@ -657,8 +750,10 @@ print(f"Is testnet: {client.is_testnet()}") # True | Model | Price | |-------|-------| -| `openai/gpt-oss-20b` | $0.003/request (flat) | -| `openai/gpt-oss-120b` | $0.004/request (flat) | +| `openai/gpt-oss-20b` | $0.001/request (flat) | +| `openai/gpt-oss-120b` | $0.002/request (flat) | + +Testnet also serves the image models and `minimax/music-2.5+` at mainnet prices — see `https://testnet.blockrun.ai/api/v1/models`. `async_testnet_client()` is the `AsyncLLMClient` equivalent. ### Manual Testnet Configuration @@ -696,20 +791,26 @@ asyncio.run(main()) ## Error Handling ```python -from blockrun_llm import LLMClient, APIError, PaymentError +from blockrun_llm import LLMClient, APIError, PaymentError, SpendLimitError, RetiredEndpointError client = LLMClient() try: response = client.chat("openai/gpt-5.5", "Hello!") +except SpendLimitError as e: + print(f"Refused before paying: {e.scope} limit ${e.limit_usd}, quote ${e.quoted_usd}") except PaymentError as e: - print(f"Payment failed: {e}") - # Check your USDC balance + print(f"Payment failed: {e}") # e.status_code / e.response carry the gateway's + # Check your USDC balance # code + reason when a 402 was rejected except APIError as e: print(f"API error ({e.status_code}): {e}") print(f"Details: {e.response}") +except RetiredEndpointError as e: + print(f"Helper retired upstream: {e}") # e.g. pm_markets() ``` +All exceptions derive from `blockrun_llm.BlockrunError`. When a paid request fails after the payment signature was sent, the error names the settlement tx hash if the gateway reported one; the SDK never advances the fallback chain (and never signs a second payment) after that point. + ## Response Types ### ChatResponse @@ -726,18 +827,24 @@ class ChatResponse: class ChatChoice: index: int message: ChatMessage - finish_reason: str + finish_reason: Optional[str] class ChatMessage: - role: str - content: str + role: str # "system" | "user" | "assistant" | "tool" + content: Optional[str] + tool_calls: Optional[List[ToolCall]] # assistant tool calls + tool_call_id: Optional[str] # tool results class ChatUsage: prompt_tokens: int completion_tokens: int total_tokens: int + cache_read_input_tokens: Optional[int] # prompt-cache hits, when reported + cache_creation_input_tokens: Optional[int] ``` +Unknown fields the gateway returns are preserved (`extra = "allow"`), never stripped. Streaming yields `ChatCompletionChunk` (`choices[0].delta.content`, `finish_reason` on the last chunk). + ## Examples ### Multi-turn Conversation @@ -780,6 +887,59 @@ code = client.chat( print(code) ``` +## Wallet helpers + +```python +from blockrun_llm import setup_agent_wallet, status, list_discovered_wallets, import_wallet + +client = setup_agent_wallet() # creates ~/.blockrun/.session (0600) if missing, prints address + funding QR +status() # "Wallet: 0x… Balance: $5.30 USDC" + +link = client.onramp(client.get_wallet_address()) # one-time Coinbase Onramp link (expires ~5 min) + +# Adopt a wallet another application created (never done automatically) +for w in list_discovered_wallets(): + print(w["address"], "from", w["source"]) +import_wallet("0x…") # backs up the current key to ~/.blockrun/.session.backup- first +``` + +Solana equivalents: `setup_agent_solana_wallet()`, `list_discovered_solana_wallets()`, `import_solana_wallet()`. Automatic wallet resolution never adopts another application's `wallet.json` (1.7.2) — importing is always explicit. + +## Anthropic SDK compatibility + +Use the official `anthropic` Python SDK against BlockRun with x402 payments handled by a custom transport: + +```bash +pip install "blockrun-llm[anthropic]" +``` + +```python +from blockrun_llm import AnthropicClient + +client = AnthropicClient() # same wallet resolution as LLMClient +response = client.messages.create( + model="claude-sonnet-4-6", + max_tokens=1024, + messages=[{"role": "user", "content": "Hello!"}], +) +print(response.content[0].text) +``` + +For verbatim native passthrough — the upstream response untouched, real thinking-block signatures, official `openai` SDK too, and `chain="solana"` on every client — see the separate [`blockrun-llm-vip`](https://pypi.org/project/blockrun-llm-vip/) package (`from blockrun_llm_vip import Anthropic, OpenAI`), which subclasses the official SDKs and only swaps the transport. Access is enabled per wallet address. + +## Transaction log and cost tracking + +Every paid call appends a line to `~/.blockrun/cost_log.jsonl`. Summarise or export it: + +```python +from blockrun_llm import get_cost_log_summary, export_cost_log_csv, export_cost_log_json + +print(get_cost_log_summary()) # grouped by endpoint by default +csv_text = export_cost_log_csv() # pass output_path=... to also write a file +``` + +For a project-local, on-chain-matchable log, opt in with `LLMClient(transaction_log=True)` (writes `./log/transactions.jsonl`), a directory path, or `BLOCKRUN_TX_LOG=1`. Each row carries model, tokens, `cost_usd`, `tx_hash`, on-chain amount, payer, payee and network. + ## Testing The SDK includes comprehensive test coverage. @@ -884,7 +1044,7 @@ except APIError as e: ### Monitoring Spending -Check your transaction history on Base: +`client.get_spending()` reports this session; the cost log and transaction log above persist across runs. Check your transaction history on Base: ```python client = LLMClient() diff --git a/docs/sdks/typescript.md b/docs/sdks/typescript.md index ded3911..ef43156 100644 --- a/docs/sdks/typescript.md +++ b/docs/sdks/typescript.md @@ -37,20 +37,35 @@ yarn add @blockrun/llm :::: +Requires Node ≥ 20. The smart router is bundled — nothing else to install for Base payments. + +**Paying on Solana?** The two Solana packages are optional *peer* dependencies (they are not pulled in automatically, to keep `bigint-buffer` out of Base-only projects). Install them explicitly: + +```bash +npm install @blockrun/llm @solana/web3.js @solana/spl-token +``` + +Calling a Solana path without them throws an error naming the exact install command. + ## Quick Start ```typescript import { LLMClient } from '@blockrun/llm'; const client = new LLMClient({ - privateKey: process.env.BLOCKRUN_WALLET_KEY as `0x${string}` + privateKey: process.env.BASE_CHAIN_WALLET_KEY as `0x${string}` }); +// Recommended: let the bundled router pick the cheapest capable model +const routed = await client.smartChat('Hello!'); +console.log(routed.response, routed.model); + +// Or pin a model yourself const response = await client.chat('openai/gpt-5.5', 'Hello!'); console.log(response); ``` -**Latest version: v1.4.3** +**Latest version: v3.13.2 on npm** (v3.13.3 is tagged in the repo as of 2026-08-26 and ships on the next GitHub release — check `npm view @blockrun/llm version`). ## Configuration @@ -60,12 +75,35 @@ console.log(response); import { LLMClient } from '@blockrun/llm'; const client = new LLMClient({ - privateKey: '0x...', // Required: wallet private key + privateKey: '0x...', // Optional if BASE_CHAIN_WALLET_KEY is set apiUrl: 'https://blockrun.ai/api', // Optional: API endpoint - timeout: 60000 // Optional: timeout in ms + timeout: 600000 // Optional: timeout in ms (default 600s) }); ``` +`LLMClient` reads `BASE_CHAIN_WALLET_KEY` when `privateKey` is omitted and throws if neither is present. It does not create a wallet for you — use `setupAgentWallet()` for that: + +```typescript +import { setupAgentWallet } from '@blockrun/llm'; + +// Resolves BLOCKRUN_WALLET_KEY / BASE_CHAIN_WALLET_KEY → ~/.blockrun/.session, +// creates and saves a new key if none exists, and returns a ready LLMClient. +const client = setupAgentWallet(); +console.log(client.getWalletAddress()); // fund this address with USDC on Base +``` + +### Environment variables + +| Variable | Used by | Description | +|----------|---------|-------------| +| `BASE_CHAIN_WALLET_KEY` | `LLMClient`, `OpenAI`, `AnthropicClient` | Base wallet private key (`0x…`) | +| `BLOCKRUN_WALLET_KEY` | `BlockrunClient`, the specialized clients, `setupAgentWallet()` | Same key; these clients accept either variable (`BLOCKRUN_WALLET_KEY` first) | +| `SOLANA_WALLET_KEY` | `SolanaLLMClient` | Solana secret key — bs58, CLI JSON array, or hex | +| `BLOCKRUN_CHAT_TIMEOUT` | all chat clients | Default chat timeout in **seconds** (default 600) | +| `BLOCKRUN_HOME` | wallet storage | Overrides `~/.blockrun` for the Base wallet file. Security-sensitive: it redirects where the signing key is read and written | +| `BASE_RPC_URL` | `getBalance()` | Custom Base RPC for balance checks | +| `SOLANA_RPC_URL` / `SOLANA_RPC_API_KEY` / `SOLANA_RPC_HEADERS` | `SolanaLLMClient` | Custom Solana RPC (default: BlockRun's own proxy) | + ## Methods ### `chat(model, prompt, options?)` @@ -75,8 +113,13 @@ Simple one-line chat interface. ```typescript const response = await client.chat('openai/gpt-5.5', 'Explain quantum computing', { system: 'You are a physics teacher.', // Optional - maxTokens: 500, // Optional - temperature: 0.7 // Optional + maxTokens: 500, // Optional (default 1024) + temperature: 0.7, // Optional + topP: 0.9, // Optional + responseFormat: { type: 'json_object' }, // Optional: JSON mode + stop: ['\n\n'], // Optional: up to 4 stop sequences + fallbackModels: ['openai/gpt-5.4'], // Optional: tried on timeout / 429 / 5xx + search: true, // Optional: Live Search (search-enabled models) }); ``` @@ -84,7 +127,7 @@ const response = await client.chat('openai/gpt-5.5', 'Explain quantum computing' ### `chatCompletion(model, messages, options?)` -Full OpenAI-compatible chat completion. +Full OpenAI-compatible chat completion, including tool calling. ```typescript import { LLMClient, type ChatMessage } from '@blockrun/llm'; @@ -97,7 +140,8 @@ const messages: ChatMessage[] = [ const result = await client.chatCompletion('openai/gpt-5.5', messages, { maxTokens: 100, temperature: 0.7, - topP: 0.9 + topP: 0.9, + // tools, toolChoice, responseFormat, stop, search, fallbackModels also accepted }); console.log(result.choices[0].message.content); @@ -106,9 +150,15 @@ console.log(`Tokens used: ${result.usage?.total_tokens}`); **Returns:** `Promise` -### `listModels()` +### `chatCompletionStream(model, messages, options?)` + +Same parameters as `chatCompletion`, streamed. Returns a standard `fetch` `Response` whose body is an SSE stream (the SDK pre-signs the payment from a 1-hour cache on repeat calls to the same model, skipping the 402 round-trip). See [Streaming](#streaming) below for a full example, or use the [OpenAI-compatible client](#drop-in-openai--anthropic-clients), which yields parsed chunks. + +**Returns:** `Promise` + +### `listModels()` / `listImageModels()` / `listAllModels()` -Get available models with pricing. +Get available models with pricing. `listModels()` returns chat models; `listImageModels()` returns per-image models; `listAllModels()` returns both. ```typescript const models = await client.listModels(); @@ -117,20 +167,29 @@ for (const model of models) { } ``` -### `getWalletAddress()` - -Get the wallet address being used. +### `getWalletAddress()` / `getBalance()` / `getSpending()` ```typescript const address = client.getWalletAddress(); console.log(`Paying from: ${address}`); + +const balance = await client.getBalance(); // USDC on Base +const spent = client.getSpending(); // this session: { totalUsd, calls, ... } ``` -## Smart Routing (ClawRouter) +### `onramp(address)` + +Mints a one-time Coinbase Onramp link (free call, Base only) so you can buy USDC by card straight into your signing wallet. The URL is single-use and expires in ~5 minutes — mint it at click time. + +```typescript +const { url } = await client.onramp(client.getWalletAddress()); +``` + +## Smart Routing (Router Core V3) **Save 88% on LLM costs automatically.** -The `smartChat()` method routes each request on [Router Core](https://github.com/BlockRunAI/router-core) — 15 weighted dimensions classify the request, capability constraints are applied as hard filters, and the surviving candidates are ranked on task affinity, cost, speed and reliability. Decisions run locally in <1ms — your prompts never leave your machine for routing, and no extra model call is made to decide. +`smartChat()` routes each request on [Router Core V3](https://github.com/BlockRunAI/router-core) — the same deterministic portfolio router that drives [ClawRouter](../products/routing/clawrouter.md), **bundled into the SDK** since v3.12.0. 15 weighted dimensions classify the request, capability constraints (tools, vision, structured output, context) are applied as hard filters, and the surviving candidates are ranked on task affinity, cost, speed and reliability. Decisions run locally in <1ms — your prompts never leave your machine for routing, and no extra model call is made to decide. ### Basic Usage @@ -138,56 +197,82 @@ The `smartChat()` method routes each request on [Router Core](https://github.com import { LLMClient } from '@blockrun/llm'; const client = new LLMClient({ - privateKey: process.env.BLOCKRUN_WALLET_KEY as `0x${string}` + privateKey: process.env.BASE_CHAIN_WALLET_KEY as `0x${string}` }); -// Let ClawRouter pick the best model automatically +// Let the router pick the best model automatically const result = await client.smartChat('What is 2+2?'); console.log(result.response); // "4" -console.log(result.model); // "deepseek/deepseek-chat" +console.log(result.model); // "google/gemini-2.5-flash" console.log(result.routing.tier); // "SIMPLE" -console.log(result.routing.savings); // 0.94 (94% savings) +console.log(result.routing.savings); // 0.88 (88% savings vs the premium baseline) +``` + +### Three ways to route + +```typescript +// 1. smartChat() — one-line routed chat +const result = await client.smartChat('What is 2+2?'); + +// 2. smartChatCompletion() — full agent/tool conversations, routed; +// the decision is attached as response.routing +const agent = await client.smartChatCompletion(messages, { tools, toolChoice: 'auto' }); + +// 3. blockrun/auto | blockrun/eco | blockrun/premium — model aliases accepted by +// chat(), chatCompletion() and chatCompletionStream() on Base and Solana +const reply = await client.chatCompletion('blockrun/auto', messages); + +// Inspect a decision without making (or paying for) a model call +const decision = await client.route('Prove the Riemann hypothesis'); ``` +The aliases are resolved locally by `LLMClient`, `SolanaLLMClient` and the OpenAI-compatible layer. `AnthropicClient` proxies straight to `/v1/messages` and does **not** resolve them — pass a concrete model id there. + ### Routing Profiles | Profile | Behavior | Best For | |---------|----------|----------| -| `"free"` | Always uses free NVIDIA models | Development, testing | -| `"eco"` | Maximizes cost savings | Bulk processing | +| `"eco"` | Cheapest capable model — ranks the free NVIDIA tier first, so simple requests cost $0 | Bulk processing, zero-cost testing | | `"auto"` | Balances quality and cost (default) | Production workloads | | `"premium"` | Always uses top-tier models | Critical tasks | +There is no `"free"` profile — `routingProfile` accepts `'eco' | 'auto' | 'premium'` (ClawRouter's `/model free` belongs to its own proxy). For guaranteed $0, pin a `nvidia/*` model with `chat()`. + ```typescript -// Force free models (great for development) -const result = await client.smartChat('Explain recursion', { - routingProfile: 'free' -}); -console.log(result.model); // "nvidia/step-3.7-flash" (a live $0 model; the free lineup rotates as NVIDIA retires SKUs) +// Guaranteed $0: call a free model directly +const free = await client.chat('nvidia/step-3.7-flash', 'Explain recursion'); -// Maximum savings mode -const result2 = await client.smartChat('Summarize this article: ...', { +// Smart-routed $0-first +const result = await client.smartChat('What is 2+2?', { routingProfile: 'eco' }); +console.log(result.model); // "nvidia/step-3.7-flash" (a live $0 model; the free lineup rotates as NVIDIA retires SKUs) +console.log(result.routing.savings); // 1 (100%) // Premium mode for critical tasks const result3 = await client.smartChat('Review this contract for legal issues...', { routingProfile: 'premium' }); -console.log(result3.model); // "anthropic/claude-opus-5" +console.log(result3.model); // "anthropic/claude-opus-4.7" ``` ### 4-Tier Model Selection -ClawRouter classifies prompts into four tiers: +The router classifies prompts into four tiers (`routing.tier`); each tier × profile anchors a candidate pool that the portfolio ranking draws from: + +| Tier | Example Tasks | Typical Models | +|------|---------------|----------------| +| **SIMPLE** | Greetings, math, lookups | Gemini Flash, GPT-4o-mini, free NVIDIA tier (eco) | +| **MEDIUM** | Explanations, summaries, code snippets | GPT-4o, Claude Sonnet, Kimi | +| **COMPLEX** | Analysis, architecture, long documents | Gemini 3.1 Pro, Claude Fable 5 (premium) | +| **REASONING** | Multi-step logic, proofs | Grok reasoning, o3, DeepSeek Reasoner | + +The [ClawRouter README](https://github.com/BlockRunAI/ClawRouter#how-it-works) is the live source of truth for the tier configs as models and prices move. -| Tier | Models | Use Case | -|------|--------|----------| -| **SIMPLE** | DeepSeek, Gemini Flash | Q&A, summaries, simple tasks | -| **MEDIUM** | GPT-5.5, Claude Sonnet 4.6 | Analysis, writing, coding | -| **COMPLEX** | Claude Opus 4.6, GPT-5.4 Pro | Advanced reasoning, research | -| **REASONING** | DeepSeek Reasoner, o1, o3 | Math, logic, proofs | +### Automatic fallback on transient errors + +`smartChat()` builds `routing.fallbacks` from the portfolio ranking, and `chat()` / `chatCompletion()` walk it automatically on timeouts, network failures, 429s and 5xx (502/503/504/522/524). Other 4xx errors and `PaymentError` propagate immediately. Each hop is logged to stderr as `[@blockrun/llm] -> (...)`. Pass `fallbackModels` yourself to get the same behaviour on a pinned model. ### Routing Decision Details @@ -196,26 +281,38 @@ const result = await client.smartChat('Prove that √2 is irrational'); // Access full routing decision const { routing } = result; -console.log(`Model: ${routing.model}`); // "deepseek/deepseek-reasoner" -console.log(`Tier: ${routing.tier}`); // "REASONING" -console.log(`Confidence: ${routing.confidence}`); // 0.97 -console.log(`Reasoning: ${routing.reasoning}`); // "Detected: math proof..." +console.log(`Model: ${routing.model}`); // the selected model id +console.log(`Tier: ${routing.tier}`); // "REASONING" +console.log(`Method: ${routing.method}`); // "portfolio" +console.log(`Task: ${routing.taskType}`); // "reasoning_math" +console.log(`Candidates: ${routing.candidates}`); // ranked, capability-eligible models +console.log(`Fallbacks: ${routing.fallbacks}`); // the chain walked on transient errors +console.log(`Confidence: ${routing.confidence}`); // 0–1 +console.log(`Reasoning: ${routing.reasoning}`); // "Detected: math proof..." console.log(`Cost: $${routing.costEstimate.toFixed(4)}`); console.log(`Baseline: $${routing.baselineCost.toFixed(4)}`); -console.log(`Savings: ${(routing.savings * 100).toFixed(0)}%`); // "97%" +console.log(`Savings: ${(routing.savings * 100).toFixed(0)}%`); +console.log(`Router: ${routing.routerVersion}`); // "v3-portfolio" ``` ### Smart Routing Types ```typescript import type { - RoutingProfile, // "free" | "eco" | "auto" | "premium" - RoutingTier, // "SIMPLE" | "MEDIUM" | "COMPLEX" | "REASONING" - RoutingDecision, // Full routing details - SmartChatResponse, // Response + model + routing + RoutingProfile, // "eco" | "auto" | "premium" + RoutingTier, // "SIMPLE" | "MEDIUM" | "COMPLEX" | "REASONING" + RoutingTaskType, // "chat" | "code_edit" | "tool_agent" | "reasoning" | ... + RoutingTierConfig, // primary + fallbacks for one tier + RoutingDecision, // Full routing details + SmartChatOptions, // ChatOptions + routingProfile + maxOutputTokens + SmartChatResponse, // Response + model + routing + SmartChatCompletionOptions, // ChatCompletionOptions + routingProfile + maxOutputTokens + SmartChatCompletionResponse, // Full ChatResponse + routing } from '@blockrun/llm'; ``` +These are derived from `@blockrun/router-core` and shipped inlined in the SDK's declaration files — nothing extra to install to typecheck. + ### With System Prompt ```typescript @@ -235,7 +332,8 @@ Already using the `openai` or `@anthropic-ai/sdk` packages? Swap the import and ```typescript import { OpenAI } from '@blockrun/llm'; -const client = new OpenAI({ walletKey: process.env.BLOCKRUN_WALLET_KEY }); +// Options: { walletKey | privateKey, baseURL?, timeout? } — falls back to BASE_CHAIN_WALLET_KEY +const client = new OpenAI({ walletKey: process.env.BASE_CHAIN_WALLET_KEY }); const res = await client.chat.completions.create({ model: 'openai/gpt-5.5', @@ -251,27 +349,84 @@ for await (const chunk of stream) process.stdout.write(chunk.choices[0]?.delta?. :::tab{label="Anthropic-compatible"} ```typescript -import { Anthropic } from '@blockrun/llm'; +import { AnthropicClient } from '@blockrun/llm'; -const client = new Anthropic({ walletKey: process.env.BLOCKRUN_WALLET_KEY }); +// Wraps the official @anthropic-ai/sdk (an optional dependency — install it if it is +// not already in your tree) with a custom fetch that signs x402 payments. +const client = new AnthropicClient({ privateKey: process.env.BASE_CHAIN_WALLET_KEY }); const msg = await client.messages.create({ model: 'anthropic/claude-opus-5', max_tokens: 512, messages: [{ role: 'user', content: 'Hello!' }], }); +console.log(msg.content[0].text); ``` ::: :::: -`create()` mirrors the upstream params (`model`, `messages`, `max_tokens`, `temperature`, `top_p`, `tools`, `tool_choice`, `response_format`, `stop`, `n`, penalties). +`create()` mirrors the upstream params (`model`, `messages`, `max_tokens`, `temperature`, `top_p`, `tools`, `tool_choice`, `response_format`, `stop`, `n`, penalties). The OpenAI-compatible client also accepts the `blockrun/auto` / `eco` / `premium` aliases; `AnthropicClient` needs a concrete model id. + +## Solana + +Pay with Solana USDC through `sol.blockrun.ai` — same API as `LLMClient`, plus the two optional peer packages from [Installation](#installation). + +```typescript +import { SolanaLLMClient, solanaClient } from '@blockrun/llm'; + +// SOLANA_WALLET_KEY env var — bs58, CLI JSON array, or hex +const client = new SolanaLLMClient(); + +// Or pass the key and RPC explicitly +const client2 = new SolanaLLMClient({ + privateKey: 'your-bs58-solana-key', + apiUrl: 'https://sol.blockrun.ai/api', // default + rpcUrl: 'https://your-helius-or-tatum-rpc', // default: BlockRun's own free proxy + rpcHeaders: { 'x-api-key': '...' }, // optional header-auth +}); + +const response = await client.chat('openai/gpt-4o', 'gm Solana'); +const routed = await client.smartChat('What is 2+2?'); // routing parity with Base +const address = await client.getWalletAddress(); // async on Solana +``` + +`SolanaLLMClient` has `chat`, `chatCompletion`, `smartChat`, `smartChatCompletion`, `route`, `listModels`, `getBalance`, `imageEdit`, `search`, `pm`/`pmQuery`, the `exa*`, `defi*`, `dex*` and `modal*` helpers, `getSpending()` and `isSolana()`. + +A Solana payment is pinned to a blockhash valid for roughly 60 seconds. If it expires between signing and verification, the client re-signs against a fresh blockhash and retries — at most twice, with a short backoff (v3.13.3). The retry fires only on the gateway's explicit verification-phase stale signal, before any transaction is broadcast, so you cannot be double-charged; settlement failures, insufficient funds and ambiguous rejections still fail immediately as `PaymentError`. + +## Streaming + +Stream tokens with automatic x402 payment. The OpenAI-compatible client is the easiest path; the native client returns a raw SSE `Response`. + +```typescript +import { LLMClient, type ChatMessage } from '@blockrun/llm'; + +const client = new LLMClient(); +const messages: ChatMessage[] = [{ role: 'user', content: 'Explain quantum computing simply' }]; + +const response = await client.chatCompletionStream('google/gemini-2.5-flash', messages); + +const reader = response.body!.getReader(); +const decoder = new TextDecoder(); +while (true) { + const { done, value } = await reader.read(); + if (done) break; + for (const line of decoder.decode(value, { stream: true }).split('\n')) { + if (!line.startsWith('data: ') || line === 'data: [DONE]') continue; + const data = JSON.parse(line.slice(6)); + process.stdout.write(data.choices?.[0]?.delta?.content ?? ''); + } +} +``` + +First call: request → 402 with price → sign locally → retry with `PAYMENT-SIGNATURE` and `stream: true`. The payment requirements are cached per model for 1 hour, so subsequent calls pre-sign and stream immediately (~200ms faster). ## Specialized clients -Like the Python SDK, every non-chat capability has its own client class, exported from `@blockrun/llm`. Each takes the same options (`{ walletKey | privateKey, apiUrl?, timeout? }`) and returns Promises. +Like the Python SDK, every non-chat capability has its own client class, exported from `@blockrun/llm`. Each takes the same options (`{ privateKey?, apiUrl?, timeout? }`) and returns Promises. :::note{title="Shared instantiation"} -Construct any client with `new XClient({ walletKey })` (or rely on `BLOCKRUN_WALLET_KEY` / `~/.blockrun/.session`). All expose `getWalletAddress()`. +Construct any client with `new XClient({ privateKey })`, or rely on `BLOCKRUN_WALLET_KEY` / `BASE_CHAIN_WALLET_KEY` in the environment. All expose `getWalletAddress()`; most expose `getSpending()`. ::: ```typescript @@ -283,7 +438,7 @@ import { // Image — generate + edit/fuse const img = new ImageClient(); const out = await img.generate('A sunset over mountains', { model: 'google/nano-banana', size: '1024x1024' }); -const fused = await img.edit('Place the logo on the shirt', [subjectDataUri, logoDataUri]); +const fused = await img.edit('Place the logo on the shirt', [subjectDataUri, logoDataUri]); // default model openai/gpt-image-2 // Video — async submit→poll handled internally const vid = new VideoClient(); @@ -292,6 +447,7 @@ const clip = await vid.generate('a red apple spinning', { model: 'bytedance/seed // Speech + sound effects const tts = new SpeechClient(); const audio = await tts.generate('Hello!', { voice: 'sarah', responseFormat: 'mp3' }); +const sfx = await tts.soundEffect('rain on a tin roof', { durationSeconds: 5 }); // Search, prices, crypto data, RPC const search = new SearchClient(); @@ -304,15 +460,46 @@ const block = await rpc.call('ethereum', 'eth_blockNumber'); // $0.003/call The full method surface mirrors the Python SDK (see the [Python](python.md) page for per-method params, pricing tiers, and `ta_…` identity assets); the only differences are camelCase options and `Promise` returns. +### `BlockrunClient` — the universal primitive + +Since v2.5.0 the SDK also ships a single `BlockrunClient` that speaks to **every** BlockRun endpoint over x402, so a new endpoint never waits on an SDK release. Four call shapes cover every endpoint type: + +```typescript +import { BlockrunClient } from '@blockrun/llm'; + +const br = new BlockrunClient(); // BLOCKRUN_WALLET_KEY or BASE_CHAIN_WALLET_KEY + +// get(path, params?) — synchronous GET (price, ranking, list, news) +const btc = await br.get('/v1/surf/market/price', { symbol: 'BTC' }); + +// post(path, body?) — synchronous POST (on-chain SQL, search) +const rows = await br.post('/v1/surf/onchain/sql', { query: 'SELECT 1' }); + +// poll(path, body?, { budgetMs, intervalMs }) — submit + poll (image, video, music, voice) +const video = await br.poll('/v1/videos/generations', { model: 'xai/grok-imagine-video', prompt: 'a red apple spinning' }); + +// stream(path, body?) — async iterator over SSE chunks (chat) +for await (const chunk of br.stream('/v1/chat/completions', { + model: 'anthropic/claude-sonnet-4.6', + messages: [{ role: 'user', content: 'Hi' }], + stream: true, +})) { + process.stdout.write(chunk?.choices?.[0]?.delta?.content ?? ''); +} +``` + +The per-API client classes above all remain and are the documented surface; `BlockrunClient` is the escape hatch for endpoints they do not wrap yet. + ## Prediction Markets (Powered by Predexon) Access real-time prediction market data from Polymarket, Kalshi, Limitless, Opinion, Predict.Fun and Binance via [Predexon](https://predexon.com). No API keys needed — pay-per-request via x402. > **Retired upstream.** `pmMarkets` / `pmListings` / `pmOutcome` (and -> `matching-markets`) hit endpoints Predexon sunset on 2026-07-20 — they return -> `410`. The dFlow endpoints return `404`; that category is gone. Use -> `markets/search` for cross-venue lookups. `sports/*` is returning an upstream -> `500` as of 2026-08-04 and is withheld from discovery until it recovers. +> `matching-markets`) hit endpoints Predexon sunset on 2026-07-20 — they now +> throw `RetiredEndpointError` before any network I/O. The dFlow endpoints +> return `404`; that category is gone. Use `markets/search` for cross-venue +> lookups. `sports/*` is returning an upstream `500` as of 2026-08-04 and is +> withheld from discovery until it recovers. ### `pm(path, params?)` @@ -390,14 +577,9 @@ const batch = await client.pmQuery("polymarket/wallet/identities", { ### Predexon v2 Convenience Helpers -Thin wrappers over `pm()` / `pmQuery()` for the most common v2 endpoints. +Thin wrappers over `pm()` / `pmQuery()` for the most common v2 endpoints that are still live. ```typescript -// Canonical cross-venue markets (Tier 1) -const markets = await client.pmMarkets({ venue: "polymarket", status: "active" }); -const listings = await client.pmListings({ category: "elections" }); -const outcome = await client.pmOutcome("PXM-12345"); - // Polymarket keyset pagination (Tier 1) const page = await client.pmPolymarketMarketsKeyset({ limit: "100" }); const nextPage = await client.pmPolymarketEventsKeyset({ @@ -408,6 +590,13 @@ const nextPage = await client.pmPolymarketEventsKeyset({ const ident = await client.pmWalletIdentity("0xabc..."); const batch = await client.pmWalletIdentities(["0xabc...", "0xdef..."]); // up to 200 const cluster = await client.pmWalletCluster("0xabc..."); + +// Sports (currently upstream 500 — see note above) +const cats = await client.pmSportsCategories(); +const games = await client.pmSportsMarkets({ category: "nba" }); + +// Retired: pmMarkets(), pmListings(), pmOutcome() throw RetiredEndpointError. +// Use pm("markets/search", { q }) instead. ``` ### Available Platforms @@ -421,7 +610,7 @@ const cluster = await client.pmWalletCluster("0xabc..."); | Limitless | Markets, Orderbooks | | Opinion | Markets, Orderbooks | | Predict.Fun | Markets, Orderbooks | -| Matching | Cross-platform market matching, exact-match pairs, unified search | +| Search | Unified cross-venue `markets/search` (the canonical-market and exact-match-pair endpoints were sunset 2026-07-20) | ### Solana Usage @@ -436,20 +625,19 @@ Works on both `LLMClient` (Base) and `SolanaLLMClient`. ## Testnet Usage -For development and testing without real USDC, use the Base Sepolia testnet: +For development and testing without real USDC, point the client at the Base Sepolia gateway. (The TypeScript SDK has no `testnetClient()` helper — that is a Python SDK convenience; here you set `apiUrl`.) ```typescript -import { testnetClient } from '@blockrun/llm'; +import { LLMClient } from '@blockrun/llm'; -// Create testnet client (uses Base Sepolia) -const client = testnetClient({ privateKey: '0x...' }); +const client = new LLMClient({ + privateKey: process.env.BASE_CHAIN_WALLET_KEY as `0x${string}`, + apiUrl: 'https://testnet.blockrun.ai/api' +}); -// Chat with testnet model +// Chat with a testnet model const response = await client.chat('openai/gpt-oss-20b', 'Hello!'); console.log(response); - -// Verify you're on testnet -console.log(client.isTestnet()); // true ``` ### Testnet Setup @@ -462,26 +650,17 @@ console.log(client.isTestnet()); // true | Model | Price | |-------|-------| -| `openai/gpt-oss-20b` | $0.003/request (flat) | -| `openai/gpt-oss-120b` | $0.004/request (flat) | - -### Manual Testnet Configuration +| `openai/gpt-oss-20b` | $0.001/request (flat) | +| `openai/gpt-oss-120b` | $0.002/request (flat) | -```typescript -import { LLMClient } from '@blockrun/llm'; - -// Configure manually with testnet API URL -const client = new LLMClient({ - privateKey: '0x...', - apiUrl: 'https://testnet.blockrun.ai/api' -}); -const response = await client.chat('openai/gpt-oss-20b', 'Hello!'); -``` +The testnet gateway also lists the image, music and video models — `curl https://testnet.blockrun.ai/api/v1/models` for the current set. ## Error Handling +All SDK errors extend `BlockrunError`. + ```typescript -import { LLMClient, APIError, PaymentError } from '@blockrun/llm'; +import { LLMClient, APIError, PaymentError, RetiredEndpointError, BlockrunError } from '@blockrun/llm'; const client = new LLMClient({ privateKey: '0x...' }); @@ -490,21 +669,32 @@ try { } catch (error) { if (error instanceof PaymentError) { console.error('Payment failed:', error.message); - // Check your USDC balance + // Check your USDC balance (Base for LLMClient, Solana for SolanaLLMClient) + } else if (error instanceof RetiredEndpointError) { + console.error('Endpoint retired upstream:', error.message); } else if (error instanceof APIError) { console.error(`API error (${error.statusCode}):`, error.message); + // error.response holds the parsed body when there is one + } else if (error instanceof BlockrunError) { + console.error('SDK error:', error.message); } else { throw error; } } ``` +Transient failures (timeouts, network errors, 429, 502/503/504/522/524) are retried down `fallbackModels` — or the router's chain after `smartChat()` — before an `APIError` surfaces. Other 4xx and `PaymentError` propagate immediately. + ## Types ```typescript interface ChatMessage { - role: 'system' | 'user' | 'assistant'; - content: string; + role: 'system' | 'user' | 'assistant' | 'tool'; + content?: string | null; + name?: string; // tool messages + tool_call_id?: string; // tool result messages + tool_calls?: ToolCall[]; // assistant messages that call tools + reasoning_content?: string; // returned by reasoning-capable models } interface ChatResponse { @@ -533,15 +723,20 @@ interface Model { name: string; description: string; provider: string; - inputPrice: number; + inputPrice: number; // per 1M tokens; 0 when billingMode !== "paid" outputPrice: number; - contextWindow: number; // mapped from API's context_window + contextWindow: number; // mapped from API's context_window maxOutput: number; // mapped from API's max_output categories: string[]; // e.g., ["chat", "reasoning", "coding", "vision"] available: boolean; + billingMode?: string; // "paid" | "free" | "flat" + flatPrice?: number; // per-request price for flat-billed models + hidden?: boolean; } ``` +Tool-calling types (`Tool`, `FunctionDefinition`, `ToolCall`, `ToolChoice`), `ResponseFormat`, and the option bags for every specialized client are exported too. + ## Examples ### Concurrent Requests @@ -562,12 +757,6 @@ console.log('Claude:', claude); console.log('Gemini:', gemini); ``` -### Streaming (Coming Soon) - -:::info -Streaming support is planned for a future release. -::: - ### Express.js Integration ```typescript @@ -575,7 +764,7 @@ import express from 'express'; import { LLMClient } from '@blockrun/llm'; const app = express(); -const client = new LLMClient({ privateKey: process.env.BLOCKRUN_WALLET_KEY }); +const client = new LLMClient({ privateKey: process.env.BASE_CHAIN_WALLET_KEY }); app.post('/chat', async (req, res) => { try { @@ -596,7 +785,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { LLMClient } from '@blockrun/llm'; const client = new LLMClient({ - privateKey: process.env.BLOCKRUN_WALLET_KEY as `0x${string}` + privateKey: process.env.BASE_CHAIN_WALLET_KEY as `0x${string}` }); export async function POST(request: NextRequest) { @@ -606,6 +795,17 @@ export async function POST(request: NextRequest) { } ``` +### Cost logging across sessions + +```typescript +import { getCostSummary } from '@blockrun/llm'; + +// Every paid call is appended to ~/.blockrun/data/costs.jsonl +const summary = getCostSummary(); +console.log(`Lifetime: $${summary.totalUsd.toFixed(2)} over ${summary.calls} calls`); +console.log(summary.byModel); +``` + ## Testing The SDK includes comprehensive test coverage. @@ -624,12 +824,12 @@ npm test -- --coverage # Run with coverage report Integration tests call the production API and require: - A funded Base wallet with USDC ($1+ recommended) -- `BLOCKRUN_WALLET_KEY` environment variable set +- `BASE_CHAIN_WALLET_KEY` environment variable set - Estimated cost: ~$0.05 per test run ```bash # Set your funded wallet key -export BLOCKRUN_WALLET_KEY=0x... +export BASE_CHAIN_WALLET_KEY=0x... # Run only integration tests npm test -- test/integration @@ -638,7 +838,7 @@ npm test -- test/integration npm test run ``` -Integration tests are automatically skipped if `BLOCKRUN_WALLET_KEY` is not set. +Integration tests are automatically skipped if `BASE_CHAIN_WALLET_KEY` is not set. ## Security Best Practices @@ -660,12 +860,13 @@ Never commit private keys to version control. A leaked key can drain your funded - Commit `.env` files to git - Share private keys in logs or error messages - Use your main wallet with large holdings +- Point `BLOCKRUN_HOME` at a directory you do not control — it redirects where the signing key is read and written ### Example Secure Setup ```bash # .env (add to .gitignore!) -BLOCKRUN_WALLET_KEY=0x...your_private_key_here +BASE_CHAIN_WALLET_KEY=0x...your_private_key_here ``` ```typescript @@ -675,12 +876,12 @@ import dotenv from 'dotenv'; dotenv.config(); -if (!process.env.BLOCKRUN_WALLET_KEY) { - throw new Error('BLOCKRUN_WALLET_KEY not set'); +if (!process.env.BASE_CHAIN_WALLET_KEY) { + throw new Error('BASE_CHAIN_WALLET_KEY not set'); } const client = new LLMClient({ - privateKey: process.env.BLOCKRUN_WALLET_KEY as `0x${string}` + privateKey: process.env.BASE_CHAIN_WALLET_KEY as `0x${string}` }); ``` @@ -691,7 +892,7 @@ The SDK validates all inputs before making API requests: - Private keys (format, length, valid hex) - API URLs (HTTPS required for production) - Model names (non-empty strings) -- Parameters (max\_tokens, temperature, top\_p ranges) +- Parameters (`temperature`, `top_p` ranges; `max_tokens` must be a positive integer — the SDK no longer caps it below what a model actually serves) ### Error Response Sanitization diff --git a/docs/sdks/xrpl.md b/docs/sdks/xrpl.md index b9a6d69..9e2e2bb 100644 --- a/docs/sdks/xrpl.md +++ b/docs/sdks/xrpl.md @@ -1,23 +1,25 @@ --- title: XRPL SDK (Python) — deprecated -description: The BlockRun XRPL SDK (pay-on-XRPL with RLUSD) is deprecated. Use Base or Solana for new integrations. +description: The BlockRun XRPL SDK (pay-on-XRPL with RLUSD) is deprecated and its gateway is offline. Use Base or Solana for new integrations. --- # XRPL SDK (Python) -:::danger{title="Deprecated — being sunset"} -The XRPL pay-on-XRPL SDK (`blockrun-llm-xrpl`, RLUSD settlement on the XRP Ledger) is being **sunset** and is no longer recommended for new integrations. Use the [Python SDK](python.md) or [TypeScript SDK](typescript.md) on **Base** or **Solana** instead — same models, same API, actively maintained. +:::danger{title="Deprecated — gateway offline"} +The XRPL pay-on-XRPL SDK (`blockrun-llm-xrpl`, RLUSD settlement on the XRP Ledger) has been **sunset**. As of 2026-08-29 the gateway it talks to, `https://xrpl.blockrun.ai/api`, no longer serves requests (`/v1/models` and `/v1/chat/completions` return HTTP 404), and the gateway repository [`BlockRunAI/blockrun-xrpl`](https://github.com/BlockRunAI/blockrun-xrpl) is archived on GitHub. The last SDK release is **0.2.0** (2026-06-25); there is no testnet mode — the gateway only ever ran against XRPL mainnet (`xrpl:0`). Calls made with this SDK will fail. -This page remains for reference for existing XRPL integrations. Read-only XRP/XRPL access via [Multi-chain RPC](../api-reference/multi-chain-rpc.md) is unaffected and stays supported. +Use the [Python SDK](python.md) or [TypeScript SDK](typescript.md) on **Base** or **Solana** instead — same models, same API, actively maintained. Read-only XRP/XRPL access via [Multi-chain RPC](../api-reference/multi-chain-rpc.md) (`xrp` network) is unaffected and stays supported. + +This page remains for reference for existing XRPL integrations. Everything below describes SDK 0.2.0 as shipped. ::: -The Python SDK for BlockRun on the XRP Ledger, using RLUSD for micropayments — pay per call, no API keys. +The Python SDK for BlockRun on the XRP Ledger, using RLUSD for micropayments — pay per call, no API keys. It only covered chat (`/v1/chat/completions`); image, video and music generation were always Base-chain-only. ::::steps :::step{title="Install"} ```bash -pip install blockrun-llm-xrpl +pip install blockrun-llm-xrpl # 0.2.0, Python 3.9+ ``` ::: @@ -30,7 +32,7 @@ response = client.chat("openai/gpt-5.5", "Hello!") print(response) ``` -That's it. The SDK handles x402 payment with RLUSD automatically. +The SDK handles x402 payment with RLUSD automatically. ::: :::: @@ -41,7 +43,8 @@ That's it. The SDK handles x402 payment with RLUSD automatically. | Variable | Description | |----------|-------------| -| `BLOCKRUN_XRPL_SEED` | Your XRPL wallet seed | +| `BLOCKRUN_XRPL_SEED` | Your XRPL wallet seed (required unless passed to the constructor) | +| `BLOCKRUN_CHAT_TIMEOUT` | Default request timeout in seconds (default `600`; reasoning models can need 200–300s+) | ### Client Options @@ -50,8 +53,9 @@ from blockrun_llm_xrpl import LLMClient client = LLMClient( seed="sEd...", # Wallet seed (or use env var) - api_url="https://xrpl.blockrun.ai/api", # Optional - timeout=60.0 # Request timeout in seconds + api_url="https://xrpl.blockrun.ai/api", # Optional (offline — see banner) + rpc_url="https://xrplcluster.com", # XRPL RPC used for balance reads + timeout=600.0 # Request timeout in seconds ) ``` @@ -70,7 +74,7 @@ Your seed never leaves your machine — it's only used for local signing. Never ## Methods -### `chat(model, prompt, **options)` +### `chat(model, message, system=None, max_tokens=1024, temperature=None)` Simple one-line chat interface. @@ -79,14 +83,14 @@ response = client.chat( "openai/gpt-5.5", "Explain quantum computing", system="You are a physics teacher.", # Optional system prompt - max_tokens=500, # Optional max output + max_tokens=500, # Optional max output (default 1024) temperature=0.7 # Optional temperature ) ``` **Returns:** `str` - The assistant's response text -### `chat_completion(model, messages, **options)` +### `chat_completion(model, messages, max_tokens=1024, temperature=None, top_p=None)` Full OpenAI-compatible chat completion. @@ -112,7 +116,7 @@ print(f"Tokens used: {result.usage.total_tokens}") ### `get_balance()` -Get your RLUSD balance. +Get your RLUSD balance (read from `rpc_url`, so it works even while the gateway is offline). ```python balance = client.get_balance() @@ -128,20 +132,23 @@ spending = client.get_spending() print(f"Spent ${spending['total_usd']:.4f} across {spending['calls']} calls") ``` -### `get_wallet_address()` +### `address` -Get the wallet address being used. +The wallet address being used (a property, not a method). ```python -address = client.get_wallet_address() -print(f"Paying from: {address}") +print(f"Paying from: {client.address}") ``` ## Smart Routing (ClawRouter) **Save up to 94% on LLM costs automatically.** -The `smart_chat()` method routes each request on [Router Core](https://github.com/BlockRunAI/router-core) — 15 weighted dimensions classify the request, capability constraints are applied as hard filters, and the surviving candidates are ranked on task affinity, cost, speed and reliability. Decisions run locally in <1ms — your prompts never leave your machine for routing, and no extra model call is made to decide. +The `smart_chat()` method routes each request with a port of ClawRouter's 14-dimension rule-based classifier — token count, code presence, reasoning markers, technical and creative vocabulary, agentic patterns and more. Decisions run locally in <1ms — your prompts never leave your machine for routing, and no extra model call is made to decide. + +:::note{title="Routing tables are frozen at 0.2.0"} +The model ids below are pinned inside SDK 0.2.0's `router.py` and were last synced in April 2026. Several (for example `moonshot/kimi-k2.5`, `xai/grok-4-1-fast-reasoning`, `google/gemini-3-pro-preview`, `nvidia/gpt-oss-120b`) are no longer in the live BlockRun catalog. They are documented here as shipped, not as recommendations. +::: ### Basic Usage @@ -150,11 +157,11 @@ from blockrun_llm_xrpl import LLMClient client = LLMClient() -# Let ClawRouter pick the best model automatically +# Let ClawRouter pick the model automatically result = client.smart_chat("What is 2+2?") print(result.response) # "4" -print(result.model) # "nvidia/step-3.7-flash" (free tier model) +print(result.model) # "moonshot/kimi-k2.5" (AUTO profile, SIMPLE tier) print(result.routing.tier) # "SIMPLE" print(result.routing.savings) # 0.94 (94% savings vs baseline) ``` @@ -163,7 +170,7 @@ print(result.routing.savings) # 0.94 (94% savings vs baseline) | Profile | Behavior | Best For | |---------|----------|----------| -| `"free"` | Always uses free NVIDIA models | Development, testing | +| `"free"` | Always uses free NVIDIA-hosted models | Development, testing | | `"eco"` | Maximizes cost savings | Bulk processing | | `"auto"` | Balances quality and cost (default) | Production workloads | | `"premium"` | Always uses top-tier models | Critical tasks | @@ -174,7 +181,7 @@ result = client.smart_chat( "Explain recursion", routing_profile="free" ) -print(result.model) # "openai/gpt-oss-120b" +print(result.model) # "nvidia/gpt-oss-120b" # Maximum savings mode result = client.smart_chat( @@ -192,14 +199,14 @@ print(result.model) # "anthropic/claude-opus-4.5" ### 4-Tier Model Selection -ClawRouter classifies prompts into four tiers: +ClawRouter classifies prompts into four tiers. Primary model per profile as pinned in 0.2.0: -| Tier | Models | Use Case | -|------|--------|----------| -| **SIMPLE** | Kimi K2.7, DeepSeek | Q&A, summaries, simple tasks | -| **MEDIUM** | Grok Code, GPT-4o | Analysis, writing, coding | -| **COMPLEX** | Gemini 3 Pro, Claude Opus | Advanced reasoning, research | -| **REASONING** | Grok 4.1, DeepSeek-R1 | Math, logic, proofs | +| Tier | `auto` | `eco` | `premium` | `free` | Use Case | +|------|--------|-------|-----------|--------|----------| +| **SIMPLE** | moonshot/kimi-k2.5 | moonshot/kimi-k2.5 | google/gemini-2.5-flash | nvidia/gpt-oss-120b | Q&A, summaries, simple tasks | +| **MEDIUM** | xai/grok-code-fast-1 | deepseek/deepseek-chat | openai/gpt-4o | nvidia/deepseek-v3.2 | Analysis, writing, coding | +| **COMPLEX** | google/gemini-3-pro-preview | xai/grok-4-0709 | anthropic/claude-opus-4.5 | nvidia/qwen3-next-80b-a3b-thinking | Advanced reasoning, research | +| **REASONING** | xai/grok-4-1-fast-reasoning | deepseek/deepseek-reasoner | openai/o3 | nvidia/qwen3-next-80b-a3b-thinking | Math, logic, proofs | ### Routing Decision Details @@ -211,6 +218,7 @@ routing = result.routing print(f"Model: {routing.model}") # "xai/grok-4-1-fast-reasoning" print(f"Tier: {routing.tier}") # "REASONING" print(f"Confidence: {routing.confidence}") # 0.97 +print(f"Method: {routing.method}") # "rules" print(f"Reasoning: {routing.reasoning}") # "Detected: math proof..." print(f"Estimated cost: ${routing.cost_estimate:.4f}") print(f"Baseline cost: ${routing.baseline_cost:.4f}") @@ -255,7 +263,7 @@ Get XRP for transaction fees (~1 XRP is plenty). ::: :::step{title="Set up a trust line"} -Set up a trust line to the RLUSD issuer. +Set up a trust line to the RLUSD issuer (`rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De`, exported as `blockrun_llm_xrpl.RLUSD_ISSUER`). ::: :::step{title="Acquire RLUSD"} @@ -289,6 +297,18 @@ if not os.getenv("BLOCKRUN_XRPL_SEED"): client = LLMClient() # Reads from environment ``` +### Balance Helpers + +Standalone helpers that read the ledger directly (no gateway involved): + +```python +from blockrun_llm_xrpl import get_xrp_balance, get_rlusd_balance, get_balances + +print(get_xrp_balance(client.address)) +print(get_rlusd_balance(client.address)) +print(get_balances(client.address)) # {"xrp": ..., "rlusd": ...} +``` + ## Async Client For async/await usage: @@ -340,15 +360,15 @@ class ChatResponse: created: int model: str choices: List[ChatChoice] - usage: ChatUsage + usage: Optional[ChatUsage] class ChatChoice: index: int message: ChatMessage - finish_reason: str + finish_reason: Optional[str] class ChatMessage: - role: str + role: Literal["system", "user", "assistant"] content: str class ChatUsage: @@ -361,25 +381,25 @@ class ChatUsage: ```python from blockrun_llm_xrpl import ( - RoutingProfile, # Literal["free", "eco", "auto", "premium"] - RoutingTier, # Literal["SIMPLE", "MEDIUM", "COMPLEX", "REASONING"] - RoutingDecision, # Full routing details - SmartChatResponse, # Response + model + routing + RoutingDecision, # model, tier, confidence, method, reasoning, cost_estimate, baseline_cost, savings + SmartChatResponse, # response, model, routing ) ``` +`routing_profile` is a plain string: `"free" | "eco" | "auto" | "premium"`; `tier` is `"SIMPLE" | "MEDIUM" | "COMPLEX" | "REASONING"`. + ## Available Models -All models from BlockRun Intelligence are available: +The XRPL gateway mirrored the main BlockRun catalog (last catalog sync in the gateway repo: 2026-06-06). Model ids were identical to the Base gateway's — see [Models Reference](../api-reference/models.md) for the live list and pricing. Ids current in the live catalog today include: | Provider | Models | |----------|--------| -| **OpenAI** | gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5, gpt-5.4, gpt-5.4-pro, gpt-5.3, gpt-5.2, gpt-5.4-mini, gpt-5-mini, gpt-5.4-nano, o1, o1-mini, o3, o3-mini | +| **OpenAI** | gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5, gpt-5.4, gpt-5.4-pro, gpt-5.3, gpt-5.2, gpt-5.4-mini, gpt-5-mini, gpt-5.4-nano, o1, o3, o3-mini | | **Anthropic** | claude-fable-5, claude-opus-5, claude-opus-4.8, claude-opus-4.7, claude-sonnet-5, claude-sonnet-4.6, claude-haiku-4.5 | | **Google** | gemini-3.1-pro, gemini-3-flash-preview, gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite | -| **xAI** | grok-4.1, grok-4, grok-3, grok-3-fast | -| **DeepSeek** | deepseek-chat, deepseek-reasoner | -| **NVIDIA** | gpt-oss-120b (FREE), step-3.7-flash (FREE) | +| **xAI** | grok-4.3, grok-4.5, grok-build-0.1 | +| **DeepSeek** | deepseek-chat, deepseek-reasoner, deepseek-v4-pro | +| **NVIDIA (FREE)** | step-3.7-flash, nemotron-3-nano-omni-30b-a3b-reasoning, nemotron-nano-9b-v2, nemotron-nano-12b-v2-vl, mistral-nemotron | See [Intelligence Pricing](../products/intelligence/pricing.md) for full pricing details. @@ -401,6 +421,7 @@ See [Intelligence Pricing](../products/intelligence/pricing.md) for full pricing - **PyPI**: [blockrun-llm-xrpl](https://pypi.org/project/blockrun-llm-xrpl/) - **GitHub**: [github.com/BlockRunAI/blockrun-llm-xrpl](https://github.com/BlockRunAI/blockrun-llm-xrpl) +- **Gateway (archived)**: [github.com/BlockRunAI/blockrun-xrpl](https://github.com/BlockRunAI/blockrun-xrpl) - **XRPL Explorer**: [xrpscan.com](https://xrpscan.com) ## What's next? diff --git a/docs/x402/endpoints.md b/docs/x402/endpoints.md index 01441f4..fcde875 100644 --- a/docs/x402/endpoints.md +++ b/docs/x402/endpoints.md @@ -22,10 +22,12 @@ The same paths are available on each gateway. The 402 response advertises which | `blockrun.ai` | Base mainnet — `eip155:8453` | USDC `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` | Per-call onchain | Default gateway. EIP-3009 / x402 v2. | | `sol.blockrun.ai` | Solana mainnet — `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` | USDC SPL `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` | Per-call onchain | Solana SPL transfers. | | `nano.blockrun.ai` | Polygon / Arbitrum / Optimism / Unichain via Circle Gateway | USDC | Batched (Nanopayments) | Gas-free, sub-cent floor, batched onchain settlement. Base buyers use `blockrun.ai` (native x402). See [the Nanopayments launch post](https://blockrun.ai/signal/nanopayments-mainnet-circle-gateway). | -| `xrpl.blockrun.ai` | XRP Ledger | RLUSD (Ripple-issued USD) | Per-call onchain | **Deprecated (being sunset)** — use Base or Solana for new integrations. See the [XRPL SDK notice](../sdks/xrpl.md). | +| `xrpl.blockrun.ai` | XRP Ledger | RLUSD (Ripple-issued USD) | — | **Sunset — the gateway no longer serves requests** (returns 404 as of 2026-08-29). Use Base or Solana. See the [XRPL SDK notice](../sdks/xrpl.md). | | `testnet.blockrun.ai` | Base Sepolia — `eip155:84532` | USDC `0x036CbD53842c5426634e7929541eC2318f3dCF7e` | Per-call onchain | Free test USDC. Smaller model catalog. | -All gateways implement [x402 v2](https://x402.org). Facilitators: Coinbase CDP for Base, native Solana verification, t54.ai for XRPL. +All live gateways implement [x402 v2](https://x402.org). Facilitators: Coinbase CDP for Base, native Solana verification, Circle Gateway for `nano.blockrun.ai`. + +Per-token chat (`/chat/completions`, `/messages`, `/responses`) is billed at provider cost with **no platform margin** — only the flat **$0.001 / request** transaction fee is added. Media generation and Live Search carry a 5% margin. Every price quoted below already includes the transaction fee. ## Discovery @@ -35,7 +37,7 @@ GET https://blockrun.ai/openapi.json # Full OpenAPI 3.1 spec (read by x4 GET https://blockrun.ai/api/v1/models # Live model catalog with pricing ``` -Replace `blockrun.ai` with `sol.blockrun.ai`, `nano.blockrun.ai`, `xrpl.blockrun.ai`, or `testnet.blockrun.ai` to discover the same endpoints scoped to a different network. +Replace `blockrun.ai` with `sol.blockrun.ai`, `nano.blockrun.ai`, or `testnet.blockrun.ai` to discover the same endpoints scoped to a different network. ## AI Model Gateway @@ -45,13 +47,18 @@ OpenAI-compatible. 72 models across chat, reasoning, coding, and vision — plus |---|---|---|---| | POST | `/api/v1/chat/completions` | OpenAI-compatible chat | Token-based, see `/api/v1/models` | | POST | `/api/v1/chat/{model}` | Chat completions with the model in the path | Token-based | -| POST | `/api/v1/messages` | Anthropic-compatible messages | Token-based | +| POST | `/api/v1/messages` | Anthropic-compatible messages (native passthrough — responses returned verbatim) | Token-based | +| POST | `/api/v1/responses` | OpenAI Responses API (stateless; `background:true` supported) | Token-based | +| GET | `/api/v1/responses/{id}` | Poll or attach (`?stream=true`) to a background response | Free (settled at create) | +| POST | `/api/v1/responses/{id}/cancel` | Cancel a background response | Free | +| POST | `/api/v1beta/models/{model}:generateContent` | Native Gemini `generateContent` passthrough | Token-based — allowlisted enterprise payers only | | POST | `/api/v1/images/generations` | Image generation (gpt-image-1/2, Nano Banana, CogView-4, Grok Imagine) | Per image, per size | | GET | `/api/v1/images/generations/{id}` | Async image poll for slow models (gpt-image-2, etc.) | Free | | POST | `/api/v1/images/image2image` | Image edit / inpainting + multi-image fusion (gpt-image-1/2, Nano Banana, Nano Banana Pro) | Per image | | POST | `/api/v1/videos/generations` | Video generation (Seedance, Grok Imagine Video, Sora 2) | Per second / token-metered, varies | | GET | `/api/v1/videos/generations/{id}` | Async video poll (settlement happens here on completion) | Free | | POST | `/api/v1/videos` | Standard multimodal `content[]` body — delegates to `/videos/generations` | Per second / token-metered | +| GET | `/api/v1/videos/{id}` | Poll a job created via `/api/v1/videos` | Free | | POST | `/api/v1/audio/speech` | Text-to-speech (ElevenLabs voices) | $0.05–$0.10 / 1k chars | | POST | `/api/v1/audio/generations` | Music generation (MiniMax Music) | $0.151 / track | | POST | `/api/v1/audio/sound-effects` | Sound-effect generation | $0.0535 / generation | @@ -62,6 +69,7 @@ OpenAI-compatible. 72 models across chat, reasoning, coding, and vision — plus | POST | `/api/v1/realface/enroll` | Finalize RealFace enrollment after H5 completes (uploads face + biometric match) | **$0.011 / enrollment** | | GET | `/api/v1/realface/status` | Poll the state of a RealFace enrollment group | Free (rate-limited) | | GET | `/api/v1/wallet/{address}/realfaces` | List a wallet's enrolled RealFaces | Free (rate-limited) | +| GET | `/api/v1/wallet/{address}/reconciliation` | Per-model token usage and spend for a wallet (30-day window) | Free (rate-limited) | Per-model pricing is published at `/api/v1/models` and embedded in every 402 response. @@ -88,6 +96,13 @@ Per-model pricing is published at `/api/v1/models` and embedded in every 402 res | POST | `/api/v1/phone/numbers/list` | List your rented numbers | $0.002 | | POST | `/api/v1/phone/numbers/release` | Release a rented number | Free | +## Wallet Funding + +| Method | Path | Purpose | Pricing | +|---|---|---|---| +| POST | `/api/v1/polymarket/fund` | Bridge USDC from Base into a Polymarket (Polygon) account — see [Polymarket Funding](../api-reference/polymarket-funding.md) | $0.011 fee + the deposit amount | +| POST | `/api/v1/onramp/token` | Mint a one-time Coinbase Onramp link for a Base wallet (the x402 signature is used as authentication only) | Free — Base USDC only | + ## Sandbox Compute Ephemeral, isolated Python sandboxes for agent code execution. @@ -115,7 +130,7 @@ Ephemeral, isolated Python sandboxes for agent code execution. | Method | Path | Purpose | Pricing | |---|---|---|---| -| GET/POST | `/api/v1/zerox/{path}` | 0x Swap + Gasless aggregation passthrough (price / quote / gasless) | **Free passthrough — no x402 payment** | +| GET/POST | `/api/v1/zerox/{path}` | 0x Swap + Gasless aggregation passthrough — `price`, `quote`, `swap/chains`, `gasless/price`, `gasless/quote`, `gasless/submit`, `gasless/status/{trade_hash}`, `gasless/approval-tokens`, `gasless/chains` | **Free passthrough — no x402 payment** | ## DeFi Data (DefiLlama) @@ -131,16 +146,16 @@ See [DefiLlama](../api-reference/defillama.md). ## Financial Data (Pyth-backed) -Real-time and historical prices. All `list` endpoints are free. **Crypto, FX, and commodity** price/history are also free; **stock** price/history (US and non-US) are **$0.0010/call**. +Real-time and historical prices. All `list` endpoints are free. **Crypto, FX, and commodity** price/history are also free; **stock** price/history (US and non-US) are **$0.002/call**. | Method | Path | Purpose | Pricing | |---|---|---|---| | GET | `/api/v1/usstock/list` | US tickers | Free | -| GET | `/api/v1/usstock/price/{symbol}` | US stock spot price | $0.0010 | -| GET | `/api/v1/usstock/history/{symbol}` | US stock OHLC | $0.0010 | +| GET | `/api/v1/usstock/price/{symbol}` | US stock spot price | $0.002 | +| GET | `/api/v1/usstock/history/{symbol}` | US stock OHLC | $0.002 | | GET | `/api/v1/stocks/{market}/list` | Non-US markets (HK, JP, ...) | Free | -| GET | `/api/v1/stocks/{market}/price/{symbol}` | Non-US stock price | $0.0010 | -| GET | `/api/v1/stocks/{market}/history/{symbol}` | Non-US OHLC | $0.0010 | +| GET | `/api/v1/stocks/{market}/price/{symbol}` | Non-US stock price | $0.002 | +| GET | `/api/v1/stocks/{market}/history/{symbol}` | Non-US OHLC | $0.002 | | GET | `/api/v1/crypto/list` | Crypto tickers | Free | | GET | `/api/v1/crypto/price/{symbol}` | Crypto spot | Free | | GET | `/api/v1/crypto/history/{symbol}` | Crypto OHLC | Free | @@ -176,6 +191,7 @@ Standard JSON-RPC 2.0 to 40 chains through one endpoint — no API key. EVM (`et | GET | `/api/v1/health/models` | Per-model availability | | GET | `/api/v1/health/regions` | Region/edge health | | GET | `/api/pricing` | Pricing info | +| GET | `/brand/numbers.json` | Published catalog counts (models, tools, chains, savings) | | GET | `/.well-known/x402` | x402 discovery (v1+v2) | | GET | `/openapi.json` | OpenAPI 3.1 spec | @@ -184,7 +200,7 @@ Standard JSON-RPC 2.0 to 40 chains through one endpoint — no API key. EVM (`et Every paid endpoint follows the same flow: 1. Client sends request without payment → server returns `HTTP 402` with `accepts[]` describing network, asset, amount, `payTo`, and resource URL -2. Client signs a payment authorization (EIP-3009 on EVM, SPL transfer on Solana, XRPL payment channel on `xrpl.blockrun.ai`) +2. Client signs a payment authorization (EIP-3009 on EVM, SPL transfer on Solana, a batched Circle Gateway authorization on `nano.blockrun.ai`) 3. Client retries with the `PAYMENT-SIGNATURE` header (x402 v2) 4. Server verifies via the network's facilitator, executes the API call, settles onchain 5. Server returns the response with `X-Payment-Receipt` (tx hash) diff --git a/docs/x402/how-it-works.md b/docs/x402/how-it-works.md index 7c96c69..4839d9f 100644 --- a/docs/x402/how-it-works.md +++ b/docs/x402/how-it-works.md @@ -61,10 +61,15 @@ Client Server When you make a request without payment, the server returns HTTP 402 with: -- **Price** - How much the request costs -- **Payment Address** - Where to send funds +- **Price** - How much the request costs (`amount` in micro-USDC, transaction fee included) +- **Payment Address** - Where to send funds (`payTo`) - **Asset** - Which token (USDC) -- **Network** - Which blockchain +- **Network** - Which blockchain (`eip155:8453` on Base, `solana:…` on the Solana gateway) +- **Validity** - `maxTimeoutSeconds` (300 on most endpoints; longer on async media jobs) + +The requirements are base64-encoded in three equivalent headers — `PAYMENT-REQUIRED` (x402 v2), `X-Payment-Required`, and `WWW-Authenticate: X402 requirements="…"` — and the JSON body repeats the price as `price.amount` in USD. + +On BlockRun the price for chat is the model's list rate — estimated input tokens plus 10% of `max_tokens` output — with no platform margin, plus a flat **$0.001 transaction fee** per paid call. Media generation (image, video, music, speech) and Live Search carry a 5% margin on top of their list rate, plus the same fee. ### Payment Authorization @@ -79,8 +84,10 @@ Benefits: The CDP (Coinbase Developer Platform) Facilitator verifies and settles payments: -1. **Verify** - Check the signature is valid -2. **Settle** - Execute the on-chain transfer +1. **Verify** - Check the signature is valid (before any upstream work; a rejection is a `402` with a machine-readable `code` — `PAYMENT_UNFUNDED`, `PAYMENT_BLOCKHASH_STALE`, `PAYMENT_REPLAY`, `PAYMENT_INVALID`) +2. **Settle** - Execute the on-chain transfer, after the request has been served + +Because verification precedes settlement, a rejected payment never costs anything, and a request that fails upstream is never settled. ## x402 v2 Payload @@ -91,22 +98,24 @@ The payment payload includes: "x402Version": 2, "resource": { "url": "https://blockrun.ai/api/v1/chat/completions", - "description": "GPT-4o API call", + "description": "GPT-5.5 API call (~17 input, 8192 max output tokens)", "mimeType": "application/json" }, "accepted": { "scheme": "exact", "network": "eip155:8453", - "amount": "1000", + "amount": "25685", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", - "payTo": "0x..." + "payTo": "0x...", + "maxTimeoutSeconds": 300, + "extra": {"name": "USD Coin", "version": "2"} }, "payload": { "signature": "0x...", "authorization": { "from": "0x...", "to": "0x...", - "value": "1000", + "value": "25685", "validAfter": "1234567890", "validBefore": "1234568190", "nonce": "0x..." @@ -115,6 +124,8 @@ The payment payload includes: } ``` +Send it base64-encoded in the `PAYMENT-SIGNATURE` header (`X-Payment` is accepted as an alias). The `accepted` block must match one `accepts[]` entry from the 402 byte-for-byte — amount, asset, network, and `payTo` — and `authorization.value` must equal `amount`. + ## Why x402? ### For Users diff --git a/docs/x402/payment-flow.md b/docs/x402/payment-flow.md index a70e7f6..753e4e8 100644 --- a/docs/x402/payment-flow.md +++ b/docs/x402/payment-flow.md @@ -36,15 +36,21 @@ Server returns payment requirements: ```http HTTP/1.1 402 Payment Required -X-Payment-Required: +Content-Type: application/json +Cache-Control: no-store, no-cache, must-revalidate, max-age=0 +PAYMENT-REQUIRED: +X-Payment-Required: +WWW-Authenticate: X402 requirements="" { "error": "Payment Required", - "price": {"amount": "0.001000", "currency": "USD"} + "message": "This endpoint requires x402 payment", + "price": {"amount": "0.025685", "currency": "USD"}, + "paymentInfo": {"network": "base", "asset": "USDC", "x402Version": 2} } ``` -The `X-Payment-Required` header contains: +The three headers carry the same base64 value. Decoded: ```json { @@ -52,16 +58,22 @@ The `X-Payment-Required` header contains: "accepts": [{ "scheme": "exact", "network": "eip155:8453", - "amount": "1000", + "amount": "25685", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", - "payTo": "0x..." + "payTo": "0x...", + "maxTimeoutSeconds": 300, + "extra": {"name": "USD Coin", "version": "2"} }], "resource": { "url": "https://blockrun.ai/api/v1/chat/completions", + "description": "GPT-5.4 API call (~17 input, 8192 max output tokens)", "mimeType": "application/json" - } + }, + "extensions": {"bazaar": {…}} } ``` + +`amount` is micro-USDC (6 decimals) and already includes the flat $0.001 transaction fee; `extra` is the EIP-712 domain to sign against. `resource.description` states the token estimate the quote was built from. ::: :::step{title="Sign Authorization"} @@ -111,7 +123,7 @@ Key points: - Private key never leaves the client - Authorization expires in 5 minutes (`validBefore`) - Clock skew tolerance of 10 minutes (`validAfter`) -- `value` is micro-USDC: `$0.003` → `"3000"` +- `value` is micro-USDC and must equal the `amount` you were quoted: `$0.025685` → `"25685"` - The header value is the **base64 of the JSON payment payload** shown in the next step (`btoa(JSON.stringify(payload))`) :::note{title="Paying on Solana"} @@ -140,16 +152,18 @@ The payment payload contains: "accepted": { "scheme": "exact", "network": "eip155:8453", - "amount": "1000", + "amount": "25685", "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", - "payTo": "0x..." + "payTo": "0x...", + "maxTimeoutSeconds": 300, + "extra": {"name": "USD Coin", "version": "2"} }, "payload": { "signature": "0x...", "authorization": { "from": "0x...", "to": "0x...", - "value": "1000", + "value": "25685", "validAfter": "1234567890", "validBefore": "1234568190", "nonce": "0x..." @@ -162,9 +176,12 @@ The payment payload contains: :::step{title="Verify, Execute, Settle"} The server: -1. **Verifies** the payment signature with the Facilitator -2. **Executes** the API request (calls the AI model) -3. **Settles** the payment on-chain +1. **Verifies** the payment signature with the Facilitator (up to three attempts on transient facilitator errors; a definitive rejection is returned at once as a `402` with a `code`) +2. **Claims the nonce** so the same authorization cannot be replayed into a second inference (`402` / `PAYMENT_REPLAY` otherwise) +3. **Executes** the API request (calls the AI model) +4. **Settles** the payment on-chain — after the response for non-streaming calls, after the final chunk for streaming calls. Async media jobs settle when the job completes. + +The successful response carries `PAYMENT-RESPONSE` (base64 JSON `{"success","transaction","network","payer"}`) and `X-Payment-Receipt: `. If the facilitator could not land the settlement in time, the response is still served and carries `X-Payment-Settled: false` — verification is the gate, settlement is bookkeeping. ``` Server Facilitator Blockchain @@ -216,39 +233,66 @@ const response = await client.chat('openai/gpt-5.4', 'Hello!'); ## Error Scenarios +Every verification failure is a `402` with `error: "Payment verification failed"` and a machine-readable `code`; nothing has been charged when you see one. + ### Insufficient Balance -If your wallet doesn't have enough USDC: +If your wallet doesn't have enough USDC, the on-chain simulation reverts: ```json { "error": "Payment verification failed", - "details": "Insufficient balance" + "code": "PAYMENT_UNFUNDED", + "message": "The payment authorization could not be executed on-chain. The usual cause is an insufficient USDC balance on Base for the quoted amount — …", + "debug": "", + "payer": "0x…" } ``` -### Expired Authorization +An authorization whose `validAfter`/`validBefore` window has already closed (or not yet opened) reverts the same way, so check your clock before topping up. + +### Replayed Authorization -If too much time passed between signing and settling: +Each nonce is single-use. Sending the same signed payload twice: + +```json +{ + "error": "Payment authorization already used", + "message": "Sign a fresh payment authorization for each request.", + "code": "PAYMENT_REPLAY", + "payer": "0x…" +} +``` + +(On image endpoints, replaying the header of a response you lost returns the job you already paid for instead of a second charge.) + +### Stale Solana Blockhash + +A Solana-signed transaction is pinned to a recent blockhash and stays valid for roughly a minute: ```json { "error": "Payment verification failed", - "details": "Authorization expired" + "code": "PAYMENT_BLOCKHASH_STALE", + "message": "The payment transaction was signed against a Solana blockhash that has since expired. Nothing was charged. Sign a fresh payment authorization against a current blockhash and send the request again." } ``` ### Invalid Signature -If the signature doesn't match: +Anything else — a signature that doesn't match, the wrong network or asset, a malformed payload: ```json { "error": "Payment verification failed", - "details": "Invalid signature" + "code": "PAYMENT_INVALID", + "message": "Message @bc1max on Telegram for help.", + "debug": "" } ``` +The Anthropic-compatible `/v1/messages` and `/v1/responses` endpoints report the same failures inside their vendor error envelopes (`"Payment verification failed: …"`) without the `code` field. + ## Timing Typical payment flow timing: @@ -257,12 +301,12 @@ Typical payment flow timing: |------|------| | Initial 402 response | ~50ms | | Client signing | ~10ms | -| Signature verification | ~100ms | +| Signature verification | ~100ms (facilitator round-trip; retried up to 3× on transient errors) | | AI model execution | 1-30s (varies) | -| On-chain settlement | ~2s | +| On-chain settlement | ~2s, after the response is ready | | **Total overhead** | **~200ms** | -The payment overhead is minimal compared to AI model execution time. +The payment overhead is minimal compared to AI model execution time. The authorization is valid for 300s (`maxTimeoutSeconds`), so the whole exchange — including a long stream — has to settle inside that window; the gateway caps a single streamed response at 500s and each upstream call at 120s. ## What's next? diff --git a/docs/x402/security.md b/docs/x402/security.md index b22895d..cbc4ad1 100644 --- a/docs/x402/security.md +++ b/docs/x402/security.md @@ -66,8 +66,9 @@ Signatures use [EIP-712](https://eips.ethereum.org/EIPS/eip-712) typed structure | Overpayment | Exact amount specified in signature | | Replay attacks | Unique nonce per transaction | | Cross-chain replay | Chain ID in signature domain | -| Stale authorizations | Time-bounded validity window | +| Stale authorizations | Time-bounded validity window (`maxTimeoutSeconds`, 300s on most endpoints) | | Man-in-the-middle | Cryptographic signature verification | +| Spoofed resource URLs | The `resource.url` in a 402 is built from BlockRun's canonical origin, never from the caller's `Host` header | ### What You Must Protect @@ -76,6 +77,7 @@ Signatures use [EIP-712](https://eips.ethereum.org/EIPS/eip-712) typed structure | Private key security | Use secure storage (env vars, secret managers) | | Wallet balance | Only fund wallets with needed amounts | | Network security | Use HTTPS endpoints | +| Signing what you were quoted | Sign exactly the `accepts[]` entry from the 402 — amount, asset, network, `payTo` — and never a value a third party hands you | ## Best Practices @@ -137,11 +139,11 @@ No. BlockRun can only claim the exact amount you authorized for a specific reque ### What if I sign but the request fails? -If the AI request fails, the payment is not settled. You only pay for successful requests. +Verification happens first and settlement only after the upstream response, so a request that fails upstream (`4xx`, `5xx`, `504` timeout) is never settled — you only pay for successful requests. Async media jobs are settled when the job completes, not when it is submitted. The one thing you cannot get back is tokens the model actually generated: the settled amount for chat is the quoted amount (estimated input + 10% of `max_tokens`), whether the model used all of it or not. ### Can someone replay my payment? -No. Each payment has a unique nonce that can only be used once. +No. Each payment has a unique nonce that can only be used once — the gateway claims it before running inference, and a second use is refused with `402` / `PAYMENT_REPLAY`. Cross-gateway replay is impossible too: the signed EIP-712 domain includes the chain id and USDC contract, and the payload names the exact `payTo` treasury. ### What if BlockRun's servers are compromised?