# Burnbound documentation > Burnbound is a buyer-side spending control plane for AI agents that pay x402 APIs. Before an agent pays, Burnbound checks the payment against rules a human set (allowed hosts, per-payment and daily caps, human approval above a threshold); only allowed payments are signed, by a wallet the customer brings. Burnbound never holds private keys or funds. --- Source: https://app.burnbound.dev/docs/quickstart # Quickstart: Claude Code and Cursor This guide takes you from a new Burnbound account to your agent's first `fetch_paid` call. You create an agent with spending rules, connect the wallet that signs its payments, add the Burnbound MCP server to Claude Code or Cursor, and make a first request. ## What do I need before I start? You need Node.js 20 or later (with `npx` on the `PATH` of your MCP client), Claude Code or Cursor, and a Burnbound account. To pay for something, you also need a wallet with a small USDC balance on Base. Burnbound never holds funds: payments come from your own wallet. - Node.js 20 or later: check with `node --version`. - An MCP client: Claude Code or Cursor. Any other MCP client that runs stdio servers works the same way. - A Burnbound account: create one at [app.burnbound.dev](https://app.burnbound.dev/register). ## Step 1: How do I create an agent and its key? Create the agent in the Burnbound dashboard; the onboarding starts right after you register. An agent has a name, a daily cap, a maximum per payment and a list of allowed hosts. Keep the caps small at first: x402 calls usually cost cents. 1. Open the onboarding in the dashboard and create the agent. 2. Set the daily cap, the maximum per payment and the hosts the agent may pay, for example `api.example.com` or `*.example.com`. 3. Continue to the wallet step (Step 2). The key is created in the step after it. The agent key starts with `bb_agent_`. The dashboard shows it **only once**, inside a ready-to-paste install command. Burnbound stores only a hash of it, so a lost key cannot be shown again: revoke it and create a new one. ## Step 2: How do I connect the wallet that signs? Every agent needs a wallet that signs its payments. Burnbound decides whether a payment is allowed; the wallet signs it. You choose one of two modes in the dashboard. See [Concepts](https://app.burnbound.dev/docs/concepts#which-signing-modes-are-there) for the trade-offs. **Coinbase CDP.** Paste your Coinbase Developer Platform credentials (API key ID, API key secret, wallet secret) and the wallet address. The private key stays with Coinbase. **Wallet on your machine (client-side signing).** Create the wallet locally and register only its public address: ```bash npx -y @burnbound/mcp wallet init ``` The command prints the address. Paste it in the dashboard. The private key stays in your OS keychain (or a file only your user can read) and is never sent to Burnbound. Then fund the wallet with a **small** USDC balance on Base. It needs no ETH: in x402 the seller's facilitator submits the transfer and pays the gas. ## Step 3: How do I add Burnbound to Claude Code? Run `claude mcp add` with your agent key in `BURNBOUND_KEY`. The dashboard shows this exact line with your key already filled in; copy it from there. ```bash claude mcp add burnbound -e BURNBOUND_KEY=bb_agent_… -- npx -y @burnbound/mcp ``` Check that the server is registered: ```bash claude mcp list ``` ## How do I add Burnbound to Cursor? Add the server to `.cursor/mcp.json` in your project, or to `~/.cursor/mcp.json` for every project, with your agent key in `env`: ```json { "mcpServers": { "burnbound": { "command": "npx", "args": ["-y", "@burnbound/mcp"], "env": { "BURNBOUND_KEY": "bb_agent_…" } } } } ``` Do not commit a file that contains the key. Restart Cursor or reload its MCP servers after you edit the file. ## Step 4: How do I check that the agent is connected? Ask your agent: "What is my Burnbound budget?". It calls `get_budget`, which returns what the agent spent today, its daily cap, what remains, the maximum per payment and its allowed hosts. If that works, the key and the connection to Burnbound are fine. If the server does not start, read its log on stderr (in Claude Code, `claude mcp list` shows it as failed): - `BURNBOUND_KEY is required.`: the key is missing from the MCP config. - `BURNBOUND_KEY has an invalid format.`: the value was pasted with extra characters. If it starts but every tool answers `unauthorized`, the key was revoked or mistyped. Create a new one in the dashboard. ## Step 5: How do I make the first paid request? Ask the agent to fetch a URL of an x402 API on one of its allowed hosts, with a small `maxAmountUsd`. The agent calls `fetch_paid`; if the URL answers HTTP 402, Burnbound checks the payment against your rules, the wallet signs it, and the agent gets the paid response. A prompt like this is enough: ```text Use fetch_paid to GET https://api.example.com/v1/report with maxAmountUsd "0.05". ``` The result tells you what happened: - `paid`: the payment was signed and sent. It includes `amountUsd`, `intentId` and a `receipt`. The payment appears in the dashboard. - `ok`: the URL did not ask for payment, so nothing was paid. - `pending_approval`: the amount is at or above the agent's approval threshold. A human approves it in the dashboard, then the agent retries (see [Concepts](https://app.burnbound.dev/docs/concepts#how-do-human-approvals-work)). - A tool error with a code, such as `policy_denied` or `host_not_allowed`: nothing was paid. A denial is also a good first test: it proves the rules are enforced. Denials are listed in the dashboard audit with their reason. ## What can go wrong on the first call? Most first-call errors come from the agent's rules or its wallet, and every one means nothing was paid. The table lists the common ones; the [MCP tools reference](https://app.burnbound.dev/docs/mcp-tools#what-errors-can-a-tool-return) has the full list. | Error | What to do | | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `host_not_allowed` | Add the URL's host to the agent's allowed hosts in the dashboard. Nothing was requested. | | `policy_denied` with `daily_cap_exceeded` | The payment would go over today's cap (UTC day). Raise the cap or wait for the next day. | | `policy_denied` with `amount_exceeds_per_transaction` | The price is over the maximum per payment. Raise it if you trust the seller. | | `policy_denied` with `network_not_allowed` | The seller asked for a network the agent's policy does not allow. New agents pay USDC on Base. | | `amount_exceeds_max` | The price is over the `maxAmountUsd` of that call. | | `client_wallet_not_initialized` | Client-side signing: run `npx -y @burnbound/mcp wallet init` and register the address. | | `client_wallet_mismatch` | Client-side signing: the local wallet is not the one registered for the agent. Register `localAddress`, or set `BURNBOUND_WALLET_PROFILE`. | | `plan_limit_reached` | Your plan's limit was reached (for example the Free plan's managed spend). A human can upgrade at `upgradeUrl`. | --- Source: https://app.burnbound.dev/docs/concepts # Concepts Burnbound sits between an AI agent and the x402 APIs it pays. This page explains the pieces: agents, policies, caps, allowed hosts, human approvals, signing modes, and how an x402 v2 payment flows through them. ## What is Burnbound? Burnbound is a buyer-side spending control plane for AI agents that pay x402 APIs. Before your agent pays, Burnbound checks the payment against rules you set: which hosts it may pay, how much per payment, how much per day, and when a human must approve. Only allowed payments are signed, by a wallet you bring. Burnbound is not a wallet, not an x402 facilitator and not a seller paywall. It never holds private keys or funds, and it never settles payments: sellers do. ## What is an agent? An agent is one identity that pays: it has its own policy, its own keys (`bb_agent_…`) and its own wallet connection. Use one Burnbound agent per AI agent or workload, so caps, approvals and audit stay separate. An agent key can only act for its own agent: read its budget and payments, ask to authorize a payment, report a receipt and check its own approvals. It cannot change rules, approve payments or create keys. Those actions need a human signed in to the dashboard. ## What is a policy? A policy is the set of rules Burnbound evaluates for every payment of an agent. A payment is allowed only if it passes every rule; otherwise it is denied, nothing is signed, and the decision is recorded with the reasons. Every agent's policy has: - **Allowed hosts**: the hosts the agent may pay. - **Maximum per payment** (`maxUsdPerTx`) and **daily cap** (`dailyCapUsd`), in USD. - **Allowed networks and assets**: new agents pay USDC on Base. Optional rules, off by default, can be turned on per agent: - **Challenge rules**: extra host patterns, path prefixes and a maximum amount for what the seller's 402 may ask. - **Velocity**: a maximum number of payments, or a maximum USD amount, per time window (60 seconds by default). - **Step-up approval**: a USD threshold from which a human must approve (see below). Policies are versioned. Changing the hosts or the caps creates a new version, and each decision records the version it used (`policyVersion`). ## Which reasons can a denial have? A denied payment carries one or more reason codes. The MCP tool `fetch_paid` returns them in `reasons` of a `policy_denied` error, and the dashboard audit shows them. | Code | Meaning | | -------------------------------- | ------------------------------------------------------------------------------- | | `host_not_allowed` | The host that answered 402 is not in the allowed hosts. | | `amount_exceeds_per_transaction` | The amount is over the maximum per payment. | | `daily_cap_exceeded` | The amount would take today's spend over the daily cap. | | `network_not_allowed` | The seller asked for a network the policy does not allow. | | `asset_not_allowed` | The seller asked for an asset the policy does not allow. | | `pay_to_denied` | The payee address is on the policy's deny list. | | `invalid_amount` | The amount could not be read. | | `challenge_host_not_allowed` | Challenge rules: the host is not in the challenge host list. | | `challenge_path_not_allowed` | Challenge rules: the path does not start with an allowed prefix. | | `challenge_amount_exceeded` | Challenge rules: the amount is over the challenge maximum. | | `velocity_tx_exceeded` | Velocity: too many payments in the current window. | | `velocity_usd_exceeded` | Velocity: too much USD in the current window. | | `resource_host_mismatch` | The seller named a resource on a different host than the one that answered 402. | ## How do allowed hosts work? Allowed hosts are the only hosts an agent may pay. An entry is either an exact host (`api.example.com`) or a wildcard (`*.example.com`), which matches any subdomain such as `data.example.com` but not `example.com` itself. With an empty list, no payment is allowed. The policy runs on the host of the URL that answered 402, and the seller's x402 challenge must name a resource on that same host. The MCP server also checks the list before it sends anything: `fetch_paid` never requests a URL whose host is not allowed, paid or not (`host_not_allowed`). ## How do the spending caps work? Each agent has two caps: a maximum per payment and a daily cap. A payment over the per-payment maximum is denied (`amount_exceeds_per_transaction`), and so is a payment that would take today's spend over the daily cap (`daily_cap_exceeded`). - The day is the UTC calendar day. - Today's spend counts payments that were authorized, submitted, pending settlement or settled. Failed and expired payments do not count. - At 80% of the daily cap the agent's cap level becomes `warning`, and at 100% `exhausted` (`capLevel` in `get_budget`; the dashboard shows a badge). - `fetch_paid` also takes a per-call `maxAmountUsd`, checked by the MCP server on top of the policy. Your plan adds a monthly limit on managed spend for the whole organization: 50 USD per calendar month (UTC) on the Free plan, no limit on Pro. Past it, payments fail with `plan_limit_reached`. ## How do human approvals work? With step-up approval on, any allowed payment at or above the agent's threshold waits for a human instead of being signed. `fetch_paid` returns `pending_approval` with an `approvalId`; a human approves or denies it in the dashboard; the agent then retries with the same `idempotencyKey` and is never charged twice. 1. The agent calls `fetch_paid`. The amount is at or above the threshold, so the result is `status: "pending_approval"` with `approvalId` and `idempotencyKey`. 2. A human sees the payment in the dashboard's approvals inbox (amount, host, payee, network) and approves or denies it. 3. The agent polls `get_approval_status` with the `approvalId`. 4. When it is `approved`, the agent calls `fetch_paid` again with the same `url`, `method`, `headers`, `body` and `idempotencyKey`. If it is `denied` or `expired`, the agent must not retry it. Details: - The approval threshold is checked only after the other rules: a payment that breaks a rule is denied, not sent for approval. - A pending approval expires after 15 minutes by default. An owner can set this between 5 minutes and 24 hours in the dashboard settings. - Once approved, the agent has 10 minutes to retry the payment. - At most 5 approvals can be pending per agent and 20 per organization; past that, `fetch_paid` returns `approval_queue_full`. - **Notifications**: the approvals inbox is on every plan. On Pro and Team, Burnbound can also notify you through a Slack incoming webhook and by email to the organization's owners. ## Which signing modes are there? Burnbound never signs with a key it holds. Each agent's wallet is connected in one of two modes: **Coinbase CDP**, where the key stays with Coinbase and Burnbound requests each signature, or **client-side signing**, where the key stays on your machine and the MCP server signs locally after Burnbound allows the payment. | | Coinbase CDP | Client-side signing | | ------------------------------ | ---------------------------------------------------- | ------------------------------------------------------------------------------------ | | Where the private key lives | At Coinbase, in your CDP wallet | On your machine: OS keychain or a `0600` file | | What Burnbound stores | Your CDP API credentials, encrypted | The wallet's public address | | Who signs | Coinbase, when Burnbound asks for an allowed payment | The MCP server on your machine, after Burnbound allows the payment | | `mode` in `fetch_paid` results | `server_signed` | `client_signs` | | Enforcement | Strong: the agent never has anything that can sign | Cooperative: see [Security](https://app.burnbound.dev/docs/security#what-is-the-limit-of-client-side-signing) | In client-side mode, before it signs, the MCP server checks that the local wallet is the one registered for the agent, that what it is asked to sign is one of the seller's own offers for the host that asked, that the amount is within `maxAmountUsd`, and that the authorization window is short (at most 300 seconds). It only signs USDC `TransferWithAuthorization` (EIP-3009) on Base or Base Sepolia. ## How does an x402 v2 payment flow through Burnbound? The buyer signs and the seller settles. Your agent asks for a URL, the seller answers HTTP 402 with a price, Burnbound decides, your wallet signs a USDC transfer authorization, and the seller submits it on-chain through its facilitator. Neither your agent nor Burnbound ever calls a facilitator's settle endpoint. 1. The agent calls `fetch_paid` with a URL. The MCP server checks that the host is allowed and requests the URL. 2. The seller answers `402 Payment Required` with a `PAYMENT-REQUIRED` header (x402 v2): price, network, asset and payee. 3. The MCP server sends that challenge and the URL to Burnbound, with an idempotency key for this purchase. 4. Burnbound evaluates the agent's policy and answers allow, deny or step-up. An allowed payment is recorded and counts against the daily cap from that moment. 5. The payment is signed as an EIP-3009 `TransferWithAuthorization` for USDC. With Coinbase CDP, Burnbound asks CDP to sign and returns the signed payload. With client-side signing, Burnbound returns exactly what to sign (the seller's offer, a nonce derived from the payment, and a short validity window) and the MCP server signs it locally. 6. The MCP server retries the same request once, with the `PAYMENT-SIGNATURE` header. It never follows a redirect after paying. 7. The seller verifies and settles the payment through its facilitator, and answers with the resource and a `PAYMENT-RESPONSE` header. 8. The MCP server reports that answer to Burnbound as a receipt. Burnbound checks the transfer on-chain before it marks the payment `settled`. ## What is an idempotency key? An idempotency key identifies one purchase attempt. Burnbound never charges twice for the same key: a repeated request with the same key replays the earlier decision (`replayed: true` in the result) instead of signing a new payment. `fetch_paid` creates a new key for every call. Pass `idempotencyKey` only to retry a purchase: after a `pending_approval` was approved, or after a `payment_outcome_unknown` error. A key reused for a different request fails with `idempotency_key_reused`. ## What does Burnbound record? Every decision and every payment is recorded in an audit log: allowed, denied (with reasons), pending approval, approved or denied by whom, signed, settled or failed. The dashboard shows spend per agent, each payment with its on-chain transaction, and the audit. The Free plan keeps 30 days of audit, Pro 365 days. --- Source: https://app.burnbound.dev/docs/mcp-tools # MCP tools reference `@burnbound/mcp` is the MCP server that lets an agent pay x402 APIs under its Burnbound policy. It runs over stdio with `npx -y @burnbound/mcp`, exposes four tools (`fetch_paid`, `get_budget`, `list_payments`, `get_approval_status`), and includes a `wallet` CLI for client-side signing. This page documents version 0.1.0. ## How do I configure the server? The server is configured only through environment variables in your MCP client config. `BURNBOUND_KEY` is the only required one. The server checks the configuration before it starts and exits with a message on stderr if something is missing or unsafe. | Variable | Required | Meaning | | ------------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | `BURNBOUND_KEY` | yes | The agent's key (`bb_agent_…`). | | `BURNBOUND_API` | no | Burnbound API base URL. Default `https://app.burnbound.dev`. Must be `https://` (`http://` only for a loopback host). | | `BURNBOUND_AGENT_ID` | only with an org key | The agent the tools act for. Ignored with an agent key, which already names its agent. | | `BURNBOUND_MAX_RESPONSE_BYTES` | no | Cap on the response body `fetch_paid` returns. Default 1048576 (1 MiB), maximum 10485760 (10 MiB). Longer bodies are truncated. | | `BURNBOUND_FETCH_TIMEOUT_MS` | no | Deadline for the whole exchange with the URL in `fetch_paid`. Default 30000, from 1000 to 120000. | | `BURNBOUND_ALLOW_PRIVATE_HOSTS` | no | `1` lets `fetch_paid` reach loopback and private networks, and `http://` on loopback. For local development only. Link-local stays blocked. | | `BURNBOUND_WALLET_PROFILE` | no | Local wallet used for client-side signing. Default `default`. 1 to 32 characters of `a-z`, `0-9`, `_` and `-`. | | `BURNBOUND_HOME` | no | Moves the `~/.burnbound` directory where file-based wallets are stored. | The server refuses to start if any `BURNBOUND_*` variable looks like a private key: wallet keys never go in the MCP config. An organization key still works together with `BURNBOUND_AGENT_ID`, with a warning on stderr: org keys have admin scope, so use an agent key. ## What does fetch_paid do? `fetch_paid` requests a URL on one of the agent's allowed hosts and, if the URL answers HTTP 402 with an x402 challenge, pays it under the agent's policy and returns the paid response. It is the only tool that can spend money, and it is marked to the MCP client as non-read-only and open-world. | Input | Type | Notes | | ---------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `url` | string, required | `https://` URL, up to 2048 characters, on one of the agent's allowed hosts. | | `method` | string | `GET` (default), `HEAD`, `POST`, `PUT`, `PATCH` or `DELETE`. | | `headers` | object of strings | Up to 32 extra headers. `Authorization`, payment headers (`PAYMENT-SIGNATURE`, `X-PAYMENT`…), `x-org-api-key`, `Host`, `proxy-*` and hop-by-hop headers are refused. | | `body` | string | Up to 256 KB. Not allowed with `GET` or `HEAD`. | | `idempotencyKey` | string | Only to retry a purchase: the key from an earlier `pending_approval` result or `payment_outcome_unknown` error. 8 to 128 characters of letters, digits, `.`, `_`, `:` and `-`. | | `maxAmountUsd` | string or number | Refuse to pay more than this many USD for this call, for example `"0.05"`. Your Burnbound policy still applies on top. | `maxAmountUsd` is checked against the seller's challenge before anything is authorized, and again against what is signed. When the seller offers several prices, every one must be within the maximum. ### Which results can fetch_paid return? `fetch_paid` returns one of three statuses. Anything else (a denial, a limit, a network problem) comes back as a tool error with a code. | `status` | Meaning and fields | | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ok` | No payment was needed. `httpStatus`, `host`, `body`. | | `paid` | Paid. `httpStatus`, `host`, `intentId`, `idempotencyKey`, `replayed` (the same purchase was answered again and nothing new was charged), `mode` (`server_signed` or `client_signs`), `amountUsd`, `network`, `payTo`, `receipt.state` (`settled`, `pending`, `failed` or `unknown`), `receipt.txHash`, `body`. | | `pending_approval` | The amount is at or above the agent's approval threshold. `approvalId`, `amountUsd`, `thresholdUsd`, `host`, `idempotencyKey`. Poll `get_approval_status`; once it is `approved`, call `fetch_paid` again with the same `url`, `method`, `headers`, `body` and `idempotencyKey`. | `body` is `{ untrusted: true, contentType, encoding, bytes, truncated, text }`. Binary content types come back base64-encoded. In the tool result the body is a separate text block, fenced and labelled as untrusted third-party content, never mixed with the metadata. Response headers are never returned. `receipt.state` is `unknown` when the receipt could not be recorded; the payment itself still happened when `status` is `paid`. If the paid response body could not be read, `bodyError` says why and the payment is still reported. ### How does fetch_paid handle redirects? Same-origin redirects before the 402 are followed, up to 3. A redirect to another origin, and any redirect after paying, is returned as is with `redirectNotFollowed: true`, so a payment is never sent anywhere other than the URL that asked for it. ## What does get_budget return? `get_budget` returns the agent's budget for today. It takes no input and is read-only; agents should call it before paying for something. | Field | Meaning | | -------------------- | ----------------------------------------------------------------------- | | `agentId` | The agent the key belongs to. | | `spentTodayUsd` | USD spent today (UTC day). | | `dailyCapUsd` | The daily cap. | | `remainingTodayUsd` | What remains today under the daily cap. | | `maxUsdPerTx` | The maximum per payment. | | `stepUpThresholdUsd` | The approval threshold, or `null` when the API does not report one. | | `allowHosts` | The hosts the agent may pay. | | `capLevel` | `ok`, `warning` (from 80% of the daily cap) or `exhausted`. | | `network`, `asset` | Network and asset of the budget, for example `eip155:8453` and `USDC`. | | `dayResetsAt` | When the daily spend resets, or `null` when the API does not report it. | ## What does list_payments return? `list_payments` returns the agent's most recent payments, newest first. It is read-only. | Input | Notes | | -------- | --------------------------------------------------------------------------------------------------------------------------- | | `limit` | 1 to 50, default 10. | | `status` | Only payments in this status: `created`, `authorized`, `submitted`, `settlement_pending`, `settled`, `failed` or `expired`. | The result is `{ agentId, payments }`. Each payment has `intentId`, `status`, `amountUsd`, `network`, `asset`, `host`, `transactionHash` (once settled) and `createdAt`. It returns the host that was paid, not the full URL. ## What does get_approval_status return? `get_approval_status` tells the agent whether a human has decided on a payment that needed approval. It takes the `approvalId` from a `pending_approval` result and is read-only. It returns `approvalId`, `status` (`pending`, `approved`, `denied` or `expired`), `intentId` (set once the approved payment was made), `amountUsd`, `host`, `createdAt`, `decidedAt` and `expiresAt`. An agent key only sees its own agent's approvals; any other id is `approval_not_found`. ## What errors can a tool return? Errors come back as an MCP tool error whose content is `{"error": {"code", "message", …}}`. The code is stable; the message is fixed text written by the server, never copied from the Burnbound API or from the seller. Extra fields are only ids, amounts and codes the server validated. ```json { "error": { "code": "policy_denied", "message": "The agent's spending policy denied this payment, so nothing was paid. See reasons.", "reasons": ["daily_cap_exceeded"], "policyVersion": 4 } } ``` **Any tool:** | Code | Meaning | | ---------------------------------------------------------------- | ---------------------------------------------------- | | `invalid_input` | The input is invalid. `reason` says why (see below). | | `unauthorized` | The API rejected `BURNBOUND_KEY`. | | `forbidden` | The key is not allowed to do this. | | `not_found` | The API did not find the resource. | | `agent_not_found` | The configured agent does not exist for this key. | | `approval_not_found` | No approval with that id for this agent. | | `rate_limited` | The API is rate limiting requests. Retry later. | | `api_unavailable`, `api_unreachable`, `api_timeout`, `api_error` | The Burnbound API could not answer. Retry later. | | `invalid_response`, `response_too_large` | The API answer could not be read. | | `config_invalid` | The server is misconfigured. | | `internal_error` | Unexpected error in the server. | `invalid_input` reasons from `fetch_paid` include `https_required`, `invalid_url`, `url_credentials`, `burnbound_api_url` (the URL is the Burnbound API itself), `contains_credential` (the input contains the Burnbound key or a wallet key), `body_not_allowed`, `body_too_large`, `too_many_headers`, `invalid_header`, `reserved_header` and `invalid_max_amount`. **`fetch_paid`, nothing was sent:** | Code | Meaning | | --------------------------- | ------------------------------------------------------------------------ | | `host_not_allowed` | The URL's host is not in the agent's allowed hosts. `host`. | | `private_address_blocked` | The URL resolves to a private, loopback or link-local address. | | `control_plane_unavailable` | The agent's allowed hosts could not be loaded, so nothing was requested. | **`fetch_paid`, nothing was paid:** | Code | Meaning | | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | `seller_unreachable`, `seller_timeout` | The URL could not be reached, or did not answer in time. | | `invalid_payment_required` | The URL answered 402 without a readable `PAYMENT-REQUIRED` header. | | `amount_exceeds_max` | The price is over `maxAmountUsd`. `maxAmountUsd`, `amountUsd`. | | `resource_mismatch` | The seller asked to be paid for a different host than the one requested. | | `policy_denied` | The policy denied the payment. `reasons` (see [Concepts](https://app.burnbound.dev/docs/concepts#which-reasons-can-a-denial-have)), `policyVersion`. | | `plan_limit_reached` | The organization reached a plan limit. `plan`, `limit` (`agents` or `monthly_managed_spend`), `max`, `current`, `upgradeUrl`. | | `approval_required` | The payment needs an approval this API cannot track. | | `approval_denied` | A human denied the payment. Do not retry it. | | `approval_expired` | The approval expired. Call `fetch_paid` without `idempotencyKey` to ask again. | | `approval_consumed` | That approval was already used for a payment. Check `list_payments`. | | `approval_queue_full` | Too many payments are waiting for approval. Retry later. | | `idempotency_key_reused` | That `idempotencyKey` belongs to a different request. | | `intent_not_replayable` | The payment for that `idempotencyKey` failed or expired. Omit the key to start a new payment. | | `payment_in_progress` | Another payment of this agent is in progress. Retry shortly with the same `idempotencyKey`. | | `payment_request_rejected` | The API rejected the payment request. `apiCode`. | | `client_signing_unavailable` | The agent uses client-side signing, which this server could not set up. | **`fetch_paid` with client-side signing, nothing was paid:** | Code | Meaning | | ------------------------------- | --------------------------------------------------------------------------------------------------------------- | | `client_wallet_not_initialized` | No local wallet. Run `npx -y @burnbound/mcp wallet init` and register the address. | | `client_wallet_unavailable` | The local wallet could not be opened (`reason`: keychain unavailable, `insecure_permissions`…). | | `client_wallet_mismatch` | The local wallet is not the one registered for the agent. `localAddress`, `registeredAddress`. | | `client_sign_refused` | The local signer refused what it was asked to sign. `reason`, for example `agent_key_required` with an org key. | **`fetch_paid`, after paying:** | Code | Meaning | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `payment_outcome_unknown` | The signed payment was sent but the seller's answer was lost. `intentId`, `idempotencyKey`. Check `list_payments`; retrying with the same `idempotencyKey` never charges twice. | ## Which wallet commands are there? The `wallet` CLI manages the local wallet used for client-side signing. Run it with `npx -y @burnbound/mcp wallet `; it never starts the MCP server. The private key is stored in the macOS keychain, the Linux Secret Service (GNOME Keyring, KWallet) when a session bus is available, or otherwise a file in `~/.burnbound/wallets/` (directory `0700`, file `0600`). | Command | What it does | | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | `wallet init` | Creates a wallet and prints its address. Refuses if the profile already has one. | | `wallet address [--all]` | Prints the address (`--all`: also retired ones). Does not unlock the key. | | `wallet import [--from-env VAR]` | Stores an existing key, read from stdin (hidden on a terminal) or once from the variable `VAR`. Remove the variable afterwards. | | `wallet export [--address ADDR]` | Prints the key, only on an interactive terminal (never to a pipe or a file), after you type `export
` to confirm. | | `wallet rotate` | Switches to a new key and keeps the old one so you can move its funds (`wallet export --address `). Register the new address. | Options: - `--profile `: wallet profile. Default `BURNBOUND_WALLET_PROFILE`, else `default`. Use one profile per agent. - `--backend `: for `init` and `import`, the storage: `macos-keychain`, `secret-service` or `file`. The storage is chosen once, at `init`; later runs never fall back to another one. On Windows only `file` is available, and it is experimental. ```bash npx -y @burnbound/mcp wallet init npx -y @burnbound/mcp wallet address ``` --- Source: https://app.burnbound.dev/docs/sdk # SDK Burnbound has a TypeScript client with `payFetch` and `wrapFetchWithPayment`, but it is **not published on npm yet**. There is no package to install today. To give an agent paid access to x402 APIs now, use the MCP server [`@burnbound/mcp`](https://app.burnbound.dev/docs/mcp-tools), which runs this same client inside. ## Is there a JavaScript or TypeScript SDK I can install? Not yet. The client that pays x402 APIs under a Burnbound policy is used internally by `@burnbound/mcp`, but it has not been released as a public npm package, so there is no package name to install. This page will give install instructions when it is published. Until then: - **For agents in Claude Code, Cursor or another MCP client**, install the MCP server. See the [Quickstart](https://app.burnbound.dev/docs/quickstart). - **For agent frameworks that support MCP tools**, connect `npx -y @burnbound/mcp` as a stdio MCP server, with `BURNBOUND_KEY` in its environment. ## What will the SDK do? The SDK makes a `fetch` that pays x402 under your Burnbound policy. It is the same flow `fetch_paid` follows: on HTTP 402 it asks Burnbound to authorize the payment, gets it signed by your wallet, retries the request once with the payment, and reports the receipt. The interface below is the current one and may change before it is published. - `wrapFetchWithPayment(options)` returns a drop-in replacement for `fetch`. A denied payment or one waiting for approval throws an error that carries Burnbound's decision. - `payFetch(options, input, init)` makes one purchase attempt and returns what happened: `free` (no payment was needed), `paid` (with `intentId`, `idempotencyKey`, `mode`, `replayed` and the receipt), `denied` (with the reason codes) or `step_up` (with the `approvalId` to poll). Both take the Burnbound API base URL, the agent key, the agent id and, for client-side signing, a signer. Like the MCP server, they send the key only to the Burnbound API, never to the seller, and they never follow a redirect after paying. ## Should I wait for the SDK or use the MCP server? Use the MCP server unless you need to pay from your own code outside an MCP client. It is published, it adds client-side guards that a plain `fetch` wrapper does not have (allowed hosts checked before any request, private addresses blocked, seller responses marked as untrusted, the key kept out of tool results), and it works with every MCP client. --- Source: https://app.burnbound.dev/docs/security # Security and trust model Burnbound decides whether your agent may pay; it does not hold your money or your wallet's private key. This page says what Burnbound stores and sees in each signing mode, what the MCP server protects against, and where the protection ends, in particular the cooperative limit of client-side signing. ## Does Burnbound hold my private keys or funds? No. Burnbound never stores a wallet private key and never holds funds. With Coinbase CDP the key stays with Coinbase; with client-side signing it stays on your machine. Payments go straight from your wallet to the seller. What Burnbound does store in Coinbase CDP mode is your CDP API credentials (API key ID, API key secret and wallet secret), because it needs them to ask Coinbase to sign the payments your policy allows. They are encrypted at rest (AES-256-GCM), decrypted only to request a signature, and never shown again in the dashboard or returned by the API. They are still powerful: treat the CDP wallet as a spending wallet and keep in it only what your agents should be able to spend. ## What does Burnbound see in each signing mode? Burnbound sees what it needs to decide and to audit: the payment request, never the content you buy. The table compares the two modes. | | Coinbase CDP | Client-side signing | | ----------------------------------- | ------------------------------------------------ | --------------------------------- | | Wallet private key | No (stays at Coinbase) | No (stays on your machine) | | Signing credentials | Your CDP API credentials, encrypted | None | | Wallet address | Yes | Yes (the only thing you register) | | Can Burnbound get a payment signed? | Yes, through CDP, for payments its policy allows | No | In both modes, for every payment Burnbound sees and records: - the URL that answered HTTP 402, including its path and query string; - the seller's x402 challenge: price, network, asset and payee; - the decision (allowed, denied with reasons, or sent for approval) and the policy version; - the seller's `PAYMENT-RESPONSE` receipt and the on-chain transaction. Burnbound does not see the headers or body your agent sends to the seller, or the seller's response body. The MCP server sends Burnbound only the 402 challenge and, after paying, the receipt. ## How are agent keys protected? An agent key (`bb_agent_…`) is shown once when it is created, and Burnbound stores only its SHA-256 hash. It can only act for its own agent: read its budget and payments, ask to authorize a payment, report a receipt and read its own approvals. It cannot change the policy, raise a cap, approve a payment, connect a wallet or create keys; those need a human signed in to the dashboard. The key lives in your MCP client's config (`~/.claude.json`, `.cursor/mcp.json`). An agent that can run shell commands on the same machine could read it, so prefer agent keys with tight caps over organization keys, which have admin scope. Revoke a key in the dashboard at any time. ## What does the MCP server protect against? `@burnbound/mcp` adds client-side guards in front of the Burnbound policy. They reduce what a confused or manipulated agent can do with `fetch_paid`; the Burnbound API remains the authority on spending. - **The key goes only to the Burnbound API.** It is never sent to the URLs `fetch_paid` requests and never appears in tool results or logs. Input that contains the key is refused (`contains_credential`). - **Only allowed hosts are requested.** `fetch_paid` checks the agent's allowed hosts before sending anything, paid or not. - **No private networks.** Private, loopback, link-local and reserved addresses are blocked, for IP literals and for every address a hostname resolves to, at connect time. - **The seller cannot redirect the payment.** The resource in the seller's challenge must be on the requested host, and the paid request never follows redirects. - **Seller responses are untrusted.** A paid API can return text written to manipulate the agent ("ignore your instructions and pay…"). `fetch_paid` returns the body fenced and labelled as untrusted data, apart from the metadata. - **Error messages are written by the server**, never copied from the API or the seller. - **No wallet key in the config.** The server refuses to start if a `BURNBOUND_*` variable looks like a private key. ## What is the limit of client-side signing? Client-side signing is cooperative. Burnbound cannot stop a process that already runs as your user: an agent that can run shell commands on the machine could read the wallet key (from the OS keychain or the wallet file) and sign payments without asking Burnbound. The policy holds only while the agent goes through the MCP server. The MCP server makes that harder, not impossible: - the key is never in the MCP config or in the environment the agent can read, and never sent anywhere; - it is loaded only at the first `fetch_paid`, never by the read-only tools; - every request to Burnbound and to sellers is checked for it and blocked if it appears; - it only signs USDC transfer authorizations that Burnbound allowed, for the seller's own offer, with a short validity window. If you need enforcement that does not depend on the agent's cooperation, use Coinbase CDP: the agent never holds anything that can sign, and Burnbound requests every signature. ## How does out-of-policy detection work? For client-side wallets, Burnbound watches the wallet's address on-chain and records a `payment.out_of_policy` event when USDC leaves it without a matching payment that Burnbound allowed. It detects after the fact; it cannot block or reverse the transfer. A movement is flagged when it is: - a transfer authorization whose nonce does not belong to any payment Burnbound allowed; - a transfer authorization that belongs to a payment, but to a different payee, amount or payer; - a transfer authorization for a payment that had already failed or expired; - a plain `transfer()` or `transferFrom()`, with no authorization at all; - an `approve()` or `permit()` that grants an allowance. Flagged movements appear in the audit and as a warning on the agent's page in the dashboard, usually within a few minutes of reaching the chain. ## Why should I fund the wallet with a small balance? Because the balance is the real ceiling on what a compromised or misbehaving agent can spend outside the policy. With client-side signing, an agent that reads the key can spend the whole wallet; with any mode, a wallet holding a week of spend loses at most a week of spend. - Keep a balance sized to what the agent should spend in a short period, and top it up. - Keep no ETH in a client-side wallet. x402 payments do not need it, and without it the wallet cannot send an ordinary transaction itself. A signed transfer authorization can still be submitted by someone else, so the USDC balance is what really bounds the risk. - Use one wallet per agent, so a problem in one does not reach the others. ## How do I keep Claude Code away from the wallet key? Deny the agent the ways to read the key in `.claude/settings.json` (or `~/.claude/settings.json`). These rules reduce accidents; they are not a sandbox. ```json { "permissions": { "deny": [ "Read(~/.burnbound/**)", "Edit(~/.burnbound/**)", "Bash(security find-generic-password:*)", "Bash(security dump-keychain:*)", "Bash(secret-tool lookup:*)", "Bash(npx -y @burnbound/mcp wallet export:*)", "Bash(burnbound-mcp wallet export:*)" ] } } ``` --- Source: https://app.burnbound.dev/docs/faq # FAQ Short answers to common questions about Burnbound. Each answer links to the page with the details. ## What is x402? x402 is an open protocol for paying for HTTP requests. A paid API answers `402 Payment Required` with a price; the client signs a stablecoin payment and repeats the request with it; the seller settles the payment and serves the response. Burnbound supports x402 v2 with the `exact` scheme in USDC. ## How do I limit what my AI agent can spend? Give the agent a Burnbound policy and let it pay only through Burnbound. You set the hosts it may pay, a maximum per payment, a daily cap and, if you want, a threshold above which a human must approve. The agent pays x402 APIs with the `fetch_paid` tool of the Burnbound MCP server, and every payment outside those rules is denied before it is signed. Start with the [Quickstart](https://app.burnbound.dev/docs/quickstart). ## Which agents and MCP clients are supported? Any MCP client that can run a stdio server: Claude Code, Cursor and others. The server is the npm package `@burnbound/mcp` and runs with `npx -y @burnbound/mcp`. A JavaScript SDK exists but is not published yet; see [SDK](https://app.burnbound.dev/docs/sdk). ## Which networks and tokens can my agent pay with? USDC on Base. New agents allow Base mainnet (`eip155:8453`) only, and the policy denies any other network (`network_not_allowed`) or asset (`asset_not_allowed`). ## Do I need ETH in the wallet? No. x402 payments are USDC transfer authorizations (EIP-3009) that the seller's facilitator submits on-chain, so the facilitator pays the gas. Your wallet only needs USDC. ## Does Burnbound hold my private keys or my money? No. Burnbound never stores a wallet private key and never holds funds: payments go from your wallet to the seller. With Coinbase CDP, Burnbound stores your CDP API credentials encrypted so it can request signatures for the payments your policy allows. With client-side signing it knows only your wallet's address. See [Security and trust model](https://app.burnbound.dev/docs/security). ## Which signing mode should I choose? Choose Coinbase CDP if your agent can run shell commands or you need enforcement that does not depend on the agent's cooperation: the agent never holds anything that can sign. Choose client-side signing if you want the key on your own machine and accept that an agent with shell access could bypass the policy. See [Concepts](https://app.burnbound.dev/docs/concepts#which-signing-modes-are-there). ## Can my agent raise its own limits? No. An agent key can read its budget and payments, ask to authorize a payment and check its approvals, but it cannot change the policy, approve a payment or create keys. Those actions need a human signed in to the dashboard. ## What happens if Burnbound is down? Your agent cannot pay, and nothing is charged. `fetch_paid` fails closed: if it cannot load the agent's allowed hosts or get a decision from Burnbound, it returns an error (`control_plane_unavailable`, `api_unavailable`, `api_unreachable` or `api_timeout`) and pays nothing. ## What happens if the seller's answer is lost after paying? `fetch_paid` returns `payment_outcome_unknown` with the `intentId` and the `idempotencyKey`. Check `list_payments` to see whether the payment settled. Retrying `fetch_paid` with the same `idempotencyKey` never charges twice. ## Does Burnbound take a fee on payments? No. Burnbound does not take a percentage of what your agents pay; payments go in full from your wallet to the seller. You pay for the plan. | Plan | Price | Agents | Managed spend per month | Audit history | Slack and email approval alerts | | ---- | ------------------------------------------ | --------- | ----------------------- | ------------- | ------------------------------- | | Free | €0 | 1 | 50 USD | 30 days | No | | Pro | €39 per month + VAT | Unlimited | Unlimited | 365 days | Yes | | Team | €199 to €399 per month + VAT (coming soon) | Unlimited | Unlimited | Unlimited | Yes | The approvals inbox, caps, allowed hosts and policy rules are on every plan. Billing is handled by Stripe. ## How do I stop an agent from paying? Revoke its key in the dashboard. The MCP server then gets `unauthorized` on every call and cannot authorize payments. To stop it immediately without revoking the key, remove its allowed hosts or lower its daily cap: an empty host list allows no payment. ## Is there documentation for LLMs? Yes. [`/llms.txt`](/llms.txt) lists every documentation page with a one-line description, and [`/llms-full.txt`](/llms-full.txt) has the whole documentation as one plain-text Markdown file.