@agentcash/router 1.5.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -128,7 +128,7 @@ interface ErrorEvent {
128
128
  interface AuthEvent {
129
129
  /** Authentication mode that was verified */
130
130
  authMode: 'siwx' | 'apiKey';
131
- /** Verified wallet address (lowercase) */
131
+ /** Verified canonical wallet address (EVM lowercase, non-EVM preserved) */
132
132
  wallet: string | null;
133
133
  /** Route key */
134
134
  route: string;
@@ -271,6 +271,49 @@ interface HandlerPaymentContext {
271
271
  transaction?: string;
272
272
  receipt?: string;
273
273
  }
274
+ interface SettlementLifecycleContext<TBody = unknown> {
275
+ route: string;
276
+ request: NextRequest;
277
+ body: TBody;
278
+ wallet: string;
279
+ account: unknown;
280
+ payment: HandlerPaymentContext;
281
+ response: NextResponse;
282
+ result: unknown;
283
+ }
284
+ interface SettlementSettledContext<TBody = unknown> extends Omit<SettlementLifecycleContext<TBody>, 'payment'> {
285
+ payment: HandlerPaymentContext & {
286
+ status: 'settled';
287
+ };
288
+ }
289
+ interface SettlementErrorContext<TBody = unknown> extends SettlementLifecycleContext<TBody> {
290
+ error: unknown;
291
+ phase: 'settle' | 'afterSettle';
292
+ }
293
+ interface SettledHandlerErrorContext<TBody = unknown> extends SettlementSettledContext<TBody> {
294
+ error: unknown;
295
+ }
296
+ interface SettlementLifecycle<TBody = unknown> {
297
+ /**
298
+ * Runs after the handler returns a successful response, before router-controlled
299
+ * settlement/broadcast. Throw with `.status` to return a specific error and
300
+ * skip settlement when the protocol flow has not already settled.
301
+ */
302
+ beforeSettle?: (ctx: SettlementLifecycleContext<TBody>) => void | Promise<void>;
303
+ /**
304
+ * Runs after successful settlement. Use for durable ledgers and audit rows.
305
+ * Errors are alerted and do not change the already-settled response.
306
+ */
307
+ afterSettle?: (ctx: SettlementSettledContext<TBody>) => void | Promise<void>;
308
+ /**
309
+ * Runs when the router has already observed a settled payment, then the
310
+ * handler returns an error response. Use for app-owned refund or
311
+ * compensation queues.
312
+ */
313
+ onSettledHandlerError?: (ctx: SettledHandlerErrorContext<TBody>) => void | Promise<void>;
314
+ /** Runs when router-controlled settlement fails after the handler succeeded. */
315
+ onSettlementError?: (ctx: SettlementErrorContext<TBody>) => void | Promise<void>;
316
+ }
274
317
  interface HandlerContext<TBody = undefined, TQuery = undefined> {
275
318
  body: TBody;
276
319
  query: TQuery;
@@ -321,22 +364,22 @@ interface RouteEntry {
321
364
  querySchema?: ZodType;
322
365
  outputSchema?: ZodType;
323
366
  /**
324
- * Conforming example for the request input (body for body routes, query params for query routes).
325
- * Required whenever `bodySchema` or `querySchema` is set. Must satisfy the corresponding schema
326
- * validated at route-registration time via the Zod schema.
367
+ * Optional conforming example for the request input (body for body routes, query params for query routes).
368
+ * When present, it must satisfy the corresponding schema and is validated at route registration.
327
369
  *
328
370
  * Emitted in the bazaar discovery extension so indexers can advertise a working sample call.
329
371
  */
330
372
  inputExample?: JsonObject;
331
373
  /**
332
- * Conforming example for the response output. Required whenever `outputSchema` is set.
333
- * Must satisfy `outputSchema` validated at route-registration time via the Zod schema.
374
+ * Optional conforming example for the response output. When present, it must
375
+ * satisfy `outputSchema` and is validated at route registration.
334
376
  *
335
377
  * Accepts any JSON value (object, array, or primitive) to support top-level array or
336
378
  * primitive response schemas.
337
379
  *
338
- * Emitted in the bazaar discovery extension. Without it the `output` block is dropped from
339
- * the declaration entirely (the output schema alone cannot be exposed in bazaar without an example).
380
+ * Emitted in the bazaar discovery extension. Without it the `output` block is
381
+ * dropped from the declaration entirely (the output schema alone cannot be
382
+ * exposed in bazaar without an example).
340
383
  */
341
384
  outputExample?: JsonValue;
342
385
  description?: string;
@@ -349,6 +392,7 @@ interface RouteEntry {
349
392
  providerName?: string;
350
393
  providerConfig?: ProviderConfig;
351
394
  validateFn?: (body: unknown) => void | Promise<void>;
395
+ settlement?: SettlementLifecycle;
352
396
  mppInfo?: MppProtocolInfo;
353
397
  }
354
398
  interface DiscoveryConfig {
@@ -423,7 +467,7 @@ interface RouterConfig {
423
467
  * createRouter({
424
468
  * mpp: {
425
469
  * secretKey: process.env.MPP_SECRET_KEY!,
426
- * currency: USDC,
470
+ * currency: TEMPO_USDC_CURRENCY,
427
471
  * useDefaultStore: true,
428
472
  * }
429
473
  * })
@@ -431,7 +475,7 @@ interface RouterConfig {
431
475
  useDefaultStore?: boolean;
432
476
  };
433
477
  /**
434
- * Payment protocols to accept on auto-priced routes (those using the `prices` config).
478
+ * Payment protocols to accept on paid routes unless a route overrides them.
435
479
  *
436
480
  * @default ['x402']
437
481
  *
@@ -439,7 +483,7 @@ interface RouterConfig {
439
483
  * // Accept both x402 and MPP payments
440
484
  * createRouter({
441
485
  * protocols: ['x402', 'mpp'],
442
- * mpp: { secretKey, currency, recipient },
486
+ * mpp: { secretKey, currency: TEMPO_USDC_CURRENCY, recipient },
443
487
  * prices: { 'exa/search': '0.01' }
444
488
  * })
445
489
  */
@@ -485,6 +529,7 @@ interface OrchestrateDeps {
485
529
  nonceStore: NonceStore;
486
530
  entitlementStore: EntitlementStore;
487
531
  payeeAddress: string;
532
+ mppRecipient?: string;
488
533
  network: string;
489
534
  x402FacilitatorsByNetwork?: Record<string, ResolvedX402Facilitator>;
490
535
  x402Accepts: X402AcceptConfig[];
@@ -521,16 +566,12 @@ type InputTypeFor<TBody, TQuery> = [TBody] extends [undefined] ? [TQuery] extend
521
566
  * because TypeScript doesn't reliably gate overload selection on `this` for
522
567
  * generic classes (structurally identical instance types collapse).
523
568
  */
524
- type HandlerArg<TBody, TQuery, HasAuth extends boolean, NeedsBody extends boolean, HasBody extends boolean, NeedsInputExample extends boolean, NeedsOutputExample extends boolean> = HasAuth extends true ? [NeedsBody, HasBody] extends [true, false] ? {
569
+ type HandlerArg<TBody, TQuery, HasAuth extends boolean, NeedsBody extends boolean, HasBody extends boolean> = HasAuth extends true ? [NeedsBody, HasBody] extends [true, false] ? {
525
570
  __missing: 'Call .body(schema) — dynamic/tiered pricing requires a body schema to resolve the price against';
526
- } : NeedsInputExample extends true ? {
527
- __missing: 'Call .inputExample(sample) — .body()/.query() routes must advertise a conforming request example for bazaar discovery';
528
- } : NeedsOutputExample extends true ? {
529
- __missing: 'Call .outputExample(sample) — .output() routes must advertise a conforming response example for bazaar discovery';
530
571
  } : (ctx: HandlerContext<TBody, TQuery>) => Promise<unknown> : {
531
572
  __missing: 'Select an auth mode: .paid(pricing), .siwx(), .apiKey(resolver), or .unprotected()';
532
573
  };
533
- declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = undefined, HasAuth extends boolean = false, NeedsBody extends boolean = false, HasBody extends boolean = false, NeedsInputExample extends boolean = false, NeedsOutputExample extends boolean = false> {
574
+ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = undefined, HasAuth extends boolean = false, NeedsBody extends boolean = false, HasBody extends boolean = false> {
534
575
  /** @internal */ readonly _key: string;
535
576
  /** @internal */ readonly _registry: RouteRegistry;
536
577
  /** @internal */ readonly _deps: OrchestrateDeps;
@@ -540,7 +581,7 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
540
581
  /** @internal */ _protocols: ProtocolType[];
541
582
  /** @internal */ _maxPrice: string | undefined;
542
583
  /** @internal */ _minPrice: string | undefined;
543
- /** @internal */ _payTo: string | ((request: Request) => string | Promise<string>) | undefined;
584
+ /** @internal */ _payTo: PayToConfig | undefined;
544
585
  /** @internal */ _bodySchema: ZodType | undefined;
545
586
  /** @internal */ _querySchema: ZodType | undefined;
546
587
  /** @internal */ _outputSchema: ZodType | undefined;
@@ -555,13 +596,14 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
555
596
  /** @internal */ _providerName: string | undefined;
556
597
  /** @internal */ _providerConfig: ProviderConfig | undefined;
557
598
  /** @internal */ _validateFn: ((body: TBody) => void | Promise<void>) | undefined;
599
+ /** @internal */ _settlement: SettlementLifecycle<TBody> | undefined;
558
600
  /** @internal */ _mppInfo: MppProtocolInfo | undefined;
559
601
  constructor(key: string, registry: RouteRegistry, deps: OrchestrateDeps);
560
602
  private fork;
561
- paid(pricing: string, options?: PaidOptions): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody, NeedsInputExample, NeedsOutputExample>;
603
+ paid(pricing: string, options?: PaidOptions): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody>;
562
604
  paid<TBodyIn>(pricing: (body: TBodyIn) => string | Promise<string>, options?: PaidOptions & {
563
605
  maxPrice?: string;
564
- }): RouteBuilder<TBody, TQuery, TOutput, True, True, HasBody, NeedsInputExample, NeedsOutputExample>;
606
+ }): RouteBuilder<TBody, TQuery, TOutput, True, True, HasBody>;
565
607
  paid(pricing: {
566
608
  field: string;
567
609
  tiers: Record<string, {
@@ -569,21 +611,26 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
569
611
  label?: string;
570
612
  }>;
571
613
  default?: string;
572
- }, options?: PaidOptions): RouteBuilder<TBody, TQuery, TOutput, True, True, HasBody, NeedsInputExample, NeedsOutputExample>;
573
- siwx(): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody, NeedsInputExample, NeedsOutputExample>;
574
- apiKey(resolver: (key: string) => unknown | Promise<unknown>): RouteBuilder<TBody, TQuery, TOutput, True, NeedsBody, HasBody, NeedsInputExample, NeedsOutputExample>;
575
- unprotected(): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody, NeedsInputExample, NeedsOutputExample>;
614
+ }, options?: PaidOptions): RouteBuilder<TBody, TQuery, TOutput, True, True, HasBody>;
615
+ siwx(): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody>;
616
+ apiKey(resolver: (key: string) => unknown | Promise<unknown>): RouteBuilder<TBody, TQuery, TOutput, True, NeedsBody, HasBody>;
617
+ unprotected(): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody>;
576
618
  provider(name: string, config?: ProviderConfig): this;
577
- body<T>(schema: ZodType<T>): RouteBuilder<T, TQuery, TOutput, HasAuth, NeedsBody, True, True, NeedsOutputExample>;
578
- query<T>(schema: ZodType<T>): RouteBuilder<TBody, T, TOutput, HasAuth, NeedsBody, HasBody, True, NeedsOutputExample>;
579
- output<T>(schema: ZodType<T>): RouteBuilder<TBody, TQuery, T, HasAuth, NeedsBody, HasBody, NeedsInputExample, True>;
619
+ body<T>(schema: ZodType<T>): RouteBuilder<T, TQuery, TOutput, HasAuth, NeedsBody, True>;
620
+ body<T>(schema: ZodType<T>, example: T & JsonObject): RouteBuilder<T, TQuery, TOutput, HasAuth, NeedsBody, True>;
621
+ query<T>(schema: ZodType<T>): RouteBuilder<TBody, T, TOutput, HasAuth, NeedsBody, HasBody>;
622
+ query<T>(schema: ZodType<T>, example: T & JsonObject): RouteBuilder<TBody, T, TOutput, HasAuth, NeedsBody, HasBody>;
623
+ output<T>(schema: ZodType<T>): RouteBuilder<TBody, TQuery, T, HasAuth, NeedsBody, HasBody>;
624
+ output<T>(schema: ZodType<T>, example: T & JsonValue): RouteBuilder<TBody, TQuery, T, HasAuth, NeedsBody, HasBody>;
580
625
  /**
581
626
  * Provide a conforming example of the request input (body or query params).
582
627
  *
583
- * **Required** whenever `.body()` or `.query()` is set enforced at compile time via
584
- * `.handler()` overloads, and at route-registration time via Zod validation of the
585
- * example against the schema. The example is embedded in the bazaar discovery extension
586
- * so indexers can advertise a working sample call.
628
+ * Optional. When provided, the example is validated against the request schema
629
+ * at route registration and embedded in the bazaar discovery extension so
630
+ * indexers can advertise a working sample call.
631
+ *
632
+ * For the common case, pass the example directly to `.body(schema, example)` or
633
+ * `.query(schema, example)` instead.
587
634
  *
588
635
  * @example
589
636
  * ```ts
@@ -594,14 +641,15 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
594
641
  * .handler(async ({ body }) => { ... });
595
642
  * ```
596
643
  */
597
- inputExample(example: InputTypeFor<TBody, TQuery> & JsonObject): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, False, NeedsOutputExample>;
644
+ inputExample(example: InputTypeFor<TBody, TQuery> & JsonObject): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody>;
598
645
  /**
599
646
  * Provide a conforming example of the response output.
600
647
  *
601
- * **Required** whenever `.output()` is set enforced at compile time via `.handler()`
602
- * overloads, and at route-registration time via Zod validation of the example against
603
- * the schema. The example is embedded in the bazaar discovery extension so indexers
604
- * can advertise the response shape.
648
+ * Optional. When provided, the example is validated against the output schema
649
+ * at route registration and embedded in the bazaar discovery extension so
650
+ * indexers can advertise the response shape.
651
+ *
652
+ * For the common case, pass the example directly to `.output(schema, example)` instead.
605
653
  *
606
654
  * Accepts any JSON value (objects, arrays, or primitives) — top-level array
607
655
  * or primitive responses (e.g. `z.array(...)`) are supported alongside the
@@ -623,7 +671,7 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
623
671
  * .handler(async () => { ... });
624
672
  * ```
625
673
  */
626
- outputExample(example: TOutput & JsonValue): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, NeedsInputExample, False>;
674
+ outputExample(example: TOutput & JsonValue): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody>;
627
675
  description(text: string): this;
628
676
  path(p: string): this;
629
677
  method(m: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH'): this;
@@ -648,8 +696,17 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
648
696
  * .handler(async ({ body }) => { ... });
649
697
  * ```
650
698
  */
651
- validate(fn: (body: TBody) => void | Promise<void>): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, NeedsInputExample, NeedsOutputExample>;
652
- handler(fn: HandlerArg<TBody, TQuery, HasAuth, NeedsBody, HasBody, NeedsInputExample, NeedsOutputExample>): (request: NextRequest) => Promise<Response>;
699
+ validate(fn: (body: TBody) => void | Promise<void>): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody>;
700
+ /**
701
+ * Add route-specific settlement hooks.
702
+ *
703
+ * `beforeSettle` runs after a successful handler response but before
704
+ * router-controlled settlement/broadcast, so it can still prevent the charge
705
+ * for x402 and MPP transaction-payload flows. `afterSettle` runs after
706
+ * settlement and is intended for durable ledgers or app-owned refund queues.
707
+ */
708
+ settlement(lifecycle: SettlementLifecycle<TBody>): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody>;
709
+ handler(fn: HandlerArg<TBody, TQuery, HasAuth, NeedsBody, HasBody>): (request: NextRequest) => Promise<Response>;
653
710
  }
654
711
 
655
712
  declare const BASE_NETWORK = "eip155:8453";
@@ -658,7 +715,7 @@ declare const TEMPO_USDC_CURRENCY = "0x20c000000000000000000000b9537d11c60e8b50"
658
715
  declare const ZERO_EVM_ADDRESS = "0x0000000000000000000000000000000000000000";
659
716
 
660
717
  type RouterEnv = Record<string, string | undefined>;
661
- type RouterConfigIssueCode = 'missing_base_url' | 'empty_protocols' | 'missing_x402_accepts' | 'missing_x402_network' | 'unsupported_x402_network' | 'missing_x402_asset' | 'invalid_x402_decimals' | 'missing_x402_payee' | 'missing_cdp_keys' | 'placeholder_payee' | 'missing_mpp_config' | 'missing_mpp_secret_key' | 'missing_mpp_currency' | 'missing_mpp_recipient' | 'missing_mpp_rpc_url' | 'missing_mpp_default_store_env';
718
+ type RouterConfigIssueCode = 'missing_base_url' | 'empty_protocols' | 'missing_x402_accepts' | 'missing_x402_network' | 'unsupported_x402_network' | 'missing_x402_asset' | 'invalid_x402_decimals' | 'missing_x402_payee' | 'missing_cdp_keys' | 'placeholder_payee' | 'missing_mpp_config' | 'missing_mpp_secret_key' | 'missing_mpp_currency' | 'invalid_mpp_currency' | 'missing_mpp_recipient' | 'invalid_mpp_recipient' | 'missing_mpp_rpc_url' | 'invalid_mpp_fee_payer_key' | 'missing_mpp_default_store_env';
662
719
  interface RouterConfigIssue {
663
720
  code: RouterConfigIssueCode;
664
721
  message: string;
@@ -699,7 +756,7 @@ interface MonitorEntry {
699
756
  critical?: number;
700
757
  }
701
758
  interface ServiceRouter<TPriceKeys extends string = never> {
702
- route<K extends string>(keyOrDefinition: K | RouteDefinition<K>): [K] extends [TPriceKeys] ? RouteBuilder<undefined, undefined, undefined, true, false, false, false, false> : RouteBuilder<undefined, undefined, undefined, false, false, false, false, false>;
759
+ route<K extends string>(keyOrDefinition: K | RouteDefinition<K>): [K] extends [TPriceKeys] ? RouteBuilder<undefined, undefined, undefined, true, false, false> : RouteBuilder<undefined, undefined, undefined, false, false, false>;
703
760
  wellKnown(): (request: NextRequest) => Promise<NextResponse>;
704
761
  openapi(): (request: NextRequest) => Promise<NextResponse>;
705
762
  llmsTxt(): (request: NextRequest) => Promise<NextResponse>;
@@ -710,4 +767,4 @@ declare function createRouter<const P extends Record<string, string> = Record<ne
710
767
  prices?: P;
711
768
  }): ServiceRouter<Extract<keyof P, string>>;
712
769
 
713
- export { type AlertEvent, type AlertFn, type AlertLevel, type AuthEvent, type AuthMode, BASE_NETWORK, type DiscoveryConfig, type EntitlementStore, type ErrorEvent, type HandlerContext, type HandlerPaymentContext, HttpError, MemoryEntitlementStore, MemoryNonceStore, type MonitorEntry, type MppProtocolInfo, type NonceStore, type OveragePolicy, type PaidOptions, type PayToConfig, type PaymentEvent, type PaymentStatus, type PluginContext, type PricingConfig, type ProtocolType, type ProviderConfig, type ProviderQuotaEvent, type QuotaInfo, type QuotaLevel, type RedisEntitlementStoreOptions, type RedisNonceStoreOptions, type RequestMeta, type ResponseMeta, RouteBuilder, type RouteEntry, RouteRegistry, type RouterConfig, RouterConfigError, type RouterConfigIssue, type RouterConfigIssueCode, type RouterConfigValidationOptions, type RouterEnv, type RouterPlugin, SIWX_CHALLENGE_EXPIRY_MS, SOLANA_MAINNET_NETWORK, type ServiceRouter, type SettlementEvent, TEMPO_USDC_CURRENCY, type TierConfig, type X402AcceptConfig, type X402FacilitatorTarget, type X402FacilitatorsConfig, type X402ResolvedAccept, type X402RouterFacilitatorConfig, type X402Server, ZERO_EVM_ADDRESS, consolePlugin, createRedisEntitlementStore, createRedisNonceStore, createRouter, formatRouterConfigIssues, getRouterConfigIssues, mppFromEnv, paidOptionsForProtocols, validateRouterConfig, x402AcceptsFromEnv };
770
+ export { type AlertEvent, type AlertFn, type AlertLevel, type AuthEvent, type AuthMode, BASE_NETWORK, type DiscoveryConfig, type EntitlementStore, type ErrorEvent, type HandlerContext, type HandlerPaymentContext, HttpError, MemoryEntitlementStore, MemoryNonceStore, type MonitorEntry, type MppProtocolInfo, type NonceStore, type OveragePolicy, type PaidOptions, type PayToConfig, type PaymentEvent, type PaymentStatus, type PluginContext, type PricingConfig, type ProtocolType, type ProviderConfig, type ProviderQuotaEvent, type QuotaInfo, type QuotaLevel, type RedisEntitlementStoreOptions, type RedisNonceStoreOptions, type RequestMeta, type ResponseMeta, RouteBuilder, type RouteEntry, RouteRegistry, type RouterConfig, RouterConfigError, type RouterConfigIssue, type RouterConfigIssueCode, type RouterConfigValidationOptions, type RouterEnv, type RouterPlugin, SIWX_CHALLENGE_EXPIRY_MS, SOLANA_MAINNET_NETWORK, type ServiceRouter, type SettledHandlerErrorContext, type SettlementErrorContext, type SettlementEvent, type SettlementLifecycle, type SettlementLifecycleContext, type SettlementSettledContext, TEMPO_USDC_CURRENCY, type TierConfig, type X402AcceptConfig, type X402FacilitatorTarget, type X402FacilitatorsConfig, type X402ResolvedAccept, type X402RouterFacilitatorConfig, type X402Server, ZERO_EVM_ADDRESS, consolePlugin, createRedisEntitlementStore, createRedisNonceStore, createRouter, formatRouterConfigIssues, getRouterConfigIssues, mppFromEnv, paidOptionsForProtocols, validateRouterConfig, x402AcceptsFromEnv };
package/dist/index.d.ts CHANGED
@@ -128,7 +128,7 @@ interface ErrorEvent {
128
128
  interface AuthEvent {
129
129
  /** Authentication mode that was verified */
130
130
  authMode: 'siwx' | 'apiKey';
131
- /** Verified wallet address (lowercase) */
131
+ /** Verified canonical wallet address (EVM lowercase, non-EVM preserved) */
132
132
  wallet: string | null;
133
133
  /** Route key */
134
134
  route: string;
@@ -271,6 +271,49 @@ interface HandlerPaymentContext {
271
271
  transaction?: string;
272
272
  receipt?: string;
273
273
  }
274
+ interface SettlementLifecycleContext<TBody = unknown> {
275
+ route: string;
276
+ request: NextRequest;
277
+ body: TBody;
278
+ wallet: string;
279
+ account: unknown;
280
+ payment: HandlerPaymentContext;
281
+ response: NextResponse;
282
+ result: unknown;
283
+ }
284
+ interface SettlementSettledContext<TBody = unknown> extends Omit<SettlementLifecycleContext<TBody>, 'payment'> {
285
+ payment: HandlerPaymentContext & {
286
+ status: 'settled';
287
+ };
288
+ }
289
+ interface SettlementErrorContext<TBody = unknown> extends SettlementLifecycleContext<TBody> {
290
+ error: unknown;
291
+ phase: 'settle' | 'afterSettle';
292
+ }
293
+ interface SettledHandlerErrorContext<TBody = unknown> extends SettlementSettledContext<TBody> {
294
+ error: unknown;
295
+ }
296
+ interface SettlementLifecycle<TBody = unknown> {
297
+ /**
298
+ * Runs after the handler returns a successful response, before router-controlled
299
+ * settlement/broadcast. Throw with `.status` to return a specific error and
300
+ * skip settlement when the protocol flow has not already settled.
301
+ */
302
+ beforeSettle?: (ctx: SettlementLifecycleContext<TBody>) => void | Promise<void>;
303
+ /**
304
+ * Runs after successful settlement. Use for durable ledgers and audit rows.
305
+ * Errors are alerted and do not change the already-settled response.
306
+ */
307
+ afterSettle?: (ctx: SettlementSettledContext<TBody>) => void | Promise<void>;
308
+ /**
309
+ * Runs when the router has already observed a settled payment, then the
310
+ * handler returns an error response. Use for app-owned refund or
311
+ * compensation queues.
312
+ */
313
+ onSettledHandlerError?: (ctx: SettledHandlerErrorContext<TBody>) => void | Promise<void>;
314
+ /** Runs when router-controlled settlement fails after the handler succeeded. */
315
+ onSettlementError?: (ctx: SettlementErrorContext<TBody>) => void | Promise<void>;
316
+ }
274
317
  interface HandlerContext<TBody = undefined, TQuery = undefined> {
275
318
  body: TBody;
276
319
  query: TQuery;
@@ -321,22 +364,22 @@ interface RouteEntry {
321
364
  querySchema?: ZodType;
322
365
  outputSchema?: ZodType;
323
366
  /**
324
- * Conforming example for the request input (body for body routes, query params for query routes).
325
- * Required whenever `bodySchema` or `querySchema` is set. Must satisfy the corresponding schema
326
- * validated at route-registration time via the Zod schema.
367
+ * Optional conforming example for the request input (body for body routes, query params for query routes).
368
+ * When present, it must satisfy the corresponding schema and is validated at route registration.
327
369
  *
328
370
  * Emitted in the bazaar discovery extension so indexers can advertise a working sample call.
329
371
  */
330
372
  inputExample?: JsonObject;
331
373
  /**
332
- * Conforming example for the response output. Required whenever `outputSchema` is set.
333
- * Must satisfy `outputSchema` validated at route-registration time via the Zod schema.
374
+ * Optional conforming example for the response output. When present, it must
375
+ * satisfy `outputSchema` and is validated at route registration.
334
376
  *
335
377
  * Accepts any JSON value (object, array, or primitive) to support top-level array or
336
378
  * primitive response schemas.
337
379
  *
338
- * Emitted in the bazaar discovery extension. Without it the `output` block is dropped from
339
- * the declaration entirely (the output schema alone cannot be exposed in bazaar without an example).
380
+ * Emitted in the bazaar discovery extension. Without it the `output` block is
381
+ * dropped from the declaration entirely (the output schema alone cannot be
382
+ * exposed in bazaar without an example).
340
383
  */
341
384
  outputExample?: JsonValue;
342
385
  description?: string;
@@ -349,6 +392,7 @@ interface RouteEntry {
349
392
  providerName?: string;
350
393
  providerConfig?: ProviderConfig;
351
394
  validateFn?: (body: unknown) => void | Promise<void>;
395
+ settlement?: SettlementLifecycle;
352
396
  mppInfo?: MppProtocolInfo;
353
397
  }
354
398
  interface DiscoveryConfig {
@@ -423,7 +467,7 @@ interface RouterConfig {
423
467
  * createRouter({
424
468
  * mpp: {
425
469
  * secretKey: process.env.MPP_SECRET_KEY!,
426
- * currency: USDC,
470
+ * currency: TEMPO_USDC_CURRENCY,
427
471
  * useDefaultStore: true,
428
472
  * }
429
473
  * })
@@ -431,7 +475,7 @@ interface RouterConfig {
431
475
  useDefaultStore?: boolean;
432
476
  };
433
477
  /**
434
- * Payment protocols to accept on auto-priced routes (those using the `prices` config).
478
+ * Payment protocols to accept on paid routes unless a route overrides them.
435
479
  *
436
480
  * @default ['x402']
437
481
  *
@@ -439,7 +483,7 @@ interface RouterConfig {
439
483
  * // Accept both x402 and MPP payments
440
484
  * createRouter({
441
485
  * protocols: ['x402', 'mpp'],
442
- * mpp: { secretKey, currency, recipient },
486
+ * mpp: { secretKey, currency: TEMPO_USDC_CURRENCY, recipient },
443
487
  * prices: { 'exa/search': '0.01' }
444
488
  * })
445
489
  */
@@ -485,6 +529,7 @@ interface OrchestrateDeps {
485
529
  nonceStore: NonceStore;
486
530
  entitlementStore: EntitlementStore;
487
531
  payeeAddress: string;
532
+ mppRecipient?: string;
488
533
  network: string;
489
534
  x402FacilitatorsByNetwork?: Record<string, ResolvedX402Facilitator>;
490
535
  x402Accepts: X402AcceptConfig[];
@@ -521,16 +566,12 @@ type InputTypeFor<TBody, TQuery> = [TBody] extends [undefined] ? [TQuery] extend
521
566
  * because TypeScript doesn't reliably gate overload selection on `this` for
522
567
  * generic classes (structurally identical instance types collapse).
523
568
  */
524
- type HandlerArg<TBody, TQuery, HasAuth extends boolean, NeedsBody extends boolean, HasBody extends boolean, NeedsInputExample extends boolean, NeedsOutputExample extends boolean> = HasAuth extends true ? [NeedsBody, HasBody] extends [true, false] ? {
569
+ type HandlerArg<TBody, TQuery, HasAuth extends boolean, NeedsBody extends boolean, HasBody extends boolean> = HasAuth extends true ? [NeedsBody, HasBody] extends [true, false] ? {
525
570
  __missing: 'Call .body(schema) — dynamic/tiered pricing requires a body schema to resolve the price against';
526
- } : NeedsInputExample extends true ? {
527
- __missing: 'Call .inputExample(sample) — .body()/.query() routes must advertise a conforming request example for bazaar discovery';
528
- } : NeedsOutputExample extends true ? {
529
- __missing: 'Call .outputExample(sample) — .output() routes must advertise a conforming response example for bazaar discovery';
530
571
  } : (ctx: HandlerContext<TBody, TQuery>) => Promise<unknown> : {
531
572
  __missing: 'Select an auth mode: .paid(pricing), .siwx(), .apiKey(resolver), or .unprotected()';
532
573
  };
533
- declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = undefined, HasAuth extends boolean = false, NeedsBody extends boolean = false, HasBody extends boolean = false, NeedsInputExample extends boolean = false, NeedsOutputExample extends boolean = false> {
574
+ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = undefined, HasAuth extends boolean = false, NeedsBody extends boolean = false, HasBody extends boolean = false> {
534
575
  /** @internal */ readonly _key: string;
535
576
  /** @internal */ readonly _registry: RouteRegistry;
536
577
  /** @internal */ readonly _deps: OrchestrateDeps;
@@ -540,7 +581,7 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
540
581
  /** @internal */ _protocols: ProtocolType[];
541
582
  /** @internal */ _maxPrice: string | undefined;
542
583
  /** @internal */ _minPrice: string | undefined;
543
- /** @internal */ _payTo: string | ((request: Request) => string | Promise<string>) | undefined;
584
+ /** @internal */ _payTo: PayToConfig | undefined;
544
585
  /** @internal */ _bodySchema: ZodType | undefined;
545
586
  /** @internal */ _querySchema: ZodType | undefined;
546
587
  /** @internal */ _outputSchema: ZodType | undefined;
@@ -555,13 +596,14 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
555
596
  /** @internal */ _providerName: string | undefined;
556
597
  /** @internal */ _providerConfig: ProviderConfig | undefined;
557
598
  /** @internal */ _validateFn: ((body: TBody) => void | Promise<void>) | undefined;
599
+ /** @internal */ _settlement: SettlementLifecycle<TBody> | undefined;
558
600
  /** @internal */ _mppInfo: MppProtocolInfo | undefined;
559
601
  constructor(key: string, registry: RouteRegistry, deps: OrchestrateDeps);
560
602
  private fork;
561
- paid(pricing: string, options?: PaidOptions): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody, NeedsInputExample, NeedsOutputExample>;
603
+ paid(pricing: string, options?: PaidOptions): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody>;
562
604
  paid<TBodyIn>(pricing: (body: TBodyIn) => string | Promise<string>, options?: PaidOptions & {
563
605
  maxPrice?: string;
564
- }): RouteBuilder<TBody, TQuery, TOutput, True, True, HasBody, NeedsInputExample, NeedsOutputExample>;
606
+ }): RouteBuilder<TBody, TQuery, TOutput, True, True, HasBody>;
565
607
  paid(pricing: {
566
608
  field: string;
567
609
  tiers: Record<string, {
@@ -569,21 +611,26 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
569
611
  label?: string;
570
612
  }>;
571
613
  default?: string;
572
- }, options?: PaidOptions): RouteBuilder<TBody, TQuery, TOutput, True, True, HasBody, NeedsInputExample, NeedsOutputExample>;
573
- siwx(): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody, NeedsInputExample, NeedsOutputExample>;
574
- apiKey(resolver: (key: string) => unknown | Promise<unknown>): RouteBuilder<TBody, TQuery, TOutput, True, NeedsBody, HasBody, NeedsInputExample, NeedsOutputExample>;
575
- unprotected(): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody, NeedsInputExample, NeedsOutputExample>;
614
+ }, options?: PaidOptions): RouteBuilder<TBody, TQuery, TOutput, True, True, HasBody>;
615
+ siwx(): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody>;
616
+ apiKey(resolver: (key: string) => unknown | Promise<unknown>): RouteBuilder<TBody, TQuery, TOutput, True, NeedsBody, HasBody>;
617
+ unprotected(): RouteBuilder<TBody, TQuery, TOutput, True, False, HasBody>;
576
618
  provider(name: string, config?: ProviderConfig): this;
577
- body<T>(schema: ZodType<T>): RouteBuilder<T, TQuery, TOutput, HasAuth, NeedsBody, True, True, NeedsOutputExample>;
578
- query<T>(schema: ZodType<T>): RouteBuilder<TBody, T, TOutput, HasAuth, NeedsBody, HasBody, True, NeedsOutputExample>;
579
- output<T>(schema: ZodType<T>): RouteBuilder<TBody, TQuery, T, HasAuth, NeedsBody, HasBody, NeedsInputExample, True>;
619
+ body<T>(schema: ZodType<T>): RouteBuilder<T, TQuery, TOutput, HasAuth, NeedsBody, True>;
620
+ body<T>(schema: ZodType<T>, example: T & JsonObject): RouteBuilder<T, TQuery, TOutput, HasAuth, NeedsBody, True>;
621
+ query<T>(schema: ZodType<T>): RouteBuilder<TBody, T, TOutput, HasAuth, NeedsBody, HasBody>;
622
+ query<T>(schema: ZodType<T>, example: T & JsonObject): RouteBuilder<TBody, T, TOutput, HasAuth, NeedsBody, HasBody>;
623
+ output<T>(schema: ZodType<T>): RouteBuilder<TBody, TQuery, T, HasAuth, NeedsBody, HasBody>;
624
+ output<T>(schema: ZodType<T>, example: T & JsonValue): RouteBuilder<TBody, TQuery, T, HasAuth, NeedsBody, HasBody>;
580
625
  /**
581
626
  * Provide a conforming example of the request input (body or query params).
582
627
  *
583
- * **Required** whenever `.body()` or `.query()` is set enforced at compile time via
584
- * `.handler()` overloads, and at route-registration time via Zod validation of the
585
- * example against the schema. The example is embedded in the bazaar discovery extension
586
- * so indexers can advertise a working sample call.
628
+ * Optional. When provided, the example is validated against the request schema
629
+ * at route registration and embedded in the bazaar discovery extension so
630
+ * indexers can advertise a working sample call.
631
+ *
632
+ * For the common case, pass the example directly to `.body(schema, example)` or
633
+ * `.query(schema, example)` instead.
587
634
  *
588
635
  * @example
589
636
  * ```ts
@@ -594,14 +641,15 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
594
641
  * .handler(async ({ body }) => { ... });
595
642
  * ```
596
643
  */
597
- inputExample(example: InputTypeFor<TBody, TQuery> & JsonObject): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, False, NeedsOutputExample>;
644
+ inputExample(example: InputTypeFor<TBody, TQuery> & JsonObject): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody>;
598
645
  /**
599
646
  * Provide a conforming example of the response output.
600
647
  *
601
- * **Required** whenever `.output()` is set enforced at compile time via `.handler()`
602
- * overloads, and at route-registration time via Zod validation of the example against
603
- * the schema. The example is embedded in the bazaar discovery extension so indexers
604
- * can advertise the response shape.
648
+ * Optional. When provided, the example is validated against the output schema
649
+ * at route registration and embedded in the bazaar discovery extension so
650
+ * indexers can advertise the response shape.
651
+ *
652
+ * For the common case, pass the example directly to `.output(schema, example)` instead.
605
653
  *
606
654
  * Accepts any JSON value (objects, arrays, or primitives) — top-level array
607
655
  * or primitive responses (e.g. `z.array(...)`) are supported alongside the
@@ -623,7 +671,7 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
623
671
  * .handler(async () => { ... });
624
672
  * ```
625
673
  */
626
- outputExample(example: TOutput & JsonValue): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, NeedsInputExample, False>;
674
+ outputExample(example: TOutput & JsonValue): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody>;
627
675
  description(text: string): this;
628
676
  path(p: string): this;
629
677
  method(m: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH'): this;
@@ -648,8 +696,17 @@ declare class RouteBuilder<TBody = undefined, TQuery = undefined, TOutput = unde
648
696
  * .handler(async ({ body }) => { ... });
649
697
  * ```
650
698
  */
651
- validate(fn: (body: TBody) => void | Promise<void>): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody, NeedsInputExample, NeedsOutputExample>;
652
- handler(fn: HandlerArg<TBody, TQuery, HasAuth, NeedsBody, HasBody, NeedsInputExample, NeedsOutputExample>): (request: NextRequest) => Promise<Response>;
699
+ validate(fn: (body: TBody) => void | Promise<void>): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody>;
700
+ /**
701
+ * Add route-specific settlement hooks.
702
+ *
703
+ * `beforeSettle` runs after a successful handler response but before
704
+ * router-controlled settlement/broadcast, so it can still prevent the charge
705
+ * for x402 and MPP transaction-payload flows. `afterSettle` runs after
706
+ * settlement and is intended for durable ledgers or app-owned refund queues.
707
+ */
708
+ settlement(lifecycle: SettlementLifecycle<TBody>): RouteBuilder<TBody, TQuery, TOutput, HasAuth, NeedsBody, HasBody>;
709
+ handler(fn: HandlerArg<TBody, TQuery, HasAuth, NeedsBody, HasBody>): (request: NextRequest) => Promise<Response>;
653
710
  }
654
711
 
655
712
  declare const BASE_NETWORK = "eip155:8453";
@@ -658,7 +715,7 @@ declare const TEMPO_USDC_CURRENCY = "0x20c000000000000000000000b9537d11c60e8b50"
658
715
  declare const ZERO_EVM_ADDRESS = "0x0000000000000000000000000000000000000000";
659
716
 
660
717
  type RouterEnv = Record<string, string | undefined>;
661
- type RouterConfigIssueCode = 'missing_base_url' | 'empty_protocols' | 'missing_x402_accepts' | 'missing_x402_network' | 'unsupported_x402_network' | 'missing_x402_asset' | 'invalid_x402_decimals' | 'missing_x402_payee' | 'missing_cdp_keys' | 'placeholder_payee' | 'missing_mpp_config' | 'missing_mpp_secret_key' | 'missing_mpp_currency' | 'missing_mpp_recipient' | 'missing_mpp_rpc_url' | 'missing_mpp_default_store_env';
718
+ type RouterConfigIssueCode = 'missing_base_url' | 'empty_protocols' | 'missing_x402_accepts' | 'missing_x402_network' | 'unsupported_x402_network' | 'missing_x402_asset' | 'invalid_x402_decimals' | 'missing_x402_payee' | 'missing_cdp_keys' | 'placeholder_payee' | 'missing_mpp_config' | 'missing_mpp_secret_key' | 'missing_mpp_currency' | 'invalid_mpp_currency' | 'missing_mpp_recipient' | 'invalid_mpp_recipient' | 'missing_mpp_rpc_url' | 'invalid_mpp_fee_payer_key' | 'missing_mpp_default_store_env';
662
719
  interface RouterConfigIssue {
663
720
  code: RouterConfigIssueCode;
664
721
  message: string;
@@ -699,7 +756,7 @@ interface MonitorEntry {
699
756
  critical?: number;
700
757
  }
701
758
  interface ServiceRouter<TPriceKeys extends string = never> {
702
- route<K extends string>(keyOrDefinition: K | RouteDefinition<K>): [K] extends [TPriceKeys] ? RouteBuilder<undefined, undefined, undefined, true, false, false, false, false> : RouteBuilder<undefined, undefined, undefined, false, false, false, false, false>;
759
+ route<K extends string>(keyOrDefinition: K | RouteDefinition<K>): [K] extends [TPriceKeys] ? RouteBuilder<undefined, undefined, undefined, true, false, false> : RouteBuilder<undefined, undefined, undefined, false, false, false>;
703
760
  wellKnown(): (request: NextRequest) => Promise<NextResponse>;
704
761
  openapi(): (request: NextRequest) => Promise<NextResponse>;
705
762
  llmsTxt(): (request: NextRequest) => Promise<NextResponse>;
@@ -710,4 +767,4 @@ declare function createRouter<const P extends Record<string, string> = Record<ne
710
767
  prices?: P;
711
768
  }): ServiceRouter<Extract<keyof P, string>>;
712
769
 
713
- export { type AlertEvent, type AlertFn, type AlertLevel, type AuthEvent, type AuthMode, BASE_NETWORK, type DiscoveryConfig, type EntitlementStore, type ErrorEvent, type HandlerContext, type HandlerPaymentContext, HttpError, MemoryEntitlementStore, MemoryNonceStore, type MonitorEntry, type MppProtocolInfo, type NonceStore, type OveragePolicy, type PaidOptions, type PayToConfig, type PaymentEvent, type PaymentStatus, type PluginContext, type PricingConfig, type ProtocolType, type ProviderConfig, type ProviderQuotaEvent, type QuotaInfo, type QuotaLevel, type RedisEntitlementStoreOptions, type RedisNonceStoreOptions, type RequestMeta, type ResponseMeta, RouteBuilder, type RouteEntry, RouteRegistry, type RouterConfig, RouterConfigError, type RouterConfigIssue, type RouterConfigIssueCode, type RouterConfigValidationOptions, type RouterEnv, type RouterPlugin, SIWX_CHALLENGE_EXPIRY_MS, SOLANA_MAINNET_NETWORK, type ServiceRouter, type SettlementEvent, TEMPO_USDC_CURRENCY, type TierConfig, type X402AcceptConfig, type X402FacilitatorTarget, type X402FacilitatorsConfig, type X402ResolvedAccept, type X402RouterFacilitatorConfig, type X402Server, ZERO_EVM_ADDRESS, consolePlugin, createRedisEntitlementStore, createRedisNonceStore, createRouter, formatRouterConfigIssues, getRouterConfigIssues, mppFromEnv, paidOptionsForProtocols, validateRouterConfig, x402AcceptsFromEnv };
770
+ export { type AlertEvent, type AlertFn, type AlertLevel, type AuthEvent, type AuthMode, BASE_NETWORK, type DiscoveryConfig, type EntitlementStore, type ErrorEvent, type HandlerContext, type HandlerPaymentContext, HttpError, MemoryEntitlementStore, MemoryNonceStore, type MonitorEntry, type MppProtocolInfo, type NonceStore, type OveragePolicy, type PaidOptions, type PayToConfig, type PaymentEvent, type PaymentStatus, type PluginContext, type PricingConfig, type ProtocolType, type ProviderConfig, type ProviderQuotaEvent, type QuotaInfo, type QuotaLevel, type RedisEntitlementStoreOptions, type RedisNonceStoreOptions, type RequestMeta, type ResponseMeta, RouteBuilder, type RouteEntry, RouteRegistry, type RouterConfig, RouterConfigError, type RouterConfigIssue, type RouterConfigIssueCode, type RouterConfigValidationOptions, type RouterEnv, type RouterPlugin, SIWX_CHALLENGE_EXPIRY_MS, SOLANA_MAINNET_NETWORK, type ServiceRouter, type SettledHandlerErrorContext, type SettlementErrorContext, type SettlementEvent, type SettlementLifecycle, type SettlementLifecycleContext, type SettlementSettledContext, TEMPO_USDC_CURRENCY, type TierConfig, type X402AcceptConfig, type X402FacilitatorTarget, type X402FacilitatorsConfig, type X402ResolvedAccept, type X402RouterFacilitatorConfig, type X402Server, ZERO_EVM_ADDRESS, consolePlugin, createRedisEntitlementStore, createRedisNonceStore, createRouter, formatRouterConfigIssues, getRouterConfigIssues, mppFromEnv, paidOptionsForProtocols, validateRouterConfig, x402AcceptsFromEnv };