Get started
Buyer quickstart
From nothing to a verified purchase using only the API — signup, keypair, wallet, funding, policy, discover, pay. Every step is a copy-paste command.
This walkthrough uses curl and one small script, so every step is visible. For a console-based walkthrough, see the Quickstart. Steps marked 👤 need a person.
export GW=https://api.zanora.devRequest a workspace 👤
curl -s -X POST $GW/v1/signup -H 'content-type: application/json' -d '{
"role": "buyer",
"email": "eng@northwind.example",
"organizationName": "Northwind",
"dailyBudgetMinor": "5000",
"password": "a long passphrase for the console"
}'
# → 202 {"signupId":"sgn_…","status":"pending","expiresAt":"…","emailSent":true}Open the emailed link. The console confirms the address and shows your root key once. Save it:
export WKEY='zk.akey_….…'
curl -s $GW/v1/whoami -H "x-api-key: $WKEY" # → "kind":"workspace","workspaceId":"wsp_…"dailyBudgetMinor: "5000" caps the whole workspace at $50.00 a day. You can leave it out.
Generate the agent's keypair
openssl genpkey -algorithm ed25519 -out agent-key.pem
chmod 600 agent-key.pem
openssl pkey -in agent-key.pem -pubout -out agent-key.pub.pemagent-key.pem never leaves this machine. The platform only sees the public half.
Create the wallet
curl -s -X POST $GW/v1/wallets -H "x-api-key: $WKEY" -H 'content-type: application/json' \
-d "$(jq -n --arg pem "$(cat agent-key.pub.pem)" '{ownerType:"agent", ownerId:"invoice-bot", publicKeyPem:$pem}')"
# → {"id":"wal_f33b5618…","ownerType":"agent","ownerId":"invoice-bot","workspaceId":"wsp_…",…}
export WAL=wal_f33b5618…You don't send workspaceId. It comes from your key.
Fund it 👤
Check which funding options this deployment offers:
curl -s $GW/v1/rail/providers -H "x-api-key: $WKEY"Then either request a top-up (topUps: true) or get a USDC deposit address (depositAddresses: true):
curl -s -X POST $GW/v1/wallets/$WAL/topup -H "x-api-key: $WKEY" \
-H 'content-type: application/json' -d '{"amountMinor":"2000"}'
curl -s -X POST $GW/v1/wallets/$WAL/deposit-address -H "x-api-key: $WKEY" \
-H 'content-type: application/json' -d '{"asset":"USDC","chain":"base"}'Money is credited when the rail confirms it. The console's Fund button does the same thing with a card form. Watch the balance:
curl -s $GW/v1/wallets/$WAL/balance -H "x-api-key: $WKEY"
# → {"balanceMinor":"2000","availableMinor":"2000","currency":"USD"}Set a policy
curl -s -X POST $GW/v1/policies -H "x-api-key: $WKEY" -H 'content-type: application/json' \
-d '{"document":"approval:\n price > 5\ndeny:\n provider.verified == false"}'Anything over $5.00 now needs a person to approve it, and unverified sellers are refused.
Discover and pay
npm install @zanora/sdkimport { readFileSync } from "node:fs";
import { ZanoraAgent, ZanoraPolicyRejection } from "@zanora/sdk";
const agent = new ZanoraAgent({
apiKey: process.env.WKEY!,
walletId: process.env.WAL!,
agentId: "invoice-bot",
privateKeyPem: readFileSync("agent-key.pem", "utf8"),
maxPriceMinor: 500n,
});
const { results } = await agent.discover({ query: "extract text from invoices", protocol: "rest", maxPriceMinor: "200" });
const best = results[0];
if (!best) throw new Error("nothing matched");
try {
const r = await agent.invoke(best.version.endpoint, { body: { imageUrl: "https://example.com/invoice.png" } });
console.log(r.data);
console.log("receipt", r.receipt?.id, "signed:", r.receiptVerified, "for this answer:", r.responseHashVerified);
if (r.receipt) await agent.rateProvider(best.provider.id, r.receipt.transactionId, 5);
} catch (err) {
if (err instanceof ZanoraPolicyRejection) console.log("refused:", err.code, err.approvalId ?? "");
else throw err;
}WKEY=$WKEY WAL=$WAL npx tsx buy.tsThe balance drops by exactly the price, and GET /v1/receipts lists the purchase.
Runnable version
The platform repository has this whole flow as two scripts, with every refusal handled: starters/d-buyer-rest (onboard.ts for steps 2–5, buy.ts for step 6).