Give an agent its account in code.
A TypeScript SDK, an MCP server and a CLI on one engine API. The same rules and the same firewall apply whichever one the agent uses.
One key, three ways in
The owner creates an agent key for one account in the dashboard. The agent uses it with the SDK, as MCP tools, or from the terminal. A refused payment is an answer with reasons, never an exception that slips through.
npm install @purser/sdkimport { Purser, usdg } from "@purser/sdk";
const purser = new Purser({
baseUrl: process.env.PURSER_ENGINE_URL,
apiKey: process.env.PURSER_API_KEY,
});
// Pay inside the owner's rules. A refused payment comes back "blocked", with reasons.
const paid = await purser.pay({ to: supplier, amount: usdg("2.50"), memo: "data report" });
if (paid.intent.status === "blocked") console.log(paid.verdict.summary);
// Call an x402 service. Purser pays the 402 from the agent's Safe, up to a maximum.
const res = await purser.x402Fetch(serviceUrl, { maxAmount: usdg("1") });
// Every dollar has a receipt.
const { items } = await purser.accounts.receipts(paid.intent.safe, { limit: 10 });MCP tools
Eleven tools over stdio, amounts in whole unitspurser_accountBalances, today's spending and what is leftpurser_rulesThe rules the agent cannot breakpurser_payPay, with the text that led to itpurser_check_paymentAsk the firewall firstpurser_receiptsEvery payment in and outpurser_x402_fetchFetch a URL and pay its 402purser_sellPut a service on salepurser_servicesThe agent's servicespurser_invoiceInvoice another agentpurser_pay_invoiceFund an invoice's escrowpurser_pnlProfit and loss- Framework guidesClaude Agent SDK, OpenAI Agents SDK, LangChain, ElizaOS
Models on the same account
Any OpenAI-compatible client works. The owner adds the providers; every call is recorded with its tokens and cost, and tagged to a task if you send X-Purser-Task.
import OpenAI from "openai";
// Any OpenAI-compatible client, paid from the agent's account.
const models = new OpenAI({
baseURL: `${process.env.PURSER_ENGINE_URL}/v1`,
apiKey: process.env.PURSER_API_KEY,
});
const reply = await models.chat.completions.create({
model: "the-model-id-your-owner-set-up",
messages: [{ role: "user", content: "Summarise these filings" }],
}, { headers: { "X-Purser-Task": "weekly-brief" } });Sell over x402
Let the engine run the paywall for an agent's service, or price an endpoint on your own server with the middleware. Either way the money lands in the agent's own Safe.
And the pay button// Or let the engine run the paywall: the agent puts a service on sale.
const service = await purser.store.create({
name: "Weekly market report",
model: "per_call",
price: usdg("4"),
upstream: "https://your-worker/report",
});
console.log(service.endpoint); // buyers call this over x402Signed webhooks
Subscribe a URL in the dashboard under Alerts. Each delivery is signed with HMAC-SHA256 and retried after 5 seconds, 30 seconds and 2 minutes.
import { verifyWebhook } from "@purser/sdk";
// Every delivery is signed. Reject anything that does not verify.
const ok = await verifyWebhook(process.env.PURSER_WEBHOOK_SECRET, req.headers["purser-signature"], rawBody);
if (!ok) return res.status(401).end();
const event = JSON.parse(rawBody);
if (req.headers["purser-event"] === "intent.blocked") notifyOwner(event);Test mode and the API
Run everything on a local copy of the chain with test money. The engine answers JSON and live events; the full reference lists every endpoint with a real answer.
# A local copy of Robinhood Chain, Purser deployed on it, the engine and three
# agents with real on-chain activity, in one command:
cd engine && npm run testmode # a plain local chain
cd engine && npm run testmode -- --fork # a fork of the live chain
# Or from anywhere in a checkout:
purser testmode --fork