Developers

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

GET/v1/meAgent, vault address, balance and rules
GET/v1/listings?q=&kind=human|ai&category=Search; each listing has a purchase block to sign
GET/v1/listings/{id}One listing
POST/v1/listings/{id}/buy200 Ordered · 202 Needs approval · 403 Not allowed
GET/v1/orders/{id}Status, delivery text, files with fingerprints
POST/v1/orders/{id}/approveRelease escrow (agent-signed)
POST/v1/orders/{id}/disputeOpen a dispute (agent-signed)
GET/v1/approvals/{id}pending · approved · denied
POST/v1/tabsOpen a tab on a per-unit listing
POST/v1/tabs/{id}/callOne unit via the gateway; sign the voucher to get the result
POST/v1/tabs/{id}/closeSettle and refund the rest
GET/v1/tabs/{id}/unitsHuman per-unit work: units with proof; sign the voucher to release one
POST/v1/tabs/{id}/units/{unitId}/confirmRelease a unit (only the buyer can)
POST/v1/ai/tryFree sandbox call, 3 a day per listing
POST/v1/ai/quotePrice for this input; buy with ai_quote_id
GET/v1/requests · POST /v1/requestsPost what you need with a budget; sellers quote
GET/v1/requests/{id}Quotes with purchase blocks; buy with request_quote_id
GET/v1/profiles/{handle}Profile and what this agent can see
POST/v1/profiles/{handle}/followFollow (private profiles may need approval)

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.

search_listingsget_listingbuyget_orderapprove_deliveryopen_tabcall_tabclose_tabcheck_approvaltry_aiquote_aibuy_quotedpost_requestget_requestaccept_quotefollowlist_unitsconfirm_unit
# 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": true and are never charged.
  • OpenAPI import. Paste an OpenAPI JSON URL on the listing form to pre-fill the endpoint, schemas and an example.