This page documents the public TypeScript SDK. Use it when you need the exact method signatures, result shapes, and error classes exposed by @paybond/kit.
For a first paid-tool guardrail integration, start with One-command guardrails. For a guided production integration, use the TypeScript quickstart. For the underlying HTTP contract, use the Harbor API reference and API documentation.
This SDK currently covers the Harbor application lifecycle, tenant-scoped ledger reads, protocol helpers, A2A discovery, read-only tenant-bound Signal reads, fraud review surfaces, and the packaged MCP server.
Package and runtime
@paybond/kit is an ESM-only package for Node.js 22+ and compatible bundlers. The root package export is the SDK surface documented below:
npm install @paybond/kit
The package also exposes:
@paybond/kit/mcp-server— programmatic MCP server classes and settings helpers.paybond— the packaged CLI;paybond loginstarts sandbox device approval and writesPAYBOND_API_KEYto.env.local.paybond-mcp-server— the packaged stdio MCP server binary.paybond-init— a scaffold command for Paybond guardrail integration helpers.
Module entry point
import { Paybond, PaybondGuardrails, PaybondCapabilityBinding, PaybondSpendGuard, PaybondSpendDeniedError, PaybondIntents, PaybondAuditExports, PaybondAudit, type AuditExportListPage, type AuditExportJobGetResponse, type AuditVerifyResult, authorizeSpend, guardTool, paybondAgentToolSpendGuard, paybondRuntimeNeutralToolSpendGuard, paybondRuntimeToolCallAdapter, paybondLangGraphToolSpendGuard, paybondMCPToolSpendGuard, ServiceAccountSignalSession, ServiceAccountFraudSession, GatewayHarborClient, HarborClient, GatewaySignalClient, GatewayFraudClient, GatewayA2AClient, GatewayProtocolClient, HarborHttpError, GatewayAuthError, SignalHttpError, A2AHttpError, ProtocolHttpError, type SettlementRail, type FundIntentResult, type IntentFundingResult, type SandboxGuardrailBootstrapResult, type SandboxGuardrailEvidenceResult, type SubmitEvidenceResult, type VerifyCapabilityResult, type A2AAgentCard, type A2ATaskContractCatalogV1, type A2ASettlementTaskContractV1, type AgentMandateV1, type SignedAgentMandateV1, type AgentRecognitionProofV1, type ProtocolTransportBindingV1, type ProtocolAuthorizationReceiptV1, type ProtocolSettlementReceiptV1, type ImportAgentMandateV1Result, type VerifyProtocolReceiptV1Result, // low-level helpers buildSignedCreateIntentBody, intentCreationSignBytesRaw, signPayeeEvidenceBinding, artifactsDigest, normalizeJson, jsonValueDigest, } from "@paybond/kit";
MCP is a subpath export:
import { PaybondMCPServer, settingsFromEnv, main as runPaybondMCPServer, type PaybondMCPSettings, } from "@paybond/kit/mcp-server";
High-level client
Paybond.open
static Paybond.open(init: { apiKey: string; // "paybond_sk_live_…" or "paybond_sk_sandbox_…" service-account key gatewayBaseUrl?: string; // default "https://api.paybond.ai" principalPath?: string; // default "/v1/auth/principal" expectedEnvironment?: "live" | "sandbox"; maxRetries?: number; // default 3 }): Promise<Paybond>;
Resolves the service-account principal at the hosted Gateway, caches the echoed tenant_id, and returns a Paybond instance bound to that tenant realm. Hosted integrations only need apiKey; the default Gateway origin is https://api.paybond.ai. Pass gatewayBaseUrl to override the origin. Scaffold helpers such as openPaybondFromEnv() and the MCP server read PAYBOND_GATEWAY_URL / PAYBOND_GATEWAY_BASE_URL from the environment. See Endpoints & environments.
Set expectedEnvironment to fail fast when a deployment is accidentally pointed at a live key from sandbox, or a sandbox key from live.
Throws GatewayAuthError on any tenant-principal lookup or credential failure. Response body text is redacted only by you at log time; avoid logging raw credential diagnostics.
Tenant isolation: the returned session is bound to the Gateway-derived tenant_id. Do not reuse the instance across tenants — build one per (tenant, service account).
Paybond instance
class Paybond { readonly harbor: GatewayHarborClient; // tenant-bound Gateway Harbor surface readonly signal: GatewaySignalClient; // tenant-bound Signal reads readonly fraud: GatewayFraudClient; // tenant-bound fraud reads and review events readonly a2a: GatewayA2AClient; // A2A discovery reads readonly protocol: GatewayProtocolClient; // protocol-v2 and recognition-gated helpers readonly guardrails: PaybondGuardrails; // sandbox-only first guardrail helpers readonly intents: PaybondIntents; // ergonomic intent helpers readonly audit: PaybondAudit; // compliance audit export helpers authorizeSpend(input: { intentId: string; token: string; operation: string; requestedSpendCents?: number; }): Promise<VerifyCapabilityResult>; spendGuard(intentId: string, capabilityToken: string): PaybondSpendGuard; aclose(): Promise<void>; }
paybond.harbor— the tenant-bound Gateway-backed Harbor surface. Use it for capability verification and recognition-gated Harbor proxy calls.paybond.signal— the tenant-boundGatewaySignalClient.paybond.fraud— the tenant-boundGatewayFraudClient.paybond.a2a— theGatewayA2AClientfor A2A discovery.paybond.protocol— the tenant-boundGatewayProtocolClientfor protocol-v2 receipts, mandate import, and recognition-gated gateway mutations.paybond.guardrails— the sandbox-onlyPaybondGuardrailsclient for first paid-tool guardrail integration.paybond.intents— seePaybondIntents.paybond.audit.exports— seePaybondAuditExports.paybond.aclose()— release HTTP resources. Always await in afinallyblock.
PaybondGuardrails
paybond.guardrails wraps the Free Developer sandbox guardrail routes. These routes derive tenant scope from the service-account API key, reject caller-supplied tenant IDs, and use simulator settlement only.
paybond.guardrails.bootstrapSandbox(input: { operation: string; requestedSpendCents: number; currency?: string; evidenceSchema?: Record<string, unknown>; metadata?: Record<string, unknown>; idempotencyKey?: string; }): Promise<SandboxGuardrailBootstrapResult>; paybond.guardrails.submitSandboxEvidence(input: { intentId: string; payload?: Record<string, unknown>; artifacts?: string[]; operation?: string; requestedSpendCents?: number; metadata?: Record<string, unknown>; idempotencyKey?: string; }): Promise<SandboxGuardrailEvidenceResult>;
Both result types include tenant_id, intent_id, operation, requested_spend_cents, and sandbox_lifecycle_status; bootstrap also returns the capability_token used with paybond.spendGuard(...).
PaybondIntents
paybond.intents wraps the principal/payee signing flows plus x402 funding. All methods derive tenant scope from the bound Harbor client; callers never pass a tenant id.
paybond.intents.create
paybond.intents.create(params: { principalDid: string; principalSigningSeed: Uint8Array; // exactly 32 bytes recognitionProof: AgentRecognitionProofV1 | Record<string, unknown>; payeeDid: string; budget: Record<string, unknown>; // canonical JSON (e.g. { currency, max_spend_usd }) predicate: Record<string, unknown>; // predicate_dsl (version 1 and root node) currency: string; amountCents: number; evidenceSchema: Record<string, unknown>; // JSON schema for evidence payloads deadlineRfc3339: string; allowedTools: string[]; predicateRef?: string; // managed-policy digest when set settlementRail: SettlementRail; // principal-signed rail request; destination stays server-owned intentId?: string; // defaults to crypto.randomUUID() idempotencyKey?: string; }): Promise<Record<string, unknown>>;
Builds a principal-signed intent body via buildSignedCreateIntentBody, applies BLAKE3 digests to canonical JSON for budget, evidence_schema, and predicate_dsl, and POSTs it through Gateway POST /harbor/intents.
paybond.intents.createSpendIntent(params) is a spend-oriented alias for the same method. Use it in agent-facing code when the user asked for agent spend controls, delegated spend guardrails, or tool-call budget checks.
Retries honor idempotencyKey on transient 5xx/429. The key is 1–256 characters and is scoped per authenticated tenant and HTTP operation, so reusing it with the same body replays the original response and reusing it with a different body returns HTTP 409 from Harbor. See Idempotency for the shared contract and the Harbor API reference for the routes that honor it.
allowedTools is a caller-defined allow-list of delegated operation names such as travel.planner.plan or crm.contact.enrich. Paybond does not define these strings; Harbor only checks that later verify calls use a matching value.
settlementRail is required and included in the principal signature. It requests one allowed rail for the new intent. Harbor validates it against the tenant's canonical settlement config, resolves the destination server-side, and snapshots the effective routing onto the intent.
paybond.intents.fund
paybond.intents.fund(params: { intentId: string; recognitionProof: AgentRecognitionProofV1 | Record<string, unknown>; paymentSignature?: string; paymentAuthorization?: string; idempotencyKey?: string; }): Promise<FundIntentResult>;
Advances Gateway POST /harbor/intents/{intent_id}/fund. Returns structured 402 challenges, 202 pending states, and 200 funded results without forcing callers to catch those statuses as exceptions.
x402_usdc_base: Harbor returnspaymentRequiredon402; retry withpaymentSignature.stripe_mpp: Harbor returnsWWW-Authenticate: Payment …on402; retry withpaymentAuthorization(Kit sendsx-paybond-payment-authorization: Payment …soAuthorization: Bearerstays free).
paybond.intents.fundWithX402
paybond.intents.fundWithX402(params: { intentId: string; recognitionProof: AgentRecognitionProofV1 | Record<string, unknown>; signPayment: (challenge: PaymentRequired) => Promise<string>; issueRecognitionProof: ( envelope: FundRequestEnvelope, ) => Promise<AgentRecognitionProofV1 | Record<string, unknown>>; pollOptions?: { maxAttempts?: number; intervalMs?: number }; idempotencyKey?: string; }): Promise<FundIntentResult>;
Orchestrates the x402 fund handshake: signs 402 paymentRequired challenges, retries with payment-signature, and polls 202 until Harbor returns a capabilityToken. Wallet keys stay app-owned via injectable signPayment and issueRecognitionProof.
For local contract testing without a remote sandbox API, use paybond dev up (WireMock scenario mappings 11–13) or paybond dev --offline (in-process x402 fund state machine). See Stablecoin funding.
paybond.intents.fundWithMppCharge
paybond.intents.fundWithMppCharge(params: { intentId: string; recognitionProof: AgentRecognitionProofV1 | Record<string, unknown>; createPaymentCredential: (challenge: PaymentAuthChallenge) => Promise<string>; issueRecognitionProof: ( envelope: FundRequestEnvelope, ) => Promise<AgentRecognitionProofV1 | Record<string, unknown>>; pollOptions?: { maxAttempts?: number; intervalMs?: number }; idempotencyKey?: string; }): Promise<FundIntentResult>;
Orchestrates one-shot Stripe MPP charge funding on stripe_mpp intents: selects the intent="charge" / method="stripe" WWW-Authenticate: Payment … challenge, calls your app-owned createPaymentCredential, retries with paymentAuthorization (x-paybond-payment-authorization through Gateway), and polls when Harbor leaves funding pending until capabilityToken is issued (funding.status = charge_succeeded). MPP wallet and SPT secrets stay app-owned — Kit never stores signing material. See Harbor MPP handshake.
paybond.intents.fundWithMppSession
paybond.intents.fundWithMppSession(params: { intentId: string; recognitionProof: AgentRecognitionProofV1 | Record<string, unknown>; createPaymentCredential: (challenge: PaymentAuthChallenge) => Promise<string>; issueRecognitionProof: ( envelope: FundRequestEnvelope, ) => Promise<AgentRecognitionProofV1 | Record<string, unknown>>; pollOptions?: { maxAttempts?: number; intervalMs?: number }; idempotencyKey?: string; }): Promise<FundIntentResult>;
Same orchestration for Tempo MPP session funding: selects the intent="session" / method="tempo" Payment Auth challenge, verifies the open-session credential, and completes when funding.status = deposit_confirmed. Live deployments may return 202 with session_open_pending while the TIP-1034 deposit confirms on-chain; helpers poll until funded. See MPP funding and Harbor MPP handshake.
paybond.intents.submitEvidence
paybond.intents.submitEvidence(params: { intentId: string; payeeDid: string; payeeSigningSeed: Uint8Array; // exactly 32 bytes recognitionProof: AgentRecognitionProofV1 | Record<string, unknown>; payload: Record<string, unknown>; // evidence JSON matching the intent's schema artifactsBlake3Hex?: string[]; // default [] submittedAtRfc3339?: string; // default current UTC second idempotencyKey?: string; }): Promise<SubmitEvidenceResult>;
Signs the payee binding via signPayeeEvidenceBinding and POSTs it through Gateway POST /harbor/intents/{intent_id}/evidence.
Gateway-backed intent mutations require a replay-safe recognition proof bound to the exact method, path, purpose, tenant, and request body.
The high-level helper defaults artifactsBlake3Hex to [] and submittedAtRfc3339 to the current UTC second. The low-level signPayeeEvidenceBinding helper still requires concrete values because they are part of the signed bytes.
The response includes state, tenant, and an optional predicatePassed boolean; a false here is not an HTTP error — the predicate evaluation failed and the intent moved to evidence_submitted with the recorded failure.
paybond.intents.confirmSettlement
paybond.intents.confirmSettlement(params: { intentId: string; recognitionProof: AgentRecognitionProofV1 | Record<string, unknown>; body?: Record<string, unknown>; // default {} — confirms action implied by evidence idempotencyKey?: string; }): Promise<Record<string, unknown>>;
Confirms settlement through Gateway POST /harbor/intents/{intent_id}/settlement/confirm. Harbor does not let this route choose release or refund; it confirms the settlement action implied by the evidence evaluation already stored on the intent. Prefer the Operator Console for human-reviewed settlement.
For protocol/A2A automation or MCP tooling, paybond.protocol.confirmHarborSettlement and paybond_confirm_settlement remain supported.
PaybondAuditExports
paybond.audit.exports wraps Gateway compliance audit export jobs (POST and GET /v1/compliance/audit-exports). Tenant scope comes from the service-account API key — callers never pass a tenant id. Local bundle verification reuses the same Ed25519 manifest checks as paybond audit exports verify.
paybond.audit.exports.create(params: { filter: { time_start?: string; time_end?: string; intent_id?: string; case_id?: string; operator_did?: string; includes?: string[]; // defaults to gateway standard set when omitted }; disclosureTier?: "standard" | "extended"; // default standard retentionHours?: number; // gateway default 168, max 720 }): Promise<AuditExportJobGetResponse>; paybond.audit.exports.list(params?: { limit?: number; // default 50, max 200 cursor?: string; }): Promise<AuditExportListPage>; paybond.audit.exports.get(jobId: string, params?: { issueDownload?: boolean; // mint download_token when the job is ready }): Promise<AuditExportJobGetResponse>; paybond.audit.exports.delete(jobId: string): Promise<{ job_id: string; deleted: true }>; paybond.audit.exports.verify( manifestOrPath: Record<string, unknown> | string, options?: { cwd?: string }, // bundle path only; defaults to process.cwd() ): Promise<AuditVerifyResult>;
list and get are also exposed as readonly MCP tools (paybond_list_audit_exports, paybond_get_audit_export) when the host uses --tool-policy readonly. Local bundle verification is SDK/CLI only — MCP hosts cannot verify downloaded ZIP paths.
CLI paybond audit exports list|get|delete|verify delegates to this SDK surface. Export job creation (create) is SDK-only — there is no paybond audit exports create subcommand. Operator Console remains a primary UI for requesting bundles; see Ledger and provenance.
const { job } = await paybond.audit.exports.create({ filter: { time_start: "2026-05-01T00:00:00Z", time_end: "2026-05-07T23:59:59Z", }, disclosureTier: "standard", }); const ready = await paybond.audit.exports.get(job.id, { issueDownload: true });
Verify (Gateway-enriched)
The Kit exposes capability verification on the underlying Harbor client rather than on paybond.intents. On hosted Gateway sessions, POST /verify runs Harbor capability validation first, then applies tenant spend policy before returning the final allow/deny outcome.
paybond.harbor.verifyCapability({ intentId: string; token: string; // base64 Biscuit token operation: string; // e.g. "travel.book_hotel" requestedSpendCents?: number; // default 0 vendorId?: string; taskId?: string; workflowId?: string; toolCallId?: string; toolName?: string; currency?: string; agentSubject?: string; approvalToken?: string; // short-lived token after operator approval idempotencyKey?: string; }): Promise<VerifyCapabilityResult>;
Pass vendor, task, workflow, tool, and agent metadata when tenant spend policy evaluates rolling caps, anomaly rules, or approval thresholds against those scopes. After an operator approves a held request in the console, retry with the same operation, amount, and metadata plus approvalToken.
Returns HTTP 200 with allow: false when Harbor or Gateway denies — that is not thrown as an error. When Gateway policy requires human approval first, the response includes approvalRequired: true and usually decisionId / approvalRequestId; handle that separately from a hard policy denial (see PaybondSpendGuard).
Gateway Harbor client
class GatewayHarborClient { constructor(gatewayBaseUrl: string, tenantId: string, options: { staticGatewayBearerToken: string; maxRetries?: number; }); readonly tenantId: string; verifyCapability(input: PaybondSpendAuthorizationInput & { intentId: string; token: string; }): Promise<VerifyCapabilityResult>; /** Finalize active scope reservations after tool execution completes or is aborted. */ completeSpendDecision(input: { decisionId: string; outcome: "consumed" | "released"; }): Promise<void>; createIntent(body: Record<string, unknown>, options: { recognitionProof: AgentRecognitionProofV1 | Record<string, unknown>; idempotencyKey?: string; }): Promise<Record<string, unknown>>; confirmSettlement( intentId: string, body: Record<string, unknown>, options: { recognitionProof: AgentRecognitionProofV1 | Record<string, unknown>; idempotencyKey?: string; }, ): Promise<Record<string, unknown>>; }
Paybond.open(...) constructs this for hosted integrations. Build it directly only when you already have a trusted tenant id from Gateway principal introspection.
HarborClient
Tenant-bound async HTTP client. Sends x-tenant-id on every request; verifies Harbor echoes the same tenant and the requested intent_id on POST /verify (confused-deputy hardening).
class HarborClient { constructor(harborBase: string, tenantId: string, options?: { harborBearerSupplier?: () => Promise<string | null | undefined>; staticHarborBearerToken?: string; maxRetries?: number; }); readonly tenantId: string; verifyCapability(input: { intentId: string; token: string; operation: string; requestedSpendCents?: number; }): Promise<VerifyCapabilityResult>; // harborVerifyCapability createIntent( body: Record<string, unknown>, options?: { idempotencyKey?: string }, ): Promise<Record<string, unknown>>; // harborCreateIntent fundIntent( intentId: string, options?: { paymentSignature?: string; idempotencyKey?: string }, ): Promise<FundIntentResult>; // harborFundIntent submitEvidence( intentId: string, evidenceBody: Record<string, unknown>, options?: { idempotencyKey?: string }, ): Promise<SubmitEvidenceResult>; // harborSubmitEvidence getLedgerTip(): Promise<Record<string, unknown>>; getLedgerAuthority(): Promise<Record<string, unknown>>; getLedgerEvents(options?: { afterSeq?: number; limit?: number }): Promise<Record<string, unknown>>; getLedgerMerkleLatest(): Promise<Record<string, unknown>>; }
Retries 429 / 500 / 502 / 503 / 504 with exponential backoff and jitter, honoring a bounded Retry-After. All other 4xx/5xx throw HarborHttpError.
FundIntentResult
Structured return type for paybond.intents.fund(...) and harbor.fundIntent(...).
statusCode:402,202, or200paymentRequired: x402 challenge header whenstatusCode === 402paymentResponse: optional Coinbase receipt header on202or200wwwAuthenticate: Payment Auth challenges fromWWW-AuthenticatewhenstatusCode === 402(MPP charge/session)paymentReceipt: Payment Auth receipt fromPayment-Receiptwhen Harbor confirms payment progresscacheControl: echoedCache-Controlfrom HarborintentId,tenant,statesettlementRail,currency,amountCentsfunded: whether Harbor has transitioned the intent into the funded statefunding: optionalIntentFundingResultcapabilityToken: present when Harbor already minted the funded capability
IntentFundingResult
Normalized Harbor funding payload fields:
settlementRailharborFundEndpointstatus- MPP (Payment Auth):
intent(charge|session),method(stripe|tempo),challengeId - MPP settlement:
settlementAsset,settlementNetwork,depositAmountBaseUnits - MPP Tempo session:
channelId,sessionProtocol(e.g.v2),acceptedCumulativeBaseUnits,pendingCumulativeBaseUnits,descriptorHash,lastVoucherAt,lastSettleTxHash,channelStatus paymentSessionIdpaymentUrlstripePaymentIntentId,clientSecret,stripeConnectDestinationstripeCustomerId,latestChargeId,paymentMethodId,mandateIdfinancialConnectionsAccountId,bankLast4,bankFingerprint,bankNameasset,network(x402)authorizationId,captureId,voidId,transferId,refundIdexpectedDebitDate,paymentReference,refundReference,refundReferenceStatussourceAddress,targetAddressauthorizationExpiresAt,captureExpiresAt,refundExpiresAtonchainTransactionHashes
SettlementRail
type SettlementRail = | "stripe_connect" | "stripe_ach_debit" | "stripe_mpp" | "adyen_manual_capture" | "x402_usdc_base";
Public type alias for the only supported client-requestable settlement rails. The SDK does not expose any client-side destination fields; Harbor resolves destinations from tenant-owned settlement config.
Stripe MPP rail: stripe_mpp (Stripe Machine Payments Protocol) is a valid SettlementRail literal for sandbox rehearsal and flagged production paths. Use paybond.intents.fundWithMppCharge for one-shot charge funding or paybond.intents.fundWithMppSession for Tempo session channel deposits. Harbor MPP runtime readiness is required for production enablement — see Fund Stripe MPP intents and Support matrix.
Adyen manual capture: adyen_manual_capture is a valid SettlementRail literal when the tenant has enabled the rail and saved a BYO Checkout destination. SDK callers never submit Adyen secrets; confirm readiness with paybond adyen ready / paybond adyen doctor — see Configure Adyen settlement and Support matrix.
Ledger reads
The SDK includes protected tenant-scoped provenance reads on the low-level direct HarborClient. Hosted Paybond.open(...) integrations should prefer Gateway's public compliance surface (paybond.audit.exports), verifier packs, verification results, and audit export bundles instead of a raw ledger event stream.
getLedgerTip()→GET /ledger/v1/tipgetLedgerAuthority()→GET /ledger/v1/authoritygetLedgerEvents({ afterSeq, limit })→GET /ledger/v1/eventsgetLedgerMerkleLatest()→GET /ledger/v1/merkle/latest
Each method validates the echoed tenant_id against the bound client tenant and fails closed on mismatch.
Signal reads
The TypeScript SDK ships a tenant-bound gateway Signal client for the supported read-only V1.0.1 surfaces.
GatewaySignalClient
class GatewaySignalClient { constructor(gatewayBaseUrl: string, tenantId: string, options: { staticGatewayBearerToken: string; maxRetries?: number; }); readonly tenantId: string; getReputationReceipt( operatorDid: string, scoreVersion?: string, ): Promise<SignalReceiptEnvelope | null>; getPortfolioSummary(scoreVersion?: string): Promise<SignalPortfolioSummary>; getSignedPortfolioArtifact( scoreVersion?: string, ): Promise<SignalSignedPortfolioArtifact>; getOperatorExplanation( operatorDid: string, scoreVersion?: string, ): Promise<Record<string, unknown> | null>; getOperatorReviewStatus( operatorDid: string, scoreVersion?: string, ): Promise<Record<string, unknown> | null>; }
This client validates the echoed tenant_id and operator_did on every response. 404 remains a null result for receipt, explanation, and review-status lookups; other non-success statuses throw SignalHttpError. getSignedPortfolioArtifact() returns the tenant-scoped signed portfolio export that partner verifiers can carry between systems without exposing a shared public leaderboard surface.
ServiceAccountSignalSession
class ServiceAccountSignalSession { static open(init: { gatewayBaseUrl: string; apiKey: string; principalPath?: string; // default "/v1/auth/principal" expectedEnvironment?: "live" | "sandbox"; maxRetries?: number; }): Promise<ServiceAccountSignalSession>; readonly signal: GatewaySignalClient; aclose(): Promise<void>; }
This is the easiest tenant-safe path for partner developers who only need Signal reads. The session resolves the tenant from GET /v1/auth/principal using the same service-account credential instead of relying on ad hoc tenant headers.
Fraud review and metrics
GatewayFraudClient
class GatewayFraudClient { constructor(gatewayBaseUrl: string, tenantId: string, options: { staticGatewayBearerToken: string; maxRetries?: number; }); readonly tenantId: string; getFraudAssessment( operatorDid: string, scoreVersion?: string, ): Promise<SignalFraudAssessmentResponse | null>; listFraudReviewQueue(options?: { state?: "none" | "open" | "in_review" | "closed" | "all" | string; severity?: "elevated" | "high" | "critical" | string; limit?: number; scoreVersion?: string; }): Promise<SignalFraudReviewQueueResponse>; getFraudMetrics(options?: { window?: "24h" | "7d" | "30d" | string; scoreVersion?: string; }): Promise<SignalFraudMetricsResponse>; getFraudReleaseGateConfig(scoreVersion?: string): Promise<SignalFraudReleaseGateConfigResponse>; setFraudReleaseGateMode( mode: "review_only" | "critical_hold" | string, ): Promise<SignalFraudReleaseGateConfigResponse>; recordFraudReviewEvent( operatorDid: string, event: { eventType: "review_open_requested" | "appeal_requested" | "replay_requested" | "review_outcome_recorded" | "confirmed_risk" | "false_positive" | "needs_more_evidence"; reviewOutcome?: "confirmed_risk" | "false_positive" | "needs_more_evidence"; signalCode?: string; intentId?: string; providerEventId?: string; summary: string; }, scoreVersion?: string, ): Promise<SignalFraudReviewEventResponse>; }
The fraud client validates echoed tenant_id and, for operator-scoped methods, operator_did. SignalFraudMetricsResponse includes rolling-window backtest and monitoring fields such as false_positive_rate_bps, labeled_coverage_bps, critical_signal_hold_candidate_count, stale_label_gap_seconds, stale_signal_family_label_gap_count, and backtest_summary. SignalFraudSignal preserves the fraud_signal_version=1.0.7 additive metadata fields signal_source, first_seen_at, last_seen_at, evidence_binding_strength, provider_event_refs, and intent_refs when Gateway returns them. setFraudReleaseGateMode() accepts only review_only or critical_hold; Gateway still enforces tenant-admin access and keeps review_only as the default. recordFraudReviewEvent() only accepts review workflow/outcome event types, does not expose settlement or money-movement events, and can pass optional signal-level feedback context through signalCode, intentId, and providerEventId.
ServiceAccountFraudSession
class ServiceAccountFraudSession { static open(init: { gatewayBaseUrl: string; apiKey: string; principalPath?: string; // default "/v1/auth/principal" expectedEnvironment?: "live" | "sandbox"; maxRetries?: number; }): Promise<ServiceAccountFraudSession>; readonly fraud: GatewayFraudClient; aclose(): Promise<void>; }
Paybond.open(...) also exposes gateway clients as paybond.signal, paybond.fraud, paybond.a2a, and paybond.protocol.
A2A discovery
GatewayA2AClient
class GatewayA2AClient { constructor(gatewayBaseUrl: string, options?: { staticGatewayBearerToken?: string; maxRetries?: number; }); getAgentCard(): Promise<A2AAgentCard>; getTaskContracts(): Promise<A2ATaskContractCatalogV1>; getTaskContract(contractId: string): Promise<A2ASettlementTaskContractV1>; }
paybond.a2a is created automatically by Paybond.open and carries the same service-account bearer token when available. Use it for:
GET /.well-known/agent-card.jsonGET /protocol/v2/a2a/task-contractsGET /protocol/v2/a2a/task-contracts/{contract_id}
Non-success statuses throw A2AHttpError. See Agent integrations and V2 protocol trust.
Protocol v2 and recognition-gated gateway mutations
GatewayProtocolClient
class GatewayProtocolClient { constructor(gatewayBaseUrl: string, tenantId: string, init?: { staticGatewayBearerToken?: string; maxRetries?: number; }); readonly tenantId: string; importAgentMandateV1(init: { signedMandate: SignedAgentMandateV1; intentId: string; transportBinding?: ProtocolTransportBindingV1; recognitionProof: AgentRecognitionProofV1 | Record<string, unknown>; }): Promise<ImportAgentMandateV1Result>; getSettlementReceiptV1(receiptId: string): Promise<ProtocolSettlementReceiptV1>; verifyProtocolReceiptV1( receipt: ProtocolAuthorizationReceiptV1 | ProtocolSettlementReceiptV1 | Record<string, unknown>, ): Promise<VerifyProtocolReceiptV1Result>; createHarborIntent(init: { body: Record<string, unknown>; recognitionProof: AgentRecognitionProofV1 | Record<string, unknown>; idempotencyKey?: string; }): Promise<Record<string, unknown>>; fundHarborIntent(init: { intentId: string; recognitionProof: AgentRecognitionProofV1 | Record<string, unknown>; paymentSignature?: string; idempotencyKey?: string; }): Promise<Record<string, unknown>>; submitHarborEvidence(init: { intentId: string; body: Record<string, unknown>; recognitionProof: AgentRecognitionProofV1 | Record<string, unknown>; idempotencyKey?: string; }): Promise<Record<string, unknown>>; confirmHarborSettlement(init: { intentId: string; body: Record<string, unknown>; // usually {}; included in the recognition envelope recognitionProof: AgentRecognitionProofV1 | Record<string, unknown>; idempotencyKey?: string; }): Promise<Record<string, unknown>>; }
paybond.protocol is created automatically by Paybond.open. It sends x-tenant-id, adds authorization when a gateway bearer token is configured, and base64url-encodes the recognition proof into x-paybond-agent-recognition-proof for recognition-gated intent mutation helpers.
Paybond is AP2-aligned at the protocol boundary. On mandate import, transportBinding.source_protocol is optional and Gateway defaults it to ap2 for imported external mandates and exported protocol receipts.
Gateway HTTP failures throw ProtocolHttpError with parsed errorCode / errorMessage when Gateway returns a JSON error envelope. Tenant, intent, and receipt echo mismatches throw plain Error and should be treated as critical tenant-isolation failures.
Core protocol types
The SDK exports the protocol shapes used by GatewayProtocolClient:
AgentMandateV1/SignedAgentMandateV1AgentRecognitionProofV1ProtocolTransportBindingV1ProtocolAuthorizationReceiptV1ProtocolSettlementReceiptV1ImportAgentMandateV1ResultVerifyProtocolReceiptV1Result
MCP server subpath
type PaybondMCPSettings = { apiKey: string; gatewayBaseUrl?: string; principalPath?: string; maxRetries?: number; }; class PaybondMCPServer { constructor(settings: PaybondMCPSettings); listTools(): Array<Record<string, unknown>>; callTool(name: string, args?: Record<string, unknown>): Promise<{ content: Array<{ type: "text"; text: string }>; structuredContent?: Record<string, unknown>; isError?: boolean; }>; handleMessage(message: Record<string, unknown>): Promise<Record<string, unknown> | null>; runStdio(): void; } settingsFromEnv(env?: Record<string, string | undefined>): PaybondMCPSettings; runPaybondMCPServer(argv?: string[]): number;
The packaged binary is usually simpler:
npx paybond-mcp-server
Required environment is PAYBOND_API_KEY. See the MCP server guide for the supported tool list and host configuration.
PaybondCapabilityBinding
Most application code should use paybond.spendGuard(intentId, capabilityToken) instead. This
class remains the lower-level backing type for callers that need to build their own guard wrapper.
class PaybondCapabilityBinding { constructor( readonly harbor: HarborClient, readonly intentId: string, readonly capabilityToken: string, ); verifySpendCapability(input: { operation: string; requestedSpendCents?: number; }): Promise<VerifyCapabilityResult>; authorizeSpend(input: { operation: string; requestedSpendCents?: number; }): Promise<VerifyCapabilityResult>; }
Binds (harbor, intentId, capabilityToken) so agent tool wrappers have a single tenant-safe handle to verify against. See the Capabilities guide and the verify walkthrough.
PaybondSpendGuard
const guard = paybond.spendGuard(intentId, capabilityToken); const guardedTool = guard.guardTool( { operation: "travel.book_hotel", requestedSpendCents: 20_000, vendorId: "vendor_acme", toolName: "travel.book_hotel", agentSubject: "agent:ops-bot", }, async (input) => bookHotel(input), );
PaybondSpendGuard is a discoverable wrapper around Gateway-enriched capability verification. It calls verifyCapability before the handler runs:
- throws
PaybondSpendDeniedErroron hard denials (allow: falsewithout approval required) - throws
PaybondSpendApprovalRequiredErrorwhen Gateway returnsapprovalRequired: true - after a successful authorization,
guardToolruns the handler and then callscompleteSpendDecisionwith outcomeconsumedon success orreleasedwhen the handler throws (best-effort release)
Operation label vs handler: guardTool authorizes the operation string and requestedSpendCents you pass in. It does not inspect or constrain what handler actually does. Keep the authorization label, spend amount, and handler side effects aligned with the bound intent's allowedTools and policy predicates. For registry-backed operation-to-handler coupling, prefer paybond.instrument() or wrapTools().
If you call authorizeSpend or assertSpendAuthorized directly instead of guardTool, call completeSpendAuthorization yourself when the guarded work finishes or aborts.
Standalone helpers are also exported. They accept either a PaybondSpendGuardInit
object ({ harbor, intentId, capabilityToken }) or the lower-level
PaybondCapabilityBinding:
authorizeSpend(source, input)guardTool(source, input, handler)paybondAgentToolSpendGuard(source, input, handler)paybondRuntimeNeutralToolSpendGuard(source, input, handler)paybondLangGraphToolSpendGuard(source, input, handler)paybondMCPToolSpendGuard(source, input, handler)
PaybondSpendAuthorizationInput fields: operation, requestedSpendCents, vendorId, taskId, workflowId, toolCallId, toolName, currency, agentSubject, approvalToken, idempotencyKey.
For agent SDKs or custom runtimes that hand your application a tool-call object and an executor,
use paybondRuntimeToolCallAdapter(...):
const runTool = paybondRuntimeToolCallAdapter({ source: { harbor: paybond.harbor, intentId, capabilityToken }, operation: (call) => call.name, requestedSpendCents: (call) => call.spendCents, execute: executeToolCall, onDeny: (result) => ({ status: "blocked", reason: result.message ?? result.code }), });
Scaffolding
Sandbox credential setup:
npx -p @paybond/kit paybond login
The login command supports --env-file, --gateway, --no-open, and --force. It writes .env.local with mode 0600, adds the default .env.local target to .gitignore when needed, refuses custom unignored env files inside git repos, and prints only masked key identity plus the target sandbox tenant.
npx -p @paybond/kit paybond-init \ --preset paid-tool-guard \ --framework provider-agnostic \ --out paybond-paid-tool-guard.ts
Supported framework labels are generic, provider-agnostic, openai, claude, anthropic, gemini, google-ai, vercel-ai, langgraph, mastra, cloudflare-agents, and mcp. The scaffold writes reusable guardrail helpers and expects your application to provide the paid-tool handler; it does not manage signing keys or payment wallets.
Result types
VerifyCapabilityResult
Gateway-enriched verify outcome. Harbor-only deployments populate the core fields; hosted Gateway sessions may also return spend-policy metadata.
type VerifyCapabilityResult = { allow: boolean; auditId: string; tenant: string; intentId: string; code?: string; message?: string; decisionId?: string; approvalRequestId?: string; policyVersion?: number; reasonCodes?: string[]; spendScope?: { scope_type: string; scope_key: string }; remainingCents?: number; retryAfter?: number; /** True when tenant policy requires operator approval before execution may proceed. */ approvalRequired?: boolean; };
| Field | Meaning |
|---|---|
allow | Final authorization decision after Harbor capability checks and Gateway spend policy. |
approvalRequired | Policy hold — not a hard deny. Surface to operators and retry with approvalToken after approval. |
decisionId | Immutable authorization decision id; used to finalize scope reservations after tool execution. |
approvalRequestId | Pending approval queue item when quorum is incomplete. |
reasonCodes | Machine-readable policy outcomes (for example approval_threshold_exceeded, daily_tenant_cap_exceeded). |
spendScope / remainingCents | Scope that governed the check and optional remaining budget headroom. |
PaybondSpendDeniedError / PaybondSpendApprovalRequiredError
PaybondSpendGuard.assertSpendAuthorized and guardTool raise PaybondSpendDeniedError when allow is false and approval is not required. They raise PaybondSpendApprovalRequiredError when approvalRequired is true. Both errors expose a result: VerifyCapabilityResult property for logging and operator UX.
SubmitEvidenceResult
type SubmitEvidenceResult = { intentId: string; tenant: string; state: string; predicatePassed?: boolean; };
ImportAgentMandateV1Result
type ImportAgentMandateV1Result = { valid: boolean; intent_id: string; mandate_digest_sha256_hex: string; mandate: AgentMandateV1; authorization_receipt: ProtocolAuthorizationReceiptV1; };
Returned by GatewayProtocolClient.importAgentMandateV1. The client verifies the echoed intent_id and mandate tenant before returning.
VerifyProtocolReceiptV1Result
type VerifyProtocolReceiptV1Result = { valid: boolean; kind: string; receipt_id: string; tenant_id: string; receipt: ProtocolAuthorizationReceiptV1 | ProtocolSettlementReceiptV1 | Record<string, unknown>; };
Returned by GatewayProtocolClient.verifyProtocolReceiptV1 for authorization and settlement receipt verification.
Canonical signing helpers
These helpers are intended for advanced callers that build signed request bodies or evidence payloads outside of paybond.intents. They match Paybond's canonical signing rules byte-for-byte (bincode 2 standard encoding; evidence binding uses EvidenceSignV1.version = 2).
buildSignedCreateIntentBody
buildSignedCreateIntentBody(params: BuildSignedCreateIntentParams): Record<string, unknown>;
Builds a signed CreateIntentRequest body for operation harborCreateIntent. Canonical JSON normalization is applied before BLAKE3 digesting and Ed25519 signing.
params.settlementRail is signed into the create-intent payload and only includes the requested rail name. No SDK surface accepts free-form Stripe destinations or x402 receive addresses.
intentCreationSignBytesRaw
intentCreationSignBytesRaw(params: BuildSignedCreateIntentParams): Uint8Array;
Returns the exact bytes that get Ed25519-signed. Useful for offline signers (for example HSMs or air-gapped principal keys).
signPayeeEvidenceBinding
signPayeeEvidenceBinding(params: SignPayeeEvidenceParams): Record<string, unknown>;
Builds a signed evidence wire body for operation harborSubmitEvidence.
artifactsDigest, normalizeJson, jsonValueDigest
BLAKE3-based helpers that match the canonical rules used by Harbor. Exported for offline verification tooling.
Typed errors
All error classes extend Error and attach structured diagnostics instead of wrapping an opaque unknown. Never log raw tokens or API keys; the Kit redacts nothing itself — redaction is a caller responsibility.
HarborHttpError
class HarborHttpError extends Error { readonly statusCode: number; readonly url: string; readonly bodyText: string; // raw Harbor response body (may be JSON) }
Thrown by HarborClient when Harbor returns any non-success status after retries. Look for the machine-readable code field inside bodyText — contracts are in the Error envelope reference.
A2AHttpError
class A2AHttpError extends Error { readonly statusCode: number; readonly url: string; readonly bodyText: string; }
Thrown by GatewayA2AClient when A2A discovery routes return non-success statuses after retries.
ProtocolHttpError
class ProtocolHttpError extends Error { readonly statusCode: number; readonly url: string; readonly bodyText: string; // raw gateway response body (may be JSON) readonly errorCode?: string; // parsed gateway "error" field when present readonly errorMessage?: string; // parsed gateway "message" field when present }
Thrown by GatewayProtocolClient and paybond.protocol for gateway-backed protocol/v2/* calls plus recognition-gated gateway /harbor/* mutation helpers. The Kit parses JSON gateway error envelopes so callers can branch on explicit codes such as unregistered_key, tenant_mismatched_key, revoked_key, mandate_agent_key_mismatch, and protocol_binding_mismatch without reparsing bodyText.
GatewayAuthError
class GatewayAuthError extends Error { readonly statusCode: number | undefined; readonly bodyText: string | undefined; }
Thrown when Gateway rejects the API key, returns malformed JSON, or omits tenant_id.
SignalHttpError
class SignalHttpError extends Error { readonly statusCode: number; readonly url: string; readonly bodyText: string; }
Thrown by GatewaySignalClient when gateway Signal routes return a non-success status after retries, excluding the 404 -> null read contract on receipt, explanation, and review-status lookups.
Tenant, intent, and receipt mismatch
HarborClient, GatewaySignalClient, GatewayFraudClient, and GatewayProtocolClient throw plain Error instances when Harbor or Gateway echoes a different tenant, intent, operator, mandate, or receipt than requested. Treat these as critical tenant-isolation failures and do not retry.
Retries, idempotency, and logging
- All mutating POSTs accept
idempotencyKey. The same key with the same body is safe to retry on transient failures; the same key with a different body returns409. - Log
tenant_id,intent_id,statusCode,url, correlation id (when present), and a redactedbodyText. Never log the API key, Harbor JWT, or capability token. - Backoff is exponential with jitter, bounded at ~5s per attempt;
Retry-Afteris honored up to 30s.