Skip to main content

gateway

Use gateway.bitplan.dev, an OpenAI-compatible inference API paid in BSV with no API keys. Use when asked to call a model through the gateway, mint a gateway token, deposit BSV to the gateway, check a gateway balance or usage, pick a model by price, or explain how gateway.bitplan.dev works.

Source facts

Repository
opldotdev/bitplan.dev
Last source activity
September 17, 2026 at 14:57
Detected SKILL.md language
English
Stars
3
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
5 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
gateway
description
Use gateway.bitplan.dev, an OpenAI-compatible inference API paid in BSV with no API keys. Use when asked to call a model through the gateway, mint a gateway token, deposit BSV to the gateway, check a gateway balance or usage, pick a model by price, or explain how gateway.bitplan.dev works.
metadata
{"version":"0.1.0"}
# gateway.bitplan.dev An OpenAI chat completions endpoint in front of every model on Vercel AI Gateway and OpenRouter. Identity is a Bitcoin key. Deposits arrive over HTTP 402. Each call is deducted from the balance at provider cost plus 20% to start, 10% after $100 a month of provider cost and 7% after $1,000 (see Prices). Base URL: `https://gateway.bitplan.dev`. Discovery: `GET /.well-known/x402-info`. | Endpoint | Auth | Purpose | | --- | --- | --- | | `GET /v1/models` | none | model list with prices | | `POST /v1/chat/completions` | bearer | OpenAI chat completions, JSON or `stream: true` | | `POST /v1/messages` | bearer | Anthropic Messages API over the same metering: `ANTHROPIC_BASE_URL=https://gateway.bitplan.dev ANTHROPIC_AUTH_TOKEN=<bearer>` makes Claude Code and Anthropic SDKs work; bare `claude-*` ids map to `anthropic/<id>` when listed | | `POST /v1/messages/count_tokens` | none | Anthropic token count, a free bytes/4 estimate | | `POST /v1/responses` | bearer | OpenAI Responses API over the same metering, for Codex CLI: `[model_providers.bitplan]` with `base_url = "https://gateway.bitplan.dev/v1"` and `env_key = "BITPLAN_TOKEN"`; stateless (`previous_response_id` is a 400), function tools only | | `GET /v1/account` | bearer | balance in sats | | `POST /v1/deposit` `{"sats": N}` | bearer | top up; always answers 402 until paid | | `POST /v1/credits/checkout` `{"usd": 5}` | bearer | buy credits by card: returns a Stripe Checkout URL for a person to open; poll `GET /v1/credits/checkout/:id` until `credited` | | `GET /v1/account/usage` | bearer | recent requests and ledger entries (`?limit=` up to 1000, default 50) | | `PUT /v1/account/paymail` `{"handle": "name"}` | bearer | claim a human paymail handle (409 when taken) | | `DELETE /v1/account/paymail` | bearer | release the handle; the automatic alias remains | | `PUT /v1/account/byok` | bearer | store your own key for one provider, encrypted | | `DELETE /v1/account/byok` | bearer | forget the stored key for one provider | Model ids: `vendor/model` routes to Vercel AI Gateway (for example `anthropic/claude-opus-4.8`, `spacexai/grok-4.6`); `openrouter/vendor/model` routes to OpenRouter; `opencode/<model>` and `opencode-go/<model>` route to OpenCode Zen and OpenCode Go and need your own key (see Bring your own key). Only ids returned by `/v1/models` are accepted. ## Quick start for an agent Use gateway.bitplan.dev for AI inference. It is an OpenAI-compatible API paid in BSV. There are no API keys. 1. Get a token from the wallet, in this order, and stop at the first that works: - `bitplan gateway token` (the `bitplan` CLI, if installed, asks the connected BRC-100 wallet to sign a 24-hour bearer; nothing to configure). - The `BITPLAN_GATEWAY_TOKEN` environment variable, if set. - With a WIF in `$WIF` only: `bun "$SKILL_DIR/scripts/token.ts" --wif "$WIF"` (see below). Pass the token as the `token` argument to MCP tools or as `Authorization: Bearer <token>` on HTTP. 2. Add the MCP server https://mcp.bitplan.dev (Streamable HTTP, no auth). If you cannot use MCP, call POST https://gateway.bitplan.dev/v1/chat/completions with an OpenAI-format body. 3. Call list_models (or GET /v1/models) and pick a starred model. Prices are per million tokens in BSV. 4. Call chat with the token. Leave `max_tokens` at the default (8192) or send 4096 or more: you pay only the tokens used, and reasoning models spend the first part of the cap thinking before any text appears. If the result says payment_required, the account has no credits for the call's reserve: fund it, then call chat again with the same arguments. Ways to fund, in order: `bitplan gateway deposit <sats>` (wallet pays); tell the person the paymail from `bitplan gateway credits` / GET /v1/account and wait for credits; or, with a WIF, pay challenge.amount_sats to challenge.payee_address and retry with proof = base64url of {"version":"bsv-tx-v1","challenge_id":"…","rawtx_base64":"…","txid":"…"}. Unused reserve stays as credits. 5. Report the model's answer and the settled cost from the chat result (`charge_sats`) or GET /v1/account/usage. ## Identity: a self-signed bearer token There is nothing to register. The client signs a token with its own key and sends `Authorization: Bearer <token>`. Token format is the `bitcoin-auth` format `pubkey|scheme|timestamp|requestPath|signature`. - **Session token**: `requestPath` is the literal `https://gateway.bitplan.dev/v1`; valid 24 hours for every `/v1/*` route. This is the "API key" for tools that only accept a static bearer. - **Per-request token**: `requestPath` is the request path (for example `/v1/account`); valid 5 minutes; the body hash is bound when a body is sent. Schemes: `brc77` (signature with a raw private key) or `brc100` (a BRC-100 wallet's `createSignature` with `protocolID [1, "bitcoin auth"]`, `keyID = timestamp`, `counterparty "anyone"`; no key export). Prefer the wallet: `bitplan gateway token` (npm `bitplan`) signs the token with the connected BRC-100 wallet and prints it. Only when you hold a raw WIF and no wallet, mint one with the bundled script. Install its two dependencies once, then run it from anywhere: ```sh (cd "$SKILL_DIR/scripts" && bun install --silent) TOKEN=$(bun "$SKILL_DIR/scripts/token.ts" --wif "$WIF") ``` Never print, log, or send the WIF anywhere. The token is safe to hold for its lifetime; anyone holding it can spend the balance until it expires. ## Deposit over HTTP 402 Any authenticated call that the balance cannot cover, and every call to `POST /v1/deposit`, answers `402` with a challenge: ```json { "error": { "type": "payment_required", "message": "..." }, "challenge": { "version": "bsv-tx-v1", "challenge_id": "<uuid>", "amount_sats": 25000000, "payee_locking_script_hex": "76a914...88ac", "payee_address": "1...", "expires_at": "2026-09-16T12:34:56.000Z", "pay_url": "https://gateway.bitplan.dev/v1/deposit" } } ``` The minimum deposit is 0.25 BSV (25,000,000 sats); `POST /v1/deposit` accepts a larger `sats` value. Pay `amount_sats` to `payee_address` in a transaction the gateway will broadcast, then send the **identical** request again with the x402 header `PAYMENT-SIGNATURE: base64({"x402Version":2,"accepted":<the accepts[0] object from the 402>,"payload":{"transaction":"<signed raw tx, base64>"}})`. The 402 is x402 protocol version 2: the `PAYMENT-REQUIRED` response header (and the body) carry `{"x402Version":2,"resource":{"url"},"accepts":[{"scheme":"exact","network":"bip122:000000000019d6689c085ae165831e93","amount":"<sats>","asset":"BSV","payTo":"<address>","maxTimeoutSeconds","extra":{"challengeId","lockingScript"}}]}`, and a paid response carries `PAYMENT-RESPONSE` with `{"success":true,"transaction":"<txid>","network","payer","amount"}`. The legacy header `X402-Proof: base64url({"version":"bsv-tx-v1","challenge_id","rawtx_base64","txid"})` still works. The challenge is bound to that exact request (method, path, body) and to the signing key, and expires after 15 minutes. With a WIF, the bundled script builds the transaction from the key's UTXOs and prints the header value without broadcasting: ```sh CHALLENGE=$(curl -s https://gateway.bitplan.dev/v1/deposit \ -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \ -d '{"sats":25000000}') PROOF=$(printf '%s' "$CHALLENGE" | bun "$SKILL_DIR/scripts/pay.ts" --wif "$WIF") curl -s https://gateway.bitplan.dev/v1/deposit \ -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \ -H "X402-Proof: $PROOF" -d '{"sats":25000000}' # legacy header; PAYMENT-SIGNATURE is the x402 form ``` The response reports `credited: true` when the balance is usable now, or `false` when the deposit waits for one block confirmation (the gateway caps how much unconfirmed value it credits at once; check `GET /v1/account` for `pending_confirmation_sats`). With a BRC-100 wallet and the `bsv-mcp` x402 tools: call `x402_request` with the gateway URL, the bearer in `headers`, and `auth: "none"`; it returns a quote for the `bsv-tx-v1` challenge; approve it with `x402_payQuote`. Do not pay a challenge twice. If a paid request fails, check `GET /v1/account/usage` and the txid before doing anything else. ## Fund by paymail Every account is also a paymail address, so a person (or any paymail wallet: HandCash, Yours, RelayX, Panda, ...) can add credits without touching the API. `GET /v1/account` reports it: ```json { "paymail": "satchmo@gateway.bitplan.dev", "paymail_auto": "02a1b2c3d4e5f60718@gateway.bitplan.dev", ... } ``` `paymail_auto` is the automatic alias, the first 16 hex characters of the identity key; `paymail` is the claimed handle when there is one, else the alias. A 402 for an authenticated caller also carries `fund: { paymail, note }`. The flow for an agent that is out of credits: 1. Read `paymail` from `GET /v1/account` (or `fund.paymail` from the 402) and tell the person: "send at least N sats to `<paymail>`". 2. Poll `GET /v1/account` until `balance_sats` covers the call (or `pending_confirmation_sats` shows the deposit waiting for a block: a large deposit is credited after one confirmation). 3. Retry the original request with the same bearer. A P2P send (the wallet delivers the transaction to the gateway) is credited within seconds; a basic paymail send is found by the gateway's on-chain scan within about ten minutes. No proof header is involved: paymail deposits credit the account directly. Claim a human handle once (3 to 20 characters of `a-z`, `0-9` and hyphen, not starting or ending with a hyphen, one per identity): ```sh curl -X PUT https://gateway.bitplan.dev/v1/account/paymail \ -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \ -d '{"handle":"satchmo"}' # 409 handle_taken when someone has it curl -X DELETE https://gateway.bitplan.dev/v1/account/paymail \ -H "Authorization: Bearer $TOKEN" # 204; the automatic alias remains ``` The bsvalias service itself is discoverable at `https://gateway.bitplan.dev/.well-known/bsvalias` (`paymail_domain` in `/.well-known/x402-info`), for wallets and tooling that resolve paymails. ## MCP server `https://mcp.bitplan.dev` is a Streamable HTTP MCP server (2026-07-28 spec) with the same capabilities: tools `list_models`, `chat`, `generate_image`, `credits`, `deposit`. Add it to a host as a bare URL. A bearer can be sent as the MCP request's `Authorization` header or as the `token` argument; with neither, `chat` and `generate_image` return `payment_required: true` with a challenge for exactly the call's reserve. Pay it from any wallet with a P2PKH input and call again with `proof`; the paying key becomes the account and the unused reserve stays there as credits. The same anonymous flow works on the HTTP API: a request with no `Authorization` header gets a 402 for exactly its reserve. ## Call a model ```sh curl https://gateway.bitplan.dev/v1/chat/completions \ -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \ -d '{"model":"spacexai/grok-4.6","messages":[{"role":"user","content":"hello"}],"max_tokens":4096}' ``` Any OpenAI SDK works: set `baseURL` to `https://gateway.bitplan.dev/v1` and `apiKey` to the session token. Streaming with `stream: true` returns standard SSE. Any Anthropic SDK works too, through `POST /v1/messages`: set `ANTHROPIC_BASE_URL=https://gateway.bitplan.dev` and `ANTHROPIC_AUTH_TOKEN=<session token>` (sent as `Authorization: Bearer`). For Claude Code add `ANTHROPIC_MODEL=anthropic/claude-fable-5.1` (or pin `ANTHROPIC_DEFAULT_SONNET_MODEL` / `_OPUS_MODEL` / `_HAIKU_MODEL` to catalog ids) and run `claude`. Errors are `{"type":"error","error":{"type","message"}}`; a 402 keeps the `challenge` body with `type: "error"` alongside, so pay it as usual and retry. OpenAI Codex CLI works through `POST /v1/responses` (the Responses API, the only wire format Codex custom providers speak). In `~/.codex/config.toml`: ```toml [model_providers.bitplan] name = "BitPlan Gateway" base_url = "https://gateway.bitplan.dev/v1" env_key = "BITPLAN_TOKEN" ``` and in `~/.codex/bitplan.config.toml` (before Codex 0.134: the same keys under `[profiles.bitplan]` in `config.toml`): ```toml model_provider = "bitplan" model = "anthropic/claude-fable-5.1" web_search = "disabled" ``` Then `export BITPLAN_TOKEN=$(bitplan gateway token)` and `codex --profile bitplan`. `env_key` is sent as `Authorization: Bearer`; no extra headers are needed. Use catalog ids from `/v1/models`. The gateway stores nothing, so `previous_response_id` answers 400 and history travels in `input`; hosted tools (`web_search`, `file_search`, `computer`) answer 400 naming the tool; `reasoning`, `include` and `store` are dropped. Errors are OpenAI-shaped `{"error":{"message","type","param","code"}}`; a 402 keeps the `challenge` body. Response headers: `x-gateway-request-id`, and on JSON responses `x-gateway-charge-sats` and `x-gateway-balance-sats`. Send `max_tokens` of 4096 or more (default 8192). The gateway holds funds before each call sized by the prompt's tokens plus `max_tokens`, and settles to the real usage afterwards: a small `max_tokens` saves nothing, it only shrinks the hold. On a reasoning model (`reasoning: true` in `/v1/models`,
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub