> ## Documentation Index
> Fetch the complete documentation index at: https://agentcash.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Hosting

> Run the router on Next.js, Hono, Bun, Node, or any fetch runtime, and serve discovery endpoints.

The router speaks Web-standard `Request`/`Response` and dispatches through an embedded [Hono](https://hono.dev) app, so the same route definitions run anywhere. Three hosting modes:

## Next.js catch-all (recommended)

One route file serves every registered route:

```typescript app/api/[[...route]]/route.ts theme={null}
import '@/lib/routes'; // side-effect import: registers all routes
import { router } from '@/lib/router';
import { nextHandlers } from '@agentcash/router/next';

export const { GET, POST, PUT, PATCH, DELETE } = nextHandlers(router);
```

The catch-all serves `/{basePath}/*` (default `/api/*`), including `/api/openapi.json` and `/api/llms.txt`. Unmatched paths get a JSON 404 with rediscovery links automatically.

## Next.js per-file

One route file per endpoint also works:

```typescript app/api/search/route.ts theme={null}
import { router } from '@/lib/router';

export const POST = router
  .route('search')
  .paid('0.01')
  .body(schema)
  .handler(handler);
```

Three per-file specifics the catch-all makes obsolete:

* **Verb declaration**: the exported const name (`GET`/`POST`) controls which verb Next.js serves, while `.method()` controls the verb advertised in discovery. The router defaults to `POST` (or `GET` when `.query()` is used), so any other GET route must declare it explicitly.
* **Discovery needs a barrel**: Next.js lazy-loads route files, so your `openapi.json` route must import a barrel module that imports every route file, or unloaded routes won't appear in the spec.
* **404 fallback**: mount `router.notFound()` in a catch-all so stale agent calls get a JSON 404 with rediscovery links instead of an HTML error page.

## Hono / Bun / Node / any fetch runtime

```typescript theme={null}
import { serve } from '@hono/node-server';
import { Hono } from 'hono';
import './routes';
import { router } from './router';

const app = new Hono();
app.route('/', router.hono()); // or use router.fetch directly
serve({ fetch: app.fetch, port: 3000 });
```

`router.fetch(request)` is a standard fetch handler; `router.hono()` exposes the internal app for mounting into a larger one. See [`examples/hono`](https://github.com/merit-systems/agentcash-router/tree/main/examples/hono) for a runnable server.

## `basePath`

`RouterConfig.basePath` (default `'api'`) controls the mount and advertised prefix: routes serve at `{baseUrl}/{basePath}/{path}`. Pass an empty string to mount at the origin root.

## Discovery endpoints

The router generates two discovery surfaces from your route definitions:

* **`/openapi.json`**: OpenAPI 3.1 with pricing extensions. Indexed by [AgentCash](https://agentcash.dev), [x402scan](https://x402scan.com), and [mppscan](https://mppscan.com).
* **`/llms.txt`**: plain-text guidance for agents.

`router.fetch` (and the Next.js catch-all) serves both under the `basePath` automatically, plus the root aliases on fetch runtimes. On Next.js, add the conventional root paths with two one-line route files:

```typescript app/openapi.json/route.ts theme={null}
import '@/lib/routes';
import { router } from '@/lib/router';
export const GET = router.openapi();
```

```typescript app/llms.txt/route.ts theme={null}
import { router } from '@/lib/router';
export const GET = router.llmsTxt();
```

<Note>
  `router.wellKnown()` (the `/.well-known/x402` listing) is deprecated. It still works for legacy x402 clients, but new integrations should rely on `/openapi.json` and `/llms.txt`. See [Become discoverable](/discovery) for the full discovery contract.
</Note>

### Bazaar catalog metadata

x402 facilitators persist service metadata from settled payments into the [Bazaar](https://docs.x402.org/extensions/bazaar) catalog. The router forwards it from discovery config onto each 402 challenge:

```typescript theme={null}
createRouterFromEnv({
  title: 'Quote API',                       // serviceName defaults to title when ≤32 ASCII chars
  serviceName: 'Quote API',                 // explicit override
  tags: ['quotes', 'wisdom'],               // ≤5 tags, each ≤32 chars
  iconUrl: 'https://example.com/icon.png',  // HTTPS, ≤2048 chars
  // ...
});
```

Invalid values fail at startup with structured config issues rather than being silently dropped by the facilitator.
