Complete API documentation for @vanar/xbpp.
Evaluates a payment request against a policy and returns a verdict. This is the core function of the SDK.
evaluate(request: PaymentRequest, policy: Policy): VerdictRuns 12 policy checks in order. Block checks take priority over escalation checks. If any block reason exists, the verdict is BLOCK regardless of escalation reasons.
Wraps a fetch-compatible function with xBPP policy evaluation. Intercepts x402 payment headers and evaluates them before the request is sent.
wrap(fetchFn: typeof fetch, policy: Policy): typeof fetchNon-payment requests (no x402 headers) pass through unchanged with zero overhead. Throws BlockedError on BLOCK or EscalateError on ESCALATE.
interface PaymentRequest {
amount: number // Required - the transaction value
currency?: string // e.g., 'USD', 'USDC', 'EUR'
recipient?: string // email, domain, or URL
metadata?: Record<string, unknown> // custom data
}interface Policy {
maxSingle?: number // Max per transaction
dailyBudget?: number // Max per rolling 24h
hourlyBudget?: number // Max per rolling 1h
askMeAbove?: number // Escalate above this amount
trustedRecipients?: string[] // Allowed recipients
blockedDomains?: string[] // Blocked domains
allowedCurrencies?: string[] // Allowed currencies
maxRequestsPerMinute?: number // Rate limit
operatingHours?: OperatingHours // Time-of-day restrictions
expiresAt?: Date // Policy expiration
}interface Verdict {
decision: 'ALLOW' | 'BLOCK' | 'ESCALATE'
reasons: PolicyReason[] // Why this decision was made
message: string // Human-readable explanation
request: PaymentRequest // Echo of original request
timestamp: Date // When evaluated
}type Decision = 'ALLOW' | 'BLOCK' | 'ESCALATE'type PolicyReason =
| 'EXCEEDS_SINGLE_LIMIT' // amount > maxSingle
| 'EXCEEDS_DAILY_BUDGET' // rolling 24h total exceeded
| 'EXCEEDS_HOURLY_BUDGET' // rolling 1h total exceeded
| 'UNFAMILIAR_RECIPIENT' // not in trustedRecipients
| 'BLOCKED_DOMAIN' // domain in blockedDomains
| 'SUSPICIOUS_PATTERN' // 3+ same recipient in 5min
| 'CURRENCY_MISMATCH' // not in allowedCurrencies
| 'RATE_LIMIT_EXCEEDED' // > maxRequestsPerMinute
| 'ABOVE_ESCALATION_THRESHOLD' // amount > askMeAbove
| 'OUTSIDE_OPERATING_HOURS' // outside operatingHours
| 'AMOUNT_ZERO_OR_NEGATIVE' // amount <= 0
| 'POLICY_EXPIRED' // past expiresAtinterface OperatingHours {
start: string // "HH:MM" format, e.g., "09:00"
end: string // "HH:MM" format, e.g., "17:00"
timezone?: string // IANA timezone, e.g., "America/New_York"
}Thrown by wrap() when the verdict is BLOCK.
class BlockedError extends Error {
reasons: PolicyReason[]
verdict: Verdict
}Thrown by wrap() when the verdict is ESCALATE. Provides callbacks to resume or cancel the request after human review.
class EscalateError extends Error {
reasons: PolicyReason[]
verdict: Verdict
onApprove(): Promise<Response> // Resume the original request
onDeny(): void // Cancel
}