Docs · 12
API reference
Every route: inputs, outputs, status codes.
Base URL https://everynth.org. Requests and responses are JSON unless noted. Routes marked session need the cookie set by POST /api/session; the browser sends it automatically. Errors are { "error": "message" } with a 4xx status.
Session#
POST/api/sessionpublic
Sign in with a wallet signature.
{ "wallet": "<base58>", "issuedAt": 1758445200000, "signature": "<base64 ed25519 over the login message>" }
→ 200 { "wallet": "<base58>" } + Set-Cookie everynth_session (httpOnly, 7 days)
→ 400 missing fields · 401 invalid or expired signature (5-minute window, host-bound)The message to sign, byte for byte:
${host} wants you to sign in to EVERYNTH.\nThis is free and does not move any funds.\n\nWallet: ${wallet}\nIssued at: ${issuedAt}DELETE/api/sessionsession
Sign out. Clears the cookie. → 200 { "ok": true }
Rate limits#
Counted per wallet (per IP when there is no session), in fixed windows, in the database — so every serverless instance counts into the same bucket. Over budget returns 429 with a retry-afterheader. Nothing here is near what normal use looks like.
POST /api/session 30 / 10 min
POST /api/products 25 / hour
POST /api/upload 20 / hour
POST /api/orders 40 / hour
POST /api/purchases/:id/review 20 / hour
POST /api/reports 10 / hour
GET /api/verify 300 / minute (per IP)Products#
POST/api/uploadsession
Token exchange for a direct browser upload to blob storage (Vercel Blob client protocol). Use upload() from @vercel/blob/client with handleUploadUrl: "/api/upload". Only application/octet-stream up to 200 MB + 28 bytes is accepted.
POST/api/productssession
Launch a product. multipart/form-data:
title 3–80 chars description 10–4000 chars
category AI Agent | API | Dataset | Tool | Research | Service | Community
price "0.02" … "1000000", ≤ 9 decimals (SOL)
kind file | secret | github
cover image/png|jpeg|webp|gif ≤ 1 MB (optional)
issuedAt ms timestamp, ≤ 5 min old
signature base64 ed25519 over the launch message, by the session wallet
kind=file key (base64, 32 bytes) fileName fileType payloadUrl (our blob host only)
kind=secret key (base64, 32 bytes) payload (Blob: iv‖ciphertext, ≤ 64 KB + 28)
kind=github repo ("owner/name") token (checked live: must have admin on the repo)
→ 200 { "id": "<uuid>" } · 400 validation · 401 bad confirmation · 403 wallet blocked · 413 too largeThe launch message, rebuilt server-side from the submitted title, price (in lamports) and kind:
${host} wants you to confirm this launch on EVERYNTH.\nThis is free and does not move any funds.\n\nTitle: ${title}\nPrice: ${lamports} lamports\nDelivery: ${kind}\nCreator: ${wallet}\nIssued at: ${issuedAt}PATCH/api/products/:idcreator + signature
Edit listing details: title description category price cover (same rules), plus issuedAt and signature. Content is immutable. → 200 { ok } · 401 bad confirmation · 404 not yours.
${host} wants you to confirm: edit listing.\nThis is free and does not move any funds.\n\nItem: ${id}\nTitle: ${title}\nPrice: ${lamports} lamports\nWallet: ${wallet}\nIssued at: ${issuedAt}POST/api/products/:id/removecreator or admin + signature
Unlist. Body: { "block": true } (admins) also blocks the creator and unlists all their products, plus issuedAt and signature. → 200 { ok } · 401 bad confirmation
${host} wants you to confirm: remove from market.\nThis is free and does not move any funds.\n\nItem: ${id}\nScope: product | product and creator\nWallet: ${wallet}\nIssued at: ${issuedAt}POST/api/purchases/:id/reviewbuyer
Review a product you paid for. One review per purchase; posting again rewrites it. { "rating": 1-5, "body": "≤ 500 chars" } → 200 { ok } · 400 bad rating or too long · 404 no paid purchase of yours.
GET/api/products/:id/coverpublic
The cover image bytes, or 404. Cached 5 minutes.
Orders and purchases#
POST/api/orderssession
{ "productId": "<uuid>" }
→ 200 { "purchaseId", "reference", "creator", "creatorAmount", "treasury", "fee" } // lamports
→ 400 own product · 404 not live · 409 already owned (also after chain recovery)Reuses the caller's open order for the product if one exists, after checking the chain for a payment.
POST/api/orders/:id/confirmbuyer
{ "signature": "<base58>" } // optional: without it the server searches by reference
→ 200 { "ok": true } · 402 { "error": "payment not found yet" | "underpaid: …" | "transaction failed on-chain" | … }GET/api/purchases/:id/contentbuyer, paid
file { "kind": "file", "fileName", "fileType", "key": "<base64>", "payloadUrl": "https://…blob…", "payload": null }
secret { "kind": "secret", "key": "<base64>", "payload": "<base64 iv‖ciphertext>", "payloadUrl": null }
github { "kind": "github", "repo": "owner/name", "githubUser": "octocat" | null }
→ 404 no paid purchasePOST/api/purchases/:id/githubbuyer, paid
{ "username": "octocat" }
→ 200 { "ok": true, "repo": "owner/name" } · 400 bad username · 409 tied to another account · 502 GitHub refusedOwnership#
GET/api/verify?product=&wallet=public · CORS *
→ 200 { "owned": boolean, "since": ISO date | null }. See Ownership check.
Moderation#
POST/api/reportssession
{ "productId", "reason" } (5–1000 chars). One per wallet per product. → 200 { ok }
POST/api/admin/unblockadmin
{ "wallet" } → 200 { ok }. Admins are the wallets listed in ADMIN_WALLETS.
Limits at a glance#
price 0.02 – 1,000,000 SOL fee 5%, floor(lamports × 500 / 10000)
file ≤ 200 MB secret ≤ 64 KB cover ≤ 1 MB
login signature valid 5 min session 7 days
blocked ext exe msi bat cmd com scr pif vbs vbe ps1 dll dmg pkg app apk deb rpm jar lnkNext