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 28, 2026 at 15:38
Detected SKILL.md language
English
Stars
5
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.2"}
# 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 a markup that falls with volume, with a small per-call floor. The numbers live in one place, the operator's price list: read `tiers`, `min_charge_sats` and `byok_fee_bps` from `GET /v1/models` (or `/.well-known/x402-info`) rather than assuming them (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 | | `GET /v1/rate` | none | BSV/USD rate and `min_deposit_sats` | | `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 | | `POST /v1/images/generations` | bearer | OpenAI image generation; `"model": "auto"` picks by Image Arena Elo | | `GET /v1/batches` | bearer | your recent batches | | `POST /v1/batches` | bearer | up to 500 requests within 24 hours at the batch price | | `GET /v1/batches/{id}` | bearer | batch status, and `results` once finished | | `POST /v1/evaluate` | bearer | typed classify/score/boolean questions over a `state` | | `GET /v1/account` | bearer | credits, tier, paymail and BYOK status | | `POST /v1/deposit` `{"sats": N}` | bearer | top up; always answers 402 until paid | | `GET /v1/credits/packs` | none | card credit packs | | `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/credits/checkout/:id` | bearer | status of one card purchase | | `GET /v1/account/usage` | bearer | recent requests and ledger entries (`?limit=` up to 1000, default 50) | | `GET /v1/account/dashboard` | bearer | usage dashboard data (the site's usage page) | | `PUT /v1/account/paymail` `{"handle": "name"}` | bearer | claim a human paymail handle at `@bitplan.dev` (409 when taken) | | `DELETE /v1/account/paymail` | bearer | release the handle; the automatic three-word 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; `cloudflare/@cf/author/model` routes to Cloudflare Workers AI (listed on `/v1/models` when the operator has it configured); `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. A request with no `model` runs on the Jev Model Router (`bitplan/auto`), which picks the model per request; name a model only when you need a specific one. ## 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, follow "When a call answers 402" below: fund the account, then call chat again with the same arguments. 5. Report the model's answer and the settled cost from the chat result (`charge_sats`) or GET /v1/account/usage. ## When a call answers 402 (payment_required) A 402 means the account's credits do not cover the call's reserve. Nothing was charged, and billing is never a reason to stop: the job is to get the account funded and run the same call again. Rules: - Never treat a 402 as the task failing, and never say the task is done. Never work around it: do not pick a cheaper model, cut the prompt, lower `max_tokens`, skip the step or quietly downgrade anything. Fund, then repeat the request exactly as it was. - Never fail silently. Every 402 ends in either a paid retry or a message to the person that says what is short and how to fund it. Do these in order and stop at the first that works: 1. Pay it yourself when a wallet is available: `bitplan gateway deposit <sats>` (the connected BRC-100 wallet pays; the wallet asks the person to approve) or, with a WIF, pay `accepts[0].amount` sats to `payTo` and repeat the request with the `PAYMENT-SIGNATURE` header. The 402's `shortfall_sats` is the least that unblocks the call. 2. If there is no wallet, or the wallet cannot pay (its own insufficient funds error), tell the person exactly, using the 402's own words (`error.message`, `balance_sats`, `shortfall_sats`, `fund.paymail`, `fund.card`): "This call needs N more sats. Send at least N sats to `<fund.paymail>` from any BSV wallet (HandCash, Yours, RelayX, ...), or buy credits by card at `<fund.card>` signed in with this key, or add BSV to the wallet and I will pay from it." Then wait: poll `GET /v1/account` every 15 to 30 seconds until `balance_sats` covers the reserve (a large deposit may sit in `pending_confirmation_sats` for one block). Then repeat the original request unchanged. 3. An anonymous call (no bearer) has no account to fund by paymail: pay the challenge from a wallet, or get a bearer first (step 1 of the quick start) and retry so the account can be funded by paymail or card. Unused reserve always comes back as credits, so paying the exact shortfall is never money lost. ## 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 deposit floor is live: `min_deposit_sats` on `GET /v1/rate` and `/.well-known/x402-info`. `POST /v1/deposit` with no `sats` asks for that floor; a larger `sats` value is accepted. A 402 asks for exactly the shortfall (at least `min_charge_sats` from `/v1/models`); send `x-gateway-deposit: minimum` to be asked for a top-up of at least the minimum deposit instead. 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 # N is at least min_deposit_sats from GET /v1/rate (or omit sats to be asked for that floor) CHALLENGE=$(curl -s https://gateway.bitplan.dev/v1/deposit \ -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \ -d "{\"sats\":$N}") # --x402 needs the full 402 body (accepts[0]); challenge-only is the legacy proof SIG=$(printf '%s' "$CHALLENGE" | bun "$SKILL_DIR/scripts/pay.ts" --wif "$WIF" --x402) curl -s https://gateway.bitplan.dev/v1/deposit \ -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \ -H "PAYMENT-SIGNATURE: $SIG" -d "{\"sats\":$N}" ``` 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. ## Batch: about half price, within 24 hours `POST /v1/batches` runs up to 500 requests within 24 hours at the model's batch price, about half of list (`pricing.batch` on `GET /v1/models` says which models and how much). Offer it when the person has many prompts and no rush. Send `endpoint` (`/v1/chat/completions`, `/v1/responses` or `/v1/messages`), `model` and `requests` as `{custom_id, body}` items inline: this is OpenRouter's inline batch shape, not OpenAI's file-based batches (no `input_file_id` or JSONL upload). ```sh curl -X POST https://gateway.bitplan.dev/v1/batches \ -H "Authorization: Bearer $TOKEN" -H "content-type: application/json" \ -d '{"endpoint":"/v1/chat/completions","model":"openai/gpt-5.6-luna", "requests":[{"custom_id":"a","body":{"messages":[{"role":"user","content":"..."}]}}]}' ``` The answer is `202` with the batch (`id`, `status`, `request_counts`, `hold_sats`). The whole batch is held once for its worst case, each request is capped at the output tokens it was held for, and the batch settles once on the usage upstream reports. A `402` here is paid like any other call: pay it and repeat the request with `PAYMENT-SIGNATURE`. `GET /v1/batches` lists your batches; poll `GET /v1/batches/<id>` (every few minutes; a batch normally finishes well under 24 hours): when `status` is `completed`, `results` lists `{custom_id, response: {status_code, body}, error}` per request and `charge_sats` is what it settled at. Text only: no images, audio or files. ## 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@bitplan.dev", "paymail_auto": "word-word-word@bitplan.dev", ... } ``` `paymail_auto` is the automatic alias, three words at `@bitplan.dev`; `paymail` is the claimed handle when there is one (`name@bitplan.dev`), 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,
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub