Skip to main content
HomeLanding pagesHow to test agent payments
LANDING PAGE

How to test agent payments

8 min read·Last updated June 2, 2026

Test on Base Sepolia with a sk_test_ key, where USDC comes from a faucet and nothing costs real money. Exercise four scenarios: a paid call that settles, a payment refused by the spend limit, an insufficient-balance failure, and a verified webhook (use webhooks.test). Confirm your agent handles each cleanly, then swap to a sk_live_ key. The key prefix selects the network.

What you will test

Everything about an agent's payments, before any real USDC is at risk. Payments are easy to test well because there is a full test network: a sk_test_ key puts the agent on Base Sepolia, where USDC comes from a faucet and behaves exactly like the real thing without being worth anything. The job is to exercise not just the happy path but the failure paths, because that is where agents misbehave, and then promote to mainnet knowing the flow holds.

This is the dedicated testing guide. The sandbox workflow is also covered in the test-agent-payments guide; here we lay out the scenarios to run. The payment API product page is the broader reference.

Set up the test environment

Point everything at testnet. A sk_test_ key gives the agent a wallet on Base Sepolia, and the network is chosen entirely by the prefix, so there is no separate config to set.

BASH
export B0X_API_KEY=sk_test_...   # sk_test_ -> Base Sepolia (eip155:84532)

Fund the agent's wallet with test USDC from a public Base Sepolia faucet, sending to the address from the dashboard or the agent's profile. That is the whole setup: a test key and a funded test wallet. Everything below runs against this, costs nothing, and behaves like mainnet.

Scenario 1: the happy path

Confirm the basic flow end to end: the agent makes a paid call, it settles, and the result returns.

TYPESCRIPT
import { createClient } from "@blockchain0x/node";
import { createX402Client } from "@blockchain0x/x402/client";

const sdk = createClient({ apiKey: process.env.B0X_API_KEY! });
const fetchWithPay = createX402Client({ sdk });

const res = await fetchWithPay("https://api.example.com/paid-endpoint");
console.assert(res.ok, "expected a paid call to succeed");

Watch the 402, the settlement, and the 200 happen, ideally in your logs. This proves the wiring; the next scenarios prove the agent survives when things do not go smoothly.

Scenario 2: a refused payment

The most important failure to test, because a runaway agent lives here. Set the agent's per_tx_wei below the price of a call, then make that call, and confirm the wallet refuses it before settlement and your agent handles the non-2xx without retrying.

TYPESCRIPT
const res = await fetchWithPay(expensiveEndpoint);
console.assert(!res.ok, "expected an over-limit payment to be refused");
// assert your agent records this and does NOT retry the same call

The thing to verify is not just that the payment was refused, the wallet guarantees that, but that your agent treats the refusal as a stop. An agent that loops on a refused payment is the failure this test exists to catch.

Scenario 3: insufficient balance

Test what happens when the wallet runs dry. Use an unfunded test agent, or drain one, then make a paid call and confirm it fails cleanly rather than hanging or crashing.

A paid call against an empty wallet should come back as a non-2xx your tool turns into a sensible message, not a timeout or an unhandled exception. This is the scenario teams forget, because in development the wallet always has funds, and then in production a busy agent empties its balance and the untested path breaks. Run it once on purpose, and decide what the agent should do when it is out of money: stop and alert, fall back to a free path, or queue the work for after a top-up. Whatever you choose, an empty wallet should be a handled state, not a crash, and the only way to know it is handled is to have run the agent dry at least once on testnet.

Scenario 4: webhook verification

Test that your webhook endpoint accepts genuine events and rejects bad ones. You do not need a real payment: send a test delivery on demand.

TYPESCRIPT
await sdk.webhooks.test(webhookId, { event: "webhook.test" });

Confirm your endpoint receives it, webhooks.verify returns ok, and you branch correctly. Then test the rejection paths: feed your handler a body with a tampered signature and confirm verify fails with a code, and a delivery with an old timestamp and confirm it is rejected as a replay. A handler that only ever sees valid events in testing is one that has never proven it rejects invalid ones, which is the whole point of verification.

Scenario 5: redelivery and idempotency

Webhook delivery is at-least-once, so the same event can arrive twice, and an agent that double-processes a payment is a real bug. Test for it directly: deliver the same verified event to your handler twice and assert the second is a no-op.

TYPESCRIPT
await handleEvent(verifiedEvent);            // first delivery: processes
const before = await ledger.count(verifiedEvent.eventId);
await handleEvent(verifiedEvent);            // duplicate: should not re-process
const after = await ledger.count(verifiedEvent.eventId);
console.assert(before === after, "duplicate delivery must not double-process");

The thing under test is your dedupe, keyed on eventId or deliveryId. This is easy to get wrong and easy to verify, and a duplicate that credits a balance twice or releases work twice is exactly the kind of bug that only shows up in production under load. Prove the handler is idempotent in a test rather than discovering it is not from a customer.

Test in CI without the network

Not every test needs the chain. Split your suite. Unit tests run in CI with no network: construct a webhook body and headers, assert webhooks.verify accepts the genuine one and rejects a tampered one, and assert that a non-2xx from a paid call makes your agent stop rather than retry. These are fast, deterministic, and catch the logic bugs.

Reserve real settlement for an integration suite that runs against Base Sepolia, less often and outside the fast feedback loop, to confirm the end-to-end flow still holds. The split keeps CI quick while still exercising real payments somewhere. The mistake is putting live testnet calls in your fast unit suite, which makes it slow and flaky; mock the inputs for logic, and use the network only where you are genuinely testing the network.

Move to mainnet with confidence

When all four scenarios pass on Base Sepolia, going live is a key swap, not a rewrite. Replace the sk_test_ key with a sk_live_ one and the same code settles real USDC on Base mainnet. Two checks before you do: re-confirm the spend limit on the live agent, because limits are per agent and your test agent's limit does not carry over, and add a boot-time prefix check so a test key never runs in production or a live key in a test context. With the failure paths already proven on testnet, the only new variable on mainnet is that the money is real.

Common pitfalls

Three traps testing agent payments.

Testing only the happy path. The failures, refused payments, empty wallets, bad webhooks, are where agents break. Test them as deliberately as the success case.

Skipping webhook rejection tests. Verifying that valid events pass is half the test. Confirm tampered and replayed deliveries fail too.

Assuming the live agent inherits test limits. Limits are per agent. Set and re-check them on the mainnet agent before you trust it.

What to ship today

Put the agent on Base Sepolia with a sk_test_ key, fund it from a faucet, and run all four scenarios: happy path, refused payment, insufficient balance, and webhook verify including the rejection cases. When they pass, swap to a sk_live_ key and re-check the limit. For the broader sandbox workflow see the test-agent-payments guide. Pricing is on the pricing page.

FAQ

Frequently asked questions.

How do I test without spending real money?

Use a sk_test_ key, which puts the agent on Base Sepolia, a test network where USDC comes from a public faucet and has no real value. Every shape of the flow (settlement, refusal, webhooks) behaves the same as mainnet, so you can exercise the whole thing for free before a sk_live_ key touches real funds.

What scenarios should I test, not just the happy path?

Four. A paid call that settles and returns. A payment refused by the spend limit (set per_tx_wei low and exceed it). An insufficient-balance failure (drain or use an unfunded wallet). And webhook verification, including a tampered or replayed delivery that should fail. The failure paths are where agents misbehave, so test them as deliberately as the happy path.

Can I test webhooks without a real payment?

Yes. client.webhooks.test(id, ...) sends a webhook.test delivery to your endpoint on demand, so you can confirm your URL, secret, and verification work end to end without waiting for a payment. Verify it with webhooks.verify exactly as you would a real event.

How do I test in CI without network calls?

Unit-test your handler logic with mocked inputs: feed it a crafted webhook body and headers and assert it verifies and branches correctly, and assert a non-2xx response makes your agent stop rather than retry. Reserve the real testnet runs for an integration suite that exercises actual settlement on Base Sepolia.

Does the same code work on mainnet after testing?

Yes. The only difference between testnet and mainnet is the key prefix: sk_test_ settles on Base Sepolia, sk_live_ on Base mainnet. Your code does not change. Re-check the spend limit on the live agent, since limits are per agent, and confirm the prefix at boot so a test key never runs in production.

Create your free agent wallet in 5 minutes.

First payment confirmed in under ten minutes. Free to start.