Skip to main content
PYDANTIC AI INTEGRATION

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.

SHORT ANSWER

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.

WHY TYPED PAYMENT TOOLS

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.

INSTALLATION

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.

INSTALL
pip install pydantic-ai blockchain0x
ENVIRONMENT VARIABLES
export 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.

THE RECIPE

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.

AGENT.PY
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.

WEBHOOK HANDLING

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.

WEBHOOK.PY
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.

SOURCE AND DOCS

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-python

The 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.

COMMON PITFALLS

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.

PITFALL 1

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.

PITFALL 2

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.

PITFALL 3

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.

PITFALL 4

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.

PITFALL 5

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.

FREQUENTLY ASKED

Three Pydantic AI-specific questions.

Is there a dedicated Pydantic AI package to install?

No. Pydantic AI already turns a function into a validated tool with @agent.tool_plain (or @agent.tool for context-aware tools), so the honest path is to wrap the real blockchain0x client yourself, as shown above. The only shipped framework packages are blockchain0x-langchain and blockchain0x-crewai (both Python) plus the @blockchain0x/mcp server. Pydantic AI, like the other frameworks without a dedicated adapter, is this few-line recipe.

Does this work across LLM providers (OpenAI, Anthropic, Google)?

Yes. The tool is provider-agnostic - it calls the Blockchain0x HTTP API, not any LLM-specific surface. As long as Pydantic AI supports the provider (OpenAI, Anthropic, Google GLA, Google VertexAI, Groq, Cohere, Mistral, Bedrock), the same function works. Just swap the model string in the Agent constructor: 'anthropic:claude-3-5-sonnet-latest' instead of 'openai:gpt-4o'.

How do I add typed inputs, or read and settle as well as send?

For stricter inputs, take a Pydantic model as the tool argument and Pydantic AI validates it for you - constrained decimals, address patterns, whatever you need. To read and settle, register more functions the same way: blockchain0x.transactions.get reads a transaction, blockchain0x.agents.get and blockchain0x.agents.list read wallets, and blockchain0x.payment_requests.settle settles an invoice you created in the dashboard with on-chain proof. Inbound payments confirm via the payment.received webhook below.

Add typed payments to your agent.

A few typed lines wrapping the real client, no package to install. Free to start.