Let your agent buy on Dunzi.
Agents pay from a USDC vault their owner funded. Every purchase is held in an on-chain escrow until delivery, and the escrow program enforces the owner's rules: daily cap, max per purchase, ask-first limit, allowed categories, expiry and revoke.
01 Connect an agent
On /agents, create a vault and rules. You get an API key and an agent keypair, shown once. The API key identifies the agent; the keypair signs purchase intents and tab vouchers. The agent never needs SOL: the Dunzi relayer pays network fees, and cannot move funds by itself.
02 TypeScript SDK
Search, buy, open tabs and pay per valid result. Responses tell you exactly what happened: 200 ordered, 202 waiting for the owner, 403 outside the rules.
import { DunziAgent } from '@wasl/sdk/agent';
const agent = new DunziAgent('http://localhost:3030', process.env.WASL_AGENT_KEY!, process.env.WASL_AGENT_SECRET!);
const [plan] = await agent.listings({ q: 'PlanForge' });
const r = await agent.buy(plan.id, { brief: 'Launch plan for a student wallet app' });
if (r.status === 200) console.log('ordered', r.order); // held in escrow, paid from the vault
if (r.status === 202) console.log('waiting for owner', r.approval);
if (r.status === 403) console.log('not allowed', r.reason); // e.g. category_not_allowed
// Per-unit AI: open a tab, pay only for valid results
const tab = await agent.buy(lumenId, { units: 5 });
if (tab.status === 200) {
for (const p of ['fox', 'bear']) console.log(await agent.call(tab.order, p));
await agent.closeTab(tab.order); // last voucher settles, rest returns to the vault
}03 REST API
Header Authorization: Agent <api_key>. Amounts are integer base units (USDC, 6 decimals).
04 Signed intents
Every purchase carries two ed25519 signatures from the agent keypair, so a 202 can be created on-chain without a second round trip. The SDK builds both.
POST /v1/listings/{offer_id}/buy
{
"brief": "Launch plan for a student wallet app",
"intent": { "id": "<random u64>", "approval_id": "<random u64>", "amount": "10000000", "expiry": 1767225600 },
"signature": "<base58 ed25519 over WASL_AGENT_BUY_V1 message>",
"ask_signature": "<base58 ed25519 over WASL_AGENT_ASK_V1 message>"
}
200 { "order": "…", "from": "vault", "held": "escrow", "tx": "…" }
202 { "approval": "…", "reason": "above_ask_limit" }
403 { "reason": "category_not_allowed" | "over_max_per_purchase" | "over_daily_cap"
| "insufficient_vault_balance" | "agent_revoked" | "vault_expired" }Intent bytes: "WASL_AGENT_BUY_V1" ‖ vault ‖ order_id u64 LE ‖ kind u8 ‖ seller ‖ delivery_authority ‖ amount u64 LE ‖ category_bit u32 LE ‖ listing_ref[32] ‖ expiry i64 LE. Tab vouchers: "WASL_VOUCHER_V1" ‖ order ‖ cumulative u64 LE ‖ nonce u64 LE.
05 MCP server
Use Dunzi from Claude Code or Claude Desktop with the same API key and keypair.
# Claude Code (stdio)
claude mcp add dunzi -e WASL_URL=http://localhost:3030 -e WASL_AGENT_KEY=wasl_ak_… -e WASL_AGENT_SECRET=… \
-- pnpm --dir /path/to/wasl --filter @wasl/mcp-server start
# Claude Desktop: claude_desktop_config.json
{
"mcpServers": {
"dunzi": {
"command": "pnpm",
"args": ["--dir", "/path/to/wasl", "--filter", "@wasl/mcp-server", "start"],
"env": { "WASL_URL": "http://localhost:3030", "WASL_AGENT_KEY": "wasl_ak_…", "WASL_AGENT_SECRET": "…" }
}
}
}
# Streamable HTTP (runs with pnpm dev on :3200)
claude mcp add --transport http dunzi http://localhost:3200/mcp --header "x-wasl-agent-key: wasl_ak_…" --header "x-wasl-agent-secret: …"06 Demo agent CLI
WASL_URL=http://localhost:3030 WASL_AGENT_KEY=… WASL_AGENT_SECRET=… \ pnpm --filter @wasl/demo-agent start -- --task launch # launch | game | arabic | audit
07 Sell an AI service
List it under Sell → AI services with an endpoint URL. The gateway POSTs JSON { order_id, unit, brief | input, n }. Return JSON { text, files: [{ name, mime, base64 }] }, or a raw file (e.g. image/png). A result is valid only if it is HTTP 200, the expected content type, not empty, under 10 MB, matches your JSON schema and arrives within 30 s. Invalid results are free for the buyer and lower your reliability score.
- Input schema. Add a JSON schema for inputs and inputs are checked before anyone pays. Agents read it from
/v1/listings/{id}. - Long jobs. Answer
202 { "status_url": "/jobs/abc" }. The gateway polls it for{ status, progress, result_url }(up to 30 minutes) and validates the result before delivery. Buyers see the progress. - Quote first. Set a quote URL. We POST
{ input }; answer{ "price_usd": 4.2 }. Buyers and agents see that price (valid 15 minutes) before paying. - Try it free. Buyers get 3 sandbox calls a day per listing. Sandbox calls carry
"sandbox": trueand are never charged. - OpenAPI import. Paste an OpenAPI JSON URL on the listing form to pre-fill the endpoint, schemas and an example.