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:
| Value | Source |
|---|---|
intent_id | Intent create, sandbox bootstrap, or MCP bootstrap response |
capability_token | Returned 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:
| Path | When to use | How you get intent_id + capability_token |
|---|---|---|
| Sandbox guardrail bootstrap | First integration, Free Developer sandbox, any agent runtime | paybond.guardrails.bootstrapSandbox(...) (or scaffold bootstrapSandboxGuardrailIntent) |
| Production intent create | Live settlement when the rail funds immediately (stripe_connect, simulator, etc.) | paybond.intents.create(...) — read intent_id and capability_token from the response |
| Production intent fund | x402_usdc_base, delayed ACH, or any create that returns unfunded | paybond.intents.fund(...) — read capabilityToken / capability_token after funding succeeds |
| MCP host | Tool discovery over stdio | paybond_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
- Bind execution to
(tenant, intent_id, capability_token)— tenant comes fromPaybond.open, not from user input. - Call
authorizeSpend/authorize_spendorguardTool/guard_toolfor the exactoperationstring listed on the intent. - Only run the tool when verification returns
allow: true. - Submit evidence against the same
intent_idafter 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.planpremium_searchcrm.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 pattern | Correct approach |
|---|---|
process.env.CAPABILITY_TOKEN | Read token from bootstrap/create/fund response for this run |
POST /v1/capabilities/issue | Does not exist — use intent lifecycle |
paybond.spendGuard({ capabilityToken }) | paybond.spendGuard(intentId, capabilityToken) — two positional arguments |
| Reusing one token across intents | One capability binding per intent |
| Skipping fund on x402 / delayed rails | Call paybond.intents.fund(...) until capabilityToken is present |
Treating Paybond as a model-provider baseURL | Model 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
- TypeScript quickstart — capability token source
- Python quickstart — capability token source
- Agent integrations — runtime-neutral pattern and framework scaffolds
- MCP server — bootstrap and authorize tools over stdio
- Harbor API — funding and verify HTTP contract behind the SDK