import { FacilitatorConfig } from '@x402/core/http'; import { ZodType, output } from 'zod'; import { PaymentRequirements, PaymentRequired, VerifyResponse, SettleResponse, Network } from '@x402/core/types'; import { Hono } from 'hono'; import * as viem from 'viem'; import { Transport } from 'mppx/server'; import { NonceStoreInterface } from 'did-auth-challenge'; interface KvStore { get(key: string): Promise; set(key: string, value: unknown): Promise; del(key: string): Promise; setNxEx(key: string, value: unknown, ttlSeconds: number): Promise; sadd(key: string, member: string): Promise; sismember(key: string, member: string): Promise; update(key: string, fn: (current: unknown) => KvChange): Promise; } type KvChange = { op: 'noop'; result: R; } | { op: 'set'; value: unknown; result: R; } | { op: 'delete'; result: R; }; interface NonceStore { check(nonce: string): Promise; } interface EntitlementStore { has(route: string, wallet: string): Promise; grant(route: string, wallet: string): Promise; } interface RequestMeta { requestId: string; method: string; route: string; origin: string; referer: string | null; walletAddress: string | null; clientId: string | null; sessionId: string | null; contentType: string | null; headers: Record; startTime: number; } interface PluginContext { readonly requestId: string; readonly route: string; readonly walletAddress: string | null; readonly clientId: string | null; readonly sessionId: string | null; verifiedWallet: string | null; setVerifiedWallet(address: string): void; } interface PaymentEvent { protocol: ProtocolType; payer: string; amount: string; network: string; } interface SettlementEvent { protocol: ProtocolType; payer: string; transaction: string; network: string; } interface ResponseMeta { statusCode: number; statusText: string; duration: number; contentType: string | null; headers: Record; /** Parsed request body (when .body() was used). undefined when no body was parsed. */ requestBody?: unknown; /** Handler return value or structured router-generated error body. */ responseBody?: unknown; /** Rich error details for non-402 failure responses. */ error?: ErrorEvent; } interface ErrorEvent { status: number; message: string; settled: boolean; requestId?: string; route?: string; method?: string; duration?: number; walletAddress?: string | null; verifiedWallet?: string | null; clientId?: string | null; sessionId?: string | null; errorName?: string; stack?: string; cause?: unknown; } interface AuthEvent { /** Authentication mode that was verified */ authMode: 'siwx' | 'apiKey'; /** Verified canonical wallet address (EVM lowercase, non-EVM preserved) */ wallet: string | null; /** Route key */ route: string; /** Account data from API key resolver (for apiKey auth) */ account?: unknown; } interface RouterPlugin { init?(config: { origin?: string; }): void | Promise; onRequest?(meta: RequestMeta): PluginContext; /** Fired after successful SIWX or API key verification, before handler */ onAuthVerified?(ctx: PluginContext, event: AuthEvent): void; onPaymentVerified?(ctx: PluginContext, payment: PaymentEvent): void; onPaymentSettled?(ctx: PluginContext, settlement: SettlementEvent): void; onResponse?(ctx: PluginContext, response: ResponseMeta): void; onError?(ctx: PluginContext, error: ErrorEvent): void; onAlert?(ctx: PluginContext, alert: AlertEvent): void; onProviderQuota?(ctx: PluginContext, event: ProviderQuotaEvent): void; } declare class HttpError extends Error { readonly status: number; constructor(message: string, status: number); } /** * Thrown when a route definition is invalid — an impossible builder * combination, a malformed price, or a route that needs config the router * wasn't given. Fires at module-import time (when the route file is first * loaded), so Next.js surfaces it as a build/dev error, never as a response. * The registration-time sibling of {@link RouterConfigError}. */ declare class RouteDefinitionError extends Error { /** Registry key of the offending route. */ readonly route: string; constructor( /** Registry key of the offending route. */ route: string, detail: string); } type AlertLevel = 'info' | 'warn' | 'error' | 'critical'; interface AlertEvent { level: AlertLevel; message: string; route: string; meta?: Record; } type AlertFn = (level: AlertLevel, message: string, meta?: Record) => void; type JsonPrimitive = string | number | boolean | null; type JsonValue = JsonPrimitive | JsonObject | JsonValue[]; type JsonObject = { [key: string]: JsonValue; }; interface X402Server { initialize(): Promise; buildPaymentRequirementsFromOptions(options: Array<{ scheme: string; network: string; price: string | { asset: string; amount: string; extra?: Record; }; payTo: string; maxTimeoutSeconds?: number; extra?: Record; }>, context: { request: Request; }): Promise; createPaymentRequiredResponse(requirements: PaymentRequirements[], resource: { url: string; method: string; description?: string; }, error?: string, extensions?: Record): Promise; findMatchingRequirements(requirements: PaymentRequirements[], payload: unknown): PaymentRequirements; verifyPayment(payload: unknown, requirements: PaymentRequirements): Promise; settlePayment(payload: unknown, requirements: PaymentRequirements, declaredExtensions?: Record, transportContext?: unknown, settlementOverrides?: { amount?: string; }): Promise; } type ProtocolType = 'x402' | 'mpp'; type AuthMode = 'paid' | 'siwx' | 'apiKey' | 'unprotected'; type RouteMethod = 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH'; interface RouteDefinition { /** Public API path segment without the `/api/` prefix (e.g. `flightaware/airports/id/flights/arrivals`). */ path: K; /** Internal route ID for pricing maps / analytics. Defaults to `path`. Disallowed under `strictRoutes` to prevent discovery drift. */ key?: string; /** Explicit HTTP method. Defaults to `POST`, or `GET` when `.query()` is used. */ method?: RouteMethod; } interface TierConfig { price: string; label?: string; } type PricingConfig = string | ((body: TBody) => string | Promise) | { field: string; tiers: Record; default?: string; }; type PayToConfig = string | ((request: Request, body?: unknown) => string | Promise); /** * CAIP-2 network identifier for x402 accepts: `eip155:` (EVM) or * `solana:`. Friendly names like `base` or `base-sepolia` are * rejected at router construction — use the exported constants * (`BASE_MAINNET_NETWORK` = `eip155:8453`, `SOLANA_MAINNET_NETWORK`) or a raw * CAIP-2 string. */ type X402Network = `eip155:${string}` | `solana:${string}`; interface X402AcceptBase { /** CAIP-2 chain identifier (e.g. `eip155:8453`, **not** `base`). See {@link X402Network}. */ network: X402Network; /** Token contract address (EVM) or mint (Solana). Defaults to USDC for the network. */ asset?: string; /** Token decimals. Defaults to USDC's 6. */ decimals?: number; /** Max payment-proof age the facilitator will accept, in seconds. */ maxTimeoutSeconds?: number; /** Extra fields passed through to the x402 PaymentRequirements `extra` block. */ extra?: Record; } interface X402AcceptConfig extends X402AcceptBase { /** `'exact'` for fixed-price one-shot payments; `'upto'` for settle-≤-cap (required for `.upTo()` routes). @default 'exact' */ scheme?: string; /** Per-accept payee override. Function form receives the request and parsed body for dynamic recipient routing. Falls back to `RouterConfig.payeeAddress`. */ payTo?: PayToConfig; } interface X402RouterFacilitatorConfig extends FacilitatorConfig { } /** A facilitator URL or a full `FacilitatorConfig` (URL + auth header builders). */ type X402FacilitatorTarget = string | X402RouterFacilitatorConfig; interface X402FacilitatorsConfig { /** Facilitator for Solana. Defaults to {@link DEFAULT_SOLANA_FACILITATOR_URL}. The EVM facilitator is hardcoded to Coinbase (CDP) — set `CDP_API_KEY_ID` / `CDP_API_KEY_SECRET`. */ solana?: X402FacilitatorTarget; } interface MppProtocolInfo { method?: string; intent?: string; currency?: string; settleBeforeHandler?: boolean; } type CheckoutSessionResponse = Record; interface CheckoutSessionContext { request: Request; route: string; body: TBody | undefined; /** Decimal-dollar price quoted for this 402 challenge. */ price: string; } type CheckoutSessionFn = (ctx: CheckoutSessionContext) => CheckoutSessionResponse | null | undefined | Promise; interface PaidOptions { protocols?: ProtocolType[]; maxPrice?: string; minPrice?: string; /** Override the payment recipient. String for static, function for body-derived (receives the Request). */ payTo?: PayToConfig; /** Override MPP protocol metadata in x-payment-info discovery. */ mpp?: MppProtocolInfo; /** Signal in discovery that clients should use an explicit checkout flow before payment. */ checkout?: boolean; /** * Build dynamic checkout review metadata for the router-owned 402 response body. * * The returned object is emitted as `{ "checkout_session": ... }` on the unpaid * payment challenge response. Payment terms remain authoritative in the x402/MPP * challenge headers. */ checkoutSession?: CheckoutSessionFn; } type PaidArg = (PaidOptions & { price: string; }) | (PaidOptions & { field: string; tiers: Record; default?: string; }); interface UpToOptions extends Omit { /** Cap on total billed amount; handler-accumulated `charge(amount)` calls cannot exceed this. */ maxPrice: string; /** Cosmetic unit label for 402 challenges / UIs. Does not affect billing. */ unitType?: string; } interface SessionOptions extends Omit { /** Cost per billable unit (positive decimal-dollar string) — the MPP session challenge's per-unit `amount`. On `.handler()` bills exactly this per request; on `.stream()` is the voucher-headroom granularity. */ unitCost: string; /** @deprecated Renamed to `unitCost`. */ tickCost?: never; /** Cap on total billed amount (streaming only — request-mode bills exactly `unitCost`). Router-enforced ceiling; MPP itself only bounds spend by voucher headroom and deposit. */ maxPrice: string; /** Unit being priced, advertised on 402 challenges as the MPP `unitType` (e.g. `'token'`, `'byte'`). Does not affect billing. */ unitType?: string; } /** @deprecated Use {@link SessionOptions} with `unitCost` via `.session()`. `tickCost` is the pre-`.session()` name for `unitCost` ("tick" is mppx SDK slang; the MPP spec prices sessions as `amount` per `unitType`). */ interface MeteredOptions extends Omit { /** @deprecated Renamed to `unitCost`. */ tickCost: string; unitCost?: never; /** Cap on total billed amount (streaming only — request-mode bills exactly `tickCost`). */ maxPrice: string; /** Cosmetic unit label for 402 challenges / UIs (e.g. `'token'`, `'byte'`). Does not affect billing. */ unitType?: string; } type PaymentStatus = 'verified' | 'settled'; interface HandlerPaymentContext { protocol: ProtocolType; status: PaymentStatus; payer: string; amount: string; network: string; recipient?: string; transaction?: string; receipt?: string; } interface SettlementLifecycleContext { route: string; request: Request; body: TBody; wallet: string; account: unknown; payment: HandlerPaymentContext; response: Response; result: unknown; } interface SettlementSettledContext extends Omit, 'payment'> { payment: HandlerPaymentContext & { status: 'settled'; }; } interface SettlementErrorContext extends SettlementLifecycleContext { error: unknown; phase: 'settle' | 'afterSettle'; } interface SettledHandlerErrorContext extends SettlementSettledContext { error: unknown; } type BeforeSettleDecision = 'continue' | 'skip'; interface SettlementLifecycle { /** After a successful handler response, before settlement: return `'skip'` to keep the 2xx body without charging, `'continue'`/void to settle; throw with `.status` to fail without settling (not when already settled at verify). Does not run for MPP session channel-only management (open/close/topUp). */ beforeSettle?: (ctx: SettlementLifecycleContext) => BeforeSettleDecision | void | Promise; /** Runs after successful settlement; for durable ledgers and audit rows. Errors are alerted but don't change the already-settled response. */ afterSettle?: (ctx: SettlementSettledContext) => void | Promise; /** Runs when payment was settled but the handler then returned an error response. Use for app-owned refund / compensation queues. */ onSettledHandlerError?: (ctx: SettledHandlerErrorContext) => void | Promise; /** Runs when router-controlled settlement fails after the handler succeeded. */ onSettlementError?: (ctx: SettlementErrorContext) => void | Promise; } type ChargeFn = () => Promise; type UptoChargeFn = (amount: string) => Promise; interface HandlerContext { body: TBody; query: TQuery; /** Path-template params extracted from the route's own `{param}` segments (e.g. `drafts/{draftId}/commit` → `{ draftId }`). Empty object when the path declares no params. */ params: Record; request: Request; requestId: string; route: string; wallet: string | null; /** Optional DID from `X-Agent-Identity` proof. Null when the client omits identity. */ actor: string | null; payment: HandlerPaymentContext | null; account: unknown; alert: AlertFn; setVerifiedWallet: (addr: string) => void; } /** Handler context for streaming `.session()` handlers (async generators). Call `charge()` once per billable unit. */ interface StreamingHandlerContext extends HandlerContext { charge: ChargeFn; } /** Handler context for `.upTo()` routes (x402-only). Call `charge(amount)` one or more times; the request settles for the accumulated total capped at `maxPrice`. */ interface UptoHandlerContext extends HandlerContext { charge: UptoChargeFn; } type OveragePolicy = 'same-rate' | 'increased-rate' | 'hard-stop'; type QuotaLevel = 'healthy' | 'warn' | 'critical'; interface QuotaInfo { remaining: number | null; limit: number | null; spend?: number; } interface ProviderConfig { extractQuota?: (result: unknown, headers: Headers) => QuotaInfo | null; monitor?: () => Promise; overage?: OveragePolicy; warn?: number; critical?: number; } interface ProviderQuotaEvent { provider: string; route: string; remaining: number | null; limit: number | null; spend?: number; level: QuotaLevel; overage: OveragePolicy; message: string; } interface RouteEntry { key: string; authMode: AuthMode; /** * Enables SIWX acceleration on paid routes. * When true, valid SIWX proofs can bypass repeat payment if entitlement exists. */ siwxEnabled?: boolean; pricing?: PricingConfig; /** `'exact'` settles a fixed price once; `'upto'` (x402-only) settles the handler-accumulated `charge(amount)` total capped at `maxPrice`; `'metered'` (MPP-only, set by `.session()`) bills per unit (`tickCost`). */ billing: 'exact' | 'upto' | 'metered'; /** True iff handler is an async generator. Streaming handlers settle per-unit over SSE; non-streaming session handlers bill exactly `tickCost` per request. Set by the builder at `.handler(fn)` time. */ streaming?: boolean; protocols: ProtocolType[]; bodySchema?: ZodType; querySchema?: ZodType; outputSchema?: ZodType; /** Optional conforming example for the request input (body or query). Validated against the schema at registration. Emitted in the bazaar discovery extension. */ inputExample?: JsonObject; /** Optional conforming example for the response output (any JSON value). Validated against `outputSchema` at registration. Without it, the bazaar `output` block is omitted (schema alone can't be exposed). */ outputExample?: JsonValue; description?: string; path?: string; method: RouteMethod; maxPrice?: string; minPrice?: string; payTo?: PayToConfig; apiKeyResolver?: (key: string) => unknown | Promise; providerName?: string; providerConfig?: ProviderConfig; validateFn?: (body: unknown) => void | Promise; settlement?: SettlementLifecycle; mppInfo?: MppProtocolInfo; hasCheckout?: boolean; checkoutSession?: CheckoutSessionFn; /** Per-unit cost (decimal-dollar), set from `.session()`'s `unitCost` (or the deprecated `tickCost` alias). Required when billing is `'metered'`. */ tickCost?: string; /** Cosmetic unit label for 402 challenges and client UIs. */ unitType?: string; } interface DiscoveryConfig { title: string; version: string; description?: string; /** Bazaar catalog display name on x402 challenges (`PaymentRequired.resource.serviceName`, ≤32 printable-ASCII chars). Defaults to `title` when the title fits the constraint. */ serviceName?: string; /** Bazaar catalog tags on x402 challenges (≤5 entries, each ≤32 printable-ASCII chars). */ tags?: string[]; /** Bazaar catalog icon on x402 challenges (HTTPS URL, ≤2048 chars). */ iconUrl?: string; contact?: { name?: string; url?: string; email?: string; }; ownershipProofs?: string[]; methodHints?: 'off' | 'non-default' | 'always'; /** Natural language guidance for agents. Served as wellknown `instructions` and `/llms.txt`. */ guidance?: string | (() => string | Promise); /** Override the OpenAPI `servers` URL. Defaults to `RouterConfig.baseUrl`. Use when the public API hostname differs from the payment realm URL. */ serverUrl?: string; } /** Sponsor fee-budget ceilings for fee-sponsored Tempo transactions. Structural mirror of mppx's `FeePayer.Policy`, all fields optional. */ interface MppFeePayerPolicy { maxGas?: bigint; maxFeePerGas?: bigint; maxPriorityFeePerGas?: bigint; maxTotalFee?: bigint; maxValidityWindowSeconds?: number; } /** Server-owned automatic settlement cadence for MPP session channels. Structural mirror of mppx's `SettlementSchedule`. */ interface MppSettlementSchedule { /** Settle after this many additional paid units since the previous settlement. */ units?: number; /** Settle after this much additional billed amount (decimal-dollar string) since the previous settlement. */ amount?: string; /** Settle after this many milliseconds since the previous settlement. */ intervalMs?: number; } interface RouterConfig { /** Default payee for paid routes — populates `payTo` on the auto-generated x402 `exact` accept and acts as the MPP `recipient` fallback. Override per-protocol via `x402.accepts[i].payTo` / `mpp.recipient`, or per-route via the `payTo` option on `.paid()` / `.upTo()` / `.session()`. */ payeeAddress?: string; /** Origin URL (required). Used as 402 realm, discovery base, OpenAPI server, and MPP memo prefix — must match the public domain or payment matching breaks. */ baseUrl: string; /** URL prefix routes are mounted and advertised under (`{baseUrl}/{basePath}/{path}`). Pass an empty string to mount routes at the origin root. @default 'api' */ basePath?: string; /** Default chain for the auto-generated x402 `exact` accept, as a CAIP-2 identifier — friendly names like `base` are rejected; import `BASE_MAINNET_NETWORK` instead of hand-writing the string. Ignored when `x402.accepts` is set. @default BASE_MAINNET_NETWORK (`eip155:8453`) */ network?: X402Network; /** x402 protocol settings. Omit to default to a single `exact`/USDC accept on `network` paid to `payeeAddress`, verified via the Coinbase default facilitator (requires `CDP_API_KEY_ID`/`CDP_API_KEY_SECRET`). */ x402?: { /** Explicit accepts list (scheme + network + asset). Overrides the auto-generated default. Add an `upto` accept here to enable `.upTo()` routes. */ accepts?: X402AcceptConfig[]; /** Per-chain facilitator overrides (`evm`/`solana`). Defaults to the Coinbase facilitator on EVM; set `solana` to accept Solana payments. */ facilitators?: X402FacilitatorsConfig; /** * Base Builder Code for ERC-8021 on-chain attribution (register at * https://dashboard.base.org → Settings → Builder Codes; must match * `^[a-z0-9_]{1,32}$`). Declared as the app code (`a`) on every x402 * payment challenge; the facilitator appends it to settlement calldata, * attributing every settled payment to this service on-chain. */ builderCode?: string; }; /** Observability hook receiving request/auth/payment/settlement events. Implement `RouterPlugin` for structured logs/analytics. */ plugin?: RouterPlugin; /** Single KV cache for SIWX nonce, SIWX entitlement, and MPP tx-hash replay (prefixed `siwx:nonce:`, `siwx:ent:`, `mpp:`). Pass `{ url, token }` for an Upstash-compatible REST endpoint (Upstash, Vercel KV), or a custom `KvStore` implementation. Omitted: auto-bootstraps from `KV_REST_API_URL` + `KV_REST_API_TOKEN`; falls back to in-memory when missing (unsafe in serverless). */ kvStore?: KvStore | { url: string; token: string; }; /** * Centralized price map keyed by route ID. `.route(key)` auto-applies * `.paid(prices[key])` when `key` is listed; per-route `.paid()` still works * for keys not in the map. * * @deprecated Price routes inline with `.paid()` — keep a central const in * your service if you want one file of prices. To catch forgotten barrel * imports (the map's validation side effect), add a consumer-side test that * globs your route files and asserts `router.registry.has(key)`, or serve * routes through the catch-all adapter where a missing import 404s in dev. * Auto-priced routes can't take pricing options, and this map is the only * reason `createRouter` is generic — it will be removed in the next major. */ prices?: Record; /** MPP (Tempo) payment-channel config. Required when `protocols` includes `'mpp'`. */ mpp?: { /** HMAC key for signing/verifying MPP challenge nonces. Persist across deploys — rotating invalidates outstanding 402 challenges. Falls back to `MPP_SECRET_KEY`. */ secretKey: string; /** Tempo currency contract address (0x-prefixed). Use `TEMPO_USDC_ADDRESS` for USDC on Tempo. */ currency: string; /** MPP payee address (EVM). Overrides `payeeAddress` for MPP only. Required when `payeeAddress` is unset. MUST equal `operatorKey`'s derived address when `session` is enabled. */ recipient?: string; /** Tempo RPC URL for on-chain verification. Falls back to `TEMPO_RPC_URL`, then to the public `DEFAULT_TEMPO_RPC_URL`. */ rpcUrl?: string; /** Hex private key. Signs channel close/settle; required for `session`. Address MUST equal `recipient`/payee — mppx asserts sender===payee on settle. Validated at init. */ operatorKey?: string; /** Hex private key. Sponsors gas for client channel open/topUp. MUST resolve to a different address than `operatorKey` — Tempo rejects sender===feePayer. Validated at init. Omit to make clients pay their own gas. The account must hold the Tempo fee token (pathUSD) to pay sponsored gas. */ feePayerKey?: string; /** Partial override of mppx's sponsor fee-budget ceilings for fee-sponsored Tempo transactions (charge co-signs and session open/topUp/close). Raise `maxTotalFee` alongside `maxGas`/`maxFeePerGas`. Omit for mppx's per-chain defaults. */ feePayerPolicy?: MppFeePayerPolicy; /** Enables MPP payment-channel sessions for `.session()` routes (registers both request and SSE session middleware). Also requires `mpp.operatorKey`. */ session?: { /** Suggested deposit on the 402 challenge = `unitCost × depositMultiplier` USDC. Route `maxPrice` overrides. @default 10 */ depositMultiplier?: number; /** Server-owned automatic settlement cadence for session channels. Omitted: channels settle only on client close. Thresholds compose (whichever trips first). */ settlementSchedule?: MppSettlementSchedule; }; }; /** Payment protocols to accept on paid routes unless overridden per route. @default ['x402'] */ protocols?: ProtocolType[]; /** When true, `.route('key')` is rejected (use `.route({ path })`) and custom `key !== path` is rejected. Prevents discovery/openapi drift. */ strictRoutes?: boolean; /** Static metadata for auto-generated discovery surfaces — `/openapi.json` (`.openapi()`) and `/llms.txt` (`.llmsTxt()`). Also feeds the deprecated `.wellKnown()` handler. */ discovery: DiscoveryConfig; } /** The composed request handler produced by `.handler()` / `.stream()` — the same function returned to per-file Next.js routes. */ type RegisteredHandler = (request: Request) => Promise; declare class RouteRegistry { private routes; private handlers; /** * Invoked on every registration. `createRouter` uses this to mount the route * on the internal Hono app, deduping by path so each distinct path is mounted * once. Firing on every register (not just the first key+method) means a * re-registration that changes the path also mounts the new path — both URLs * then dispatch to the latest handler (last write wins), instead of the new * URL 404ing. */ onRegister?: (entry: RouteEntry) => void; private mapKey; /** * Record a route entry (and, when provided, its composed request handler — * required for `router.fetch()` dispatch; discovery-only stubs may omit it). * Re-registering a key+method overwrites both, so the last write wins. */ register(entry: RouteEntry, handler?: RegisteredHandler): void; /** * Request-time dispatcher preserving last-write-wins semantics: the handler * is looked up on every call, so re-registering a key+method routes new * requests to the newest handler. */ dispatch(key: string, method: string): RegisteredHandler; get(key: string): RouteEntry | undefined; entries(): IterableIterator<[string, RouteEntry]>; has(key: string): boolean; get size(): number; validate(expectedKeys?: string[]): void; } type NetworkFamily = 'evm' | 'solana'; interface ResolvedX402Facilitator { family: NetworkFamily; network: Network; url?: string; config: X402RouterFacilitatorConfig; } /** Service metadata forwarded into `PaymentRequired.resource` on every x402 challenge. */ interface X402ResourceMetadata { serviceName?: string; tags?: string[]; iconUrl?: string; } type MppxMiddlewareResponse = { status: 402; challenge: Transport.ChallengeOutputOf; } | { status: 200; withReceipt: Transport.WithReceipt; }; type MppxMiddleware = (options: TOptions) => (input: Request) => Promise>; interface RouterDeps { x402Server: X402Server | null; initPromise: Promise; x402InitError?: string; mppInitError?: string; plugin?: RouterPlugin; nonceStore: NonceStore; agentIdentityNonceStore: NonceStoreInterface; entitlementStore: EntitlementStore; payeeAddress: string; mppRecipient?: string; network: string; x402FacilitatorsByNetwork?: Record; x402Accepts: X402AcceptConfig[]; /** Base Builder Code declared as the ERC-8021 app code (`a`) on every x402 challenge. */ builderCode?: string; /** Bazaar service metadata (`serviceName`/`tags`/`iconUrl`) merged into `PaymentRequired.resource`. */ x402ResourceMetadata?: X402ResourceMetadata; kvStore?: KvStore; mppx?: { charge: MppxMiddleware<{ amount: string; }, Transport.Http>; sessionRequest?: MppxMiddleware<{ amount: string; unitType?: string; suggestedDeposit?: string; }, Transport.Http>; sessionStream?: MppxMiddleware<{ amount: string; unitType?: string; suggestedDeposit?: string; }, Transport.Sse>; } | null; mppSessionConfig?: { depositMultiplier: number; } | null; tempoClient?: viem.Client | null; } type True = true; type False = false; declare const ROUTE_ERROR: unique symbol; interface RouteError { readonly [ROUTE_ERROR]: M; } type InputTypeFor = [TBody] extends [undefined] ? [TQuery] extends [undefined] ? never : TQuery : TBody; type RequestHandlerFn = (ctx: HandlerContext) => Promise; type UptoHandlerFn = (ctx: UptoHandlerContext) => Promise; type StreamingHandlerFn = (ctx: StreamingHandlerContext) => AsyncIterable; /** * Pricing-mode discriminator threaded through the builder. `'none'` until a * pricing method is chained; then `'exact'` (`.paid()`), `'upto'`, or * `'metered'` (`.session()` / deprecated `.metered()`). Gates repeat pricing * calls and picks the handler shape. */ type BillingMode = 'none' | 'exact' | 'upto' | 'metered'; /** * Identity-mode discriminator threaded through the builder. `'none'` until an * identity method is chained; then `'siwx'`, `'apiKey'`, or `'open'` * (`.unprotected()`). Gates the mutually-exclusive combinations at compile * time, mirroring the registration-time throws. */ type IdentMode = 'none' | 'siwx' | 'apiKey' | 'open'; /** Resolves to `TSelf` when a pricing method may be chained, else a `RouteError`. Mirrors the runtime guards in `applyPaid`. */ type PricingGate = Ident extends 'open' ? RouteError<`Cannot combine .unprotected() and .${M}() on the same route`> : Bill extends 'none' ? TSelf : RouteError<'Cannot combine .paid(), .upTo(), and .session() — pick one pricing mode'>; type HandlerArg = [Ident, Bill] extends ['none', 'none'] ? RouteError<'Pick an auth mode first: .paid(...), .upTo(...), .session(...), .siwx(), .apiKey(...), or .unprotected()'> : [NeedsBody, HasBody] extends [true, false] ? RouteError<'Call .body(schema) — body-derived/tiered pricing reads the parsed body'> : Bill extends 'upto' ? UptoHandlerFn : RequestHandlerFn; type StreamArg = [Ident, Bill] extends ['none', 'none'] ? RouteError<'Pick an auth mode first: .session({ ... }) — streaming requires session pricing'> : Bill extends 'metered' ? [NeedsBody, HasBody] extends [true, false] ? RouteError<'Call .body(schema) — session pricing reads the parsed body'> : StreamingHandlerFn : Bill extends 'upto' ? RouteError<'Streaming is not supported on .upTo() — use .session() on MPP for per-yield billing'> : RouteError<'Streaming requires .session({ unitCost, maxPrice }) — static/free routes cannot meter per-chunk billing'>; interface RouteBuilderDefaults { protocols?: ProtocolType[]; } declare class RouteBuilder { #private; constructor(key: string, registry: RouteRegistry, deps: RouterDeps, defaults?: RouteBuilderDefaults); private fork; /** * Fixed-price string sugar: `paid('0.01')` charges 0.01 USDC per request. * * @example * ```ts * router.route('search').paid('0.01').handler(handler); * ``` */ paid(this: PricingGate, Ident, Bill, 'paid'>, price: string, options?: PaidOptions): RouteBuilder; /** * Compute the price from the parsed body before issuing the 402 challenge. * Throw an `HttpError` from the pricing function to reject the request * before payment is requested. Requires `.body(schema)`. * * @example * ```ts * router.route('llm') * .paid((body) => `${body.tokens * 0.0001}`, { maxPrice: '5.00' }) * .body(schema) * .handler(handler); * ``` */ paid(this: PricingGate, Ident, Bill, 'paid'>, fn: (body: TBodyIn) => string | Promise, options?: PaidOptions): RouteBuilder; /** * Options-object form of fixed or body-derived pricing. Pass exactly one of: * * - `{ price }` — fixed price (object form of the string sugar). * - `{ field, tiers, default? }` — pick a tier from `body[field]`. * * Common knobs (`protocols`, `maxPrice`, `minPrice`, `payTo`, `mpp`, `checkout`, * `checkoutSession`) live alongside the pricing shape. Set `mpp.settleBeforeHandler` * on slow upstream routes so MPP transaction (pull) credentials broadcast at verify. * For auto-priced routes from `RouterConfig.prices`, chain `.mpp({ settleBeforeHandler: true })`. * * @example * ```ts * router.route('upload') * .paid({ field: 'size', tiers: { sm: { price: '0.01' }, lg: { price: '0.10' } } }) * .body(schema).handler(handler); * ``` */ paid(this: PricingGate, Ident, Bill, 'paid'>, arg: T): RouteBuilder; } ? True : False, HasBody, 'exact'>; /** * x402-only handler-computed billing. The handler receives `charge(amount)` * and the request settles once for the accumulated total, capped at * `maxPrice`. Requires an `'upto'` accept on at least one configured network. * Pass a bare string as sugar for `{ maxPrice }`. * * @example * ```ts * router.route('llm') * .upTo('0.05') * .body(schema) * .handler(async ({ body, charge }) => { await charge('0.001'); ... }); * ``` */ upTo(this: PricingGate, Ident, Bill, 'upTo'>, arg: string | UpToOptions): RouteBuilder; /** * MPP-only per-unit billing over a payment channel (the MPP `session` * intent). `.handler()` bills exactly `unitCost` per request; `.stream()` * calls `charge()` (no-arg) per yield, settling per unit up to `maxPrice`. * Requires `RouterConfig.mpp.session`. * * @example * ```ts * router.route('llm/stream') * .session({ unitCost: '0.0001', maxPrice: '0.05', unitType: 'token' }) * .stream(async function* ({ charge }) { await charge(); yield 'hi'; }); * ``` */ session(this: Ident extends 'siwx' ? RouteError<'Cannot combine .siwx() and .session() — per-unit MPP billing has no entitlement model'> : PricingGate, Ident, Bill, 'session'>, options: SessionOptions | MeteredOptions): RouteBuilder; /** * @deprecated Renamed to {@link session} — the MPP intent is named `session` * (and `tickCost` is now `unitCost`). This alias behaves identically and * will be removed in a future release. */ metered(this: Ident extends 'siwx' ? RouteError<'Cannot combine .siwx() and .session() — per-unit MPP billing has no entitlement model'> : PricingGate, Ident, Bill, 'metered'>, options: MeteredOptions | SessionOptions): RouteBuilder; private applyPaid; /** * Require Sign-In-with-X wallet identity on this route — clients prove * control of a wallet via a signed challenge. Composes with `.paid()` and * `.upTo()` for pay-once-then-replay: the first request settles normally, * subsequent requests with a valid SIWX signature for the same wallet skip * payment (on `.upTo()`, `charge(amount)` becomes a no-op on the replay). * Mutually exclusive with `.session()`. * * @example * ```ts * router.route('profile').siwx().handler(async ({ wallet }) => getProfile(wallet)); * router.route('inbox').paid('0.01').siwx().handler(async ({ wallet }) => getInbox(wallet)); * ``` */ siwx(this: Ident extends 'open' ? RouteError<'Cannot combine .unprotected() and .siwx() on the same route'> : Ident extends 'apiKey' ? RouteError<'Combining .siwx() and .apiKey() is not supported on the same route'> : Bill extends 'metered' ? RouteError<'Cannot combine .session() and .siwx() — per-unit MPP billing has no entitlement model'> : RouteBuilder): RouteBuilder; /** * Require an `X-API-Key` header (or `Authorization: Bearer `); the * resolver returns the account record, or `null` for 401. Composes with * `.paid()` — key is checked first, payment second. * * @example * ```ts * router * .route('admin/users') * .apiKey(async (key) => db.admin.findByKey(key)) * .handler(async ({ account }) => db.user.list(account.orgId)); * ``` */ apiKey(this: Ident extends 'open' ? RouteError<'Cannot combine .unprotected() and .apiKey() on the same route'> : Ident extends 'siwx' ? RouteError<'Combining .apiKey() and .siwx() is not supported on the same route'> : RouteBuilder, resolver: (key: string) => unknown | Promise): RouteBuilder; /** * Mark the route as public — no auth, no payment, no SIWX. The handler * receives `null` for `wallet`, `payment`, and `account`. * * @example * ```ts * router.route('health').unprotected().handler(async () => ({ status: 'ok' })); * ``` */ unprotected(this: Bill extends 'none' ? Ident extends 'siwx' ? RouteError<'Cannot combine .unprotected() and .siwx() on the same route'> : Ident extends 'apiKey' ? RouteError<'Cannot combine .unprotected() and .apiKey() on the same route'> : RouteBuilder : RouteError<'Cannot combine .unprotected() and a pricing mode on the same route'>): RouteBuilder; /** * Tag the route with an upstream provider for discovery and provider-side * monitoring. The provider name and config surface in OpenAPI discovery * output. * * @example * ```ts * router * .route('search') * .paid('0.01') * .provider('exa', { quotaPerMonth: 1000 }) * .handler(handler); * ``` */ provider(name: string, config?: ProviderConfig): this; /** * Declare the request body's Zod schema. Parsed body is typed as `ctx.body` * in the handler. Use `.inputExample()` to attach a discovery example. * * @example * ```ts * .body(z.object({ query: z.string() })) * .handler(async ({ body }) => search(body.query)); * ``` */ body(schema: S): RouteBuilder, TQuery, TOutput, Ident, NeedsBody, True, Bill>; /** * Declare a query-string Zod schema and switch the route to `GET`. Parsed * query is typed as `ctx.query` in the handler. Use `.inputExample()` to * attach a discovery example. * * @example * ```ts * .query(z.object({ id: z.string() })) * .handler(async ({ query }) => getById(query.id)); * ``` */ query(schema: S): RouteBuilder, TOutput, Ident, NeedsBody, HasBody, Bill>; /** * Declare the response output's Zod schema for OpenAPI generation. The * runtime does not validate handler return values — use Zod's `.parse()` * inside the handler if strict output validation is required. Use * `.outputExample()` to attach a discovery example. * * @example * ```ts * .output(z.object({ result: z.string() })) * .handler(async () => ({ result: 'ok' })); * ``` */ output(schema: S): RouteBuilder, Ident, NeedsBody, HasBody, Bill>; /** * Attach an example of the request body or query for discovery output, * validated against the registered schema at registration. * * @example * ```ts * .body(searchSchema).inputExample({ query: 'cats' }); * ``` */ inputExample(example: InputTypeFor & JsonObject): RouteBuilder; /** * Attach an example response for discovery output, validated against the * registered output schema at registration. * * @example * ```ts * .output(resultSchema).outputExample({ result: 'ok' }); * ``` */ outputExample(example: TOutput & JsonValue): RouteBuilder; /** * Set a human-readable summary of the route. Surfaces in OpenAPI and * `llms.txt` discovery output. * * @example * ```ts * .description('Search indexed web pages by full-text query'); * ``` */ description(text: string): this; /** * Override the URL path the route is mounted and advertised under. Defaults * to the registry key passed to `.route()`. Normalized like `.route()` paths * (leading slashes and an `api/` prefix stripped) so both produce the same * mounted URL. * * @example * ```ts * router.route('search').path('/v2/search').handler(handler); * ``` */ path(p: string): this; /** * Override the HTTP method advertised in discovery. Defaults to `POST`, or * `GET` when `.query()` has been called. * * @example * ```ts * router.route('items/delete').method('DELETE').handler(handler); * ``` */ method(m: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH'): this; /** * Run validation against the parsed body before the 402 challenge. Throw * `Object.assign(new Error('...'), { status })` to reject with a custom * status code; defaults to 400. Requires `.body()` to be called first. * * @example * ```ts * .body(RegisterSchema).validate(async (body) => { * if (await isTaken(body.name)) { * throw Object.assign(new Error('taken'), { status: 409 }); * } * }); * ``` */ validate(fn: (body: TBody) => void | Promise): RouteBuilder; /** * Override MPP protocol metadata and per-route MPP settlement options. * Merges with any `mpp` options passed to `.paid()`. Use on auto-priced routes * (from `RouterConfig.prices`) when you cannot call `.paid()` again. * * @example * ```ts * router.route('exa/answer') * .mpp({ settleBeforeHandler: true }) * .body(schema) * .handler(handler); * ``` */ mpp(info: MppProtocolInfo): RouteBuilder; /** * Hook into the settlement lifecycle. `beforeSettle` runs after the handler * succeeds but before on-chain settlement and can cancel the charge; * `afterSettle` runs after settlement completes (success or failure). * * @example * ```ts * .settlement({ * beforeSettle: ({ result }) => (result.refund ? 'skip' : 'continue'), * afterSettle: ({ tx }) => analytics.track('settled', { tx }), * }); * ``` */ settlement(lifecycle: SettlementLifecycle): RouteBuilder; /** * Register the request handler and return the Next.js route function. The * handler receives a typed context and may return a value (serialized to * JSON), a raw `Response`, or throw an `HttpError` for a non-2xx status. * * @example * ```ts * export const POST = router * .route('search') * .paid('0.01') * .body(schema) * .handler(async ({ body, wallet }) => searchService(body, wallet)); * ``` */ handler(fn: HandlerArg): (request: Request) => Promise; /** * Register a streaming handler (`async function*`) and return the Next.js * route function — the SSE transport of an MPP session. Each `charge()` * call bills one unit (`unitCost` USDC) up to `maxPrice`; requires * `.session({ ... })` and MPP session mode. * * @example * ```ts * export const POST = router * .route('llm/stream') * .session({ unitCost: '0.0001', maxPrice: '0.05', unitType: 'token' }) * .body(schema) * .stream(async function* ({ body, charge }) { * for await (const token of streamLLM(body.prompt)) { * await charge(); * yield token; * } * }); * ``` */ stream(fn: StreamArg): (request: Request) => Promise; private register; } type RouterConfigIssueCode = 'missing_base_url' | 'invalid_base_url' | 'empty_protocols' | 'missing_x402_accepts' | 'missing_x402_network' | 'unsupported_x402_network' | 'missing_x402_asset' | 'invalid_x402_decimals' | 'missing_x402_payee' | 'invalid_x402_payee' | 'invalid_solana_payee' | 'invalid_solana_facilitator_url' | 'missing_cdp_keys' | 'missing_payment_credentials' | 'placeholder_payee' | 'missing_mpp_config' | 'missing_mpp_secret_key' | 'missing_mpp_currency' | 'invalid_mpp_currency' | 'missing_mpp_recipient' | 'invalid_mpp_recipient' | 'invalid_mpp_rpc_url' | 'invalid_mpp_fee_payer_key' | 'invalid_mpp_operator_key' | 'mpp_operator_equals_fee_payer' | 'mpp_operator_recipient_mismatch' | 'missing_discovery_title' | 'missing_discovery_description' | 'missing_discovery_guidance' | 'invalid_builder_code' | 'invalid_discovery_service_name' | 'invalid_discovery_tags' | 'invalid_discovery_icon_url' | 'invalid_server_url' | 'kv_url_without_token' | 'kv_token_without_url' | 'invalid_kv_url' | 'missing_kv_in_production'; type RouterConfigIssueSeverity = 'error' | 'warning'; interface RouterConfigIssue { code: RouterConfigIssueCode; message: string; protocol?: ProtocolType; /** @default 'error' — warnings are surfaced via `console.warn` and do not throw. */ severity?: RouterConfigIssueSeverity; } /** Options for {@link createRouterFromEnv} / {@link routerConfigFromEnv}. */ interface CreateRouterFromEnvOptions = Record> { /** Defaults to `process.env`. Pass an explicit object in tests. */ env?: Record; /** Discovery title. Shown in `/openapi.json` and `/llms.txt`. */ title: string; /** Discovery description. */ description: string; /** Long-form usage guidance for agent consumers. Served at `/llms.txt`. Pass an empty string to opt out. */ guidance: string; /** Discovery version. @default '1.0.0' */ version?: string; /** Optional contact metadata published in discovery. */ contact?: DiscoveryConfig['contact']; /** Optional ownership proofs published in `/openapi.json` (and the deprecated `/.well-known/x402`). */ ownershipProofs?: string[]; /** Per-route HTTP method hint visibility. */ methodHints?: DiscoveryConfig['methodHints']; /** Override the OpenAPI `servers[].url`. Defaults to `BASE_URL`. */ serverUrl?: string; /** Bazaar catalog display name on x402 challenges (≤32 printable-ASCII chars). Defaults to `title` when the title fits. */ serviceName?: string; /** Bazaar catalog tags on x402 challenges (≤5 entries, each ≤32 printable-ASCII chars). */ tags?: string[]; /** Bazaar catalog icon on x402 challenges (HTTPS URL, ≤2048 chars). */ iconUrl?: string; /** * Centralized price map keyed by route id. `route(key)` auto-applies * `.paid(prices[key])` for matching keys. * * @deprecated Price routes inline with `.paid()` — keep a central const in * your service if you want one file of prices. Will be removed in the next * major. */ prices?: TPrices; /** Observability plugin. */ plugin?: RouterPlugin; /** Custom KV store. When omitted, the router auto-bootstraps from `KV_REST_API_URL` + `KV_REST_API_TOKEN`. */ kvStore?: KvStore; /** Override x402 facilitators. The Solana facilitator defaults to `SOLANA_FACILITATOR_URL` env or `DEFAULT_SOLANA_FACILITATOR_URL`. */ x402Facilitators?: X402FacilitatorsConfig; /** Explicit protocol list. Default: inferred from credentials — `'x402'` when `CDP_API_KEY_ID`/`CDP_API_KEY_SECRET` are set, `'mpp'` when `MPP_SECRET_KEY` is set. At least one must be configured. */ protocols?: readonly ProtocolType[]; /** Require `route({ path })` form for every route. @default false */ strictRoutes?: boolean; } declare class RouterConfigError extends Error { readonly issues: RouterConfigIssue[]; constructor(issues: RouterConfigIssue[]); } /** * Build a {@link RouterConfig} from environment variables. * * `envShape` in this file is the single source of truth for env vars; README * and `.env.example` are drift-tested against `ENV_KEYS`. Validates every * required value up front and throws a single {@link RouterConfigError} * containing all issues at once. Soft warnings (e.g. half-configured KV) are * emitted via `console.warn`. */ declare function routerConfigFromEnv = Record>(options: CreateRouterFromEnvOptions): RouterConfig & { prices?: TPrices; }; /** Base mainnet CAIP-2 network ID (`eip155:8453`). */ declare const BASE_MAINNET_NETWORK = "eip155:8453"; /** Solana mainnet CAIP-2 network ID. */ declare const SOLANA_MAINNET_NETWORK = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"; /** USDC contract address on Tempo (the `mpp.currency` value for Tempo USDC). */ declare const TEMPO_USDC_ADDRESS = "0x20c000000000000000000000b9537d11c60e8b50"; /** USDC has 6 decimals on Tempo. */ declare const TEMPO_USDC_DECIMALS = 6; /** USDC contract on Base mainnet. */ declare const BASE_USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"; /** USDC has 6 decimals on Base. */ declare const BASE_USDC_DECIMALS = 6; /** All-zeros EVM address. Used as a placeholder/sentinel; not a valid payee. */ declare const ZERO_EVM_ADDRESS = "0x0000000000000000000000000000000000000000"; /** Public Solana x402 facilitator. Override per-deployment via `SOLANA_FACILITATOR_URL`. */ declare const DEFAULT_SOLANA_FACILITATOR_URL = "https://facilitator.payai.network"; /** Public Tempo JSON-RPC endpoint used for MPP on-chain verification. Override per-deployment via `TEMPO_RPC_URL`. */ declare const DEFAULT_TEMPO_RPC_URL = "https://rpc.tempo.xyz"; interface MonitorEntry { provider: string; route: string; monitor: () => Promise; overage: OveragePolicy; warn?: number; critical?: number; } interface ServiceRouter { route(keyOrDefinition: K | RouteDefinition): [K] extends [TPriceKeys] ? RouteBuilder : RouteBuilder; /** * @deprecated The `/.well-known/x402` surface is no longer a recommended * discovery location. The handler keeps working for legacy x402 clients, * but new integrations should mount `.openapi()` (at `/openapi.json`) and * `.llmsTxt()` (at `/llms.txt`) instead. */ wellKnown(): (request: Request) => Promise; /** OpenAPI 3.1 discovery document. Mount at `GET /openapi.json`. */ openapi(): (request: Request) => Promise; /** Plain-text agent guidance. Mount at `GET /llms.txt`. */ llmsTxt(): (request: Request) => Promise; /** JSON 404 fallback with rediscovery links. Mount in a catch-all route. */ notFound(): (request: Request) => Promise; monitors(): MonitorEntry[]; registry: RouteRegistry; /** * Standard fetch handler serving all registered routes at * `/{basePath}/{path}` plus the discovery surfaces (`/.well-known/x402`, * `/openapi.json`, `/llms.txt` — at the root and under the basePath). * Unmatched requests get the `.notFound()` JSON envelope. This is the entry * point for the Next.js catch-all adapter (`@agentcash/router/next`) and any * fetch runtime (Bun, Deno, Node ≥18 via `serve`-style adapters). */ fetch(request: Request): Promise; /** The internal Hono app, for mounting into a larger app: `app.route('/', router.hono())`. */ hono(): Hono; } /** * Inference is deliberately scoped to the `prices` map. A `const C extends * RouterConfig` generic over the whole config captures the entire literal * (guidance strings, accepts tuples, plugin closures) in the router's exported * type, and every route file that touches the router re-resolves that literal — * on large configs that lands at TypeScript's instantiation-depth limit * ("Type instantiation is excessively deep"), tripping check-order-dependently * in consumer builds. Only the `prices` keys are used at the type level. * * The `string extends keyof P` guard maps a non-literal `prices` type (a config * annotated as plain `RouterConfig`, or a map built at runtime) to `never`, so * routes stay unpriced at the type level. Caveat: the runtime still * auto-applies `.paid(prices[key])` for keys actually present in the map, so a * runtime-built map leaves `.route(key).handler(...)` untypeable while * chaining `.paid()` would throw at registration ("Cannot combine"). The fix * is a literal map — or dropping the deprecated map and pricing inline. */ type PriceKeysOf

= [P] extends [Record] ? string extends keyof P ? never : Extract : never; /** * Build a {@link ServiceRouter} from a fully-specified {@link RouterConfig}. * Most consumers should use {@link createRouterFromEnv}; use this when you * need settings env doesn't expose (custom networks, multi-payee, plugins). * * With `protocols: ['x402']` and EVM accepts, `CDP_API_KEY_ID` / * `CDP_API_KEY_SECRET` must be present in env — but only presence is checked, * never validity. Placeholder values boot fine for local dev: paid routes * still serve correct 402 challenges via the hardcoded facilitator baseline * (a `[x402] facilitator /supported failed` warning is logged); real keys are * only needed for payment verification and settlement. */ declare function createRouter

| undefined = undefined>(config: RouterConfig & { prices?: P; }): ServiceRouter>; /** * Build a {@link ServiceRouter} from environment variables. * * Validates every required env var up front and throws a single * {@link RouterConfigError} containing all problems at once. Most consumers * should use this entry point. Use {@link createRouter} when you need to * construct a {@link RouterConfig} programmatically. * * The env vars this function reads are the canonical schema in * `src/config/schema.ts` (`ENV_SPEC`). * * x402 is enabled by the *presence* of `CDP_API_KEY_ID` / `CDP_API_KEY_SECRET` * — the keys are never validated against Coinbase at boot. Placeholder values * are a supported local-dev path: paid routes serve correct 402 challenges via * the hardcoded facilitator baseline; real keys are only needed for payment * verification and settlement. * * @example * ```ts * export const router = createRouterFromEnv({ * title: 'My API', * description: 'Pay-per-call search.', * guidance: 'POST /search with { q: string }. Returns top 10 results.', * }); * ``` */ declare function createRouterFromEnv = Record>(options: CreateRouterFromEnvOptions): ServiceRouter>; export { BASE_MAINNET_NETWORK, BASE_USDC_ADDRESS, BASE_USDC_DECIMALS, type BeforeSettleDecision, type CheckoutSessionContext, type CheckoutSessionFn, type CheckoutSessionResponse, type CreateRouterFromEnvOptions, DEFAULT_SOLANA_FACILITATOR_URL, DEFAULT_TEMPO_RPC_URL, type DiscoveryConfig, type HandlerContext, HttpError, type KvStore, type MeteredOptions, type MppProtocolInfo, type PaidOptions, type ProtocolType, RouteDefinitionError, type RouteEntry, type RouterConfig, RouterConfigError, type RouterConfigIssue, type RouterConfigIssueCode, type RouterConfigIssueSeverity, type RouterPlugin, SOLANA_MAINNET_NETWORK, type ServiceRouter, type SessionOptions, type SettlementErrorContext, type SettlementLifecycleContext, type SettlementSettledContext, TEMPO_USDC_ADDRESS, TEMPO_USDC_DECIMALS, type UpToOptions, type X402FacilitatorsConfig, type X402Network, ZERO_EVM_ADDRESS, createRouter, createRouterFromEnv, routerConfigFromEnv };