In short: give the agent a spend limit before it can call Stripe, and get a receipt after. Your agent should not call Stripe to charge a card or capture a PaymentIntent without a bounded spend boundary and an audit trail. Paybond sits at the tool execution boundary — Harbor verifies the operation and amount before your handler runs.
Spending limits
Harbor verifies operation and amount before Stripe side effects run.
Audit evidence
Signed stripe_charge digests bind the tool result to the intent.
PaymentIntent binding
Metadata ties agent charges to your Harbor intent and tenant.
Works with Kit
Guard charge tools with paybond.instrument() — any orchestrator.
Why not just Stripe?
Stripe APIs create PaymentIntents. They do not enforce per-agent spend limits, per-operation permission checks (capability tokens), or a signed receipt of what ran. Paybond adds that guardrail layer without replacing the Stripe SDK or ACS.
Creates PaymentIntent
- Stripe API alone
- Yes — Stripe SDK / ACS
- With Paybond
- Yes — same Stripe surfaces, after Harbor authorize
Spending limits
- Stripe API alone
- Radar / account rules; no per-agent Harbor budget
- With Paybond
- Per-call and intent budgets enforced before the tool runs
Audit evidence
- Stripe API alone
- Stripe Dashboard payment history
- With Paybond
- Signed evidence digests + settlement receipts
Agent authorization
- Stripe API alone
- Secret key / restricted key access
- With Paybond
- Capability token + policy decision at verify time
Settlement receipts
- Stripe API alone
- Stripe charge / PI records
- With Paybond
- Evidence evaluation + optional Agent Receipt Standard
| Capability | Stripe API alone | With Paybond |
|---|---|---|
| Creates PaymentIntent | Yes — Stripe SDK / ACS | Yes — same Stripe surfaces, after Harbor authorize |
| Spending limits | Radar / account rules; no per-agent Harbor budget | Per-call and intent budgets enforced before the tool runs |
| Audit evidence | Stripe Dashboard payment history | Signed evidence digests + settlement receipts |
| Agent authorization | Secret key / restricted key access | Capability token + policy decision at verify time |
| Settlement receipts | Stripe charge / PI records | Evidence evaluation + optional Agent Receipt Standard |
How it works
How it works
Agent
Calls payments.charge_customer (or your Stripe wrapper)
Paybond Guard
Harbor authorize before your handler runs
- Verify spend and operation
- Generate / bind intent
- Issue capability token
Stripe API
Create / confirm PaymentIntent with binding metadata
Webhook
payment_intent.succeeded funds the intent (separate from tool evidence)
Evidence
Submit stripe_charge after tool success
Settlement
Capture / release per settlement rails
Agent charge guarded by Paybond: verify spend, call Stripe, fund via webhook separately, submit evidence, then settle.
3-minute quickstart
Smoke the sandbox contract with the stripe-commerce preset — no Stripe credentials required:
terminal
paybond login
paybond agent sandbox smoke \
--preset stripe-commerce \
--format tableOptional policy scaffold: paybond policy init --preset stripe-commerce.
When the smoke succeeds you should see:
- ✓ Spend approved
- ✓ Charge completed
- ✓ Evidence verified (
stripe_charge)
Explicit smoke (same contract as the preset)
terminal
paybond agent sandbox smoke \
--operation payments.charge_customer \
--requested-spend-cents 2500 \
--evidence-preset stripe_charge \
--result-body '{"status":"succeeded","cost_cents":2500,"payment_intent_id":"pi_smoke","charge_id":"ch_smoke"}' \
--format tableWhat success looks like
What success looks like
Sandbox charge · illustrative
- Operation
- payments.charge_customer
- Status
- Approved
- Requested
- $25.00
- Evidence
- Verified
- Preset
- stripe_charge
Scaffold a Stripe-aware policy
terminal
paybond policy init --preset stripe-commerce --out paybond.policy.yamlThat writes a local policy with payments.charge_customer (stripe_charge evidence) and a read-only payments.list_invoices tool. Adjust budgets and tool names before deploy if needed:
version: 1
name: stripe-commerce-agent-v1
default_deny: true
tools:
payments.charge_customer:
side_effecting: true
max_spend_cents: 50000
evidence_preset: stripe_charge
payments.list_invoices:
side_effecting: false
intent:
allowed_tools:
- payments.charge_customer
budget:
currency: usd
max_spend_usd: 500
Validate before deploy:
terminal
paybond policy validate-tools --file paybond.policy.yaml --local-onlyWire middleware
Paybond's default path works with any orchestrator that exposes { name, execute } tools — including a custom Stripe wrapper. Tenant and intent IDs come from the Paybond session binding — never from unauthenticated tool args.
paybond-session.ts
import Stripe from "stripe";
import {
Paybond,
buildPaybondStripeMetadata,
mapStripeToolResultToEvidence,
} from "@paybond/kit";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
const paybond = await Paybond.open({ apiKey: process.env.PAYBOND_API_KEY! });
async function chargeCustomer(args: {
customerId: string;
/** Trusted invoice id — load the amount server-side; do not take free-form dollars from the model. */
invoiceId: string;
tenantId: string;
intentId: string;
}) {
const invoice = await stripe.invoices.retrieve(args.invoiceId);
if (invoice.customer !== args.customerId) {
throw new Error("invoice does not belong to customer");
}
const amountCents = invoice.amount_due;
if (amountCents <= 0) {
throw new Error("invoice has nothing due");
}
const metadata = buildPaybondStripeMetadata({
tenantId: args.tenantId,
intentId: args.intentId,
rail: "stripe_connect",
});
const pi = await stripe.paymentIntents.create({
amount: amountCents,
currency: invoice.currency,
customer: args.customerId,
confirm: true,
metadata,
automatic_payment_methods: { enabled: true },
});
const toolResult = {
status: pi.status,
cost_cents: amountCents,
invoice_id: invoice.id,
payment_intent_id: pi.id,
charge_id:
typeof pi.latest_charge === "string"
? pi.latest_charge
: pi.latest_charge?.id,
};
mapStripeToolResultToEvidence(toolResult, { preset: "stripe_charge" });
return toolResult;
}
const instrumented = await paybond.instrument({
policy: "./paybond.policy.yaml",
tools: {
"payments.charge_customer": chargeCustomer,
"payments.list_invoices": listInvoices,
},
});
// Per session (production): const runtime = await instrumented.bind({ intentId, capabilityToken });
// Sandbox quickstart: paybond.agent({ policy: "./paybond.policy.yaml", tools: { ... } })mapStripeToolResultToEvidence rejects Stripe funding webhook envelopes (payment_intent.succeeded event shapes). Those fund Harbor intents — they are not tool-completion evidence.
Binding metadata
When your app creates Stripe PaymentIntents (ACS checkout or guarded SDK tools), attach Harbor binding metadata so Gateway webhook preflight can resolve tenant_id and paybond_intent_id. Use Kit helpers — never trust unauthenticated client input for those ids.
Binding metadata
Create PaymentIntent
ACS checkout or guarded Stripe SDK tool
Inject metadata
buildPaybondStripeMetadata
- tenant_id
- paybond_intent_id
- paybond_settlement_rail (optional)
Webhook preflight
Gateway resolves intent + tenant from metadata
Attach Harbor binding metadata on every app-owned PaymentIntent create so Gateway webhook preflight can resolve tenant scope without trusting the agent.
paybond-session.ts
import { buildPaybondStripeMetadata } from "@paybond/kit";
// tenantId / intentId from authenticated Paybond session — not from the browser
const metadata = buildPaybondStripeMetadata({
tenantId: session.tenantId,
intentId: session.intentId,
rail: "stripe_connect", // optional: stripe_connect | stripe_ach_debit
});Canonical keys match Harbor webhook preflight: tenant_id, paybond_intent_id, and optional paybond_settlement_rail.
Production checklist
Production checklist
- Configure settlement rails (manual capture when ACS conditional)
- Create and fund an intent for the agent run
- Bind middleware per request with intentId + capabilityToken
- Attach buildPaybondStripeMetadata on every app-owned PaymentIntent
- Keep Stripe webhook funding separate from tool-completion evidence
Works with
Works with
- Stripe
- OpenAI
- Anthropic
- MCP
Ready to test?
Related guides
- Paybond with Stripe agentic commerce — ACS / ACP / UCP integration guide and manual capture
- Configure settlement rails — tenant-admin prerequisites (manual capture when ACS conditional)
- Fund intents by rail — create and fund an intent for the agent run
- Agent middleware — run binding, registry, auto-evidence
- Agent-agnostic spend controls — default
{ name, execute }wiring
Developer reference: /docs/kit/agent-middleware. Runnable template: paybond init --template paybond-stripe-agent-demo.