Skip to main content
Glama
vAPI-Network

@vapi-network/mcp

Official
by vAPI-Network

vAPI Network

One wallet, every x402 API. vAPI Network is an open-source, non-custodial TypeScript toolkit for discovering and paying x402 services from a terminal, an MCP client, or your own code. Your key is generated on your machine, encrypted under your passphrase, and never leaves it. vAPI applies spend policy before signing, sends the payment straight to the service, and writes a local receipt. Every payment identifies this client as vapi through x402 builder-code; when an API advertises payment-identifier, the generated id is kept on that receipt.

Call works today. Tasks and Compute are next.

Quickstart

npm i -g vapi-network
vapi init                        # creates ~/.vapi, the wallet main, and prints its address
vapi backup                      # write the 12 words down; vAPI cannot recover them
vapi fund                        # opens the hosted funding page for your address
vapi search "weather"            # every catalogue, merged, with provenance
vapi pay <listing-ref> --max 0.02
vapi receipts                    # one line per paid call: quote, settlement, latency

That is the same order vapi init prints as its next steps, and the same order vapi alone lists. Two more are worth knowing early: vapi inspect <ref> shows a listing's request contract and live 402 quote for free, before you pay, and vapi balance shows what the wallet holds.

vapi search tags each listing with its group — [vapi], [added], [partner] or [external] — and prints the network fee that is already inside the price. vAPI and added APIs carry a 5% network fee inside the quoted price; partner and external listings carry none. vapi inspect and --json return the same group and fee fields.

Listing on vAPI is permissionless, and verification is a tier on top of it. Each result also carries [verified], [requested] or [unverified], and a mirrored catalog row carries [external] instead. By default vapi search answers with vAPI-verified listings plus the mirrored external catalogs; --include-unverified also returns self-listed APIs that passed vAPI's automated x402 probe but were never reviewed. vapi inspect prints a Verification: line, vapi pay says so in one line before the result when the listing it just paid is not verified, and --json carries verification on all three.

vapi inspect also says how a listing has behaved lately, when the registry has measured it: a Liveness: line with its uptime over the last seven days of hourly re-probes and its p50 and p95 latency, and a Conformance: line with the x402 version its 402 declares, whether it follows that version, where the offer travels, and any issue codes — the same codes vapi check reports. A registry that has not measured a listing sends neither, and neither line is printed. --json carries them as liveness and conformance.

When the registry knows it, vapi inspect also prints the listing's ERC-8004 agent identity on Base and its reputation. The client reads these fields from the registry and does not read the chain.

No install? Prefix any command with npx vapi-network, for example npx vapi-network init. (npx vapi cannot work: the bare vapi name on npm belongs to an unrelated package.)

Related MCP server: agent-tools-mcp

Wallets

One machine can hold several wallets: your own, and a capped one per agent. Each keystore lives in $VAPI_HOME/wallets/<name>.json; $VAPI_HOME/wallets.json records which one is the default, what each may spend, and its label. Names are 1 to 32 characters of lowercase letters, digits and dashes.

vapi wallet list                                 # address, default marker, caps, unlocked, label
vapi wallet create agent --label "claude code"   # a wallet of its own for the agent
vapi wallet caps agent --per-call 0.05 --per-day 1
vapi wallet use agent                            # make it the default for later commands
vapi pay <listing-ref> --wallet agent --max 0.02
vapi receipts --wallet agent                     # or --all-wallets for every one

Every command that touches a wallet takes --wallet <name>: balance, accounts, fund, pay, sweep, receipts, stats, export-key, backup, import, passphrase, unlock, lock and mcp. Without it, VAPI_WALLET decides; without that, the default set by vapi wallet use does. Each of them names the wallet it used — Wallet: main (0x…) on the first line, or a wallet field in --json — so neither you nor an agent can be wrong about which key just moved.

Spend caps belong to the wallet, not to the machine, and today's total is counted per wallet: an agent cannot spend your daily allowance. Removing a wallet is a move, not a delete — the encrypted keystore goes to wallets/.trash/, where vapi wallet restore can bring it back.

Coming from 0.2.x? Nothing to do. The first command you run opens the wallet store, moves keystore.json to wallets/main.json with its contents untouched, turns config.json's spend caps into the caps of main, and leaves keystore.json behind as a mode 0600 symlink for one release so existing scripts keep working. A home with no keystore migrates nothing.

Your wallet is yours

vapi init generates a 12-word BIP-39 recovery phrase on your machine, derives the Base account (m/44'/60'/0'/0/0) and, with --networks base,solana, the Solana account (m/44'/501'/0'/0') from it, and writes the phrase encrypted under your passphrase to $VAPI_HOME/wallets/main.json (mode 0600). The phrase is shown once, on a terminal, and never in --json or piped output.

Three things vAPI cannot do:

  • See it. No key, phrase or passphrase ever leaves the machine.

  • Recover it. There is no copy anywhere to restore from.

  • Freeze it. Payments go straight from your wallet to the service.

Back it up

vapi backup                       # the 12 words, numbered, on a terminal
vapi backup --json                # { "wallet": "main", "recoveryPhrase": "..." }

Write the words on paper and keep them somewhere only you reach. Anyone holding them can spend the wallet, so never type them into a website or a chat.

Wallets created before 0.2.5 have no recovery phrase. vapi backup says so and points at vapi export-key, which prints the private key itself; back that key up, or vapi import --key it into a new wallet. Restoring a phrase-based wallet in MetaMask, Rabby, Coinbase Wallet or Phantom gives the same addresses.

Import and move a wallet

vapi import --phrase                          # type the words at the prompt
vapi import --phrase --networks base,solana   # restore both accounts
vapi import --key                             # a 0x private key instead of words
vapi import --phrase --wallet backup-2026     # into a named wallet of its own

vapi import reads the secret from a prompt, never from the command line where a shell history would keep it, and asks for a new passphrase. It writes a new named wallet rather than replacing one: --wallet <name> chooses the name, and main is assumed only on a machine with no wallet yet. --replace moves the named wallet to wallets/.trash/ first, and still refuses a wallet that holds USDC on Base unless you add --force.

Change the passphrase

vapi passphrase                   # current passphrase, then the new one twice

The wallet, its addresses and its recovery phrase are unchanged. Any copy of the old passphrase stored by vapi unlock is removed at the same time, so nothing is left behind that no longer opens the wallet.

Agents and secrets

An agent can drive vAPI all day without ever seeing a secret. It can search, inspect, pay from the wallet you gave it, read balances and receipts, and pick a wallet by name with wallet.use — which changes only that session, never your default on disk. It cannot see a recovery phrase, a private key or a passphrase, and the MCP server has no tool that creates, removes, renames, backs up or exports a wallet.

vAPI agents combine a local profile, a dedicated wallet and vAPI Router into a small run loop on your machine. By default they use only verified listings, ask before a call above $0.50 and stop after at most 12 model steps. vAPI does not host agents; they run wherever you run vapi agent run.

vapi backup and vapi export-key print a secret, so they run only when a person is demonstrably there: stdin and stdout are both a real terminal, no agent or CI marker is set, and you type the wallet's own name to confirm. Otherwise they print nothing and say:

Run this yourself in a terminal; an agent must never see these words.

The markers vAPI refuses on are VAPI_NO_SECRETS, CLAUDECODE, CLAUDE_CODE, CURSOR_AGENT, CODEX_SANDBOX, OPENAI_CODEX, AGENT and CI. Set VAPI_NO_SECRETS=1 in a machine's agent configuration to switch secret printing off outright. vapi init and vapi wallet create still create the wallet under those conditions; they simply say Recovery phrase: run vapi backup yourself in a terminal to see it.

In the SDK the same line is drawn by the module layout: the functions that return a phrase or a key live in the separate @vapi-network/core/secrets entry point, which the MCP package is forbidden by lint to import.

Where the passphrase lives

An unlock looks in three places, in this order:

  1. VAPI_KEYSTORE_PASSWORD, kept for CI and for Windows.

  2. The OS secret store: the macOS Keychain, or libsecret on Linux, under the service vapi-network and the wallet's name.

  3. A prompt, when a person is there to answer it.

vapi unlock --wallet agent        # store it, once, in your own terminal
vapi wallet list                  # the UNLOCKED column says which agents can pay
vapi lock --all                   # take every stored passphrase back out

vapi unlock runs only on a real terminal with no agent marker set, and only after it has verified that the passphrase really opens that wallet. The passphrase is handed to the OS binary over its standard input, never as a command-line argument, so it never appears in ps. A run that finds no passphrase anywhere and has no terminal says so and names both other routes; an agent is never prompted. Windows has no store yet and keeps VAPI_KEYSTORE_PASSWORD.

The audit log

Every secret export, every wallet change — create, import, remove, restore, rename, default, caps, passphrase, unlock, lock — and every MCP session wallet switch appends one JSON line to $VAPI_HOME/audit.log (mode 0600): the time, the event, the wallet, whether a terminal was attached, and which marker was set. The line never contains the secret itself, so the log answers "did anything export my phrase while the agent was running" without you having to trust the agent's own account of it.

CLI reference

Every command accepts --json, which writes one JSON value — success or error — to stdout. Exit codes are 0 for success, 1 for an operational failure, and 2 for invalid usage or an announced preview-only command.

Command

Options

What it does

vapi init

--networks <base,arc,solana>

Creates ~/.vapi, the wallet main and the config. Says so if one exists.

vapi wallet list

—

Name, address, default marker, caps in USD, unlocked, label

vapi wallet create <name>

--networks <base,arc,solana>, --label <text>

A new wallet, with its own phrase, caps and passphrase

vapi wallet use <name>

—

Makes it the default for every later command

vapi wallet rename <old> <new>

—

Renames the keystore, the registry entry and that wallet's receipts

vapi wallet remove <name>

--force

Moves the keystore to wallets/.trash/; asks you to type the name

vapi wallet restore <name>

—

Brings a removed wallet back, same passphrase

vapi wallet caps <name>

--per-call <usd>, --per-day <usd>

Sets that wallet's spend caps, in US dollars

vapi fund

--amount <usd>, --wallet <name>

Prints and opens the hosted funding page. Makes no network call.

vapi accounts

--enable <solana|arc>, --wallet <name>

One deposit account per configured network, with balances and guidance

vapi search [query]

--kind <kind> (repeatable), --network <caip2>, --limit <n>, --cursor <cursor>, --include-unverified

Merged discovery across every configured source, tagged by group and tier

vapi inspect <id>

--endpoint <name>

Verification, fee, liveness, conformance, identity, contract and live quote, for free

vapi pay <id-or-url>

--method, --endpoint, --body <json>, --content-type, --network <caip2>, --expected-pay-to, --max <usd>, --wallet <name>

Calls the API and pays it from the local wallet, naming an unverified tier

vapi pay --resume <receipt-id>

—

Checks settlement, shows its payment id, and never pays

vapi check <url>

--method <method>

Grades its 402, extensions and same-origin discovery. Never pays

vapi balance

--wallet <name>

The wallet's address and USDC balances

vapi receipts

--limit <n>, --wallet <name>, --all-wallets

The local append-only call ledger, newest last

vapi receipts export

--format <json|csv>, --range <24h|7d|30d>, --wallet <name>, --all-wallets

Raw receipts for a spreadsheet or dashboard

vapi stats

--range <24h|7d|30d>, --wallet <name>, --all-wallets

Spend, outcomes, latency percentiles and top services

vapi sweep [<address>]

--network <caip2>, --wallet <name>

Moves USDC to an address, or to the linked owner when omitted

vapi agent create <name>

--model <id>, --instructions <file>, --call-budget <usd>, --max-per-call <usd>, --router-budget <usd>, --approve-above <usd>, --include-unverified, --max-steps <n>

Creates a capped local agent wallet, profile and owner link

vapi agent run <name> "<task>"

None

Runs the named agent on this machine

vapi agent list

None

Lists profiles, links and today's Call and Router budgets

vapi agent pause <name>

None

Stops future runs until the agent is resumed

vapi agent resume <name>

None

Allows a paused agent to run again

vapi agent revoke <name>

None

Revokes the link and removes the profile, but keeps its wallet

vapi login

--wallet <name>, --label <name>, --publish, --no-browser

Links the local agent wallet to your vAPI account

vapi logout

--wallet <name>

Removes the selected wallet's agent link and stored credentials

vapi whoami

--wallet <name>

Shows the chosen wallet's owner link and permissions, whether the link is live, and its ERC-8004 agent identity.

vapi router models

--wallet <name>

Lists the vAPI Router model ids

vapi router usage

--wallet <name>

Shows the agent's Compute allowance and Router balance

vapi router chat --model <id> "<prompt>"

--system <text>, --max-tokens <n>, --wallet <name>

Sends one chat request through vAPI Router

vapi router key

--rotate, --wallet <name>

Prints a gated Router key or rotates it without printing it

vapi router buy <1|5|20|50>

--wallet <name>, --json

Buys vAPI Router balance with USDC from your wallet

vapi router buy --auto <tier> --below <usd>

--wallet <name>, or use --auto off

Sets or clears automatic vAPI Router balance refill

vapi stake status

--wallet <name>

Shows the linked owner's stake and Compute today

vapi stake open

--wallet <name>, --no-browser

Prints and optionally opens the staking page

vapi export-key

--network <caip2>, --wallet <name>

Prints the private key. Terminal only, never for an agent.

vapi backup

--wallet <name>

Prints the 12 words. Terminal only, never for an agent.

vapi import

--phrase or --key, --wallet <name>, --networks <base,arc,solana>, --replace, --force

Restores a wallet from a prompt, never from argv

vapi passphrase

--wallet <name>

Re-encrypts the keystore under a new passphrase

vapi unlock

--wallet <name>

Puts that wallet's passphrase in the OS secret store

vapi lock

--wallet <name>, --all

Takes a stored passphrase back out

vapi report "<what>"

--include-addresses, --send

Writes a privacy-preserving local bug report

vapi auth set-key

—

Types the registry API key on a prompt into the OS secret store

vapi auth status

—

Whether this machine has a key and where it comes from, masked

vapi auth clear

—

Takes the stored key back out

vapi publish <url>

--method, --mode <origin|endpoint|openapi>, --name <text>, --description <text>, --category <ai|data|crypto|compute|search>, --select <names>, --wallet <name>, --yes, --resume

Probes your API, lists the endpoints you pick, signs the payout wallet

vapi publish activate <slug>

—

Takes a listing live once its FeeSplitter is deployed

vapi publish verify-request <slug>

—

Asks vAPI to review the listing

vapi publish list

—

Every listing this API key owns

vapi claim <origin>

--wallet <name>

Takes over the listings vAPI indexed from your API, signed by their payee

vapi mcp

--wallet <name>

Serves the MCP tools over stdio

vapi serve

—

Preview only; exits 2 with a message

vapi version

—

The client version, also as --version or -v

vapi help

—

The same help bare vapi shows, also as --help or -h

vapi pay also accepts --max-price-usd as a long-standing alias for --max; the two cannot be combined. When a paid call loses its response, vapi pay refuses to guess and names its receipt: vapi pay --resume <receipt-id> asks the token contract on that receipt's network, with EIP-3009 authorizationState(authorizer, nonce), whether the signed authorization was used. Settled means the payment went through — do not pay again. Expired means it was never used and the chain is past its validBefore, so it never can be — paying again is safe. Pending means it is unused but still valid — wait until the time it prints. It unlocks no wallet and signs nothing. EVM only for now; a Solana receipt says so, and a receipt written before 0.4.0 does not record the nonce. Every payment payload carries the client code vapi in the x402 builder-code extension. When the API advertises payment-identifier, vAPI generates one id, sends it with the payment, records it as paymentId on the receipt, and shows it again in vapi pay --resume. mcp --json is accepted as a no-op, because the stdio transport is already JSON-RPC.

Check your API

vapi check is a free x402 conformance doctor for the API you are building. It asks the URL for its price without paying, grades the 402 the way a client reads it, and looks for the origin's discovery documents:

vapi check https://weather.example/forecast
vapi check https://weather.example/alerts --method POST --json

Rule

Passes when

status

the URL answers HTTP 402

transport

the offer is readable: a base64 PAYMENT-REQUIRED header, a JSON body, or both; or the 402 carries an MPP (WWW-Authenticate: Payment) challenge, which is recognised but not payable with vapi pay

version

it declares x402Version 2 (1 is a warning: v2-only clients cannot pay it)

fields

every field that version requires is present and well-typed

scheme

at least one accepted option is exact

asset

an exact option pays canonical USDC, with USDC's EIP-712 domain, on Base, Arc mainnet, Arc testnet or Solana

pay_to

every exact option's payTo is a valid, non-zero address for its network

timeout

maxTimeoutSeconds is a whole number between 10 and 3600

extensions

the offer advertises Bazaar metadata; every known advertised extension is listed

discovery

/.well-known/x402 serves a JSON document (a warning otherwise)

openapi

a discovered OpenAPI document describes the operation with x-payment-info

The JSON report's transport field is x402 or mpp; x402-only rules are skipped for an MPP 402.

The extensions rule reports bazaar, builder-code, payment-identifier, sign-in-with-x, offer-and-receipt and auth-hints in that order. Missing Bazaar metadata is a bazaar_metadata_missing warning because adding it makes the API discoverable in Coinbase's Bazaar and by Coinbase for Agents. This local warning does not appear in the registry-shaped conformance.issues.

For OpenAPI, the check tries openapi.json beside the checked path and then in each ancestor directory, nearest first, ending at the origin's /openapi.json. If none has a paths object, it reads /.well-known/api-catalog and follows up to 10 RFC 9264 service-desc links in order. It only follows same-origin links.

Offer findings carry stable snake_case codes — v2_missing_resource, offer_header_only, v2_header_malformed, scheme_unsupported and the rest — the same codes the registry records for a listing and vapi inspect prints. --json returns the whole report, including the advertised extensions list and a conformance object in the registry's shape. The exit code is 0 when nothing failed, warnings included, 1 when a rule failed, and 2 for invalid usage. No wallet is opened, nothing is signed, and no registry is called: the only requests go to the origin being checked, through the same network guard as every other request.

In CI, the repository is also a GitHub Action that runs the published CLI:

- uses: vAPI-Network/vapi-network@main
  with:
    url: https://weather.example/forecast
    fail-on: warn # or fail, the default

It prints every rule, annotates failures and warnings, exposes the JSON report as the report output, and fails the step on a failed rule — or on a warning too, with fail-on: warn.

Publish an API

Listing on vAPI is permissionless. vAPI probes the URL you hand it; if it answers x402, the listing exists, and you decide when it goes live.

vapi auth set-key                        # paste the key from the console, once
vapi publish https://weather.example     # probe, pick endpoints, sign the payout wallet
vapi publish activate weather-call       # once the FeeSplitter is deployed
vapi publish verify-request weather-call # ask for the review that ends the [unverified] tag
vapi publish list

vapi publish takes an origin, a single endpoint, or an OpenAPI document, plus --mode when the registry should not have to guess which. It prints every probe step; on a refusal it prints the code, the reason and the hint, and exits 2 without signing anything. On a terminal it asks which endpoints to list — a script names them with --select forecast,alerts, or takes all of them with --yes, and then has to supply --name, --description and --category itself.

A listing holds at most 20 endpoints, so a larger catalog is published as several listings of up to 20 — Weather (1/4), Weather (2/4) and so on, in probe order — each with its own signature of the payout line, and every endpoint gets one result line: listed with its slug, failed with the reason, or pending when an earlier refusal stopped the run. A batch refused on its own merits does not stop the next one; a rejected key, a rate limit or an outage does. Run the same command again with --resume and it asks vapi publish list what this key already lists, skips those endpoints, and lists only the rest.

Your wallet signs one line, Confirm this wallet receives vAPI Call payouts, so the registry knows where the money goes. It is an EIP-4361 message bound to the registry's own host and to Base: nothing is paid, nothing is approved, and no key leaves the machine.

Payouts arrive through a FeeSplitter you own. Deploying it is a wallet transaction against the factory, so it stays in the console at <registry>/providers; vapi publish prints the address it will have on each network and the exact next step. An active listing answers vapi search --include-unverified, and vapi publish verify-request <slug> asks for the review that puts it in the default search.

Claim a listing vAPI indexed

vAPI mirrors public x402 catalogs, so your API may already be listed without you. If it is, you can own those listings instead of publishing new ones:

vapi claim https://weather.example --wallet payout   # the wallet the listings pay

The registry sends an EIP-4361 message bound to its own host and to Base, with the statement Claim the vAPI Call listings served from <origin>. vapi checks that the message says exactly that for this wallet before it signs anything, signs it the same way vapi publish signs its payout line, and the registry matches the signer against the listings' payTo. Every unowned indexed listing served from that origin that pays this wallet becomes yours; they stay paid directly to it with no vAPI fee, vapi publish list shows them, and vapi publish verify-request <slug> asks for review. A wallet that is not the payee, an origin with nothing to claim, and listings that already have an owner each get their own sentence and exit 1. Like publishing, claiming needs vapi auth set-key and has no MCP tool.

The API key is a secret like any other here. vapi auth set-key reads it from a prompt, never from an argument, and keeps it in the same OS secret store as your passphrase — or in ~/.vapi/config.json at mode 0600 on a platform that has none. VAPI_API_KEY is the route for CI. No agent can reach it: there is no publish tool on the MCP server, and the key lives behind its own package entry point that packages/mcp is forbidden to import.

MCP

Add this to Claude Code, Claude Desktop or Cursor:

{
  "mcpServers": {
    "vapi": {
      "command": "npx",
      "args": ["-y", "vapi-network", "mcp"]
    }
  }
}

Then hand the passphrase to your operating system once, in your own terminal:

vapi unlock            # the passphrase goes into the macOS Keychain or libsecret

The passphrase never leaves the machine and never has to appear in an editor's configuration file. vapi lock takes it back out. On Windows, until there is a Credential Manager path, keep using "env": { "VAPI_KEYSTORE_PASSWORD": "your-passphrase" } instead.

Give an agent its own capped wallet by creating it yourself and pinning the agent to it:

vapi wallet create agent-claude --label "claude code"
vapi wallet caps agent-claude --per-day 5
vapi unlock --wallet agent-claude
{
  "mcpServers": {
    "vapi": {
      "command": "npx",
      "args": ["-y", "vapi-network", "mcp", "--wallet", "agent-claude"],
      "env": {
        "VAPI_WALLET": "agent-claude",
        "VAPI_NO_SECRETS": "1"
      }
    }
  }
}

The agent pays from agent-claude and no more than $5 a day, whatever it asks for; your own wallet is not reachable from that session, because only agent-claude was unlocked. vapi wallet list shows which wallets are unlocked that way.

Tools

Tool

Input

Result

auth.link

label

Starts linking this wallet to a person's vAPI account for browser approval with default permissions only; returns the code and URL

auth.status

wallet

Link status, owner, label, permissions, Router-key presence, and any pending browser approval

router.models

—

Model ids available from vAPI Router

router.usage

wallet

Router usage plus the linked owner's stake and stake-funded Compute

router.chat

model, messages[], max_tokens, wallet

Completion content, resolved model, and token usage; the Router key stays in the OS secret store and is never returned

router.buy

usd (1, 5, 20, or 50), wallet

Receipt summary and the new vAPI Router balance; the Router key stays in the OS secret store and is never returned

call.search

query, kinds[], network, limit, cursor, includeUnverified

One discovery page: items with group, fee and verification, plus nextCursor, unavailableKinds, rankingVersion

call.inspect

id, endpoint

A listing's verification, fee, liveness, conformance, identity, request contract and live quote

call.pay

wallet, id or url, method, endpoint, body, contentType, network, expectedPayTo, maxPriceUsd

wallet, status, body, payment, verification for a registry listing, and expectedRequest when a 402 named one

wallet.address

wallet

wallet, address

wallet.balance

wallet

wallet, address, balances[] per configured network

wallet.accounts

wallet

wallet, accounts[] with USDC, gas balance and deposit guidance

wallet.list

—

wallet, default, and every wallet with caps in atomic USDC and dollars, balances, and balanceError when an RPC is unreachable

wallet.use

name

wallet, active, previous, scope: "session"

wallet.fund

wallet, amountUsd

wallet, address, network, url, instructions

receipts.list

wallet, allWallets, limit

wallet, receipts[]

receipts.stats

wallet, allWallets, range

wallet, range, generatedAt, totals, outcomes, latency, topServices, search

support.report

message, includeAddresses, send

path, issueUrl, the report itself, and responseCode when sent

wallet is optional on every tool that takes it: without it the session's active wallet is used, then VAPI_WALLET, then the machine default. Every result names the wallet it used. call.pay applies that wallet's own spend caps before it signs and tags the receipt with its name. When a paid call's outcome is uncertain, call.pay fails with settlement_unknown, says not to retry automatically, and names the vapi pay --resume <receipt-id> that settles the question on-chain. wallet.use moves the session onto another wallet for this process only — it never rewrites wallets.json, so your own terminal keeps the default you chose — and appends a wallet.use.session line to the audit log. Reads need no passphrase at all.

Spend caps default to $0.10 per call and $1.00 per day.

call.search returns vAPI-verified listings plus mirrored external catalogs; includeUnverified: true adds unverified self-listed APIs, which passed vAPI's automated x402 probe but were not reviewed. Every result of call.search, call.inspect and call.pay carries verification, one of "none", "requested" or "verified" — a mirrored external row is always "none". Prefer a verified listing, and read the request contract and the price with call.inspect before paying one that is not.

Deprecated tool aliases

These four pre-namespace names still work and still behave identically, but each result carries one DEPRECATED: line naming its replacement. They will be removed in a later release.

Alias

Use instead

search

call.search

inspect

call.inspect

call

call.pay

wallet

wallet.balance

What the MCP server cannot do

Create, rename, remove, restore, back up, import or export a wallet, or return a recovery phrase, a private key or a passphrase. Those stay in the CLI, in front of a person.

SDK

createVapiClient

Install the bundled client when you want Call, vAPI Router and local agents behind one wallet-bound API:

npm i vapi-network
import { createVapiClient } from "vapi-network";

const vapi = await createVapiClient({ wallet: "researcher" });
const reply = await vapi.router.chat({
  model: "openai/gpt-5-mini",
  messages: [{ role: "user", content: "Summarise Base DEX activity." }],
});
console.log(reply.content);

const listings = await vapi.call.search("Base DEX volume");
console.log(listings);
const result = await vapi.call.pay({ id: "base-dex-volume", maxPriceUsd: 0.05 });
console.log(result.body);

const purchase = await vapi.router.buy(5);
console.log(purchase.balance);

await vapi.router.openai() returns the vAPI Router base URL and key for an OpenAI-compatible framework. Use this in your own code. Do not pass it into a model prompt.

Bought vAPI Router balance never expires and is used after the daily Compute allowance runs out. Your wallet's client spend caps apply to every purchase, including automatic refill.

Lower-level packages

npm i @vapi-network/core @vapi-network/sources

@vapi-network/core is the main entry point: the x402 protocol, the wallet store, spend policy, the network guard, discovery merge and the receipt ledger. @vapi-network/core/secrets is a second, deliberately separate entry point for the four functions that return a recovery phrase or a private key — exportRecoveryPhrase, exportKeystoreKeys, createKeystoreWithPhrase and decryptPrivateKey. They are not re-exported from the main index, and the MCP package is forbidden by lint to import them, so a surface an agent drives cannot reach a secret by accident.

Search and pay:

import {
  LocalWallet,
  SpendPolicy,
  WalletStore,
  getVapiPaths,
  loadConfig,
  resolvePassphrase,
  spendCapsForWallet,
} from "@vapi-network/core";
import { vapiRegistrySource } from "@vapi-network/sources";

const paths = getVapiPaths();
const config = await loadConfig(paths.config);
const [listing] = await vapiRegistrySource(config.marketplaceDiscoveryUrl, {
  discoveryUrl: config.discoveryUrl,
}).search("weather");

const store = await WalletStore.open(paths.directory);
const { name } = store.resolve();
const account = await store.unlock(name, (await resolvePassphrase(name)).passphrase);
const wallet = new LocalWallet(
  account,
  new SpendPolicy(await spendCapsForWallet(store, name), {
    ledgerPath: paths.ledger,
    wallet: name,
  }),
);

Manage wallets:

import { WalletStore, getVapiPaths, usdToAtomic } from "@vapi-network/core";

const store = await WalletStore.open(getVapiPaths().directory);
for (const wallet of await store.list()) {
  console.log(wallet.name, wallet.address, wallet.isDefault, wallet.spendCaps);
}
await store.setSpendCaps("agent", {
  perCallAtomic: String(usdToAtomic(0.05)),
  perDayAtomic: String(usdToAtomic(1)),
});

WalletStore is the one entry point for the layout: open, list, resolve, create, importKey, unlock, setDefault, rename, setLabel, setSpendCaps, remove, restore and listTrash. It never rewrites key material; it moves, names and caps keystores.

Two runnable examples live in examples/: pay-with-sdk.ts (pnpm example:pay -- weather) and wallets.ts (pnpm example:wallets -- agent-demo).

Configuration

Local state lives in ~/.vapi/:

config.json           registry URLs, networks and RPC endpoints
wallets.json          which wallet is the default, plus per-wallet caps and labels
wallets/
  main.json           one encrypted keystore per wallet, mode 0600
  .trash/             removed wallets, kept encrypted, never deleted for you
keystore.json         a 0600 symlink to wallets/main.json, for one release
audit.log             one JSON line per secret export or wallet change
receipts.jsonl        the append-only call ledger
searches.jsonl        one line per discovery query
spend-ledger.json     today's total, per wallet
reports/              what vapi report writes

Variable

What it does

VAPI_HOME

Use a different directory instead of ~/.vapi

VAPI_WALLET

The wallet to use when no --wallet is given

VAPI_REGISTRY_URL

Replace the registry base; the canonical discovery paths derive from it

VAPI_KEYSTORE_PASSWORD

The passphrase, for CI and for Windows. Checked before the OS secret store.

VAPI_API_KEY

The registry key vapi publish authenticates with, for CI

VAPI_NO_SECRETS

Set to 1 to stop vapi backup and vapi export-key printing anything

ARC_RPC_URL overrides the Arc mainnet RPC. ARC_TESTNET_RPC_URL and SOLANA_RPC_URL point those two networks at an endpoint you trust. On first use of the default home, the client copies an existing ~/.vapi/agent-cash/ configuration into ~/.vapi/ when it can do so without overwriting files, prints a notice, and leaves the old directory alone.

Funding

vapi fund                 # open the funding page for your address
vapi fund --amount 25     # prefill a US dollar amount
vapi fund --json          # { address, network, url }

vapi fund prints <registry>/fund/<your-address> and opens it in your default browser when you are on a terminal. The page offers three routes: a card via Coinbase (needs a Coinbase account; US guest checkout), a transfer from MetaMask, Coinbase Wallet or WalletConnect, or a bridge from another chain. It is public, takes no sign-in, and mints the card session when you click — so the link keeps working while you log in, and nothing expires in your scrollback.

The command itself makes no network call: it works offline, and it always also prints

Send USDC on Base (eip155:8453) to this address; add a little ETH for gas if you plan to sweep.

Whichever route you pick, the USDC lands on your local address on Base. vAPI never holds your funds, never proxies the payment, and never sees your card details or your private key. MCP clients use wallet.fund, which returns the same { address, network, url } plus a line telling the agent to hand the link to its human.

Networks and accounts

Network

x402 identifier

USDC

Gas / RPC notes

Base mainnet

eip155:8453

0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913

ETH; defaults to https://mainnet.base.org

Arc mainnet

eip155:5042

0x3600000000000000000000000000000000000000

USDC is also the gas token; defaults to https://rpc.mainnet.arc.io

Arc testnet

eip155:5042002

0x3600000000000000000000000000000000000000

USDC is also the gas token; set ARC_TESTNET_RPC_URL

Solana mainnet

solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d

EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v

SOL; defaults to https://api.mainnet-beta.solana.com

The x402 reference packages shorten the Solana CAIP-2 reference to solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp; vAPI accepts that identifier as an alias while persisting the full genesis hash above. Enable Arc mainnet with vapi init --networks base,arc (or the same --networks value on vapi wallet create or vapi import), vapi accounts --enable arc, or ARC_RPC_URL. Its explorer is https://explorer.arc.io.

vapi accounts lists one deposit account per configured network: its CAIP-2 ID, network name, address, atomic and formatted USDC balance, gas-token balance, and deposit guidance. Account lookup dispatches by CAIP namespace, so the EVM and Solana adapters stay independent of one another. For Arc testnet, add the faucet to that network's config.json entry:

{
  "depositUrl": "https://your-arc-faucet.example",
  "depositInstructions": "Use the configured Arc testnet faucet, then send USDC to this address."
}

Arc mainnet uses the default public RPC or ARC_RPC_URL and needs no faucet configuration.

Create both local accounts at initialization with vapi init --networks base,solana, or add an Ed25519 account to an existing keystore with vapi accounts --enable solana. Fund the printed Solana address with SPL USDC. Exact x402 payments use the facilitator advertised in the challenge as fee payer, so they do not consume the local SOL balance; vapi sweep is a separate transaction and does need a little SOL. The default public Solana RPC is rate-limited and has no availability guarantee; set SOLANA_RPC_URL to a dedicated endpoint for regular use.

Discovery sources

Discovery is a plugin interface. The client merges listings from several sources, de-duplicates them by normalized resource URL, and keeps each listing's provenance:

  • vAPI Registry, enabled by the default distribution, using https://api.vapinetwork.ai/api/call/discovery and https://api.vapinetwork.ai/api/call/services. If the primary returns HTTP 404 or cannot be resolved, the client logs one notice and tries the hosts in registryFallbacks on the same canonical paths. The historical /api/marketplace/discovery and /api/network/services paths are deprecated; a base URL supplied on either is normalized to the canonical pair.

  • Coinbase Bazaar, the public x402 v2 /discovery/resources catalogue exposed by a facilitator.

  • Local file, a JSON array of listings for private or development catalogues.

  • x402scan, a deliberate stub until x402scan documents a stable public read API this client can safely target.

Use @vapi-network/sources to compose only the catalogues you trust. Every outbound request is guarded against local and private destinations before it is made, and redirects and resolved IP addresses are re-validated as new destinations.

Sign-in with X

x402 v2 services can require Sign-In-With-X (SIWX) before returning a price. When vapi pay or call.pay receives that challenge, vAPI checks that both the challenge domain and URI match the final resource origin, signs the canonical EIP-4361 message locally with EVM personal_sign, and retries once with SIGN-IN-WITH-X. The proof is never sent to a redirect or a different host.

If the retry returns a normal 402 quote, the usual spend-policy and payment flow continues. If the resource is free after sign-in, the result has outcome: "signed_in" and the local receipt records amountAtomic: "0".

Metrics and bug reports

vAPI measures call and discovery health locally and uploads nothing. Receipts can include the listing name and provider host, policy decision, retry count, client version, outcome, total latency, and discovery, quote, signing, request and settlement phase timings. Policy declines are recorded with the quoted amount but without a payer or transaction, so blocked spend stays visible without creating a payment authorization. Search events record the query, sources tried, per-source latency and result count, merged result count and timestamp.

vapi stats --range 7d
vapi receipts export --format csv --range 30d

When the registry reports it, vapi stats also shows the network-wide amount routed through vAPI for 24h and 30d in USD plus the 30d transaction count. This covers all vAPI clients, not just this wallet.

vapi report "<what happened>" writes $VAPI_HOME/reports/<timestamp>.json and prints that path plus a prefilled GitHub issue URL. Reports contain the message, client version, OS and Node information, and only the newest five receipt IDs. Wallet and payee addresses are included only with --include-addresses; amounts never are. Nothing is uploaded unless --send is explicit.

Packages

Package

Purpose

vapi-network

zero-dependency distribution with the vapi binary and defaults

@vapi-network/core

x402 protocol, wallet store, spend policy, discovery merge and receipts

@vapi-network/sources

vAPI Registry, Coinbase Bazaar, local-file and x402scan source adapters

@vapi-network/mcp

stdio MCP server with namespaced payment tools

@vapi-network/cli

command parsing and the command implementations behind vapi

Every npm tarball is bundled and has zero runtime dependencies. The scoped packages are useful for embedding; most users should start with vapi-network.

Roadmap

  • A local gateway daemon (vapi serve) with per-key budgets

  • OpenTelemetry traces and metrics, with exporters enabled only by the user

  • task.* tools for posting, funding, delivering and reviewing work

  • compute.* tools for discovering models and paying for inference

Until the gateway lands, vapi serve and the provider-side vapi publish exit with a preview message. Payments stay direct from the local wallet to each service.

Development

A pnpm workspace for Node.js 22 and pnpm 10. The gate, in order:

pnpm install
pnpm format:check
pnpm lint
pnpm typecheck
pnpm test
pnpm build
pnpm pack:check

All five packages share one version and are released together. npm reads a package manifest before lifecycle hooks run, so the release is made from the generated publish/ directories, never from the workspace package roots.

pnpm install --frozen-lockfile
pnpm pack:check
npm login
pnpm --dir packages/core publish:npm
pnpm --dir packages/sources publish:npm
pnpm --dir packages/mcp publish:npm
pnpm --dir packages/cli publish:npm
pnpm --dir packages/vapi-network publish:npm

publish:npm publishes a package's staged ./publish directory to the latest npm tag; publish:npm:next publishes the same tarball to next. The manual release workflow verifies artifacts and prints these commands; it never receives npm credentials and never publishes.

See AGENTS.md for layout and invariants, CONTRIBUTING.md for the workflow, CHANGELOG.md for what shipped in each release, and SECURITY.md for private vulnerability reports.

License

Apache-2.0. See LICENSE.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Marketplace MCP for paid HTTP APIs. Pay per call in USDC on Base via the open x402 standard — non-custodial. 13 tools for discovery, buying, and publishing APIs.
    7
    45 npm
    2
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables MCP-compatible agents to discover and call x402 paid services from a directory of over 2,000 APIs.
    Apache 2.0
  • A
    license
    Not graded
    quality
    F
    maintenance
    MCP server for the x402 protocol that lets AI agents discover and call payment-gated HTTP APIs automatically.
    113 npm
    Apache 2.0
  • A
    license
    Not graded
    quality
    C
    maintenance
    An MCP server for agentic commerce, enabling AI agents to discover services, make x402 payments with USDC across multiple chains, and manage crypto wallets and token swaps.
    415 npm
    1
    MIT