This path uses the runtime-neutral adapter in paybond_kit.agent_adapters. The same binding and adapter shown below can plug into provider SDKs, local-model runtimes, queues, and custom orchestrators that expose an application-owned tool executor.
What you will build
One guarded tool flow:
- Open a tenant-bound Paybond session.
- Create an intent for
travel.book_hotel. - Read the returned
capability_token. - Run the Paybond runtime-neutral adapter against the tool call.
- Execute the tool only if Harbor allows it.
- Submit signed evidence and inspect the resulting Harbor state.
Install
pip install paybond-kit
For a complete example application, see the reference implementation linked at the end of this page.
Required environment
Only PAYBOND_API_KEY comes from Paybond. The APP_* variables are example-local placeholders for values your app or trusted signer owns.
paybond-kit-login
export APP_PRINCIPAL_DID="did:web:example.com#principal"
export APP_PRINCIPAL_SEED_HEX="..."
export APP_PAYEE_DID="did:web:example.com#hotel-booker"
export APP_PAYEE_SEED_HEX="..."
export APP_INTENT_CREATE_RECOGNITION_PROOF_JSON='{"key_id":"..."}'
export APP_EVIDENCE_RECOGNITION_PROOF_JSON='{"key_id":"..."}'
export APP_SETTLEMENT_RAIL="x402_usdc_base" # optional
export APP_X402_PAYMENT_SIGNATURE="demo-signature" # optional
End-to-end flow
import asyncio
import json
import os
from uuid import UUID, uuid4
from paybond_kit import (
Paybond,
paybond_runtime_tool_call_adapter,
)
def seed32_from_hex(env_name: str) -> bytes:
raw = bytes.fromhex(os.environ[env_name])
if len(raw) != 32:
raise ValueError(f"{env_name} must decode to exactly 32 bytes")
return raw
def required_json_env(env_name: str) -> dict[str, object]:
value = json.loads(os.environ[env_name])
if not isinstance(value, dict):
raise ValueError(f"{env_name} must contain a JSON object")
return value
async def book_hotel(city: str, nightly_budget_cents: int) -> dict[str, object]:
return {
"hotel": "Harbor House",
"city": city,
"status": "confirmed",
"price_cents": nightly_budget_cents,
"confirmation": "HB-2049",
}
async def main() -> None:
paybond = await Paybond.open(
api_key=os.environ["PAYBOND_API_KEY"],
expected_environment="sandbox",
)
try:
intent_id = uuid4()
created = await paybond.intents.create(
principal_did=os.environ["APP_PRINCIPAL_DID"],
principal_signing_seed=seed32_from_hex("APP_PRINCIPAL_SEED_HEX"),
payee_did=os.environ["APP_PAYEE_DID"],
budget={"currency": "usd", "max_spend_usd": 200},
predicate={
"version": 1,
"root": {
"op": "and",
"clauses": [
{
"op": "completion",
"path": ["reservation", "status"],
"value": "confirmed",
},
{
"op": "budget_cap",
"path": ["reservation", "price_cents"],
},
],
},
},
currency="usd",
amount_cents=20_000,
evidence_schema={"type": "object", "properties": {"reservation": {"type": "object"}}},
deadline_rfc3339="2030-12-31T23:59:59Z",
allowed_tools=["travel.book_hotel"],
recognition_proof=required_json_env("APP_INTENT_CREATE_RECOGNITION_PROOF_JSON"),
settlement_rail=(
"x402_usdc_base"
if os.environ.get("APP_SETTLEMENT_RAIL") == "x402_usdc_base"
else "stripe_connect"
),
intent_id=intent_id,
idempotency_key=f"intent:{intent_id}",
)
capability_token = str(created.get("capability_token") or "")
if not capability_token:
raise RuntimeError("intent created without capability_token; ensure the intent is funded")
guard = paybond.spend_guard(UUID(str(intent_id)), capability_token)
run_tool = paybond_runtime_tool_call_adapter(
guard,
operation=lambda call: call["name"],
requested_spend_cents=lambda call: int(call["requested_spend_cents"]),
execute=lambda call: book_hotel(
str(call["city"]),
int(call["nightly_budget_cents"]),
),
)
reservation = await run_tool(
{
"name": "travel.book_hotel",
"requested_spend_cents": 18_700,
"city": "Lisbon",
"nightly_budget_cents": 18_700,
}
)
submitted = await paybond.intents.submit_evidence(
UUID(str(intent_id)),
{"reservation": reservation},
payee_did=os.environ["APP_PAYEE_DID"],
payee_signing_seed=seed32_from_hex("APP_PAYEE_SEED_HEX"),
recognition_proof=required_json_env("APP_EVIDENCE_RECOGNITION_PROOF_JSON"),
idempotency_key=f"evidence:{intent_id}",
)
print(
{
"intent_state": created["state"],
"guard": "allow",
"settlement_state": submitted["state"],
"predicate_passed": submitted.get("predicate_passed"),
}
)
finally:
await paybond.aclose()
asyncio.run(main())
Where agent SDKs fit
The direct adapter call above uses the same objects that a real agent runtime uses:
paybond.spend_guard(...)carries(harbor, intent_id, capability_token).paybond_runtime_tool_call_adapter(...)performs HarborPOST /verify.- The adapter's
operationresolver becomes the Paybond operation string, sotravel.book_hotelmust appear in the intent allow-list exactly.
When you wire this into a live agent run, pass the runtime's tool-call object into the adapter and map denials with on_deny if the framework expects a structured tool error instead of an exception.
Stablecoin rail variant
Set APP_SETTLEMENT_RAIL="x402_usdc_base" to request the Base / USDC rail. The example keeps the commercial amount in USD (budget["currency"] == "usd", amount_cents, max_spend_usd) while Harbor handles x402 funding and settlement in USDC on Base.
If create does not return capability_token, the example calls paybond.intents.fund, prints the payment_required challenge, and retries after you provide APP_X402_PAYMENT_SIGNATURE.
Expected Harbor outcome
createmay returncapability_tokenimmediately, or the example may obtain it throughpaybond.intents.fundonx402_usdc_base.submit_evidencereturns a state pluspredicate_passed.- Release vs refund confirmation remains an operator-tier action in the current Kit surface. See Harbor API for settlement confirmation.
Common failure cases
- Missing
capability_token: the intent is not funded yet. - Missing
APP_X402_PAYMENT_SIGNATURE: Harbor returned an x402 challenge and is waiting for a signed retry. - Guardrail rejection:
allowed_toolsdid not match the tool name, spend exceeded the capability, or the token is invalid. - Tenant mismatch: treat as a critical tenant-isolation error; do not retry with another client or tenant ID.
- Evidence rejection: the payee seed does not match the payee DID bound on the intent, or the payload does not satisfy the predicate/schema.
Reference implementation
- Example app:
examples/paybond-kit-runtime-agent-python/app.py - Example README:
examples/paybond-kit-runtime-agent-python/README.md