Skip to main content
LearnGuidesAdd payments to your AI agent
GUIDE

How to add payments to your AI agent.

10 minutes
SHORT ANSWER

Add payments by creating an agent profile, configuring a spend policy, requesting payment via the API (or an x402 response from your endpoint), and handling the confirmation webhook. The agent never touches private keys directly. Setup is under ten minutes from sign-up to your first confirmed USDC payment on Base, in either TypeScript or Python.

PREREQUISITES

Before you start.

  • A Blockchain0x account (free signup).
  • An API key from the dashboard (use a sk_test_ key for this guide; you will switch to sk_live_ later).
  • Node.js 20+ or Python 3.11+ in your agent runtime.
  • An agent built on any framework - LangChain, CrewAI, AutoGen, LlamaIndex, OpenAI Agents SDK, MCP, or plain SDK code. The instructions are framework-agnostic.
  • An HTTPS endpoint reachable from the public internet to receive webhooks (ngrok or a deploy preview is fine for development).
STEP 1 OF 5

Create the agent profile.

The agent profile is the addressable identity behind every payment your agent sends or receives. It carries the wallet address, the public page, the verification badges, and (later) the spend policy. Create one per logical agent.

TypeScript
import { Blockchain0x } from "@blockchain0x/sdk";

const client = new Blockchain0x({ apiKey: process.env.BLOCKCHAIN0X_API_KEY! });

const agent = await client.agents.create({
  name: "research-bot",
  slug: "research-bot",
  purpose: "Generates Q4 LLM market analysis",
  plan: "free",
});

console.log(agent.id);          // "agt_01J9QKE..."
console.log(agent.public_url);  // "https://wallet.blockchain0x.com/a/research-bot"
Python
from blockchain0x import Client
import os

client = Client(api_key=os.environ["BLOCKCHAIN0X_API_KEY"])

agent = client.agents.create(
    name="research-bot",
    slug="research-bot",
    purpose="Generates Q4 LLM market analysis",
    plan="free",
)

print(agent.id)          # "agt_01J9QKE..."
print(agent.public_url)  # "https://wallet.blockchain0x.com/a/research-bot"

After this call, the agent has a public page at https://wallet.blockchain0x.com/a/<slug> that any counterparty (human or agent) can hover for verification info. See the agent payment identity glossary entry for what that page exposes.

STEP 2 OF 5

Request a payment.

A payment request is a hosted checkout URL with a price, a reason, and an expiry. The buyer (your end user, or another agent on the wire) pays at the URL; you get a webhook when settlement finalizes. Return the URL from your own API as part of an HTTP 402 response if you want agent payers to settle programmatically.

TypeScript
const request = await client.paymentRequests.create({
  agent_id: agent.id,
  amount_usdc: "5.00",
  reason: "Research report on Q4 2025 LLM market",
  callback_url: "https://my-agent.com/webhooks/payment",
  expires_in_seconds: 3600,
});

// Hand this URL to the payer (or return it in your API's 402 response).
return res.status(402).json({
  hosted_url: request.hosted_url,
  amount_usdc: request.amount_usdc,
  expires_at: request.expires_at,
});
Python
request = client.payment_requests.create(
    agent_id=agent.id,
    amount_usdc="5.00",
    reason="Research report on Q4 2025 LLM market",
    callback_url="https://my-agent.com/webhooks/payment",
    expires_in_seconds=3600,
)

# Return this in your API's 402 response.
return {
    "hosted_url": request.hosted_url,
    "amount_usdc": request.amount_usdc,
    "expires_at": request.expires_at,
}, 402
STEP 3 OF 5

Handle the confirmation webhook.

Webhooks are how you find out a payment settled. The handler must do three things: verify the HMAC signature, branch on the event type, and respond 2xx quickly. Anything heavier (delivering the work, sending email, updating a database) should be queued behind the 2xx so the webhook does not time out on retry.

TypeScript (Express)
import crypto from "node:crypto";
import express from "express";

const app = express();
const SIGNING_SECRET = process.env.BLOCKCHAIN0X_SIGNING_SECRET!;

app.post(
  "/webhooks/payment",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const signature = req.header("X-Blockchain0x-Signature") ?? "";
    const expected = crypto
      .createHmac("sha256", SIGNING_SECRET)
      .update(req.body)
      .digest("hex");

    if (
      !crypto.timingSafeEqual(
        Buffer.from(signature, "hex"),
        Buffer.from(expected, "hex"),
      )
    ) {
      return res.status(401).send("Invalid signature");
    }

    const event = JSON.parse(req.body.toString());
    if (event.type === "payment.confirmed") {
      // Final: deliver the work, fulfil the order, etc.
      void deliver(event.data.payment_request_id);
    }
    res.status(200).send("ok");
  },
);
Python (Flask)
import hmac, hashlib, os, json
from flask import Flask, request, abort

app = Flask(__name__)
SIGNING_SECRET = os.environ["BLOCKCHAIN0X_SIGNING_SECRET"].encode()

@app.post("/webhooks/payment")
def webhook():
    raw = request.get_data()
    signature = request.headers.get("X-Blockchain0x-Signature", "")
    expected = hmac.new(SIGNING_SECRET, raw, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(signature, expected):
        abort(401)

    event = json.loads(raw)
    if event["type"] == "payment.confirmed":
        # Final: deliver the work, fulfil the order, etc.
        deliver(event["data"]["payment_request_id"])
    return ("ok", 200)
STEP 4 OF 5

Add a spend policy.

If your agent only receives payments, you can skip this. If it also pays other agents or APIs, attach a spend policy now (not later). The policy is evaluated at the API layer on every payment intent, so it survives prompt injection in a way agent-side rules never can.

TypeScript
await client.agents.updateSpendPolicy(agent.id, {
  daily_cap_usdc: "20.00",
  per_payment_cap_usdc: "2.00",
  allowed_counterparties: [
    "0xMcpServerA...",
    "0xMcpServerB...",
  ],
});
Python
client.agents.update_spend_policy(
    agent.id,
    daily_cap_usdc="20.00",
    per_payment_cap_usdc="2.00",
    allowed_counterparties=[
        "0xMcpServerA...",
        "0xMcpServerB...",
    ],
)
STEP 5 OF 5

Test the whole flow on Base Sepolia.

Before flipping to sk_live_ keys, run the full path end-to-end with sk_test_. Test keys mint test-USDC on Base Sepolia, hit the sandbox webhook endpoints, and have identical response shapes to live. The differences are: confirmations land slightly slower, balances reset nightly, and gas is sponsored regardless of plan.

Specifically, exercise three scenarios: a happy-path payment that confirms, a payment that expires unpaid (verify your payment.failed handler fires), and a webhook retry (force a 500 from your handler the first time, return 200 the second time, and confirm your idempotency works). When all three pass on test, swap the key and ship.

COMMON PITFALLS

Five mistakes that cost teams a week.

Skipping webhook signature verification

If you accept any POST to /webhooks/payment as authoritative, an attacker can mint fake 'payment.confirmed' events and trick your agent into delivering work for free. Always HMAC-verify with the signing secret, using a constant-time comparison. The first compromise is almost always the missing verification.

Treating payment.received as final

payment.received fires when the chain has seen the transaction but it is not yet confirmed by your platform's finality rules. A reorg can still reverse it. Wait for payment.confirmed before delivering anything irreversible. If your work is cheap to redo, payment.received is acceptable as a hint; if it is expensive or external, do not act on it.

No idempotency on webhook handlers

Webhooks retry on non-2xx responses, and the same event will arrive multiple times under load. Your handler must be idempotent: keep a small table of event IDs you have already processed and skip duplicates. Otherwise a transient blip will deliver the same work twice and you will spend hours debugging double-fulfilments.

Mixing test and live API keys

Test keys (sk_test_) hit the sandbox and use Base Sepolia; live keys (sk_live_) hit production and use Base mainnet. Mixing them up in environment configs is the cause of most 'works in dev, fails in prod' tickets. Hard-fail at startup if your runtime environment and key prefix do not match.

Forgetting to handle payment.failed

Most teams wire up payment.confirmed and forget payment.failed. When a payment request expires or the buyer abandons, you need a path that releases any held resources, clears the in-progress state, and (optionally) notifies the user. Without it, an abandoned payment leaves the agent stuck in a 'waiting for funds' loop.

NEXT STEPS

Once you have your first payment.

With basic payments working, the highest-leverage follow-ups are spend controls (so the agent cannot run away with the budget), webhook robustness (so payments do not silently drop under load), and identity verification (so counterparties trust the agent's public page).

Full API reference lives at docs.blockchain0x.com. Product surface for the same APIs: Payment API.

Last reviewed: 2026-05-15. Published under CC BY 4.0.

One POST and your agent is getting paid.

Free to start. Test keys included. First payment confirmed in under ten minutes.