Back to Blog
Engineering
2026-08-0812 min readBy StableOps

AI Agent Payments: A Safe Stablecoin Stack for Buyers and Sellers

Build AI agent payments with buyer-side policies, budgets, approvals, and customer-controlled signing, plus seller-side payment orders, finality, webhooks, and reconciliation.

AI agent payments
Stablecoin payments
x402

An AI agent payment is not safe merely because an agent can answer an HTTP 402 challenge. The buyer still needs to prevent an autonomous process from spending outside its mandate. The seller still needs to know which business order was paid, whether the transfer is final, and whether fulfillment can be replayed safely after a failure.

That creates two separate control problems around the same payment:

  • On the buyer side, the agent should be able to request a purchase without receiving unrestricted wallet authority.
  • On the seller side, a successful protocol exchange must become a durable order, a final onchain payment, and an auditable business outcome.

StableOps covers those two sides with separate products. Agent Payments controls how an agent can spend. The existing collection product controls how a merchant receives, confirms, and reconciles a stablecoin transfer. x402 connects the HTTP request to the payment requirement, but it does not collapse the two sides into one shared state machine.

One payment, two different trust boundaries

The buyer and seller do not trust the same systems, hold the same credentials, or answer the same operational questions. Keeping their responsibilities separate makes the transaction easier to reason about.

SideMust decideStableOps primitivesMust not receive
Buyer operatorWhich origins and recipients are allowed, how much one agent may spend, and when a human must approvePayment Agent, immutable policy version, organization and agent budgets, approval, execution grantThe seller's API key or receiving-address controls
Buyer agent runtimeWhich paid resource to request and how to resume the same approved attemptRestricted Agent Key, Agent SDK, task-scoped idempotency keyManagement credentials or a raw private key
Buyer signerWhether the exact network, asset, recipient, amount, nonce, and validity match an authorized paymentCustomer-hosted signer, local authorization store, local key or AWS KMSArbitrary signing requests from the model
Seller applicationWhich business order the transfer belongs to and when downstream work is safePayment Order, receiving address, confirmation lifecycle, signed Webhook, delivery auditThe buyer's policy, budget, or signing material

This is intentionally not a single account shared by both parties. A seller can use StableOps collection even when the payer uses another x402 client. An Agent Payments customer can buy from an x402 seller that uses another merchant stack. When both sides use StableOps, they gain consistent controls at both boundaries, but each side still owns its own credentials and records.

The end-to-end AI agent payment flow

For an x402 resource backed by a StableOps Payment Order, the complete path looks like this:

Seller creates or reuses a Payment Order
  -> seller binds its allocated address, chain, asset, and amount to x402 payTo
  -> agent requests the resource and receives 402 / PAYMENT-REQUIRED
  -> Agent Payments checks origin, recipient, amount, policy, and two budgets
  -> an administrator approves when the request is outside the automatic range
  -> StableOps issues a short-lived, one-time execution grant
  -> the customer-hosted signer verifies the grant and signs exact payment data
  -> the Agent SDK retries the GET with PAYMENT-SIGNATURE
  -> the x402 resource server asks the facilitator to verify and settle, then returns the resource
  -> the seller's Payment Order detects and confirms the exact onchain transfer
  -> payment.finalized Webhook drives durable seller-side accounting and fulfillment

There are two important handoffs in that flow.

First, the seller must explicitly bind the StableOps payment instruction to the x402 requirement. Creating a Payment Order by itself does not associate an unrelated settlement. The payTo address, network, asset contract, and amount exposed by the resource server must describe the same transfer the order expects.

Second, protocol settlement and merchant finality answer different questions. The paid HTTP response tells the buyer how the resource request ended. When present, PAYMENT-RESPONSE also reports the protocol settlement result. The seller's payment.finalized event tells the merchant application that the matching transfer reached its configured irreversible boundary. If the HTTP response also starts a separate long-lived job, grants durable quota, or changes an external system, make that downstream action replay-safe and reconcile it against the merchant order.

The earlier article x402 Payments and Stablecoins explains that protocol boundary in detail. Here, the focus is how the current buyer and seller products meet on each side of it.

Give the seller a durable order before exposing payTo

The seller should establish business identity before the agent can pay. Use one stable merchant order ID and idempotency key for one charge attempt, allocate an exact receiving address, and place enough request context in metadata to investigate the payment later.

import { StableOps } from '@stableops/api-sdk'
import { parseUnits } from 'viem'

const BASE_SEPOLIA_USDC = '0x036CbD53842c5426634e7929541eC2318f3dCF7e'
const X402_MAX_TIMEOUT_SECONDS = 300

const collection = new StableOps({
  apiKey: process.env.STABLEOPS_API_KEY!,
})

export async function createAgentCharge(input: {
  requestId: string
  amount: string
  resource: string
}) {
  const order = await collection.paymentOrders.create(
    {
      merchantOrderId: `agent-request:${input.requestId}`,
      amount: input.amount,
      acceptedAssets: [{ chain: 'base-sepolia', asset: 'USDC' }],
      expiresAt: new Date(Date.now() + 30 * 60 * 1000).toISOString(),
      metadata: { rail: 'x402', resource: input.resource },
    },
    { idempotencyKey: `agent-request:${input.requestId}` },
  )

  const instruction = order.paymentInstructions.find(
    (item) => item.chain === 'base-sepolia' && item.asset === 'USDC',
  )

  if (!instruction) throw new Error('Base Sepolia USDC payment instruction was not allocated')

  return {
    paymentOrderId: order.id,
    payTo: instruction.address,
    network: 'eip155:84532' as const,
    asset: BASE_SEPOLIA_USDC,
    amount: order.amount,
    amountAtomic: parseUnits(order.amount, 6).toString(),
    maxTimeoutSeconds: X402_MAX_TIMEOUT_SECONDS,
    expiresAt: order.expiresAt,
  }
}

A StableOps Payment Order expresses amount as a decimal string in asset units, while x402 PaymentRequirements.amount is an integer string in the asset's smallest unit. The seller must use the returned amountAtomic when constructing the requirement, not order.amount. Bind payTo, network, asset, and maxTimeoutSeconds exactly as returned. Do not accept a caller-provided receiving address or silently substitute another chain.

Order expiry must also constrain when the payment requirement is offered. Before returning the first 402 and every refreshed challenge, the resource server must retrieve the Payment Order again. It may continue exposing the original requirement only while the order is still created and its remaining lifetime covers maxTimeoutSeconds plus a suitable settlement buffer. If the order has expired or has too little time left, stop accepting the old requirement. A new charge requires a new merchant order ID and idempotency key, and the buyer must not use an old approval to authorize it. This prevents a delayed approval from sending funds to an order that can no longer match the transfer.

The order and idempotency key solve different replay problems. merchantOrderId preserves the link to the business request. The idempotency key makes a retry of the same create call return the original result instead of allocating another charge. The Payment Orders guide covers allocation and matching behavior.

Let the agent request payment, not control the wallet

On the buyer side, an operator prepares the payment rails before the model runs:

  1. Register and verify a payment wallet.
  2. Create a Payment Agent and bind the wallet for the target network.
  3. Activate an immutable policy version with allowed origins, recipients, asset, per-payment limit, and automatic-payment threshold.
  4. Set organization and Agent daily budgets.
  5. Issue a restricted Agent Key and keep the management API key outside the runtime.
  6. Run the signer in the customer's environment with a separate authorization store.

The runtime then makes the paid request through the Agent SDK:

import {
  AgentPaymentsControlClient,
  HttpAgentSignerSidecar,
  SafeHttpsRequester,
  SettlementUnknownError,
  StableOpsAgent,
} from '@stableops/agent-sdk'

const payments = new StableOpsAgent({
  control: new AgentPaymentsControlClient({
    agentKey: process.env.STABLEOPS_AGENT_KEY!,
  }),
  sidecar: new HttpAgentSignerSidecar({
    url: 'http://127.0.0.1:8789',
    authToken: process.env.STABLEOPS_SIDECAR_TOKEN,
  }),
  requester: new SafeHttpsRequester(),
})

let result: Awaited<ReturnType<typeof payments.x402Fetch>>

try {
  result = await payments.x402Fetch('https://api.example.com/paid-report', {
    idempotencyKey: 'research-task-284:paid-report:v1',
  })
} catch (error) {
  if (error instanceof SettlementUnknownError) {
    const payment = await payments.getPayment(error.intentId)
    console.error({
      message: 'Settlement is unknown; do not create another payment authorization',
      intentId: error.intentId,
      authorizationId: error.authorizationId,
      paymentStatus: payment.status,
    })
  }
  throw error
}

if (result.status === 'paid' || result.status === 'not_required') {
  if (!result.response.ok) {
    const body = await result.response.text()
    throw new Error(`Resource request failed: HTTP ${result.response.status} ${body}`)
  }

  const contentType = result.response.headers.get('content-type') ?? ''
  const report = contentType.includes('application/json')
    ? await result.response.json()
    : await result.response.text()
  if (result.status === 'paid') {
    const payment = await payments.getPayment(result.intentId)
    console.log({
      report,
      intentId: result.intentId,
      paymentStatus: payment.status,
      protocolSettlement: result.paymentResponse,
      resultReported: result.resultReported,
    })
  } else {
    console.log(report)
  }
} else if (result.status === 'awaiting_approval') {
  // Persist result.intentId and resume this Intent after approval.
  console.log(`Waiting for approval: ${result.intentId}`)
}

The Agent Key can create and inspect its own payment attempts, but it cannot change wallets, policies, budgets, or approvals. The signer does not trust the Agent runtime: it checks every field against a short-lived execution grant and rejects arbitrary messages or transactions. The wallet key remains in the customer's process or KMS boundary.

The current product scope is deliberately narrower than “let the model use a wallet.” Agent Payments supports server-side Node.js, HTTPS x402 v2 exact resources, GET, and configured USDC networks. It does not expose direct transfers, arbitrary token spending, POST, browser execution, or a raw signing interface. See the Agent Payments support matrix for the current networks and the quickstart for the complete setup.

Policies decide when autonomy is acceptable

A budget alone is not a sufficient payment policy. An agent that spends within a daily total can still pay the wrong recipient, accept a manipulated quote, or repeat the wrong task many times.

StableOps evaluates the origin, payTo, network, asset, and amount before reserving both the organization and Agent budgets. A request can then take one of three paths:

ResultMeaningOperator response
RejectedThe payment exceeds a hard policy or platform limitChange the task or publish a new policy version; do not bypass the decision with another key
Awaiting approvalThe amount is within the hard limit but the recipient, origin, or automatic threshold requires a human decisionInspect the locked payment details, approve or reject once, then resume the same Intent
Approved automaticallyEvery allowlist, amount, and budget condition passesContinue through the short-lived grant and customer signer

New Payment Agents start conservatively: the automatic threshold is zero and payments require approval until an operator explicitly activates broader rules. Approval is not a way for the agent to edit the charge. If the refreshed x402 requirement changes the recipient, network, asset, or raises the amount, the old approval cannot authorize the new payment.

Design retries around uncertainty, not optimism

AI systems retry aggressively, while payment systems must assume a timeout can happen after value moved. That is why both sides need stable identities.

  • The buyer reuses one task-scoped idempotency key for the same purchase intent.
  • If approval is required, it persists the returned Intent ID and resumes that Intent instead of creating another.
  • After a signed request times out, it queries the existing payment and lets reconciliation determine the result. It must not create a replacement Intent just because no response arrived.
  • The seller reuses the merchant order ID and create-call idempotency key for the same charge.
  • The seller deduplicates signed Webhooks by event ID and makes fulfillment idempotent independently of delivery attempts.

These rules contain failures on both sides. The buyer avoids a double charge; the seller avoids duplicate fulfillment. Neither guarantee replaces the other. The stablecoin Webhook guide shows the seller-side inbox and replay pattern.

Keep custody out of both control planes

“Non-custodial” has a different practical meaning on each side of this transaction.

For the buyer, StableOps does not receive the wallet private key. The customer's signer verifies a precisely bound grant locally, and the x402 facilitator submits the supported payment. The Agent runtime has a revocable Agent Key, not general wallet access.

For the seller, StableOps allocates from addresses whose private keys the merchant controls. StableOps detects and confirms matching transfers but does not hold the received funds. Treasury sweeps and refunds remain merchant actions.

This separation limits the blast radius of every credential. A compromised Agent Key can be revoked without moving the wallet. A seller API key cannot sign from a buyer wallet. A model prompt cannot change an immutable policy or make the customer signer accept arbitrary data.

Production checklist

  • Give every paid task one stable buyer idempotency key and every seller charge one stable merchant order ID.
  • Bind the seller's exact order address, network, asset, and smallest-unit amount into the x402 requirement.
  • Check the seller order status and remaining lifetime before returning or refreshing each 402; never keep collecting against an expired order.
  • Keep Agent Payments management credentials outside the Agent runtime; give the runtime only its Agent Key.
  • Start with one origin, one recipient, a small per-payment limit, a small daily budget, and human approval.
  • Run the signer in the customer trust boundary and persist its authorization state across restarts.
  • On approval, resume the same payment Intent instead of opening another one.
  • Treat a signed-request timeout as an unknown settlement, not as permission to pay again.
  • Deduplicate seller Webhooks by event ID and keep downstream fulfillment replay-safe.
  • Record the buyer task ID, Agent Payment Intent ID, seller payment order ID, and transaction reference wherever both sides are operated by the same business.
  • Test approval, rejection, duplicate calls, a timeout after signing, failed Webhook delivery, and seller reconciliation before increasing limits.

FAQ

Do AI agent payments require x402?

Not as a general concept: an agent can participate in other payment workflows. The current StableOps Agent Payments product intentionally supports x402 v2 exact GET resources rather than unrestricted transfers. StableOps collection does not require x402; it can receive ordinary wallet payments through Payment Orders and Hosted Checkout.

Does the AI agent need access to a wallet private key?

No. The Agent runtime holds a restricted, revocable Agent Key. A customer-hosted signer keeps the wallet key or KMS permission outside the model process and signs only payment data that exactly matches a valid short-lived execution grant.

Does x402 settlement mean the seller can perform every irreversible action?

It is enough for the x402 resource server to follow its protocol-level delivery rules, but it does not automatically complete the seller's wider business workflow. When a StableOps Payment Order backs the charge, use the verified payment.finalized Webhook for durable accounting and irreversible downstream work, and make those operations idempotent.

Start with one controlled transaction

Choose one paid GET resource, one Base Sepolia USDC seller order, one buyer Agent, and a deliberately small limit. Follow the Agent Payments quickstart to configure the buyer, then use Payment Orders to bind the seller's receiving address to the x402 requirement. Confirm the paid response, Agent Payment record, onchain transfer, seller final event, and business ledger before enabling automatic payment.

That narrow pilot proves the whole point of an AI agent payment stack: the machine can complete the purchase, while neither side loses the controls it needs when the transaction leaves the happy path.

Related articles

Use x402 for HTTP-native agent payments, but keep merchant-side order state, policy, finality, webhooks, and audit trails separate.

Learn crypto payment reconciliation across business orders, payment events, and on-chain transfers with a safe script and daily and monthly checklists.

An underpaid crypto payment, overpayment, wrong-network transfer, or late payment should never silently complete an order. Learn how to detect, resolve, and prevent each mismatch.

There is no single best chain for stablecoin payments. Compare payer-side fees, finality, and wallet distribution, then accept a set of chains and match each transfer to an order.