For agents
Buy a skill without opening a browser
Every route a human uses by clicking has a machine-facing counterpart. Find a listing, sign in with nothing but a keypair, run its free trial, then pay and download in the same request over x402 — no wallet extension, no session started by hand.
The examples below run against https://skillstrade.xyz, the origin this instance is configured with. Slugs and prices are the seeded adversarial-diff-audit listing — swap them for whatever the catalog actually returns.
Search, trial, pay, download
- 1
Search the catalog
No query parameters returns the whole catalog, unpaginated —
{ skills }. Add any ofq,category,tag,limitorcursorand the shape switches to{ skills, nextCursor }, an opaque base64url cursor that isnullon the last page.curl "https://skillstrade.xyz/api/skills?q=diff+audit&limit=5"
200{ "skills": [ { "slug": "adversarial-diff-audit", "name": "Adversarial Diff Audit", "category": "ENGINEERING", "priceUsd": 48, "trialRuns": 1, "status": "live" /* ...the rest of the listing */ } ], "nextCursor": null } - 2
Read one listing in full
The record, a USD price with an optional live token quote (never a stale one —
nullwhen the feed is down), how to trial it, and how to buy it.curl "https://skillstrade.xyz/api/skills/adversarial-diff-audit"
200{ "skill": { "slug": "adversarial-diff-audit", "priceUsd": 48, "trialRuns": 1, "status": "live", /* ... */ }, "pricing": { "priceUsd": 48, "quote": { "usdPerToken": 0.0031, "at": 1755600000000 } }, "trial": { "freeRuns": 1, "method": "POST", "endpoint": "/api/skills/adversarial-diff-audit/trial", "authenticated": true }, "payment": { "protocol": "x402", "endpoint": "/api/skills/adversarial-diff-audit/download", "note": "GET without ownership returns 402 with PaymentRequirements" } } - 3
Sign in, headlessly
Only the trial needs a session — the payment path below does not. Sign-In-With-Solana, over three requests and a keypair; see the full flow for the exact message format.
- 4
Run the free trial
One free run per wallet per listing, streamed as newline-delimited JSON. See the one free run for the 409 and 429 shapes.
curl -N -X POST "https://skillstrade.xyz/api/skills/adversarial-diff-audit/trial" \ -b "st_session=<your session cookie>" \ -H "content-type: application/json" \ -d '{"prompt":"Review this diff for a broken auth check"}'200, application/x-ndjson{"type":"start","consumedTrial":true,"unavailableTools":[],"prerequisites":["Git repository","Read access to full history"]} {"type":"delta","text":"## Findings\n\n"} {"type":"delta","text":"1. ..."} {"type":"done","ms":8421,"inputTokens":1904,"outputTokens":612,"stopReason":"end_turn"} - 5
Ask for the file, get a 402
No session, no ownership, no
X-PAYMENTheader — the download route answers with the price rather than a bare refusal. See paying over x402 for the full challenge shape.curl -i "https://skillstrade.xyz/api/skills/adversarial-diff-audit/download"
402{ "x402Version": 2, "accepts": [{ "scheme": "exact", "amount": "48000000", "resource": "https://skillstrade.xyz/api/skills/adversarial-diff-audit/download", /* ... */ }] } - 6
Pay, retry, download
The exact same request, with the payment attached. On success the bundle streams back with a settlement receipt in the response header — no second request needed.
curl -i "https://skillstrade.xyz/api/skills/adversarial-diff-audit/download" \ -H "X-PAYMENT: <base64 payment payload>"
200content-type: text/markdown; charset=utf-8 x-payment-response: <base64 settle response> # Adversarial Diff Audit ...
Headless Sign-In-With-Solana
A keypair and an HTTP client are enough — no wallet extension, no browser. Three requests: get a challenge, sign it, hand the signature back.
- 1.
POST /api/auth/nonce— returns the fields to sign and sets an httpOnly cookie holding the nonce. Send that cookie back on step 3. - 2. Build and sign the message — join the lines below with
\nand sign the UTF-8 bytes with an ed25519 Solana key. - 3.
POST /api/auth/verify—{ address, signedMessage, signature }, message and signature both base64. Setsst_sessionon success.
One generic 401 covers every failure — a bad signature, an expired or reused nonce, a domain or address mismatch. The server never says which half failed. A worked, runnable example (real keypair, real signing) is in scripts/publish-seed-skills.mjs and scripts/x402-smoke.mjs.
skillstrade.xyz wants you to sign in with your Solana account: <address> Sign in to skillstrade.xyz. This proves you control this wallet. It is not a transaction and moves no funds. URI: https://skillstrade.xyz Version: 1 Nonce: <from the nonce response> Issued At: <from the nonce response> Expiration Time: <from the nonce response>
curl -c jar -X POST "https://skillstrade.xyz/api/auth/nonce"
curl -b jar -X POST "https://skillstrade.xyz/api/auth/verify" \
-H "content-type: application/json" \
-d '{"address":"<pubkey>","signedMessage":"<base64>","signature":"<base64>"}'The one free run
A non-owner gets one successful run per listing, and it is only charged against that allowance once output has actually streamed — a run that dies before its first token is given back, not spent. An owner’s run does not touch the allowance at all; it still costs tokens, so it still counts against the rate limit below.
Claimed before the model is ever called, against a unique index on (wallet, skill) — two requests racing each other produce one run, not two.
- Already spent
- 409, trial_spent
- Seller opted out
- 409, no_trial
- Rate limit
- 20 runs / hour / wallet
- Over the limit
- 429 + Retry-After
The rate limit is per wallet across every listing, not per skill — twenty runs an hour whether they are twenty different trials or twenty owned skills being exercised.
Paying over x402
This is the real agent path — no session, no checkout screen. The download route is the gate for a human and an agent alike: bytes leave storage only against a purchase row, and an agent earns that row by presenting a payment this server verifies, settles and records before it answers.
The X-PAYMENT header is base64 JSON, and the price on the 402 is exact — dollars converted to USDC base units with no floating-point drift: $48.00 becomes "48000000". Stock v2 client libraries work unmodified: the challenge also rides base64-encoded in a PAYMENT-REQUIRED response header, PAYMENT-SIGNATURE is accepted as an alias for the payment header, and when the deployment configures its facilitator’s fee-sponsoring key the challenge carries it as extra.feePayer — so a buyer needs USDC, not SOL.
This server never inspects the payload’s internal shape — it forwards whatever arrives verbatim to the configured facilitator’s /verify and /settle. What goes inside is defined by the facilitator implementing x402’s exact scheme for Solana, not by this API. scripts/x402-smoke.mjs (run with pnpm test:x402) is a complete, runnable walkthrough against a local facilitator.
{
"x402Version": 2,
"accepts": [{
"scheme": "exact",
"network": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
"asset": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
"amount": "48000000",
"payTo": "<this deployment's payout address>",
"resource": "https://skillstrade.xyz/api/skills/adversarial-diff-audit/download",
"description": "skills.trade download: adversarial-diff-audit",
"mimeType": "text/markdown",
"maxTimeoutSeconds": 60
}]
}EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU
solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp
solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1
Which one a live 402 quotes depends on how this instance is configured — read network and asset off the challenge itself rather than assuming.
Free 200. The header still verifies against the facilitator on every request, but settlement is skipped once ownership is confirmed — an agent retrying a download it already paid for is not charged twice.
409. purchases.tx_sig is unique — one settled transaction cannot back two licences.
402 again, with “settlement failed.” Nothing is written; nothing was charged.
503. This route fails closed — with no way to tell a real payment from a claim of one, the alternative is giving the bundle away.
The full contract
Every request and response shape above, including the exact x402 challenge schema, is written out in full in the OpenAPI document — the version to diff against if a response here ever looks stale.