Sign In

@t2000/serve

Package Overview
Dependencies
Maintainers
1
Versions
74
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@t2000/serve

Merchant-side x402 router for Sui — wrap any API so agents can discover it and pay per call in USDC. One builder: .paid().body().handler(). Sign-then-settle, no seller key, no seller gas.

Source
npmnpm
Version
10.35.1
Version published
Weekly downloads
872
-63.73%
Maintainers
1
Weekly downloads
 
Created
Source

@t2000/serve

Merchant-side x402 router for Sui — wrap any API so agents can discover it and pay per call in USDC.

// app/api/search/route.ts (Next.js) — a complete paid endpoint
import { asNextRoute, createServeFromEnv } from '@t2000/serve';

const serve = createServeFromEnv(); // reads T2000_PAY_TO from env

export const { POST, OPTIONS } = asNextRoute(
  serve
    .route({ path: 'search' })
    .paid('0.01') // USDC per call
    .body(searchSchema) // zod v4 / valibot / arktype / anything Standard-Schema
    .handler(async ({ body }) => search(body)),
);

Next.js dispatches only the methods a route.ts exports — you must export OPTIONS too (asNextRoute does it), or browser buyers fail CORS preflight; serve's own CORS handling can't run on a request Next never delivers. Fetch runtimes that mount one handler (Bun / Deno / workers) don't need this — OPTIONS already reaches the same handler.

That route now answers x402 payment challenges, validates inputs, verifies and settles payments on Sui, and is sellable as a Service on your Agent ID and t2000.ai — where every agent running t2 pay or the t2000 MCP can find and pay it.

Why Sui / why this package

  • No seller key, no seller gas. Payment settles sign-then-settle: the buyer signs a gasless USDC transfer, this package verifies and submits it. Your server never holds a private key and never pays gas. (Every EVM x402 router needs an operator wallet key in the server env. This one doesn't.)
  • No charge on failure. The handler runs before settlement. Invalid body → 422, handler throws → 500 — in both cases the buyer's payment is never submitted. Getting this wrong by hand is the most common seller bug.
  • Correct by construction. Challenge-once + digest-once replay protection, structural payment verification (right amount, right recipient, gasless-only, framework-calls-only), CORS for browser wallets — all defaults.

Setup

npm install @t2000/serve
Env varRequiredWhat it is
T2000_PAY_TOyesYour Sui address — payments settle here (t2 address prints it)
T2000_NETWORKnomainnet (default) or testnet
T2000_ACTIVITY_REPORT_URLnoFire-and-forget x402.paid reports after each successful settle (chain-verified server-side; never affects buyer responses). Default-on for createServeFromEnv: unset → https://t2000.ai/api/activity/x402, so your sold routes appear on the t2000.ai activity tape. Opt out with false | 0 | off | none, or set a custom URL. Code-constructed new Serve({...}) stays silent unless you pass activityReportUrl.
T2000_BASE_URLnoPublic URL of the deployed app (used in challenges + discovery)
KV_REST_API_URL / KV_REST_API_TOKENserverless: yesUpstash-compatible KV for durable replay protection. Without it the store is in-memory (fine for one long-lived process, wrong for serverless).

No wallet yet? npm i -g @t2000/cli && t2 init — wallet + free on-chain Agent ID.

Routes

serve.route({ path: 'search' }).paid('0.01').body(schema).handler(fn);  // paid
serve.route({ path: 'health' }).unprotected().handler(() => ({ ok: true })); // free
  • Prices are human-unit USDC strings ('0.01'), max 6 decimals.
  • Prices above 5 USDC work but are the wrong shape for per-call x402 — deliverable-priced work belongs in escrow (t2 service create).
  • Handlers receive { body, req, payer }payer is the buyer's verified Sui address (wallet-based identity, no accounts).
  • Return any JSON-serializable value (wrapped in a 200) or a Response.

Discovery — agents find you

// Next.js
export const GET = serve.openapi(); // app/openapi.json/route.ts
export const GET = serve.llms();    // app/llms.txt/route.ts

// Fetch runtimes (Bun / Deno / Hono / Workers) — one handler for everything:
Bun.serve({ fetch: serve.fetch });
app.all('*', (c) => serve.fetch(c.req.raw)); // Hono

/openapi.json is OpenAPI 3.1 with the x-payment-info pricing extension on every paid operation (the shape x402 tooling indexes); /llms.txt is plain-text guidance for agents. Pass a JSON Schema as .body(schema, jsonSchema) (zod v4: z.toJSONSchema(schema)) and buyers' agents build request bodies without guessing — a wrong guess against a direct seller is a paid error.

Declare what a paid call returns with .response(jsonSchema) — published under the 200 response and carried through the catalog so buyer surfaces render your deliverable by type. Annotate with contentMediaType ("image/svg+xml", "text/markdown") and format: "color" where they apply:

const output = z.object({ svg: z.string().meta({ contentMediaType: 'image/svg+xml' }) });
serve.route({ path: 'logo' }).paid('0.05').body(schema).response(z.toJSONSchema(output)).handler(fn);

Get listed

Deploy, then:

console.log(serve.catalogSubmitCommand('https://api.example.com'));

prints the dry-run (/api/catalog/preview) and submit (/api/catalog/submit) curls. The catalog verifies your live 402 challenge — no forms, no approval queue.

Docs

Full guide: docs.t2000.ai → Sell to agents.

Keywords

x402

FAQs

Package last updated on 15 Aug 2026

Related posts