Sign In

@t2000/serve

Package Overview
Dependencies
Maintainers
1
Versions
55
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.

latest
Source
npmnpm
Version
10.30.5
Version published
Maintainers
1
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 12 Aug 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts