New endpoints for AI agent payments via the x402 protocol, plus Base and Arbitrum added as supported blockchains for all payment sessions.
Two new endpoints enable AI agents to pay for API resources using gasless stablecoin transfers. Supports EIP-3009, Permit2, and Solana signing schemes. CoinCircuit handles on-chain submission and gas fees.
Settle gasless stablecoin payments from AI agents. Accepts signed payloads from three schemes: EIP-3009 (USDC on Base, Arbitrum), Permit2 (USDC, USDT on Base, Arbitrum, BSC), and Solana native fee payer abstraction. CoinCircuit submits the transaction on-chain and covers gas.
Pre-validate a signed agent payment without touching the blockchain. Runs scheme-specific checks: authorization timestamps and nonce replay for EIP-3009, deadline and balance for Permit2, transaction validity for Solana. Returns 200 if the payment will succeed, or 400 with per-check pass/fail results.
Payment sessions can now be used in the x402 flow. Create a session via POST /payments, return the deposit details to the agent as an HTTP 402 response, receive the signed payload back, and settle via the new /payments/agent/settle endpoint. The agent only needs a wallet and signing key.
EIP-3009 for USDC on Base and Arbitrum (native transferWithAuthorization). Permit2 for USDC, USDT, and any ERC-20 on Base, Arbitrum, and BSC (Uniswap universal approval). Solana native fee payer abstraction for SOL, USDC, and USDT. All schemes are gasless for the agent.
Base (Coinbase L2) and Arbitrum are now fully supported for all payment sessions, settlements, and agent payments.
Coinbase's Layer 2 blockchain is now available for all payment sessions. USDC on Base settles in under 2 seconds. Base is the default blockchain for EIP-3009 gasless agent payments.
Arbitrum One is now available for all payment sessions. Supports USDC and USDT via both EIP-3009 and Permit2 signing schemes with sub-second finality.
Major updates to the Payment API and Payouts system, introducing new payload shapes, unified endpoints, and better error handling.
Updated session response shape, renamed webhook event types (session.* → payment.*), and a restructured amount object with inline settlements. Existing API keys are unaffected. New API keys automatically use the updated format.
Payment session webhook event types have been renamed. Previous keys used session.* prefixes; new keys use payment.* prefixes. The webhook payload shape (data.session.*) is unchanged — only the event type string differs. This applies to all payment session outcome events.
Versioning is tied to your API key. New API keys created in the dashboard automatically use the updated response format and new webhook event types. Existing API keys continue to receive the current responses and event types — no action required to preserve your current integration.
The session API response has been restructured. The top-level status field is replaced by state (open/closed). Amount and currency are flat strings instead of a nested object. The financial breakdown (gross, fees, net) moves to a separate settlements object.
The top-level status field is now state — "open" while active and awaiting payment, "closed" once finalized. The payment outcome is available in payment.status.
Payment session responses now include a settlements object inline with the settlement currency, gross amount with conversion rate, fee breakdown (processing + gas), and net amount. No need to call the settlements endpoint separately.
The legacy payout endpoints (/payout/fiat, /payout/crypto) are deprecated and will be removed on March 30, 2026. The new /payouts endpoint unifies fiat and crypto payouts into a single request.
The existing /payout/fiat and /payout/crypto endpoints are deprecated and will be removed on March 30, 2026. Migrate to the new unified POST /payouts endpoint. Separate fee endpoints (/payout/fiat/fee and /payout/crypto/fee) are replaced by GET /payouts/fees.
A single POST /payouts endpoint handles both fiat and crypto payouts. Specify the method in the request body. The bankAccountId and addressId fields are replaced by a single recipientId field.
In the old API, amount was the total debited from your balance (including fees) — the recipient got less. In the new API, amount is exactly what the recipient receives. Fees are charged separately on top. No need to add fees to your amount.
The response replaces the nested amount object and separate bankAccount/crypto objects with flat amount/fee/total strings, a unified recipient object, and a conversion object when the payout asset differs from the balance currency.
Follow these steps to transition to the new format. Each step is independent — you can adopt them incrementally.
New API keys automatically use the updated format. Go to the dashboard → API Keys → create a new key. Your existing key keeps working as-is — use the new key in a test environment first before switching production traffic.
Replace all session.* event type strings with the corresponding payment.* equivalents in your webhook handler.
payment.partial (previously session.partial) is now strictly mid-flow. It only fires when partial payments are enabled and the session is still open awaiting more funds. Terminal underpayment always uses payment.underpaid.
The session object has been restructured. Update any code that reads the nested amount object — amount and currency are now flat strings, status is replaced by state, and the financial breakdown moves to settlements.
Replace POST /payout/fiat and POST /payout/crypto with the unified POST /payouts. Add a method field to the request body and use recipientId instead of bankAccountId or addressId. Replace /payout/fiat/fee and /payout/crypto/fee with GET /payouts/fees.
In the old API, amount was the total debited from your balance (fees included). In the new API, amount is what the recipient receives — fees are charged separately. Remove any fee addition logic from your integration.
The response replaces the nested amount object and separate bankAccount/crypto objects with flat fields and a unified recipient object.
v2.1.0March 2026v2.0.0February 2026
NewPOST /payments/agent/settle
POST /api/v1/payments/agent/settle
"sessionReference"
"0xAgentWallet..."
"0xDepositAddress..."
"0xrandom32bytes..."
NewPOST /payments/agent/verify
POST /api/v1/payments/agent/verify
// Response (200)
Newx402 HTTP 402 payment flow support
>_ example.javascript
resource delivered
NewThree gasless signing schemes supported
| Scheme | Assets | Chains |
NewBase blockchain support
NewArbitrum blockchain support
UpdatedWebhook event types renamed: session.* → payment.*
// Previous event types "session.completed" "session.expired" "session.partial" "session.failed"
// Previous event types
"session.completed"
"session.expired"
"session.partial"
"session.failed"
// New event types "payment.completed" "payment.expired" "payment.partial" "payment.underpaid" "payment.failed"
// New event types
"payment.completed"
"payment.expired"
"payment.partial"
"payment.underpaid"
"payment.failed"
NewNew API keys use the updated format automatically
// Existing key → receives current events and response shape
// New key → receives updated events and response shape
// No config needed — version is determined by which key you use
NewRedesigned session response shape
// New session response
"550e8400-e29b-41d4-a716-446655440000"
"cs_ref_abc123xyz789"
"amountReceived"
"conversionRate"
Newstate replaces status
// "pending" | "completed" | "expired" | "failed"
// "open" | "closed"
// payment outcome lives in payment.status
Newsettlements included in payment API response
UpdatedPayout API deprecated — migrate to /payouts by March 30
POST /payout/fiat // fiat payouts POST /payout/crypto // crypto payouts GET /payout/fiat/fee // fiat fee lookup GET /payout/crypto/fee // crypto fee lookup
POST /payout/fiat
POST /payout/crypto
// crypto payouts
GET /payout/fiat/fee
// fiat fee lookup
GET /payout/crypto/fee
// crypto fee lookup
POST /payouts // unified — specify method in body GET /payouts/fees // unified fee lookup
// unified — specify method in body
GET /payouts/fees
// unified fee lookup
NewUnified payout endpoint
// New unified request body
// "fiat" | "crypto"
// amount recipient receives
"7c9e6679-7425-40de-944b-e07fc1f90ae7"
"CLIENT-REF-123"
// optional idempotency key
UpdatedAmount is what the recipient receives
// Old: amount = total deducted (fee included)
// recipient gets: 10000.00
// New: amount = what recipient gets (fee is separate)
// total deducted: 10050.00
NewRedesigned payout response
// New payout response (USDT balance → USDC payout)
"convertedAmount"
// Replace each event type:
// payment.partial → session still open, more payments expected
// data.session.state === "open"
notifyCustomerOfPartialPayment
// payment.underpaid → session closed, insufficient total received
// data.session.state === "closed"
initiateRefundFlow
// Previous — nested amount object
// New — flat amount + settlements object
// "10000.00" (plain string)
// "NGN" (plain string)
// Old — separate endpoints
// New — unified endpoint
// was balanceCurrency
// was bankAccountId or addressId
// optional idempotency key (new)
// Old — amount includes fee, recipient gets less
// you had to add fee
// recipient gets 10000.00, fee was 50.00
// New — amount IS what recipient gets
// recipient gets 10000.00, fee (50.00) charged separately
// total deducted from balance: 10050.00
// Old response fields
// nested object
// New response fields
// plain string (what recipient gets)
// plain string (amount + fee)
// "bankAccount" | "cryptoAddress"
// cross-currency (e.g. USDT→USDC) or null
POST /api/v1/payments/agent/settle
{
"sessionReference": "cs_ref_abc123",
"scheme": "eip3009",
"chain": "base",
"asset": "USDC",
"payload": {
"from": "0xAgentWallet...",
"to": "0xDepositAddress...",
"value": "1000000",
"validAfter": "0",
"validBefore": "1774055094",
"nonce": "0xrandom32bytes...",
"signature": "0xabcd...1234"
}
}
POST /api/v1/payments/agent/verify
{
"scheme": "eip3009",
"chain": "base",
"asset": "USDC",
"payload": { ... }
}
// Response (200)
{
"valid": true,
"checks": {
"balance": "pass",
"nonce": "pass",
"authorization": "pass"
}
}
// x402 Flow
Agent -> GET /api/premium-data
Server -> CoinCircuit: POST /payments (creates session)
Server -> Agent: 402 (deposit address, amount)
Agent -> Signs payment (eip3009 / permit2 / solana)
Agent -> Server: POST /pay (signed payload)
Server -> CoinCircuit: POST /payments/agent/settle
Server -> Agent: 200 (resource delivered)
| Scheme | Assets | Chains |
| eip3009 | USDC | Base, Arbitrum |
| permit2 | USDC, USDT | Base, Arbitrum, BSC |
| solana | SOL,USDC,USDT| Solana |
// Existing key → receives current events and response shape
// New key → receives updated events and response shape
// No config needed — version is determined by which key you use
// New session response
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"reference": "cs_ref_abc123xyz789",
"state": "closed",
"type": "payment",
"amount": "10000.00",
"currency": "NGN",
"payment": {
"status": "completed",
"asset": "USDT",
"chain": "tron",
"amount": "6.84",
"amountReceived": "6.84",
"address": "T...",
"txHash": "0x..."
},
"settlements": {
"currency": "NGN",
"gross": { "amount": "10000.00", "conversionRate": "1.0" },
"fees
// Previous
{ "status": "completed" } // "pending" | "completed" | "expired" | "failed"
// New
{ "state": "closed" } // "open" | "closed"
// payment outcome lives in payment.status
{
"settlements": {
"currency": "NGN",
"gross": { "amount": "10000.00", "conversionRate": "1.0" },
"fees": {
"processing": { "amount": "100.00", "paidBy": "merchant" },
"gas": { "amount": "50.00", "paidBy": "merchant" }
},
"net": { "amount": "9850.00" }
}
}
// New unified request body
{
"method": "fiat", // "fiat" | "crypto"
"currency": "NGN",
"amount": "10000.00", // amount recipient receives
"recipientId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"narration": "Payout",
"reference": "CLIENT-REF-123" // optional idempotency key
}
// Old: amount = total deducted (fee included)
{ "amount": "10050.00" }
// fee: 50.00
// recipient gets: 10000.00
// New: amount = what recipient gets (fee is separate)
{ "amount": "10000.00" }
// fee: 50.00
// total deducted: 10050.00
// New payout response (USDT balance → USDC payout)
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"method": "crypto",
"type": "manual",
"status": "success",
"amount": "100.00",
"fee": "1.50",
"total": "101.50",
"currency": "USDC",
"conversion": {
"from": "USDT",
"to": "USDC",
"rate": "0.9998",
"convertedAmount": "100.00"
},
"recipient": {
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"type": "cryptoAddress",
"address": "T...",
"chain": "tro
// Replace each event type:
"session.completed" → "payment.completed"
"session.expired" → "payment.expired"
"session.partial" → "payment.partial"
"session.failed" → "payment.failed"
// payment.partial → session still open, more payments expected
if (event === "payment.partial") {
// data.session.state === "open"
notifyCustomerOfPartialPayment(data.session.payment.amountReceived);
}
// payment.underpaid → session closed, insufficient total received
if (event === "payment.underpaid") {
// data.session.state === "closed"
initiateRefundFlow(data.session.payment.amountReceived);
}
// Previous — nested amount object
session.status // "pending" | "completed" | "expired" | "failed"
session.amount.requested.amount // "10000.00"
session.amount.requested.currency // "NGN"
session.amount.gross // { amount, conversionRate }
session.amount.fees // { processing, gas }
session.amount.net // { amount }
// New — flat amount + settlements object
session.state // "open" | "closed"
session.amount //
// Old — separate endpoints
POST /payout/fiat { balanceCurrency, amount, bankAccountId, narration }
POST /payout/crypto { balanceCurrency, amount, addressId }
// New — unified endpoint
POST /payouts {
method: "fiat" | "crypto",
currency: "NGN", // was balanceCurrency
amount: "10000.00",
recipientId: "...", // was bankAccountId or addressId
narration: "...",
reference: "..." // optional idempotency key (new)
}
// Old — amount includes fee, recipient gets less
const amount = recipientAmount + fee; // you had to add fee
POST /payout/fiat { amount: "10050.00" }
// recipient gets 10000.00, fee was 50.00
// New — amount IS what recipient gets
POST /payouts { amount: "10000.00" }
// recipient gets 10000.00, fee (50.00) charged separately
// total deducted from balance: 10050.00
// Old response fields
payout.amount.gross // nested object
payout.amount.fee
payout.amount.net
payout.amount.currency
payout.bankAccount // fiat only
payout.crypto.amount // crypto only
payout.crypto.chain
payout.crypto.address
// New response fields
payout.amount // plain string (what recipient gets)
payout.fee // plain string
payout.total // plain string (amount + fee)
payout.currency // plain string
payout.rec