Skip to content

Rails

What is a rail?

A rail is a payment network adapter. It takes a verified, signed spend envelope and routes the payment through a specific financial infrastructure: Airwallex, Wise, Stripe, USDC on Base, or x402.

Envelopes are rail-agnostic. You declare which rails are allowed (allowedRails), and at dispatch time, the SDK selects the appropriate adapter.

Available rails

RailIDBest forSettlement
AirwallexairwallexACH, international wire, cross-border1–3 business days
WisewiseInternational wire, multi-currency accounts1–2 business days
StripestripeCard payments, Stripe-connected merchantsInstant (card)
USDC on Baseusdc-baseCrypto vendors, on-chain payables~2 seconds (Base L2)
x402x402HTTP-native AI-to-AI micropaymentsInstant

Rail selection

const envelope = createSpendEnvelope({
allowedRails: ['airwallex', 'wise'], // Envelope permits either
// ...
})
// At dispatch, specify which rail to use:
const result = await executeAgentPayment(signed, {
recipient: 'vendor@example.com',
amount: 100,
rail: 'wise', // Must be in envelope.allowedRails
})

If rail is omitted from executeAgentPayment(), the SDK uses PQSAFE_DEFAULT_RAIL from your environment config.

Rail-specific configuration

Each rail requires API credentials in your environment:

# Airwallex
AIRWALLEX_CLIENT_ID=...
AIRWALLEX_API_KEY=...
# Wise
WISE_API_TOKEN=...
WISE_PROFILE_ID=...
# Stripe
STRIPE_SECRET_KEY=sk_live_...
# USDC on Base
WALLET_PRIVATE_KEY=0x...
BASE_RPC_URL=https://mainnet.base.org
# x402
X402_FACILITATOR_URL=https://x402.org/facilitator

Error handling across rails

Rail failures are typed and recoverable:

import { executeAgentPayment, PQSafeError } from '@pqsafe/agent-pay'
try {
const result = await executeAgentPayment(signed, { recipient, amount })
} catch (err) {
if (err instanceof PQSafeError) {
switch (err.code) {
case 'RAIL_TIMEOUT':
// Retry with exponential backoff
break
case 'INSUFFICIENT_BALANCE':
// Notify operator — rail account needs top-up
break
case 'RECIPIENT_REJECTED':
// Recipient bank/wallet rejected the transfer
break
case 'RAIL_NOT_ALLOWED':
// Envelope doesn't permit this rail
break
}
}
}

Deep dive on each rail