How To · Agent setup · Machine: index.json

How to set up an agent that can shop here

Concrete requirements and patterns so an autonomous agent can discover skills, evaluate free outlines, handle HTTP 402, settle USDC on Base, and unlock sealed packs — without a human UI.

Canonical path — Human MetaMask is optional. Agents should use /api/pay only. Full sequence: Agent shopping flow · Docs source: AGENT-PURCHASE.md · Machine index: /how-to/index.json

Required capabilities

CapabilityWhy
HTTP client (GET/POST, custom headers)Shop, challenge, unlock, proof
Handle non-2xx (especially 402)Challenge is not an error — it is the quote
Parse JSON body + optional base64 payment headersx402Version, maxAmountRequired, payTo
Sign/send ERC-20 USDC transfer on Base (8453)Exact atomic amount to payTo from the live challenge
POST JSON + X-PAYMENT headerUnlock sealed pack after payment
Persist sealed files + txHashIdempotent re-download without re-paying

Without wallet settlement, an agent can still discover and evaluate free outlines — but cannot unlock sealed packs.

Recommended patterns

Recommended discovery path

Fetch in this order when bootstrapping an agent:

  1. /llms.txt — curated map (includes How To links)
  2. /how-to/index.json — this guide set (machine summary)
  3. /api/shopnext_action, payment contract, featured
  4. /catalog.json — full inventory (authoritative skill_count)
  5. /agent.json · A2A card · MCP as needed
SurfaceURLUse
llms.txt/llms.txtAgent-oriented site map
How-to index/how-to/index.jsonGuides + canary + sequence
Shop entry/api/shopnext_action, featured, proof, payment contract
Catalog/catalog.jsonFull inventory (authoritative count)
Search/api/catalogQuery / filter skills
Agent card/agent.jsonDiscovery + endpoints
A2A card/.well-known/agent-card.jsonA2A-compatible
MCP/api/mcpTool surface when enabled
Fleet/fleet.jsonSisters, health paths, honesty notes

Free evaluation (always before pay)

curl -sS "https://lvlltd.com/skills/agent-x402-first-buy/outline.json" | head -c 800
curl -sS "https://lvlltd.com/skills/agent-x402-first-buy/sample.md" | head -n 40

If outline is empty/template, skip or buy only after human review. Prefer skills tagged Deep / Execution Verified.

When to skip a skill — outline steps are empty or pure boilerplate; sample is missing; depth is outline-only and price is high; tags include template-outline. Prefer free evaluation only, or pick another id.

How to read a 402 challenge correctly

GET challenge returns HTTP 402 with JSON body. Read both top-level fields and accepts[0].

Never hard-code payTo, amounts, or asset contracts from old docs or screenshots. Always parse the live challenge body for that skill.
curl -si "https://lvlltd.com/api/pay?skill=agent-x402-first-buy" | head -n 60
FieldMeaning
x402Version / x402_versionProtocol version on body (apex skills use 2)
maxAmountRequired / amountAtomic USDC (6 decimals). $0.05 → 50000
payToReceiver (treasury on Base)
assetContractUSDC on Base 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
network / chain_idbase / 8453
skill / skill_idMust match unlock POST
outline / sampleFree eval URLs
Do not treat 402 as a hard failure. It is the priced quote. Missing tx on POST → 402 PROOF_REQUIRED.

Settlement + unlock + verification

How to settle payment on Base

  1. Use chain id 8453 (Base mainnet)
  2. ERC-20 transfer of exactly maxAmountRequired atomic units of USDC to payTo
  3. Wait for inclusion (server multi-RPC polls; client can wait 2–5s after broadcast)
  4. Keep txHash for unlock and re-download

USDbC is also accepted on some challenges — prefer native USDC when possible.

How to unlock the sealed pack

curl -sS -X POST "https://lvlltd.com/api/pay" \
  -H "Content-Type: application/json" \
  -H 'X-PAYMENT: {"txHash":"0xYOUR_TX","skill":"agent-x402-first-buy"}' \
  -d '{"txHash":"0xYOUR_TX","skill":"agent-x402-first-buy"}'

Success: ok: true and sealed_pack.files (map of path → text). Persist files + txHash.

How to verify on /api/proof

curl -sS "https://lvlltd.com/api/proof?limit=10" | python3 -m json.tool | head -80

Only confirmed unlocks appear. Low or zero counts are valid. Do not invent GMV. Swarm smoke pointers are not ledger unlocks.

Recover after paid-but-failed unlock

curl -sS -X POST "https://lvlltd.com/api/recover" \
  -H "Content-Type: application/json" \
  -H 'X-PAYMENT: {"txHash":"0xYOUR_TX","skill":"agent-x402-first-buy"}' \
  -d '{"txHash":"0xYOUR_TX","skill":"agent-x402-first-buy"}'

Same body as unlock. Idempotent — does not invent refunds; only re-verifies and re-delivers.

Common errors and how agents should handle them

HTTP / codeMeaningAgent action
402 PAYMENT_REQUIREDChallenge (GET) or missing proof (POST)Parse challenge or attach txHash
402 PAYMENT_VERIFICATION_FAILEDBad amount/payTo/tx/networkCheck BaseScan; wait; retry verify
400 SKILL_REQUIREDMissing skill idFix request
403Not for sale / invalid licenseStop; pick another skill
503Payments disabled / outageBackoff; check /api/health

Paid but no files: POST /api/recover with same txHash + skill (same as unlock; idempotent). Human: /recover/

Wrong network — Must be Base mainnet (8453), not Ethereum L1.
Wrong amount — Use exact atomic maxAmountRequired from the challenge (e.g. 50000 = $0.05).
Wrong payTo — Only from the live 402 body for that skill.
402 on GET — Expected. Parse the quote; do not abort the shop loop.
Empty / low /api/proof — Honesty, not a bug. Never invent volume.

Idempotent re-download

Re-POST the same verified txHash + skill to re-fetch sealed files. Does not move funds again. Store license token if returned for portable redeem.

Minimal Node example

// Pseudocode — wire your wallet for the transfer step
const BASE = "https://lvlltd.com";
const skill = "agent-x402-first-buy";

const shop = await fetch(`${BASE}/api/shop`).then((r) => r.json());
// prefer shop.next_action.for_agents

const outline = await fetch(`${BASE}/skills/${skill}/outline.json`).then((r) => r.json());

const chRes = await fetch(`${BASE}/api/pay?skill=${skill}`);
// expect 402
const challenge = await chRes.json();
const amount = challenge.maxAmountRequired || challenge.accepts?.[0]?.maxAmountRequired;
const payTo = challenge.payTo || challenge.accepts?.[0]?.payTo;
// wallet.transferUsdc({ to: payTo, amount, chainId: 8453 }) → txHash

const unlock = await fetch(`${BASE}/api/pay`, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-PAYMENT": JSON.stringify({ txHash, skill }),
  },
  body: JSON.stringify({ txHash, skill }),
}).then((r) => r.json());

if (!unlock.ok) throw new Error(unlock.error_code || unlock.error);
// write unlock.sealed_pack.files to disk

Minimal Python example

import json, urllib.request

BASE = "https://lvlltd.com"
skill = "agent-x402-first-buy"

def get(url):
    with urllib.request.urlopen(url) as r:
        return json.loads(r.read().decode())

shop = get(f"{BASE}/api/shop")
outline = get(f"{BASE}/skills/{skill}/outline.json")

req = urllib.request.Request(f"{BASE}/api/pay?skill={skill}")
try:
    urllib.request.urlopen(req)
except urllib.error.HTTPError as e:
    challenge = json.loads(e.read().decode())
    # challenge["maxAmountRequired"], challenge["payTo"]
    # settle with your web3 stack → tx_hash

# unlock:
# body = json.dumps({"txHash": tx_hash, "skill": skill}).encode()
# req = urllib.request.Request(f"{BASE}/api/pay", data=body, method="POST",
#   headers={"Content-Type":"application/json",
#            "X-PAYMENT": json.dumps({"txHash": tx_hash, "skill": skill})})

SDK one-liner

import { LvlAgentShop } from "https://lvlltd.com/sdk/agent-shop.mjs";
const shop = new LvlAgentShop();
const challenge = await shop.challenge("agent-x402-first-buy");
// transfer challenge.maxAmountRequired to challenge.payTo on Base
const unlock = await shop.unlock("agent-x402-first-buy", txHash);
← How To hub Full shopping flow → Human path
Canonical flow /api/shop Canary 402