# Canonical agent purchase path (zero human)

Signature: ♾️  
Live entry: https://lvlltd.com/api/shop · https://lvlltd.com/llms.txt · https://lvlltd.com/agent.json

This is the **single source of truth** for autonomous agents buying sealed skills on lvlltd.com.
The human MetaMask marketplace path remains supported but is **not required**.

## Prerequisites

| Item | Value |
|------|--------|
| Network | Base (chain id **8453**) |
| Asset | USDC on Base (`0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`) |
| Also accepts | USDbC (`0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA`) |
| payTo | From challenge (treasury `0xa00876513bAA433ce2B58A5341Fd06d2b6f9A6ED`) |
| Protocol | x402 (HTTP 402 → settle → X-PAYMENT unlock) |
| Agent wallet | Any Base-capable wallet that can ERC-20 transfer USDC |

## Sequence (machine-executable)

```
1. GET  /api/shop                         → payment contract + fleet map
2. GET  /api/catalog?q=…&max_price=…      → pick skill_id
3. GET  /skills/<id>/outline.json         → FREE evaluate fit
4. GET  /api/pay?skill=<id>               → HTTP 402 challenge
5. Transfer maxAmountRequired USDC → payTo on Base
6. POST /api/pay
      Header: X-PAYMENT: {"txHash":"0x…","skill":"<id>"}
      Body:   {"txHash":"0x…","skill":"<id>"}   (optional duplicate)
7. Persist sealed_pack.files + payment.txHash (+ optional license.token)
8. Re-redeem anytime with same txHash or license token
```

## Challenge shape (GET → 402)

Agents MUST accept either top-level fields **or** `accepts[0].*`:

| Field | Meaning |
|-------|---------|
| `maxAmountRequired` / `amount` | Atomic USDC (6 decimals) |
| `payTo` | Receiver address |
| `network` | `base` |
| `asset` | `USDC` |
| `assetContract` | USDC on Base |
| `skill` / `skill_id` | Catalog id |
| `error_code` | `PAYMENT_REQUIRED` on challenge |
| `sample` / `outline` | Free evaluation URLs |

Headers: `PAYMENT-REQUIRED` / `X-PAYMENT-REQUIRED` (base64 JSON of challenge body).

## Unlock response (POST → 200)

| Field | Meaning |
|-------|---------|
| `ok` | `true` |
| `sealed_pack.files` | Full pack text files |
| `how_to_use` | Install steps |
| `license.token` | Optional portable redeem token (when KV bound) |
| `payment.txHash` | Settled proof |
| `revenue_ledger` | Whether unlock was recorded |

## Parseable errors (never silent)

| HTTP | error_code | When |
|------|------------|------|
| 400 | `SKILL_REQUIRED` | Missing skill id |
| 402 | `PAYMENT_REQUIRED` | Challenge (GET) |
| 402 | `PROOF_REQUIRED` | POST without txHash |
| 402 | `PAYMENT_VERIFICATION_FAILED` | Bad/insufficient/missing tx |
| 403 | `INVALID_LICENSE` | Bad license token |
| 503 | `INTERNAL_ERROR` / `PAYMENTS_DISABLED` | Outage or X402_LIVE=0 |

Retry tip: after broadcast, wait 2–5s; server multi-RPC polls automatically.

## Free evaluation

```
GET /skills/<id>/outline.json
GET /skills/<id>/sample.md
GET /skills/index.json
```

Full `src/`, `ops/`, schemas stay sealed until unlock.

## Health / readiness

```
GET /api/health  → liveness
GET /api/ready   → challenge generation + sealed pack path OK
```

## SDK

```js
import { LvlAgentShop } from "https://lvlltd.com/sdk/agent-shop.mjs";
const shop = new LvlAgentShop();
const { skills } = await shop.search({ q: "orchestration", max_price: 10 });
const challenge = await shop.challenge(skills[0].id);
// wallet: transfer challenge.maxAmountRequired to challenge.payTo
const unlock = await shop.unlock(skills[0].id, txHash);
```

## Automated test

```bash
npm run test:agent-purchase
# optional real redeem:
AGENT_PURCHASE_TX=0x… AGENT_PURCHASE_SKILL=aie-premium-access-token npm run test:agent-purchase
```

## Human path (optional)

`https://lvlltd.com/hub/marketplace?buy=<id>&pay=1` — MetaMask UI. Agents should use `/api/pay` only.

## Related docs

- `/llms.txt` · `/.well-known/llms.txt`
- `/protocols.json` · `/agent.json` · `/.well-known/agent-card.json`
- `docs/AGENT-SHOP.md` · `docs/X402-UNLOCK.md`
