paybondpaybond
Sign in

Capabilities

What capability tokens are, how Paybond mints them, how to obtain them in sandbox and production flows, and how to verify them before a tool runs.

A capability token is an intent-scoped spend authorization credential. Paybond mints it when an intent reaches a funded state (or through the sandbox guardrail bootstrap path). Your application passes it with intent_id into paybond.spendGuard(...) / paybond.spend_guard(...) so Gateway POST /verify can authorize a specific tool operation before side-effecting work runs.

Capabilities are not API keys, not model-provider credentials, and not something you generate locally or store long-term in .env as CAPABILITY_TOKEN.

What you need at runtime

Every guarded tool call needs both:

ValueSource
intent_idIntent create, sandbox bootstrap, or MCP bootstrap response
capability_tokenReturned alongside that intent when it is funded or bootstrapped

TypeScript:

const guard = paybond.spendGuard(intentId, capabilityToken);
await guard.authorizeSpend({
  operation: "travel.book_hotel",
  requestedSpendCents: 20_000,
});

Python:

guard = paybond.spend_guard(intent_id, capability_token)
await guard.authorize_spend(
    operation="travel.book_hotel",
    requested_spend_cents=20_000,
)

Do not call a separate capability issuance API. Paybond does not expose POST /v1/capabilities/issue or a gateway.capabilities.issue(...) helper. The token always comes from the intent lifecycle below.

How to obtain a capability token

Pick the path that matches your environment:

PathWhen to useHow you get intent_id and capability_token
Sandbox guardrail bootstrapFirst integration, Free Developer sandbox, any agent runtimepaybond.guardrails.bootstrapSandbox(...) (or scaffold bootstrapSandboxGuardrailIntent)
Production intent createLive settlement when the rail funds immediately (stripe_connect, simulator, etc.)paybond.intents.create(...) — read intent_id and capability_token from the response
Production intent fundx402_usdc_base, delayed ACH, or any create that returns unfundedpaybond.intents.fund(...) — read capabilityToken / capability_token after funding succeeds
MCP hostTool discovery over stdio or Streamable HTTPpaybond_bootstrap_sandbox_guardrail, then pass values to paybond_authorize_agent_spend

Sandbox (fastest path)

const paybond = await Paybond.open({
  apiKey: process.env.PAYBOND_API_KEY!,
  expectedEnvironment: "sandbox",
});

const guardrail = await paybond.guardrails.bootstrapSandbox({
  operation: "premium_search",
  requestedSpendCents: 500,
  currency: "usd",
});

const intentId = guardrail.intent_id;
const capabilityToken = guardrail.capability_token;
const guard = paybond.spendGuard(intentId, capabilityToken);

Python parity: await paybond.guardrails.bootstrap_sandbox(...).

See One-command guardrails for the generated scaffold.

Production create (immediate fund)

When the selected settlement rail places a hold and funds the intent in the same create response, Paybond mints the capability token automatically:

const created = await paybond.intents.create({
  /* principal, payee, budget, predicate, allowedTools, settlementRail, recognitionProof, ... */
});

const intentId = String(created.intent_id);
const capabilityToken = String(created.capability_token ?? "");
if (!capabilityToken) {
  throw new Error(
    "intent created without capability_token; fund the intent before guarding tools",
  );
}

Production fund (x402 or delayed funding)

If create does not return a token, fund first. On x402_usdc_base, prefer paybond.intents.fundWithX402 / fund_with_x402 (handles 402 signing and polling). On stripe_mpp, prefer paybond.intents.fundWithMppCharge / fundWithMppSession or fund_with_mpp_charge / fund_with_mpp_session (handles Payment Auth credential creation and polling):

const funded = await paybond.intents.fundWithX402({
  intentId,
  recognitionProof: fundProof,
  signPayment: (challenge) => x402Wallet.signPayment(challenge),
  issueRecognitionProof: (envelope) => issueAgentRecognitionProofV1({ /* bind envelope */ }),
});
const capabilityToken = funded.capabilityToken;

See Stablecoin funding for manual /fund control and local WireMock/offline mocks. See MPP funding for stripe_mpp Payment Auth helpers.

Agent run lifecycle

For any agent runtime — OpenAI, Gemini, Claude/Anthropic, Vercel AI SDK, LangGraph, MCP, or a custom orchestrator — bind one intent per agent run (or per user task), not one global env var:

Agent run starts

  ├─ Sandbox: paybond.guardrails.bootstrapSandbox(...)
  └─ Production: paybond.intents.create(...) → fund if needed


Read intent_id and capability_token


paybond.spendGuard(intentId, capabilityToken)

  ├─ authorizeSpend / guardTool before each paid tool


Execute paid API / vendor action


paybond.intents.submitEvidence(...) or sandbox submitSandboxEvidence

Store intent_id and capability_token in run context (closure, request scope, graph state, MCP tool args). Rotate them when you create a new intent for a new run.

Verification pattern

  1. Bind execution to (tenant, intent_id, capability_token) — tenant comes from Paybond.open, not from user input.
  2. Call authorizeSpend / authorize_spend or guardTool / guard_tool for the exact operation string listed on the intent.
  3. Only run the tool when verification returns allow: true.
  4. Submit evidence against the same intent_id after guarded work completes.

A token minted for one intent must never be reused for another intent, even inside the same tenant.

Gateway may return HTTP 200 with allow: false for a business denial or approval hold. That is not a transport error. See Agent integrations — approval holds.

For stripe_mpp session intents, POST /verify may also require a Tempo voucher credential (mpp_voucher_required until your wallet supplies one). Cumulative voucher spend is capped at the signed intent amount_cents; Paybond rejects over-budget vouchers without writing pending state. See Session voucher metering at verify.

Choose operation names deliberately

allowed_tools / allowedTools use your operation names:

  • travel.planner.plan
  • premium_search
  • crm.contact.enrich

Paybond does not define the catalog. Paybond compares the operation you pass at verify time with the names attached to the intent.

Incorrect integration patterns

Capability tokens come from the intent lifecycle, not from a separate issuance API or long-lived configuration. If an integration does not match the flows above, check these mismatches first:

Incorrect patternCorrect approach
process.env.CAPABILITY_TOKENRead token from bootstrap/create/fund response for this run
POST /v1/capabilities/issueDoes not exist — use intent lifecycle
paybond.spendGuard({ capabilityToken })paybond.spendGuard(intentId, capabilityToken) — two positional arguments
Reusing one token across intentsOne capability binding per intent
Skipping fund on x402 / delayed railsCall paybond.intents.fund(...) until capabilityToken is present
Treating Paybond as a model-provider baseURLModel client stays on OpenAI, Gemini, Claude, or your host; Paybond guards tools only — see Endpoints & environments

Design recommendations

  • Keep operation names stable so policy and reporting stay readable.
  • Verify as close as possible to execution time.
  • Keep one capability binding per active run or workflow step.
  • Treat a deny response as a business decision, not as a transport error.
  • Never log capability tokens; treat them like session secrets.

Implementation guides