@paybox-sh/sdk
v1.0.0
Published
Typed Node SDK + `paybox` CLI for the PayBox agent API, with REST core operations and runtime MCP plugin discovery and invocation.
Readme
@paybox-sh/sdk
Typed Node SDK and paybox CLI for the PayBox agent API. It mirrors the MCP tool
surface — list_credentials, request_payment, claim_payment_credentials,
request_wallet_sign, request_secret, request_swap, discover_services,
pay_x402, use_service, get_portfolio, get_buy_link,
request_account_change, get_request, and the generic plugin discovery,
contract, and invocation flow. Core operations use the REST API; plugins use
the same MCP tools as connector hosts, with no hardcoded per-plugin paths.
Wallet signing is non-custodial and runs in-process: the SDK reuses the exact
signing core from the wallet-sign iframe (@paybox/mcp-app), builds the chain
digest, and ed25519-signs the MoonX envelope with your pbxk1. key. The MoonX
secret never reaches the client — the secret-gated /sign happens server-side.
Install
pnpm add @paybox-sh/sdk # library
npx @paybox-sh/sdk login # or use the CLI directlyDriving this CLI from an agent? SKILL.md ships in the package and
describes the install and login flow as an agent skill: what the human has to do
(approve wallets, paste one signing key), and what the agent should not do
around it. Use the CLI on Muse, Grok Bot, Grok Computer, and any agent on a
computer with no connector screen — not the grok.com MCP connector path.
CLI
# Authenticate: OAuth in the browser (approve with a passkey), or store a key.
# `paybox login` is a guided two-step: OAuth, then it opens the app to provision
# a wallet signing key and prompts you to paste the `pbxk1.` token back.
# The OAuth step is a device-code grant, so the browser that approves does not
# have to be on the machine running the CLI.
paybox login # device-code OAuth, then provision a key
paybox login --no-provision # OAuth only, skip the key step
paybox login --key pbx_live_… # personal API key instead (no OAuth)
PAYBOX_SIGNING_KEY=pbxk1.… paybox login # supply the signing key non-interactively
paybox credentials # list usable credentials
# `--credential <id>` below is the `credential.id` from that list, not the wallet name.
paybox pay --credential <id> --merchant Acme --url https://acme.com --amount 1999
paybox secret --credential <id> --purpose "deploy" # one-time token
paybox secret --credential <id> --raw # raw value (grant-gated)
paybox sign --credential <id> --intent '{"op":"message","message":"gm"}'
paybox swap --credential <id> --src-chain eip155:8453 \
--src-token native --dst-token 0x… --amount 1000000000000000
paybox portfolio --address 0x… --networks 1,8453
paybox discover "weather api" # browse curated paid x402 services
paybox discover --raw "weather api" # debug: uncurated compact summaries
paybox pay-x402 --credential <id> --url https://svc/api --accepts @accepts.json
paybox use-service --credential <id> --url https://svc/api # paybox pays + fetches
paybox claim <request_id> # claim an approved payment's card (one-time)
paybox buy-link --credential <id> --amount-usd 50 # defaults EVM funding to Base
paybox buy-link --credential <id> --destination eip155:1 --currency eth
paybox account-change --note "need a wallet grant" # manage_url for the user to open
paybox request <request_id> --wait # poll a pending request
# Plugins (enable them in the paybox app first)
paybox plugins list --status enabled
paybox plugins contract paybox://plugins/world
paybox plugins use world world_get_events --input '{"status":"active"}'
paybox version # installed version (+ update check)
paybox update # update the global install to latest
paybox uninstall # clear stored config (prints npm rm)
paybox --json credentials # machine-readable output on any commandversion/update/uninstall are conveniences over npm: update runs
npm i -g @paybox-sh/sdk@latest (use --print to just show it); uninstall
clears ~/.config/paybox and prints the npm rm -g line (a package can't remove
its own binary). For project installs use pnpm up/pnpm rm, or npx
@paybox-sh/sdk@latest to always run the latest without installing.
sign, swap, pay-x402, and use-service can complete in-process with a
configured pbxk1. key. Plugin calls return the raw MCP tool result.
For a Solana swap the SDK re-pins a fresh recent blockhash immediately before
signing (parity with the signing window — the quote-time blockhash expires
within ~60–90s, often before signing), so a swap that dwells for approval no
longer broadcasts as BlockhashNotFound. EVM swaps are unaffected.
pay-x402 is header mode — on success output.value.x_payment is the X-PAYMENT
header to replay on the original request. use-service is gateway mode — PayBox
makes the paid call for you and returns the resource's reply on
output.value.response. Pass --accepts from the resource's own 402 (start from
the curated Bazaar set with paybox discover).
Library
import { PayboxClient } from "@paybox-sh/sdk";
const paybox = PayboxClient.fromConfig(); // reads ~/.config/paybox + env
// or: new PayboxClient({ apiKey: "pbx_live_…", signingKey: "pbxk1.…" });
const { credentials, ungranted } = await paybox.listCredentials();
const pay = await paybox.requestPayment({
credentialId: credentials[0].credential.id,
merchant: "Acme",
merchantUrl: "https://acme.com",
amountCents: 1999,
});
// Ungranted credentials the user owns but hasn't granted this connector — pass
// an id to request_account_change's `add` to ask for access.
console.log(ungranted.map((c) => `${c.name} (${c.kind})`));
const sig = await paybox.requestWalletSign({
credentialId: walletId,
chain: "eip155:8453",
intent: { op: "message", message: "gm" },
}); // signs in-process if a key is set
// x402: discover a paid endpoint, then pay it — header mode (you replay the
// X-PAYMENT header) or gateway mode (paybox fetches the paid content for you).
const services = await paybox.discoverServices("weather api");
const rawServices = await paybox.discoverServices("weather api", { raw: true }); // debug only
const paid = await paybox.useService({ credentialId: walletId, url: services[0].resource });
// paid.response.output.value.response holds the resource's reply
const plugins = await paybox.discoverPlugins({ status: "enabled" });
const selected = plugins.plugins.find((plugin) => plugin.id === "aave");
const contract = await paybox.getPluginContract(selected!.contract_uri!);
const result = await paybox.usePlugin({
pluginId: contract.contract.id,
toolId: contract.contract.tools[0].id,
input: {},
});Configuration
Resolution order: explicit constructor options → environment → stored config.
| Env | Meaning |
|-----|---------|
| PAYBOX_API_URL | API origin (default https://api.paybox.sh). |
| PAYBOX_API_KEY | A pbx_live_* personal API key. |
| PAYBOX_ACCESS_TOKEN | A raw OAuth access token. |
| PAYBOX_SIGNING_KEY | A pbxk1. wallet signing key (enables sign/swap). |
| PAYBOX_CONFIG_DIR | Override the config dir (default ~/.config/paybox). |
paybox login writes ~/.config/paybox/config.json (mode 0600 on
macOS/Linux). On Windows the POSIX mode bits don't restrict access — the file
is not access-protected there, so prefer PAYBOX_API_KEY / PAYBOX_SIGNING_KEY
env vars on shared Windows machines.
Develop
pnpm-lock.yaml also contains the ../mcp-app importer because this SDK
consumes the iframe's signing core through file:../mcp-app. The iframe build
shares this dependency resolution but keeps its installed modules separate.
pnpm install
pnpm typecheck
pnpm build # tsup → dist/ (ESM + types + paybox bin)
# Refresh the generated tool types from a running server (the source of truth):
PAYBOX_MCP_URL=http://127.0.0.1:8080/mcp PAYBOX_API_KEY=pbx_live_… pnpm genThe argument and response types in src/generated.ts mirror the server's MCP
tool schemas (schemars-derived) and server/src/models.rs. pnpm gen reads the
live list_tools schemas so the client can't drift from the server contract.
