SKILLSTRADE

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.

Quickstart

Search, trial, pay, download

  1. 1

    Search the catalog

    No query parameters returns the whole catalog, unpaginated — { skills }. Add any of q, category, tag, limit or cursor and the shape switches to { skills, nextCursor }, an opaque base64url cursor that is null on 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. 2

    Read one listing in full

    The record, a USD price with an optional live token quote (never a stale one — null when 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. 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. 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. 5

    Ask for the file, get a 402

    No session, no ownership, no X-PAYMENT header — 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. 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>"
    200
    content-type: text/markdown; charset=utf-8
    x-payment-response: <base64 settle response>
    
    # Adversarial Diff Audit
    ...
Sign-in

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. 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. 2. Build and sign the message — join the lines below with \n and sign the UTF-8 bytes with an ed25519 Solana key.
  3. 3. POST /api/auth/verify { address, signedMessage, signature }, message and signature both base64. Sets st_session on 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.

The message this deployment signs
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>
Nonce, then verify
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>"}'
Trial

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.

Payment

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.

The 402 challenge
{
  "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
  }]
}
USDC, by network
Mainnet asset

EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v

Devnet asset

4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU

Mainnet network

solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp

Devnet network

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.

A retry with the same X-PAYMENT you already spent

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.

A different payer settling to a signature already on record

409. purchases.tx_sig is unique — one settled transaction cannot back two licences.

Settlement fails at the facilitator

402 again, with “settlement failed.” Nothing is written; nothing was charged.

No facilitator configured, or it is unreachable

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.

Reference

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.

Reviewed listings onlyBrowse skills