DEV Community

Paul Spread
Paul Spread

Posted on Originally published at agentbadge.xyz

Your Agent Shouldn't Have to Guess the Price — One Catalog, Declared on Every 402

Originally published: https://agentbadge.xyz/blog/arc-c15-x402-bazaar

An agent buying from an API answers three questions before it pays: what does it cost, what do I send, is there a free way to try? Most payment-gated APIs make it guess all three. We closed that loop: GET /api/v1/services returns the full machine-readable catalog, and every 402 across the platform declares itself for x402 indexers — price, input schema, free alternatives, before a cent moves.

One registry, three consumers

The catalog isn't a hand-maintained list — it's a declarative SKU registry reading the same config sources the payment middleware reads. The price in the catalog IS the price the gate charges. One registry feeds /api/v1/services, the bazaar extension on every 402, and the llms.txt Paid Services section.

Diagram: one SKU registry feeds the catalog, every 402's bazaar extension, and llms.txt

14 SKUs across 8 paid surfaces: scans, passport mints, marketplace buys, keeperhub, EaaS verdicts/jobs/subscriptions, bstock passes, venue subscriptions. sku_id is surface:slug, immutable — a golden test fails CI on rename.

Every 402 declares itself (bazaar)

Every payment gate — x402 Hedera, MPP/Stripe, bstock freemium, the manual settle seam, all three EaaS middlewares — attaches bazaarExtensionFor("<sku_id>"). The emitted inputSchema is inputSchemaOf(sku), byte-equal to the catalog entry. One function produces the schema — catalog and wire can't drift (D-179-4).

Even the L402 macaroon gate is accounted for — it lives on an explicit exception list (no JSON slot) instead of pretending.

Free as the front door

free[] sits next to services[]: health, scan-packs catalog, market browsing — each with a next_call pointer to the natural paid step. Discovery → free trial → paid call, zero doc reading.

Diagram: agent journey — discovery to free endpoint to 402 bazaar to paid call

No paid endpoint without a declaration

tests/e2e/catalog-coverage.test.ts keeps a GATE_TABLE — every gate ↔ SKU both directions. A new paid route without a SKU fails CI. The two non-SKU gates (l402, attestation-api) are an explicit allowlist with written reasons.

curl -s https://agentbadge.xyz/api/v1/services | jq '.services[].sku_id'
Enter fullscreen mode Exit fullscreen mode

Full article with decoded 402 payloads:
https://agentbadge.xyz/blog/arc-c15-x402-bazaar

Top comments (0)