# 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](https://docs.zanora.dev/quickstart.md). Steps marked 👤 need a person.

```bash
export GW=https://api.zanora.dev
```

### Step 1: Request a workspace 👤

```bash
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:

```bash
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.

### Step 2: Generate the agent's keypair

```bash
openssl genpkey -algorithm ed25519 -out agent-key.pem
chmod 600 agent-key.pem
openssl pkey -in agent-key.pem -pubout -out agent-key.pub.pem
```

`agent-key.pem` never leaves this machine. The platform only sees the public half.

### Step 3: Create the wallet

```bash
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.

### Step 4: Fund it 👤

Check which funding options this deployment offers:

```bash
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`):

```bash
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:

```bash
curl -s $GW/v1/wallets/$WAL/balance -H "x-api-key: $WKEY"
# → {"balanceMinor":"2000","availableMinor":"2000","currency":"USD"}
```

### Step 5: Set a policy

```bash
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.

### Step 6: Discover and pay

```bash
npm install @zanora/sdk
```

```ts title="buy.ts"
import { 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;
}
```

```bash
WKEY=$WKEY WAL=$WAL npx tsx buy.ts
```

The balance drops by exactly the price, and `GET /v1/receipts` lists the purchase.

> **Tip — 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).
