NavenDocs
NavenDocs
Introduction
QuickstartHow x402 Payments WorkBuyer/Client GuideNaven CLISeller/Server Guide
Back to Naven Network
Getting Started

Seller/Server Guide

Scaffold, configure, and deploy a Bun and Hono server that accepts x402 payments.

Create, customize, verify, and deploy an x402 v2 server with the official Naven CLI template. The generated project is a standalone Bun and Hono application with a protected example endpoint, validation, tests, and Docker support.

Prerequisites

  • Bun 1.2 or later
  • An EVM address that will receive payments

Create a server

bunx naven-cli@latest create server my-server
cd my-server

The command copies the template into my-server, creates .env, and installs dependencies. It will not overwrite a non-empty directory.

Pass --no-install if you want to inspect the template before installing:

bunx naven-cli@latest create server my-server --no-install
cd my-server
bun install

If you use Naven regularly, install the CLI globally:

bun add --global naven-cli@latest
naven --version
naven create server my-server

See the Naven CLI reference for all commands, options, generated scripts, and troubleshooting.

Understand the generated template

The CLI creates an independent application. You do not need naven-cli installed to run or deploy it after generation.

my-server/
├── .env
├── .env.example
├── Dockerfile
├── README.md
├── package.json
├── scripts/
│   └── test-challenge.ts
└── src/
    ├── app.ts
    ├── config.ts
    └── index.ts
FileWhat to change
.envPayment recipient, public origin, port, facilitator, and CORS origin.
src/app.tsPaid route definitions, resource metadata, and service handlers.
src/config.tsNetwork, token, price, timeout, and environment validation.
src/index.tsBun server startup behavior.
scripts/test-challenge.tsChallenge assertions after renaming or adding paid routes.
DockerfileContainer runtime details when your platform needs different defaults.

Configure

The command creates .env from .env.example. Set the EVM address that should receive payments. This is a public recipient address, not a private key:

X402_PAY_TO=0xYourEvmAddress

For local development, the remaining defaults are ready to use:

PUBLIC_URL=http://localhost:4021
PORT=4021
FACILITATOR_URL=https://facilitator.naven.network
CORS_ORIGIN=*

Set PUBLIC_URL to the public HTTPS origin when you deploy the server. It must be an origin without a path, query, or fragment. For example, use https://api.example.com, not https://api.example.com/v1.

Run

bun run dev

The generated project includes:

  • GET /health, a free health check
  • GET /weather, an x402-protected example
  • x402 v2 middleware for Bun and Hono
  • Robinhood Chain USDG payment configuration
  • environment and wallet-address validation
  • browser CORS and payment response headers
  • a Dockerfile and TypeScript checks

The default payment terms are:

SettingValue
NetworkRobinhood Chain (eip155:4663)
AssetUSDG (0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168)
Price100 atomic units, or 0.0001 USDG
Payment timeout300 seconds

Verify the challenge

Run the included smoke test. It verifies the server returns a valid PAYMENT-REQUIRED challenge without spending funds:

bun run test:challenge

You can also inspect the response directly:

curl -i http://localhost:4021/weather

Expected responses:

  • GET /health returns 200 OK.
  • GET /weather without a payment returns 402 Payment Required and a PAYMENT-REQUIRED header.

The challenge test does not submit or settle an onchain payment.

Add your service

Open src/app.ts and replace the example handler with your service logic. Do expensive work only inside the protected handler so unpaid requests cannot trigger it:

app.get("/weather", async c => {
  const report = await loadYourPaidData();
  return c.json({ report });
});

If you rename /weather or change its HTTP method, update both the route key in paymentMiddleware and the Hono handler. They must match exactly:

// Payment configuration
"GET /your-resource": {
  // accepts, resource metadata, and price
}

// Protected handler
app.get("/your-resource", async c => {
  return c.json({ result: await loadYourPaidData() });
});

Also update the resource, description, mimeType, and serviceName metadata shown to buyers:

resource: new URL("/your-resource", config.publicUrl).toString(),
description: "Describe what the buyer receives",
mimeType: "application/json",
serviceName: "Your service name",

If you change the example route, update scripts/test-challenge.ts so resourceUrl points to the same path.

Set the price

Change priceAtomic in src/config.ts to set a different USDG price. USDG has six decimal places, so 1 is 0.000001 USDG and 1000000 is 1 USDG.

Keep prices as integer strings in atomic units. Do not use a decimal string such as "0.10" for priceAtomic.

Change the network or token

The template defaults to USDG on Robinhood Chain. To accept a different supported token, update network, asset, assetDecimals, assetName, and assetVersion together in src/config.ts. Confirm that the selected network and payment scheme are supported by the configured facilitator before deployment.

The payment middleware validates the buyer's payment before the handler returns the protected result and uses the facilitator to settle it.

Add more paid routes

Add each paid route to the paymentMiddleware configuration and define its matching Hono handler. Free routes such as /health need only a Hono handler.

paymentMiddleware(
  {
    "GET /weather": weatherPaymentTerms,
    "POST /analysis": analysisPaymentTerms,
  },
  resourceServer,
)

Keep authentication, request validation, rate limiting, and upstream API credentials in this generated server. Do not put secrets in route metadata or responses.

Check before deployment

bun run check

This runs the TypeScript check and verifies the x402 challenge in memory. It does not spend funds and does not require the development server to be running.

To build and run the included Docker image:

docker build -t my-server .
docker run --env-file .env -p 4021:4021 my-server

After deployment, request the public protected URL without payment and confirm it returns 402 with a PAYMENT-REQUIRED header.

Then use an x402-compatible client to make one low-value paid request and confirm the response includes payment settlement metadata. The Buyer/Client Guide shows the client-side flow.

Production checklist

  • Use an HTTPS PUBLIC_URL that matches the buyer-facing endpoint.
  • Restrict CORS_ORIGIN when the API is called from known browser applications.
  • Keep .env and any upstream API credentials out of source control.
  • Add timeouts and input validation to your own service logic.
  • Run a real low-value paid request before announcing the endpoint publicly.
  • Monitor failed verification and settlement responses in production.

Naven CLI

Create and customize an x402 paid server from the official Naven template.

Overview

Launch and operate agentic trading strategies through Naven's managed runtime layer.

On this page

PrerequisitesCreate a serverUnderstand the generated templateConfigureRunVerify the challengeAdd your serviceSet the priceChange the network or tokenAdd more paid routesCheck before deploymentProduction checklist