This document is the canonical contract for the paybond CLI shipped by both kits:
- TypeScript:
@paybond/kitexposes thepaybondbinary. - Python:
paybond-kitexposes thepaybondbinary.
Both implementations must expose the same command names, global flags, JSON output keys, exit codes, error categories, and redaction behavior. Parity tests compare --help, JSON schemas, and error handling across both kits.
Legacy entry points remain as thin wrappers over this tree:
| Legacy alias | Canonical command |
|---|---|
paybond-kit-login | paybond login |
paybond-init / paybond-kit-init | paybond init guardrail |
paybond-mcp-server | paybond mcp serve |
Design rules
- Tenant scope always comes from authenticated credentials (
PAYBOND_API_KEYor the configured env file). Normal tenant-scoped commands must not accept tenant IDs from unauthenticated CLI arguments. - Human output defaults to safe, masked values suitable for terminals and CI logs.
- Machine output uses
--format jsonand returns stable, documented keys. JSON mode never omits required envelope fields. - Destructive or live-money actions require
--yesand are still subject to server-side RBAC. - Secrets on disk use file mode
0600. Default.env.localtargets are added to.gitignorewhen needed. Custom env-file paths inside a git repo must already be ignored.
Global flags
These flags are accepted on every subcommand unless a command explicitly documents an exception. Global flags may appear before or after the subcommand.
| Flag | Default | Description |
|---|---|---|
--gateway <url> | https://api.paybond.ai | Gateway base URL for authenticated API calls. |
--env-file <path> | .env.local | Local secrets file containing PAYBOND_API_KEY. Login writes here; other commands read from here unless the key is already in the process environment. |
--format table|json | table | Human table output or stable JSON envelope (see below). |
--profile <name> | (unset) | Named credential profile stored in the Paybond CLI config file. When set, overrides --env-file for credential resolution. |
--request-id <id> | (generated) | Correlation ID sent to Gateway on API calls and echoed in JSON output. When omitted, the CLI generates a ULID-style identifier. |
--yes | false | Skip interactive confirmation for destructive or irreversible operations. |
--no-open | false | Do not open a browser for device login or other browser-assisted flows. |
Commands may define additional flags. Command-specific flags must not redefine global flag names with different semantics.
Output formats
Table (default)
- Write human-readable lines to stdout.
- Write errors to stderr.
- Apply redaction rules to every field before printing.
- Do not print raw API keys, capability tokens, signing seeds, or Harbor bearer tokens.
- Success lines should be concise and actionable (for example, masked key identity, resource IDs, and next steps).
JSON (--format json)
Every JSON response uses one envelope regardless of command:
{
"ok": true,
"command": "whoami",
"data": {},
"warnings": [],
"request_id": "01JABY5EXAMPLE",
"error": null
}
On failure:
{
"ok": false,
"command": "keys revoke",
"data": null,
"warnings": [],
"request_id": "01JABY5EXAMPLE",
"error": {
"category": "forbidden",
"code": "cli.rbac.denied",
"message": "caller lacks keys:revoke permission",
"details": {
"gateway_status": 403,
"gateway_code": "auth.forbidden"
}
}
}
Envelope fields:
| Field | Type | Description |
|---|---|---|
ok | boolean | true on success, false on failure. |
command | string | Canonical command path (for example, login, whoami, audit exports list). |
data | object | null | Command payload on success; null on failure. |
warnings | string[] | Non-fatal notices (for example, skipped browser open). |
request_id | string | Correlation identifier for support and log correlation. |
error | object | null | Populated on failure; null on success. |
Error object fields:
| Field | Type | Description |
|---|---|---|
category | string | Stable error class (see error categories). |
code | string | Stable machine-readable code (cli.* for local errors; Gateway error.code when proxied). |
message | string | Safe, loggable summary without secrets. |
details | object | Optional structured context (HTTP status, resource IDs, confirmation hints). |
JSON mode writes the envelope to stdout only (including failures). Exit code still reflects success or failure.
Exit codes
| Code | Meaning |
|---|---|
0 | Success. |
1 | General failure: usage error, validation error, business-state conflict, or unclassified CLI error. |
2 | Authentication failure: missing, invalid, or expired credentials. |
3 | Authorization failure: authenticated but RBAC or entitlement denied. |
4 | Confirmation required: destructive command run without --yes. |
5 | Gateway or upstream unavailable (503, network failure, timeout). |
6 | Local environment failure: unreadable env file, git-ignore refusal, filesystem permission error. |
When Gateway returns a structured error, the CLI maps HTTP status to exit code:
| HTTP status | Exit code | Error category |
|---|---|---|
400, 409, 422, 428 | 1 | validation |
401 | 2 | auth |
403 | 3 | forbidden |
404 | 1 | not_found |
410 | 1 | gone |
429 | 5 | rate_limit |
500, 502, 503, 504 | 5 | gateway |
Error categories
| Category | When used |
|---|---|
usage | Unknown command, invalid flag, or missing required argument. |
auth | Credential missing, malformed, expired, or rejected by Gateway principal lookup. |
forbidden | Authenticated caller lacks the required role or entitlement. |
validation | Request rejected because of invalid input or incompatible resource state. |
not_found | Resource absent in the authenticated tenant scope. |
gone | Temporary download or export no longer available. |
confirmation_required | Destructive command attempted without --yes. |
rate_limit | Gateway rate limit exceeded. |
gateway | Upstream Gateway or dependency error. |
network | Transport failure before a structured Gateway response is received. |
environment | Local filesystem, git-ignore, or permission failure. |
internal | Unexpected CLI bug; should be rare. |
Redaction rules
Apply these rules in table output and in any human-readable log lines. JSON data fields follow the per-command schema below; secret fields are omitted or replaced with masked placeholders unless the command explicitly documents a full-value JSON field for automation.
| Material | Table output | JSON data |
|---|---|---|
API key (paybond_sk_*) | Mask to paybond_sk_{env}_{first8}...{last4}; fallback paybond_sk_... | key_masked in list/read responses; include one-time plaintext api_key only for keys create and keys rotate when the Gateway returns a new secret; never include the raw login secret in JSON (login uses key_written instead) |
| Capability token | Never print | capability_token allowed only when the command creates or returns a new token for immediate use (for example, guardrails bootstrap); otherwise omit |
| Harbor / Gateway bearer token | Never print | Never include |
| Signing seed / private key material | Never print | Never include |
| Device codes during login | Print user_code and verification URL (required for approval); never print device_code | Include user_code and verification_uri; omit device_code |
request_id from Gateway | Print when present | Always echo in envelope and in proxied error details |
Key masking algorithm (both kits must match):
- Split the key on
_. - When the shape is
paybond_sk_{environment}_{key_id}_{secret}, emitpaybond_sk_{environment}_{key_id[0:8]}...{key_id[-4:]}whenlen(key_id) > 12, otherwisepaybond_sk_{environment}_redacted. - Otherwise emit
paybond_sk_....
Command tree
Top-level invocation:
paybond [--global-flags] <command> [<subcommand> ...] [args] [--command-flags]
paybond --help
paybond <command> --help
paybond login
Sandbox device login. Writes PAYBOND_API_KEY to the env file.
| Flag | Default | Notes |
|---|---|---|
--env sandbox | sandbox | Only sandbox is supported. Live device login is rejected. |
--force | false | Replace an existing PAYBOND_API_KEY in the target env file. |
Inherits global flags --gateway, --env-file, --no-open, --format.
Table output (success): verification URL, user code, env file path, masked key, target tenant, optional expiry notice.
login is the one command that intentionally performs a local secret write; it still never prints the raw key.
JSON data fields:
| Field | Type |
|---|---|
env_file | string |
key_masked | string |
key_written | boolean |
tenant_id | string |
tenant_uuid | string |
environment | string |
expires_at | string (RFC 3339, optional) |
verification_uri | string |
user_code | string |
paybond init guardrail
Scaffold a sandbox paid-tool guardrail integration file.
| Flag | Default |
|---|---|
--preset paid-tool-guard | paid-tool-guard |
--framework <name> | provider-agnostic |
--out <path> | paybond-paid-tool-guard.ts or paybond_paid_tool_guard.py |
--force | false |
Allowed --framework values: generic, provider-agnostic, openai, claude, anthropic, gemini, google-ai, vercel-ai, langgraph, mcp.
JSON data fields: out, preset, framework, bytes_written.
paybond mcp
| Subcommand | Description |
|---|---|
serve | Start the stdio MCP server (replaces paybond-mcp-server). |
install | Write MCP host configuration for Claude, Codex, OpenAI, or generic stdio clients. |
tools | List tools exposed by the local MCP server. |
mcp install flags:
| Flag | Default |
|---|---|
--host claude|codex|openai|generic | (required) |
--scope local|project|user | project |
--env-file <path> | .env.local |
Generated MCP configs must reference PAYBOND_ENV_FILE by default, not embed raw API keys.
JSON data fields (install): host, scope, config_path, server_command, printed. When printed is true (local scope) and --format json is set, payload contains the generated config text.
paybond doctor
Validate local runtime, package version, env file, key shape, principal lookup, and optional agent/MCP setup.
| Flag | Default |
|---|---|
--agent | false — when set, also validate MCP server startup and tool listing. |
JSON data fields: checks[] with { name, ok, message, details? }, summary (pass | fail).
paybond config
| Subcommand | Description |
|---|---|
get <key> | Read a config value (sensitive values are redacted using the same rules as list). |
set <key> <value> | Write a config value. |
unset <key> | Remove a config value. |
list | List all config entries (values redacted when sensitive). |
Config is profile-scoped when --profile is set.
paybond whoami
Resolve the authenticated principal and tenant realm.
JSON data fields: tenant_id, tenant_uuid, environment, service_account_role, principal (Gateway principal payload, secrets stripped).
paybond keys
| Subcommand | Description | Destructive |
|---|---|---|
list | List service-account keys for the tenant. | |
create | Create a new key. | |
rotate <key_id> | Rotate an existing key. | yes — requires --yes |
revoke <key_id> | Revoke a key. | yes — requires --yes |
JSON data fields (list): keys[] with key_id, key_masked, role, created_at, expires_at, status.
JSON data fields (create, rotate): key_id, key_masked, plus one-time plaintext api_key when the Gateway returns a newly minted secret.
paybond intents
| Subcommand | Description |
|---|---|
list | List intents. |
get <intent_id> | Fetch one intent. |
create | Create an intent. |
fund <intent_id> | Request funding. |
evidence <intent_id> | Submit evidence. |
settlement-confirm <intent_id> | Confirm settlement. |
Subcommands accept Gateway-aligned flags for amounts, rails, and idempotency keys. --request-id is forwarded as a correlation header.
paybond guardrails
| Subcommand | Description |
|---|---|
bootstrap | Bootstrap a sandbox guardrail intent and capability. |
evidence | Submit sandbox guardrail evidence. |
JSON data fields (bootstrap): tenant_id, intent_id, capability_token, operation, requested_spend_cents, sandbox_lifecycle_status.
paybond spend authorize
Authorize delegated spend for a tool call.
JSON data fields: authorized, intent_id, operation, requested_spend_cents, deny_reason (when not authorized).
paybond signal
| Subcommand | Description |
|---|---|
reputation | Read reputation summaries. |
portfolio | Read portfolio summaries. |
fraud | Read fraud signals. |
Each subcommand accepts resource selectors documented in the SDK references.
paybond receipts
| Subcommand | Description |
|---|---|
get <receipt_id> | Fetch a receipt. |
verify <receipt_id> | Verify receipt signatures and binding. |
paybond mandates
| Subcommand | Description |
|---|---|
verify | Verify a mandate artifact. |
import | Import a mandate into the tenant. |
paybond a2a
| Subcommand | Description |
|---|---|
card | Agent card discovery and validation. |
contracts | Contract listing and inspection. |
paybond audit exports
| Subcommand | Description | Destructive |
|---|---|---|
list | List compliance audit export jobs. | |
get <job_id> | Fetch export job status and download pointers. | |
verify <path> | Verify a downloaded export bundle locally. | |
delete <job_id> | Delete an export job. | yes — requires --yes |
JSON data fields (list): exports[] with job_id, status, created_at, expires_at, scope.
Security invariants
Both kits must enforce:
- No live device login — reject
--env liveand hidden live flags. - Git-ignore gate — refuse to write secret files that are not ignored inside a git repository (except when adding the default
.env.localto.gitignore). - 0600 secret files — env files containing API keys are written with owner-read/write only.
- No tenant override — reject
--tenant-idand similar unauthenticated tenant selectors on normal commands. - Confirmation gate — refuse destructive subcommands without
--yes. - Operator key expectation for login — reject device-login tokens whose role is not
operator.
Parity and testing
Both kits must maintain:
- Snapshot parity for
paybond --helpand each subcommand--help. - JSON shape parity for
whoami,doctor,keys list,guardrails bootstrap, and representative error cases. - Identical exit codes and error categories for the same failure inputs.
- Identical key masking output for the same raw key material.
Run kit tests from the repository root:
cd kit/ts && npm run test
cd kit/python && uv run pytest