paybondpaybond
Sign in

Capabilities

What capability tokens are, how Harbor 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. Harbor 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 + 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 stdiopaybond_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, Harbor 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:

const funded = await paybond.intents.fund({ intentId, recognitionProof });
const capabilityToken = funded.capabilityToken;
if (!capabilityToken) {
  throw new Error("funding pending; poll /fund before guarding tools");
}

See the /fund walkthrough in TypeScript quickstart or Python quickstart.

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 + 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.

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. Harbor 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