{"openapi":"3.1.0","info":{"title":"skills.trade API","version":"1.0.0","description":"A marketplace for Agent Skills — SKILL.md bundles, human-reviewed and priced in dollars. This document covers the machine-facing surface: search and read the catalog, run one free trial per listing, sign in headlessly over Solana Sign-In-With-Solana, and pay for a download with x402. See GET /llms.txt for the narrative version of this same flow."},"servers":[{"url":"https://skillstrade.xyz"}],"tags":[{"name":"catalog","description":"Search and read listings."},{"name":"trial","description":"One free evaluation run per listing."},{"name":"download","description":"The paid payload, gated by ownership or an x402 payment."},{"name":"auth","description":"Headless Sign-In-With-Solana."},{"name":"token","description":"The live $SKILLS/USD rate."}],"paths":{"/api/skills":{"get":{"tags":["catalog"],"summary":"Search or list live listings.","description":"With no query parameters, returns { skills } — every live listing, unpaginated. Adding any of q, category, tag, limit or cursor switches to the filtered, paginated shape: { skills, nextCursor }, where nextCursor is an opaque base64url cursor. In that shape nextCursor is always present in the body; its value is null when there is no next page.","parameters":[{"name":"q","in":"query","schema":{"type":"string"},"description":"Case-insensitive substring match against name, tagline and overview."},{"name":"category","in":"query","schema":{"type":"string"},"description":"Exact match against one of the fixed category codes."},{"name":"tag","in":"query","schema":{"type":"string"},"description":"Listing must carry this tag."},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Page size, clamped to [1, 100]."},{"name":"cursor","in":"query","schema":{"type":"string"},"description":"Opaque base64url cursor from a previous response's nextCursor."}],"responses":{"200":{"description":"The catalog, or a page of search results.","content":{"application/json":{"schema":{"type":"object","properties":{"skills":{"type":"array","items":{"type":"object","description":"A listing, as recorded in the catalog. See src/lib/types.ts for the full field set.","additionalProperties":true,"properties":{"id":{"type":"string"},"slug":{"type":"string"},"name":{"type":"string"},"tagline":{"type":"string"},"overview":{"type":"string"},"category":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"version":{"type":"string"},"body":{"type":"string","description":"Public preview only — the first previewLines of the sealed file, never the full payload."},"priceUsd":{"type":"number"},"trialRuns":{"type":"integer"},"status":{"type":"string","enum":["draft","in_review","live","delisted"]}},"required":["id","slug","name","tagline","category","priceUsd","status"]}},"nextCursor":{"oneOf":[{"type":"string"},{"type":"null"}],"description":"Present in the body of the paginated shape whenever a search param is set (absent only from the plain { skills } shape returned when none is). null when there is no next page."}},"required":["skills"]}}}},"400":{"description":"Invalid limit or cursor.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/skills/{slug}":{"get":{"tags":["catalog"],"summary":"Look up one listing.","description":"The agent-facing counterpart to the /skill/[slug] page: the listing, a USD price with an optional live token quote, how to run its free trial, and how to pay for the download.","parameters":[{"$ref":"#/components/parameters/slug"}],"responses":{"200":{"description":"The listing and how to trial or buy it.","content":{"application/json":{"schema":{"type":"object","properties":{"skill":{"type":"object","description":"A listing, as recorded in the catalog. See src/lib/types.ts for the full field set.","additionalProperties":true,"properties":{"id":{"type":"string"},"slug":{"type":"string"},"name":{"type":"string"},"tagline":{"type":"string"},"overview":{"type":"string"},"category":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"version":{"type":"string"},"body":{"type":"string","description":"Public preview only — the first previewLines of the sealed file, never the full payload."},"priceUsd":{"type":"number"},"trialRuns":{"type":"integer"},"status":{"type":"string","enum":["draft","in_review","live","delisted"]}},"required":["id","slug","name","tagline","category","priceUsd","status"]},"pricing":{"type":"object","properties":{"priceUsd":{"type":"number"},"quote":{"oneOf":[{"type":"object","properties":{"usdPerToken":{"type":"number"},"at":{"type":"integer"}},"required":["usdPerToken","at"]},{"type":"null"}],"description":"null when the price feed is down — never a stale rate."}},"required":["priceUsd","quote"]},"trial":{"type":"object","properties":{"freeRuns":{"type":"integer"},"method":{"type":"string","enum":["POST"]},"endpoint":{"type":"string"},"authenticated":{"type":"boolean"}},"required":["freeRuns","method","endpoint","authenticated"]},"payment":{"type":"object","properties":{"protocol":{"type":"string","enum":["x402"]},"endpoint":{"type":"string"},"note":{"type":"string"}},"required":["protocol","endpoint","note"]}},"required":["skill","pricing","trial","payment"]}}}},"404":{"description":"No such listing, or it is not live.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/skills/{slug}/trial":{"post":{"tags":["trial"],"summary":"Run the one free evaluation, streamed.","description":"Runs the buyer's prompt against the seller's sealed SKILL.md and streams the model's answer back as newline-delimited JSON (application/x-ndjson) — one JSON object per line: {type:\"start\"}, {type:\"delta\", text}, {type:\"halted\", message} if the output starts reproducing the sealed instructions, then {type:\"done\", ms, inputTokens, outputTokens, stopReason} or {type:\"error\", message, refunded}. Requires a session cookie from POST /api/auth/verify. An owner's run does not spend the free trial; a non-owner gets one successful run per listing, and the run is only charged against that allowance once output has actually streamed.","security":[{"sessionCookie":[]}],"parameters":[{"$ref":"#/components/parameters/slug"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"type":"string","maxLength":2000}},"required":["prompt"]}}}},"responses":{"200":{"description":"NDJSON event stream. See the description above for the event shapes.","content":{"application/x-ndjson":{"schema":{"type":"string"}}}},"400":{"description":"Missing or oversized prompt, or a malformed request body.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Not signed in.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"404":{"description":"No such listing, or it is not live.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The free run for this wallet and this listing has already been used (\"trial_spent\"), or the seller has opted the listing out of free runs (\"no_trial\").","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"string"},"reason":{"type":"string","enum":["trial_spent","no_trial"]}},"required":["ok","error","reason"]}}}},"422":{"description":"This listing has no runnable skill file.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"More than 20 runs in an hour from this wallet, across all listings.","headers":{"retry-after":{"schema":{"type":"integer"},"description":"Seconds until another run is allowed."}},"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"502":{"description":"The sealed skill file could not be loaded from storage. Try again.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"Trial runs are unavailable: the model or object storage is not configured.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/skills/{slug}/download":{"get":{"tags":["download"],"summary":"The purchased payload, or an x402 challenge to buy it.","description":"Streams the sealed file to whoever already owns it — a session that purchased at checkout, or a caller that pays for it in the same request over x402 — and answers everyone else with a 402 x402 challenge, signed in or not: there is no ownership-gated 403, only 'pay to unlock this'. Retry the same request with an X-PAYMENT header carrying the base64-encoded payment payload; a successful paid response carries an X-PAYMENT-RESPONSE header. See the x402PaymentRequired schema for the exact 402 body. A session is one way in, not a requirement — the x402 path is deliberately sessionless, with payment riding entirely in the X-PAYMENT header.","security":[{"sessionCookie":[]},{}],"parameters":[{"$ref":"#/components/parameters/slug"},{"name":"format","in":"query","schema":{"type":"string","enum":["md","zip"],"default":"md"},"description":"md streams the entrypoint file; zip streams the whole bundle."},{"name":"X-PAYMENT","in":"header","required":false,"schema":{"type":"string"},"description":"Base64-encoded x402 payment payload, sent on the retry after a 402. PAYMENT-SIGNATURE is accepted as an alias — it is the header v2 client libraries send; X-PAYMENT wins when both are present."}],"responses":{"200":{"description":"The file. text/markdown for format=md, application/zip for format=zip.","headers":{"X-PAYMENT-RESPONSE":{"schema":{"type":"string"},"description":"Present when this response was unlocked by a paid retry."}},"content":{"text/markdown":{"schema":{"type":"string"}},"application/zip":{"schema":{"type":"string","format":"binary"}}}},"402":{"description":"No session that owns this listing, and no valid X-PAYMENT presented — missing, malformed, unverified, or settlement failed. Goes to any caller without a purchase, anonymous included; quotes the price to retry against. See the x402PaymentRequired schema for the exact body.","headers":{"PAYMENT-REQUIRED":{"schema":{"type":"string"},"description":"The same challenge body, base64-encoded. v2 client libraries read the challenge from this header and treat its absence as a v1 flow."}},"content":{"application/json":{"schema":{"type":"object","description":"402 body returned by GET /api/skills/{slug}/download when the caller does not own the listing.","properties":{"x402Version":{"type":"integer","enum":[2]},"accepts":{"type":"array","items":{"type":"object","description":"One accepted way to pay for the resource, in the x402 'exact' scheme.","properties":{"scheme":{"type":"string","enum":["exact"]},"network":{"type":"string","description":"CAIP-2 chain identifier, e.g. \"solana:mainnet\"."},"asset":{"type":"string","description":"Mint address of the payment asset."},"amount":{"type":"string","description":"Integer amount in the asset's base units, as a string."},"payTo":{"type":"string","description":"Address the payment must settle to."},"resource":{"type":"string","format":"uri","description":"The URL being paid for."},"description":{"type":"string"},"mimeType":{"type":"string"},"maxTimeoutSeconds":{"type":"integer","enum":[60]},"extra":{"type":"object","description":"Facilitator hints the buyer's client needs to build the transaction. Present when the deployment has configured them.","properties":{"feePayer":{"type":"string","description":"The facilitator's fee-sponsoring key. Client libraries set it as the transaction's payer, so the buyer needs no SOL — only the payment asset."}},"required":["feePayer"]}},"required":["scheme","network","asset","amount","payTo","resource","description","mimeType","maxTimeoutSeconds"]}},"error":{"type":"string"}},"required":["x402Version","accepts"]}}}},"404":{"description":"No such listing, the listing is not live, or it has no files.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"The payment settled, but this transaction signature — or this payer's licence — was already on record: a replay of an already-settled payment. No bytes are returned and nothing is charged again.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"500":{"description":"Payment settled but the purchase row could not be written, or the listing vanished before it could be recorded. Logged server-side for manual reconciliation.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"502":{"description":"The purchased file could not be read from storage.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"503":{"description":"The x402 payment rail is unconfigured or unreachable. Fails closed — never a free download.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/auth/nonce":{"post":{"tags":["auth"],"summary":"Issue a Sign-In-With-Solana challenge.","description":"First step of headless sign-in. Sets an httpOnly cookie holding the nonce and returns the fields to build a SIWS message. Build the message by joining these lines with \\n: `${domain} wants you to sign in with your Solana account:`, the wallet address, an empty line, statement, an empty line, `URI: ${uri}`, `Version: ${version}`, `Nonce: ${nonce}`, `Issued At: ${issuedAt}`, `Expiration Time: ${expirationTime}` — see scripts/publish-seed-skills.mjs for a worked example.","responses":{"200":{"description":"The SIWS fields to sign.","content":{"application/json":{"schema":{"type":"object","properties":{"domain":{"type":"string"},"uri":{"type":"string"},"statement":{"type":"string"},"version":{"type":"string"},"nonce":{"type":"string"},"issuedAt":{"type":"string","format":"date-time"},"expirationTime":{"type":"string","format":"date-time"}},"required":["domain","uri","statement","version","nonce","issuedAt","expirationTime"]}}}}}}},"/api/auth/verify":{"post":{"tags":["auth"],"summary":"Complete sign-in.","description":"Verifies the ed25519 signature over the exact SIWS message bytes, checks the message's domain, address and nonce against the server's own record, then sets the session cookie. The nonce cookie from POST /api/auth/nonce must be sent along with this request; it is single-use and is spent even when verification fails.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"address":{"type":"string","description":"base58 Solana public key."},"signedMessage":{"type":"string","description":"base64 of the exact bytes that were signed."},"signature":{"type":"string","description":"base64 ed25519 signature."},"wallet":{"type":"string","description":"Wallet name, cosmetic only."}},"required":["address","signedMessage","signature"]}}}},"responses":{"200":{"description":"Signed in. A session cookie is set.","content":{"application/json":{"schema":{"type":"object","properties":{"address":{"type":"string"}},"required":["address"]}}}},"400":{"description":"Malformed request body, address or encoding.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"One generic message for every failure mode (bad signature, expired or reused nonce, domain or address mismatch) — the server never says which half failed.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}},"/api/auth/session":{"get":{"tags":["auth"],"summary":"Who is signed in, if anyone.","responses":{"200":{"description":"{ address: null } when signed out.","content":{"application/json":{"schema":{"type":"object","properties":{"address":{"type":["string","null"]},"wallet":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"}},"required":["address"]}}}}}},"delete":{"tags":["auth"],"summary":"Sign out.","responses":{"200":{"description":"{ address: null }.","content":{"application/json":{"schema":{"type":"object","properties":{"address":{"type":"null"}}}}}}}}},"/api/token/quote":{"get":{"tags":["token"],"summary":"The live $SKILLS/USD rate.","description":"Public and unauthenticated. Never a stale fallback — 503 when the feed is down.","responses":{"200":{"description":"The current rate.","content":{"application/json":{"schema":{"type":"object","properties":{"usdPerToken":{"type":"number"},"at":{"type":"integer"}},"required":["usdPerToken","at"]}}}},"503":{"description":"No rate available right now.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}}}}},"components":{"parameters":{"slug":{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"description":"The listing's URL slug, e.g. \"diff-audit\"."}},"securitySchemes":{"sessionCookie":{"type":"apiKey","in":"cookie","name":"st_session","description":"Issued by POST /api/auth/verify after a headless SIWS sign-in: POST /api/auth/nonce, sign the returned fields with a Solana keypair, POST /api/auth/verify."}},"schemas":{"x402PaymentRequired":{"type":"object","description":"402 body returned by GET /api/skills/{slug}/download when the caller does not own the listing.","properties":{"x402Version":{"type":"integer","enum":[2]},"accepts":{"type":"array","items":{"type":"object","description":"One accepted way to pay for the resource, in the x402 'exact' scheme.","properties":{"scheme":{"type":"string","enum":["exact"]},"network":{"type":"string","description":"CAIP-2 chain identifier, e.g. \"solana:mainnet\"."},"asset":{"type":"string","description":"Mint address of the payment asset."},"amount":{"type":"string","description":"Integer amount in the asset's base units, as a string."},"payTo":{"type":"string","description":"Address the payment must settle to."},"resource":{"type":"string","format":"uri","description":"The URL being paid for."},"description":{"type":"string"},"mimeType":{"type":"string"},"maxTimeoutSeconds":{"type":"integer","enum":[60]},"extra":{"type":"object","description":"Facilitator hints the buyer's client needs to build the transaction. Present when the deployment has configured them.","properties":{"feePayer":{"type":"string","description":"The facilitator's fee-sponsoring key. Client libraries set it as the transaction's payer, so the buyer needs no SOL — only the payment asset."}},"required":["feePayer"]}},"required":["scheme","network","asset","amount","payTo","resource","description","mimeType","maxTimeoutSeconds"]}},"error":{"type":"string"}},"required":["x402Version","accepts"]},"paymentRequirements":{"type":"object","description":"One accepted way to pay for the resource, in the x402 'exact' scheme.","properties":{"scheme":{"type":"string","enum":["exact"]},"network":{"type":"string","description":"CAIP-2 chain identifier, e.g. \"solana:mainnet\"."},"asset":{"type":"string","description":"Mint address of the payment asset."},"amount":{"type":"string","description":"Integer amount in the asset's base units, as a string."},"payTo":{"type":"string","description":"Address the payment must settle to."},"resource":{"type":"string","format":"uri","description":"The URL being paid for."},"description":{"type":"string"},"mimeType":{"type":"string"},"maxTimeoutSeconds":{"type":"integer","enum":[60]},"extra":{"type":"object","description":"Facilitator hints the buyer's client needs to build the transaction. Present when the deployment has configured them.","properties":{"feePayer":{"type":"string","description":"The facilitator's fee-sponsoring key. Client libraries set it as the transaction's payer, so the buyer needs no SOL — only the payment asset."}},"required":["feePayer"]}},"required":["scheme","network","asset","amount","payTo","resource","description","mimeType","maxTimeoutSeconds"]}}}}