Pydantic AI payment integration.
There is no Pydantic AI package, and you do not need one. Register a typed function with @agent.tool_plain, call the real blockchain0x client, and your agent can move USDC on Base.
There is no Pydantic AI-specific package, and you do not need one. Pydantic AI turns a function into a validated tool with , so you wrap the real Python client in a typed function and the agent can send USDC, settle invoices, and read wallets. It works across OpenAI, Anthropic, Google, Mistral, Groq, and every other Pydantic AI provider, because the tool calls the HTTP API, not the model. Payments settle on Base.
Pydantic AI's typing wins. Use it for payments too.
Pydantic AI's whole point is strong typing: agent inputs are typed, outputs are typed, dependencies are typed, retries are typed. The framework rejects malformed data at the boundary instead of letting it propagate through the LLM call. Your wallet tool rides on exactly that - declare the arguments with types, and Pydantic AI builds a validated schema from the signature and docstring before the model is ever allowed to call it.
The practical benefit: if the model tries to call send_usdc with a float instead of a base-unit string, the failure happens at Pydantic's validation layer before any HTTP call to us. You see a clear ValidationError with a useful message rather than a 422 from the API with cryptic field paths. That is the whole reason to wrap the client yourself here rather than reach for a generic adapter - you keep the typing all the way to the wire.
Install Pydantic AI and the core SDK. Two keys.
There is no blockchain0x Pydantic AI package to add. You install Pydantic AI (Python 3.10+) and the real blockchain0x core SDK, then write the function below. That is the whole dependency list.
pip install pydantic-ai blockchain0xexport OPENAI_API_KEY=sk-... export BLOCKCHAIN0X_API_KEY=sk_test_... # sk_test_ = Base Sepolia, sk_live_ = Base mainnet
OPENAI_API_KEY (or the equivalent for whichever provider you use). BLOCKCHAIN0X_API_KEY is a sk_test_ testnet or sk_live_ mainnet key from your dashboard; the client reads it from the environment. If your agent also receives money, the webhook handler additionally needs BLOCKCHAIN0X_WEBHOOK_SECRET.
A typed function that pays, registered as a tool.
Below is the whole integration. send_usdc calls the real blockchain0x client; @agent.tool_plain registers it with a schema built from the type hints and docstring. Run it and the agent moves USDC on Base, with the arguments validated before the call.
from pydantic_ai import Agent from blockchain0x import Client blockchain0x = Client() # reads BLOCKCHAIN0X_API_KEY from the environment agent = Agent( "openai:gpt-4o", system_prompt="You pay vendor invoices in USDC within owner-set limits.", ) # Register a plain function as a tool. No dedicated package needed. @agent.tool_plain def send_usdc(agent_id: str, to: str, amount_wei: str) -> str: """Send a USDC payment from an agent wallet. amount_wei is USDC base units (6 decimals), so "10000" is 0.01 USDC. """ return str( blockchain0x.payments.create(body={"agentId": agent_id, "to": to, "amountWei": amount_wei}) ) result = agent.run_sync( "Pay 0.01 USDC from agent agt_123 to 0xVendor for the dataset." ) print(result.output)
When the agent decides to pay, it calls send_usdc, the SDK submits the transfer, and you get a transaction hash back. amount_wei is base units, so 0.01 USDC is "10000". A sk_test_ key keeps it on Base Sepolia until you switch to sk_live_. Want a typed result object instead of a string? Set the agent's output_type to your own Pydantic model and return it from the tool. Add read and settle functions the same way.
Confirm inbound payments with a signed webhook.
If your agent also receives USDC, confirm it with the webhook rather than polling. The verify helper ships in the Node SDK; in a Python service you verify by hand against the documented HMAC. If you want typed events, define your own Pydantic model and model_validate_json the raw body after the signature checks out - the typing is yours to add. FastAPI example below.
import hmac, hashlib, os, time from fastapi import FastAPI, Request, HTTPException app = FastAPI() SECRET = os.environ["BLOCKCHAIN0X_WEBHOOK_SECRET"].encode() @app.post("/webhooks/payment") async def receive(request: Request): raw = await request.body() # RAW bytes - do not parse first sig = request.headers.get("X-Blockchain0x-Signature", "") ts = request.headers.get("X-Blockchain0x-Timestamp", "") parts = dict(p.split("=", 1) for p in sig.split(",") if "=" in p) t, v1 = parts.get("t", ts), parts.get("v1", sig) want = hmac.new(SECRET, t.encode() + b"." + raw, hashlib.sha256).hexdigest() if not hmac.compare_digest(want, v1) or abs(time.time() - int(t)) > 300: raise HTTPException(status_code=401) if request.headers.get("X-Blockchain0x-Event-Type") == "payment.received": await trigger_followup() # USDC landed - run the next step return {"ok": True}
The algorithm is HMAC-SHA256 over the string t.rawBody, a constant-time compare, and a 300-second replay window. Read the raw body via await request.body(), never request.json() re-serialized, because that changes the bytes the signature covers. The shipped events are payment.received, payment.sent, wallet.deployed, and webhook.test; narrow on the X-Blockchain0x-Event-Type header to branch.
The client you are wrapping is open. Read it.
There is no Pydantic AI starter package to clone - the recipe above is the integration. The blockchain0x SDKs are open source on GitHub; this recipe wraps the Python SDK (blockchain0x-python), with the full method surface in the docs. Read it for a reference for the function bodies.
github.com/tosh-labs/blockchain0x-pythonThe full SDK method surface and scopes are documented at the docs. Start on a sk_test_ key against Base Sepolia, then switch to sk_live_ when the function does what you expect.
Five Pydantic AI-specific traps to avoid.
Pydantic AI's strong typing catches most integration bugs at the boundary; these are the few that slip through.
There is no Pydantic AI package - you register a tool
Blockchain0x ships adapters for LangChain and CrewAI plus the MCP server; there is no dedicated Pydantic AI package. The recipe above is the path: a plain typed function decorated with @agent.tool_plain that calls the real blockchain0x client. Pydantic AI reads the signature and docstring to build a validated tool schema, which is exactly the typing win you came to Pydantic AI for.
Keep amount a string; let Pydantic hold the line
payments.create takes amountWei: a string of USDC base units (6 decimals), so 0.01 USDC is "10000" and 5 USDC is "5000000". Type the tool argument as str and Pydantic AI rejects a float at the boundary with a clear ValidationError, before any HTTP call. That is the whole point of doing this in Pydantic AI - the malformed value never reaches the API.
send_payment can answer 503 early on
payments.create does not retry by default and can return 503 until the chain adapter is wired for your network. Catch the error inside your tool and return a clear message the model can act on, rather than letting the agent loop. The auto-minted idempotency key means a manual retry will not double-pay.
Provider-prefixed model strings
Pydantic AI uses provider-prefixed model strings: 'openai:gpt-4o', 'anthropic:claude-3-5-sonnet-latest', 'google-gla:gemini-1.5-pro'. Omitting the prefix gives a cryptic 'unknown model' error. Your tool is provider-agnostic because it calls the HTTP API, not the model - swap the prefix freely and the wallet function is unchanged.
tool_plain vs tool (RunContext)
Use @agent.tool_plain when the function needs nothing from the run, as above. If you want the agent's dependencies inside the tool (a per-request client id, a spend cap), use @agent.tool and declare the first argument as ctx: RunContext[Deps] to read ctx.deps. Mixing the two - declaring a ctx argument on tool_plain - raises a TypeError at registration.