@parallel-protocol/x402 0.9.0 → 0.10.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/README.md CHANGED
@@ -40,11 +40,12 @@ Agent Merchant (this SDK) Facilitator
40
40
  - The handler only runs after the signature is verified.
41
41
  - The on-chain settlement happens after the handler produces a 2xx response — the agent is not charged on errors.
42
42
  - The settle is bound to the merchant that verified: `POST /x402/verify` hands the middleware a settle token that `POST /x402/settle` must present. The token stays in memory for the life of the request and never reaches the agent.
43
- - If settlement fails, the middleware discards the handler's response and returns `402` with `{ error: "PAYMENT_SETTLEMENT_FAILED", reason, message }`: `reason` is the facilitator's code (`INSUFFICIENT_BALANCE`, `AUTHORIZATION_USED`, `PAYMENT_EXPIRED`, ...) or this SDK's (`SETTLE_TIMEOUT`, `FACILITATOR_UNAVAILABLE`, `FACILITATOR_INVALID_RESPONSE`, `AMOUNT_MISMATCH`, `UNKNOWN`), `message` the facilitator's text or this SDK's, and the facilitator's `Retry-After` is copied when it sent one. A settle that timed out or could not reach the facilitator is retried once first, with the same settle token: once the first attempt has finished, the facilitator answers its recorded outcome, and any other answer to the retry tells the agent to check with the merchant.
43
+ - If settlement fails, the middleware discards the handler's response, its headers, cookies and trailers included (for the `cookies()` of Next.js, see [Next.js](#nextjs-app-router)), and returns `402` with `{ error: "PAYMENT_SETTLEMENT_FAILED", reason, message }`: `reason` is the facilitator's code (`INSUFFICIENT_BALANCE`, `AUTHORIZATION_USED`, `PAYMENT_EXPIRED`, ...) or this SDK's (`SETTLE_TIMEOUT`, `FACILITATOR_UNAVAILABLE`, `FACILITATOR_INVALID_RESPONSE`, `AMOUNT_MISMATCH`, `UNKNOWN`), `message` the facilitator's text or this SDK's, and the facilitator's `Retry-After` is copied when it sent one. A settle that timed out or could not reach the facilitator is retried once first, with the same settle token: once the first attempt has finished, the facilitator answers its recorded outcome, and any other answer to the retry tells the agent to check with the merchant.
44
44
  - A metered settle must settle the amount the handler asked for: a confirmation for another amount is refused and the response discarded.
45
45
  - Handlers are bounded by a 30-second timeout on Express, Hono and Next.js — a slow handler returns `504` and the agent is not charged. (Fastify manages handler execution itself, so no timeout is applied there.) Metered routes get their announced window minus two minutes kept for the settlement and its retry.
46
+ - On paid Express and Fastify routes, the handler does not see the conditional request headers (`If-Match`, `If-None-Match`, `If-Modified-Since`, `If-Unmodified-Since`): Express, or a plugin such as `@fastify/etag`, would turn the paid response into a `304` or a `412`, which is not settled. A paid request always gets the resource.
46
47
  - On paid Fastify routes, writing to `reply.raw` (the SSE/streaming pattern) fails loudly instead of sending: a raw response skips the settlement hooks and would go out unpaid. Streaming is not supported on paid routes.
47
- - CORS preflight (`OPTIONS`) is always passed through without challenge.
48
+ - CORS preflight (`OPTIONS`) is passed through without challenge, unless its path has `.` or `..` segments. On a paid route the handler still answers it, but only its status code and its `Access-Control-*`, `Vary` and `Allow` headers go out, with the standard reason phrase: no body, no other header, no trailer and no early hints. A handler that answers every method does not serve the paid content. On Next.js the handler does not run, see [Next.js](#nextjs-app-router).
48
49
 
49
50
  ---
50
51
 
@@ -61,8 +62,8 @@ Install your framework separately (all are optional peer dependencies):
61
62
  ```bash
62
63
  npm install express # Express 4 or 5
63
64
  npm install next # Next.js 14 or 15
64
- npm install fastify # Fastify 4 or 5
65
- npm install hono # Hono 4+
65
+ npm install fastify # Fastify 5.12.7 or a later 5.x
66
+ npm install hono # Hono 4
66
67
  ```
67
68
 
68
69
  TypeScript consumers should also install `viem` (peer dependency) — the public types use its `Address` type. Node ≥ 18 is required.
@@ -123,6 +124,8 @@ export const GET = withPayment(config, async (req) => {
123
124
  });
124
125
  ```
125
126
 
127
+ On a paid route, a CORS preflight (`OPTIONS`) gets an empty `204` without running the handler, so set the route's CORS headers in the `headers` of `next.config.js`. Next.js adds the cookies set with `cookies()` from `next/headers` to the response the route returns, even to the `402` of a failed settlement. Set the cookies of a paid response on the `NextResponse` the handler returns instead: they leave with it only.
128
+
126
129
  ### Fastify
127
130
 
128
131
  ```typescript
@@ -149,6 +152,8 @@ app.get("/api/data", async () => {
149
152
  });
150
153
  ```
151
154
 
155
+ On a refusal after the handler, only the cookies already set on the reply before the handler go out. Cookies that a plugin such as `@fastify/cookie` writes from its own `onSend` hook are left out of the `402`, even those set with `reply.setCookie` before the handler.
156
+
152
157
  ### Hono
153
158
 
154
159
  ```typescript
@@ -201,13 +206,13 @@ interface PaymentMiddlewareConfig {
201
206
  | `decimals` | `number` | No | Override for the decimals used to parse a string price. By default each accepted token is priced with its own decimals from the `@parallel-protocol/chains` catalog, so a `"0.01"` price is correct whether paid in USDp (18) or USDC (6), with no configuration. Only needed for a custom token outside the catalog: without it, an unknown token raises `X402ConfigError` rather than being silently guessed. When set, it applies to every token of the route. |
202
207
  | `network` | `string` | Yes | Chain slug: `"ethereum"`, `"base"`, `"avalanche"`, `"hyperevm"`. |
203
208
  | `payTo` | `Address` | Yes | The merchant's receiving address. |
204
- | `acceptedTokens` | `Address[]` | No | List of accepted token addresses. Defaults to `[USDp, USDC, sUSDp]` for known networks (see [Default tokens](#default-tokens)). On an `upto` route it defaults to USDp alone, and any other token is rejected at construction: metered payments are USDp only. |
209
+ | `acceptedTokens` | `Address[]` | No | List of accepted token addresses. Defaults to `[USDp, USDC, sUSDp]` for known networks (see [Default tokens](#default-tokens)). On an `upto` route it defaults to USDp alone. It may also list the network's USDC where the escrow takes Permit2 payments (Base), see [Payments from standard x402 clients](#payments-from-standard-x402-clients-usdc-through-permit2). Any other token is rejected at construction. |
205
210
  | `description` | `string` | No | Human-readable description of the resource, included in the 402 body. |
206
211
  | `scheme` | `"exact" \| "upto"` | No | `exact` (default) charges `price` as invoiced. `upto` invoices `price` as a **cap** the payer signs; the handler names the actual amount to charge and the rest is refunded on-chain — see [Metered routes](#metered-routes-upto). |
207
212
  | `meter` | `(ctx) => string \| bigint \| undefined` | No | `upto` routes only: reads the amount to charge from the handler's response, see [Reading the amount from the response](#reading-the-amount-from-the-response-meter). |
208
213
  | `maxTimeoutSeconds` | `number` | No | Settlement window announced to the payer (default 300). `@parallel-protocol/x402-fetch` signs for it on `upto` routes, and for five minutes on `exact` routes. Metered routes announce how long their handler may take. On an `upto` route it is 3600 (one hour) at most: a larger value raises `X402ConfigError` at construction, and the facilitator refuses an authorization valid for longer with `WINDOW_TOO_LONG`. On metered routes, Express, Hono and Next.js derive the handler timeout from it, minus two minutes kept for the settlement and its retry: a 300 s window leaves the handler 180 s, and a handler always gets at least 30 s. Fastify does not time handlers out, set the server's `connectionTimeout` / `requestTimeout` yourself. |
209
214
 
210
- Route matching uses **longest prefix wins**: `/api/v1/users` will match a `/api/v1` route config before a `/api` one.
215
+ Route matching uses **longest prefix wins**: `/api/v1/users` will match a `/api/v1` route config before a `/api` one. The request path and the route keys are compared once normalized: case, percent-encoding, repeated or trailing slashes, a fragment, `;` path parameters and an absolute-form origin make no difference, so another form of a paid path is charged like the path itself. A key with a trailing slash, such as `/api/`, covers the paths below it as `/api` does. Routers and proxies do not all resolve `.` and `..` segments, so a path that has some and reaches a paid route is refused with a `400` and `INVALID_PATH`. Under an Express mount prefix, such as `app.use("/api", ...)`, a key can be written with the full path or relative to the mount, and the key `/` covers only the mount's root: write `/api` to cover every path below it. A Next.js route handler sees the path without the `basePath`, so write keys without it.
211
216
 
212
217
  ---
213
218
 
@@ -314,8 +319,17 @@ interface PaymentRequired {
314
319
  amount: string; // smallest unit of `asset`, per its own decimals — the cap for "upto"
315
320
  payTo: string;
316
321
  maxTimeoutSeconds: number; // the route's settlement window (default 300)
317
- extra: { decimals: number }; // decimals of `asset` — payers need not guess
322
+ extra: {
323
+ decimals: number; // decimals of `asset` — payers need not guess
324
+ // on an "upto" entry paid through Permit2 (USDC): the token's EIP-712 domain and the escrow
325
+ name?: string;
326
+ version?: string;
327
+ facilitatorAddress?: string;
328
+ };
318
329
  }>;
330
+ extensions?: { // present when an entry is paid through Permit2
331
+ eip2612GasSponsoring: { info: { description: string; version: "1" }; schema: object };
332
+ };
319
333
  }
320
334
  ```
321
335
 
@@ -364,7 +378,13 @@ interface PaymentConfirmation {
364
378
  gasSponsored: boolean;
365
379
  networkId: string; // "eip155:8453"
366
380
  amountSettled?: string; // metered routes: what was actually charged (smallest unit)
381
+ amountDelivered?: string; // paid through Permit2: the USDp the merchant received (smallest unit), when the facilitator read it
367
382
  alreadySettled?: true; // the facilitator answered from a recorded outcome, this call broadcast nothing
383
+ // The fields of the standard x402 settlement response, read by clients built on the x402 SDKs:
384
+ transaction?: string; // the txHash
385
+ network?: string; // "eip155:8453"
386
+ amount?: string; // the amount charged, in the smallest unit of the paid token
387
+ payer?: string;
368
388
  }
369
389
  ```
370
390
 
@@ -374,7 +394,7 @@ The `access-control-expose-headers` header is set automatically so browsers can
374
394
 
375
395
  ## Metered routes (`upto`)
376
396
 
377
- Some resources only know their cost after the work is done: an LLM completion is priced by the tokens it generates. A route declared with `scheme: "upto"` invoices `price` as a **cap**: the payer signs an authorization for that cap, the handler serves the request and names the amount it actually charges, and the facilitator settles that amount through Parallel's escrow contract, refunding the remainder to the payer in the same transaction. Metered payments are made in USDp: `acceptedTokens` defaults to the network's USDp and may not list any other token. They settle only on chains where the escrow is deployed, which today is Base alone (`UPTO_CHAINS` in `@parallel-protocol/chains`). On another chain the facilitator refuses the payment at verify with `PAYMENT_METHOD_NOT_SUPPORTED`.
397
+ Some resources only know their cost after the work is done: an LLM completion is priced by the tokens it generates. A route declared with `scheme: "upto"` invoices `price` as a **cap**: the payer signs an authorization for that cap, the handler serves the request and names the amount it actually charges, and the facilitator settles that amount through Parallel's escrow contract, refunding the remainder to the payer in the same transaction. Metered payments are made in USDp, and in USDC from standard x402 clients ([below](#payments-from-standard-x402-clients-usdc-through-permit2)). They settle only on chains where the escrow is deployed, which today is Base alone (`UPTO_CHAINS` in `@parallel-protocol/chains`). On another chain the facilitator refuses the payment at verify with `PAYMENT_METHOD_NOT_SUPPORTED`.
378
398
 
379
399
  ```typescript
380
400
  routes: {
@@ -430,11 +450,36 @@ Instead of a header the handler sets, a route may carry a `meter` function that
430
450
  | `"0"` | The request is served for free, nothing is settled. |
431
451
  | `undefined` or `null` | The `payment-settle-amount` header decides, with the rules above (absent charges the cap). |
432
452
  | it throws, or returns something that is not an amount | The response is discarded and the client gets 402 `INVALID_SETTLE_AMOUNT`. |
453
+ | a bigint, on a route that accepts tokens with different decimals | Refused like a failing meter, since a bigint is in one token's unit. Return a decimal string such as `"0.75"`. |
433
454
 
434
455
  The response body is read only on routes that have a `meter` function, from a clone or from the buffered chunks the middleware already holds, so other routes are untouched. Reading it gets the handler's own time budget: a body that streams from a slow upstream and is not complete within the window is treated like a failing meter. Return a buffered body on metered routes. `meter` only applies to `upto` routes.
435
456
 
436
457
  Timing: the payer's authorization expires after `maxTimeoutSeconds`, and settlement may take up to two minutes, an attempt of up to 60 s and one retry. On Express, Hono and Next.js the handler timeout is raised to `maxTimeoutSeconds − 120 s` for metered routes. Fastify handlers are never timed out by the middleware, so size the server's own `connectionTimeout` and `requestTimeout` to the window. A handler that finishes after the window has done the work, but the payment can no longer settle: the client gets 402 `PAYMENT_SETTLEMENT_FAILED` with reason `PAYMENT_EXPIRED`, and the merchant is not paid. Keep a margin.
437
458
 
459
+ ### Payments from standard x402 clients (USDC through Permit2)
460
+
461
+ Agents built on the x402 SDKs, AgentCash among them, pay a metered route with a Permit2 signature of the cap in USDC. To accept them, list the network's USDC in `acceptedTokens`:
462
+
463
+ ```typescript
464
+ import { PARALLEL_TOKENS } from "@parallel-protocol/x402";
465
+
466
+ routes: {
467
+ "/v1/chat/completions": {
468
+ scheme: "upto",
469
+ price: "2", // the cap, in USDC or in USDp
470
+ network: "base",
471
+ payTo: "0xYourReceivingAddress",
472
+ acceptedTokens: [PARALLEL_TOKENS.base.usdc, PARALLEL_TOKENS.base.usdp],
473
+ },
474
+ },
475
+ ```
476
+
477
+ The 402 lists the USDC entry first, because these clients pay the first `upto` entry, and declares the `eip2612GasSponsoring` extension, so an agent that has not approved Permit2 signs a permit instead ([402 response](#402-response-payment-required)). The escrow converts the USDC to USDp, which the merchant receives. The handler names the amount to charge as for any metered route. The `payment-response` receipt gives the USDC charged in `amountSettled`, and the USDp the merchant received in `amountDelivered` when the facilitator could read it from the settlement.
478
+
479
+ Before listing USDC, approve the escrow once for USDC from the `payTo` address, since the escrow takes the USDC back to convert it. Its address is `getUptoEscrowAddress("base")` from `@parallel-protocol/chains`. Receive on a dedicated address rather than a treasury: it bounds what the approval exposes.
480
+
481
+ The price must be a decimal string such as `"2"`, and so must the amount a `meter` returns. A bigint, or an explicit `decimals`, is in one token's unit and is refused on a route that accepts both tokens. Standard clients also refuse a cap above their spending limit, a few dollars by default, so keep the cap of such a route low.
482
+
438
483
  ---
439
484
 
440
485
  ## Per-request pricing
@@ -455,7 +500,7 @@ routes: {
455
500
  },
456
501
  ```
457
502
 
458
- The function gets `{ method, path, url, header(name), body }`. `body` is the request body as the framework parsed it (`undefined` when there is none), so it is available where your framework parses it:
503
+ The function gets `{ method, path, routePath, url, header(name), body }`. `path` is the request path as the framework gives it, and `routePath` the path the route matched, in the form routes are matched on. Under an Express mount, that is the full path when the route key is written with it. To price by path, compare `routePath`, so that another form of the same path, such as `/API/premium`, gets the same price. `body` is the request body as the framework parsed it (`undefined` when there is none), so it is available where your framework parses it:
459
504
 
460
505
  | Framework | `body` |
461
506
  |-----------|--------|
@@ -541,7 +586,7 @@ Without `meter`, what is charged after the response is the handler's call: set t
541
586
 
542
587
  ## Payment routes
543
588
 
544
- The facilitator supports 13 settlement routes. The route is selected automatically based on the signed payload:
589
+ The facilitator supports 14 settlement routes. The route is selected automatically based on the signed payload:
545
590
 
546
591
  | Route | Credential method(s) | tokenIn (agent pays) | tokenOut (merchant receives) | Gas est. | Notes |
547
592
  |-------|--------------------------|---------------------|------------------------------|----------|-------|
@@ -558,6 +603,7 @@ The facilitator supports 13 settlement routes. The route is selected automatical
558
603
  | **K** | `redeemWithAuthorization` + `swapExactOutputWithAuthorization` | sUSDp | non-USDC backing (exact) | ~180K | Same as I but for other Parallelizer collaterals (atomic multicall3) |
559
604
  | **L** | `partialRedeemWithAuthorization` | sUSDp (partial) + USDp | USDp or backing collateral | ~180K | Partial sUSDp redeem combined with existing USDp; delivers to merchant (atomic multicall3) |
560
605
  | **U** | `uptoWithAuthorization` | USDp (cap) | USDp (actual) | ~130K | Metered payment: the escrow pulls the signed cap, pays the merchant the settled amount and refunds the rest atomically (scheme `upto`) |
606
+ | **V** | `uptoPermit2` | USDC (cap), from a standard x402 client | USDp (actual, converted) | ~390K | Metered payment signed with Permit2: the x402 proxy pays the merchant the settled amount in USDC, which the escrow converts to USDp in the same transaction (scheme `upto`) |
561
607
 
562
608
  **Chain requirements:**
563
609
  - Routes B, D, E, G, I, K, L require the **Parallelizer** to be deployed on the target chain (Ethereum, Base, Avalanche, HyperEVM).
@@ -565,7 +611,7 @@ The facilitator supports 13 settlement routes. The route is selected automatical
565
611
  - Routes C, G, H, I, J, K, L require **sUSDp** (savings module) to be deployed on the target chain.
566
612
  - Agents pay with USDp, sUSDp or the chain's USDC. Route F takes the chain's USDC, routes E and G the chain's USDC where it is a collateral (Base and Avalanche). On these three routes any other `tokenIn` is refused at verify and at settle with `PAYMENT_METHOD_NOT_SUPPORTED`, before the signature is checked.
567
613
  - The facilitator settles on Ethereum, Base, Avalanche and HyperEVM. A payment on another chain is refused at verify and at settle with `NETWORK_MISMATCH`.
568
- - Route U needs the escrow contract, deployed on Base only. A metered payment on another chain is refused at verify with `PAYMENT_METHOD_NOT_SUPPORTED`.
614
+ - Routes U and V need the escrow contract, deployed on Base only. A metered payment on another chain is refused at verify with `PAYMENT_METHOD_NOT_SUPPORTED`.
569
615
  - Gas sponsorship is decided by the facilitator per payment and reported as `gasSponsored` in the settlement receipt.
570
616
  - The facilitator's `GET /capabilities?chain=<slug>` lists the routes available on a chain right now, with the gas estimates above and the burn fees.
571
617
 
@@ -635,6 +681,7 @@ import {
635
681
 
636
682
  // Utils
637
683
  encodeBase64,
684
+ normalizeRoutePath,
638
685
  parsePrice,
639
686
  toEip155Network,
640
687
  validateAddress,
@@ -647,8 +694,8 @@ import {
647
694
  |-------------|--------|-----------|
648
695
  | `@parallel-protocol/x402/express` | `paymentMiddleware(config)` | Express 4/5 |
649
696
  | `@parallel-protocol/x402/next` | `withPayment(config, handler)` | Next.js 14/15 |
650
- | `@parallel-protocol/x402/fastify` | `paymentMiddleware(config)` | Fastify 4/5 |
651
- | `@parallel-protocol/x402/hono` | `paymentMiddleware(config)` | Hono 4+ |
697
+ | `@parallel-protocol/x402/fastify` | `paymentMiddleware(config)` | Fastify 5 (5.12.7 or later) |
698
+ | `@parallel-protocol/x402/hono` | `paymentMiddleware(config)` | Hono 4 |
652
699
 
653
700
  Each subpath also exports its adapter class (`ExpressAdapter`, `NextAdapter`, `FastifyAdapter`, `HonoAdapter`) for use with [`createPaymentGate`](#advanced-createpaymentgate).
654
701
 
@@ -702,7 +749,9 @@ const result = await gate({
702
749
  });
703
750
 
704
751
  if (result.type === "pass") {
705
- // Route not configured for payment, proceed normally
752
+ // Route not configured for payment, proceed normally.
753
+ // result.preflight: an OPTIONS on a paid route, send only the handler's
754
+ // status code and its Access-Control-*, Vary and Allow headers
706
755
  } else if (result.type === "error") {
707
756
  // Send result.result.status (402, or the status of a price refusal)
708
757
  // with result.result.headers and result.result.body
@@ -779,12 +828,13 @@ What the middleware answers on a paid route instead of the handler's response:
779
828
  | 402 | `{ error: "INVALID_SETTLE_AMOUNT", message }` | A metered route named an amount the middleware cannot settle, see [Metered routes](#metered-routes-upto) |
780
829
  | 504 | `{ error: "HANDLER_TIMEOUT", message }` on Express and Hono, an empty body on Next.js | The handler did not answer within its timeout, and nothing is settled. Next.js also answers this empty 504 when the handler throws |
781
830
  | 400, or the status the price function chose | `{ error, message }` | A price function refused the request with `X402PriceRejectedError`, and nothing was invoiced |
831
+ | 400 | `{ error: "INVALID_PATH", message }` | The path has `.` or `..` segments, and reaches a paid route. Nothing was invoiced |
782
832
 
783
833
  ### Thrown by the SDK
784
834
 
785
835
  | Error class | When |
786
836
  |-------------|------|
787
- | `X402ConfigError` | Invalid config at construction, the message naming the first field at fault: a bad `payTo`, a facilitator URL that is neither https nor plain http on `localhost`, `127.0.0.1` or `[::1]`, empty `routes`, a `network` of the catalog other than `ethereum`, `base`, `avalanche` or `hyperevm`, a malformed or non-positive `price`, a `meter` on a route that is not `upto`, or an `upto` route with `maxTimeoutSeconds` above 3600 or a token other than USDp. Then, on a request to a route: an unknown network with no `acceptedTokens`, an accepted token whose decimals are neither in the catalog nor set via `decimals`, a price finer than the token's precision, or a price function that throws or returns something that is not a price |
837
+ | `X402ConfigError` | Invalid config at construction, the message naming the first field at fault: a bad `payTo`, a facilitator URL that is neither https nor plain http on `localhost`, `127.0.0.1` or `[::1]`, empty `routes`, a `network` of the catalog other than `ethereum`, `base`, `avalanche` or `hyperevm`, a malformed or non-positive `price`, two routes that are the same path once normalized with different configs, a `meter` on a route that is not `upto`, or an `upto` route with `maxTimeoutSeconds` above 3600, a token other than USDp and the network's USDC paid through Permit2, or a bigint price or an explicit `decimals` while it accepts tokens with different decimals. Then, on a request to a route: an unknown network with no `acceptedTokens`, an accepted token whose decimals are neither in the catalog nor set via `decimals`, a price finer than the token's precision, or a price function that throws, returns something that is not a price, or returns a bigint on an `upto` route that accepts tokens with different decimals |
788
838
  | `X402RuntimeError` | Runtime failure; `.code` is one of the SDK codes below, or a facilitator error code propagated verbatim (see next table) |
789
839
  | `X402PriceRejectedError` | Thrown by a price function to refuse a request before anything is invoiced; the client gets `.status` (400 by default) with `{ error: .code, message }`. `isPriceRejection(err)` recognises one by shape. |
790
840
 
@@ -825,12 +875,15 @@ When the facilitator refuses a payment at verify, its code is forwarded as the `
825
875
 
826
876
  | Code | Facilitator status | Meaning | What the agent should do |
827
877
  |------|--------------------|---------|--------------------------|
828
- | `INVALID_PAYLOAD` | 400 | The payload does not have the shape of its route, carries a field another route would read, or uses a method of the other scheme | Sign a payment that matches one of the `accepts` entries |
829
- | `INVALID_SIGNATURE` | 400 | EIP-712 signature recovery failed | Sign again |
878
+ | `INVALID_PAYLOAD` | 400 | The payload does not have the shape of its route, carries a field another route would read, or uses a method of the other scheme, or its EIP-2612 permit is not for the cap or ends before the Permit2 deadline | Sign a payment that matches one of the `accepts` entries |
879
+ | `INVALID_SIGNATURE` | 400 | EIP-712 signature recovery failed, or the payer's contract wallet refused the signature (payments through Permit2) | Sign again |
830
880
  | `INVALID_NONCE` | 409 | Nonce already used: the same payment reached the facilitator before, and may have settled | Do not sign another payment before checking with the merchant |
831
881
  | `PAYMENT_EXPIRED` | 402 | `validBefore` has passed | Sign a new payment |
882
+ | `PAYMENT_NOT_YET_VALID` | 402 | A payment through Permit2 whose `validAfter` has not passed yet | Sign again with a `validAfter` in the past |
832
883
  | `INSUFFICIENT_AMOUNT` | 402 | The amount the route guarantees the merchant is below the price, or the authorization is for zero | Sign for the invoiced `amount` |
833
884
  | `INSUFFICIENT_BALANCE` | 402 | The payer does not hold the signed amount (metered payments) | Fund the wallet, sign a new payment |
885
+ | `PERMIT2_ALLOWANCE_REQUIRED` | 412 | A payment through Permit2 from a payer whose Permit2 approval for the token is below the cap, with no EIP-2612 permit or one the token refused. The message starts with `permit2_allowance_required`, the reason standard x402 clients look for | Approve Permit2 for the token once, or sign the payment with an EIP-2612 permit |
886
+ | `TOKEN_BLOCKED` | 403 | The token refuses the transfer: an address is blacklisted, or the token is paused. Nothing was taken | Pay with another token |
834
887
  | `ASSET_MISMATCH` | 400 | The route does not deliver the token the merchant requires | Pay so that the merchant receives one of the `accepts` assets |
835
888
  | `RECIPIENT_MISMATCH` | 400 | The signed recipient is not the merchant's `payTo` | Sign to the invoiced `payTo` |
836
889
  | `NETWORK_MISMATCH` | 400 | The facilitator does not support the payload's chain, or it is not the route's chain | Sign on the invoiced network |
@@ -840,6 +893,9 @@ When the facilitator refuses a payment at verify, its code is forwarded as the `
840
893
  | `RATE_LIMIT_EXCEEDED` | 429 | The payer passed 1,000 verifies in the current UTC hour | Pay again after `Retry-After`, which points to the next hour |
841
894
  | `PARALLELIZER_PAUSED` | 503 | The Parallelizer has closed the swap the payment needs, or its state could not be read. Nothing was taken | Pay with another token, or pay again later |
842
895
  | `SAVINGS_PAUSED` | 503 | The sUSDp savings vault the payment deposits into or redeems from is paused, or its state could not be read. Nothing was taken | Pay with another token, or pay again later |
896
+ | `MERCHANT_NOT_ENABLED` | 503 | The merchant's `payTo` has not approved the escrow for USDC yet, so a USDC payment cannot be converted (see [Payments from standard x402 clients](#payments-from-standard-x402-clients-usdc-through-permit2)). Nothing was taken | Pay with USDp, or pay again later |
897
+ | `SWAP_UNAVAILABLE` | 503 | The Parallelizer cannot convert the token now: a cap is reached or a price feed is stale. Nothing was taken | Pay with USDp, or pay again later |
898
+ | `SETTLEMENT_WOULD_FAIL` | 402 | A payment through Permit2 whose settlement, simulated at verify, would revert for another reason. Nothing was taken | Pay with another token, or sign a new payment later |
843
899
  | `SERVICE_UNAVAILABLE` | 503 | The facilitator could not read the chain or its own store | Pay again after a short wait |
844
900
 
845
901
  An `error` that is not a code (capitals, digits and underscores) is not forwarded: the 402 carries `error: "FACILITATOR_INVALID_RESPONSE"` with the facilitator's message instead. The facilitator answers this way with `Bad Request` to a payment payload that does not match its request schema, and with `Too Many Requests` from its per-IP limit (see [Rate limits](#rate-limits)).
@@ -848,7 +904,7 @@ When the facilitator refuses at settle, the 402 body is `{ error: "PAYMENT_SETTL
848
904
 
849
905
  | Reason | Facilitator status | Meaning | What the agent should do |
850
906
  |--------|--------------------|---------|--------------------------|
851
- | `INSUFFICIENT_BALANCE` | 402 | The payer no longer holds the signed amount | Fund the wallet, sign a new payment |
907
+ | `INSUFFICIENT_BALANCE` | 402 | The payer no longer holds the signed amount, or for a payment through Permit2 the amount charged | Fund the wallet, sign a new payment |
852
908
  | `AUTHORIZATION_USED` | 409 | The authorization is already consumed or cancelled on-chain. It may have paid the merchant | Do not sign another payment before checking with the merchant |
853
909
  | `PAYMENT_EXPIRED` | 402 | The authorization expired before settlement | Sign a new payment |
854
910
  | `PAYMENT_NOT_VERIFIED` | 402 | No verified payment for this authorization, or an earlier settle of it failed. After `AUTHORIZATION_USED`, `RECEIPT_UNAVAILABLE` or `SUBMISSION_FAILED`, a repeated settle gets that code again instead. The merchant may have been paid, or may still be | Do not sign another payment before checking with the merchant |
@@ -857,7 +913,7 @@ When the facilitator refuses at settle, the 402 body is `{ error: "PAYMENT_SETTL
857
913
  | `RELAYER_UNAVAILABLE` | 503 | The facilitator is temporarily unable to settle. The payment was not taken, and the facilitator will not settle it | Retry later with a new payment, after `Retry-After` when given |
858
914
  | `PARALLELIZER_PAUSED`, `SAVINGS_PAUSED`, `SERVICE_UNAVAILABLE` | 503 | The swap or the savings vault the payment needs is paused, or its state could not be read, or the facilitator or the chain is temporarily unable to settle. This settle sent nothing, but the payment may still settle until its authorization expires, and an earlier attempt may already have settled it | Do not sign another payment before checking with the merchant |
859
915
  | `TX_REVERTED` | 500 | The transaction reverted for a reason the facilitator could not attribute | Sign a new payment |
860
- | `SWAP_PRICE_MOVED` | 402 | The Parallelizer refused the swap: its price or fee moved, between signing and settlement, beyond the margin signed with the payment. The payment was not taken, and the facilitator will not settle it | Sign a new payment |
916
+ | `SWAP_PRICE_MOVED` | 402 | The Parallelizer refused the swap: its price or fee moved beyond the payment's margin, the one signed with it, or for a payment through Permit2 the one the facilitator sets under the Parallelizer's quote. The payment was not taken, and the facilitator will not settle it | Sign a new payment |
861
917
  | `SWAP_UNAVAILABLE` | 503 | The Parallelizer refused the swap because it cannot make it now: the swap closed since the facilitator checked it, a cap or a maximum fee is reached, the amount exceeds what it can release, or a price feed is stale. The payment was not taken, and the facilitator will not settle it | Pay with another token, or later with a new payment |
862
918
  | `SUBMISSION_FAILED` | 500 | The settle failed. When the message says the outcome is unknown, a transaction may have been sent, or the facilitator could not read whether the authorization was used, and the payment may have settled or may still settle | Do not sign another payment before checking with the merchant. x402-fetch says so for this reason whatever the message. A client that reads the 402 itself may sign a new payment when the message says no transaction was submitted |
863
919
  | `RECEIPT_UNAVAILABLE` | 500 | The transaction was sent but its confirmation could not be read | Do not sign another payment before checking with the merchant |
@@ -901,6 +957,7 @@ import {
901
957
  | `validateAddress` | `(address: string) => address is Address` | Check 0x + 40-hex-char format |
902
958
  | `toEip155Network` | `(network: string) => string` | `"base"` → `"eip155:8453"` |
903
959
  | `encodeBase64` | `(obj: unknown) => string` | `JSON.stringify` → base64 |
960
+ | `normalizeRoutePath` | `(path: string) => string` | The form routes are matched on: `normalizeRoutePath("/API//Data/")` is `"/api/data"` |
904
961
  | `getDefaultAcceptedTokens` | `(network: string) => [Address, Address, Address] \| undefined` | `[USDp, USDC, sUSDp]` for known networks |
905
962
 
906
963
  ---
@@ -5,6 +5,8 @@ var X402_ERROR_CODES = {
5
5
  FACILITATOR_UNAVAILABLE: "FACILITATOR_UNAVAILABLE",
6
6
  FACILITATOR_INVALID_RESPONSE: "FACILITATOR_INVALID_RESPONSE",
7
7
  INVALID_PAYMENT: "INVALID_PAYMENT",
8
+ /** A path with `.` or `..` segments on a paid route, refused with 400. */
9
+ INVALID_PATH: "INVALID_PATH",
8
10
  /** The facilitator did not answer a settle within the client's timeout: the payment may still settle. */
9
11
  SETTLE_TIMEOUT: "SETTLE_TIMEOUT"
10
12
  };