@weftlabs/sdk 0.25.0

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.
Files changed (124) hide show
  1. package/README.md +499 -0
  2. package/dist/client-C9_zh3Qg.d.mts +11 -0
  3. package/dist/client-C9_zh3Qg.d.ts +11 -0
  4. package/dist/facilitator/index.d.mts +18 -0
  5. package/dist/facilitator/index.d.ts +18 -0
  6. package/dist/facilitator/index.js +180 -0
  7. package/dist/facilitator/index.js.map +1 -0
  8. package/dist/facilitator/index.mjs +149 -0
  9. package/dist/facilitator/index.mjs.map +1 -0
  10. package/dist/facilitator/middleware/index.d.mts +539 -0
  11. package/dist/facilitator/middleware/index.d.ts +539 -0
  12. package/dist/facilitator/middleware/index.js +1522 -0
  13. package/dist/facilitator/middleware/index.js.map +1 -0
  14. package/dist/facilitator/middleware/index.mjs +1493 -0
  15. package/dist/facilitator/middleware/index.mjs.map +1 -0
  16. package/dist/index.d.mts +5667 -0
  17. package/dist/index.d.ts +5667 -0
  18. package/dist/index.js +5555 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/index.mjs +5553 -0
  21. package/dist/index.mjs.map +1 -0
  22. package/dist/server.d.mts +8 -0
  23. package/dist/server.d.ts +8 -0
  24. package/dist/server.js +1587 -0
  25. package/dist/server.js.map +1 -0
  26. package/dist/server.mjs +1574 -0
  27. package/dist/server.mjs.map +1 -0
  28. package/docs/APIKeysApi.md +219 -0
  29. package/docs/AccountApi.md +72 -0
  30. package/docs/AccountBootstrapApproval.md +36 -0
  31. package/docs/AccountBootstrapCreated.md +44 -0
  32. package/docs/AccountBootstrapCreatedApproval.md +38 -0
  33. package/docs/AccountBootstrapCreatedResponse.md +32 -0
  34. package/docs/AccountBootstrapRequest.md +42 -0
  35. package/docs/AccountBootstrapStatus.md +40 -0
  36. package/docs/AccountBootstrapStatusResponse.md +32 -0
  37. package/docs/AccountDetails.md +43 -0
  38. package/docs/AgentBootstrapApi.md +223 -0
  39. package/docs/ApiKey.md +42 -0
  40. package/docs/ApiKeyCreated.md +40 -0
  41. package/docs/ApiKeyCreatedResponse.md +32 -0
  42. package/docs/ApiKeyListResponse.md +34 -0
  43. package/docs/AuthApi.md +408 -0
  44. package/docs/AuthResponse.md +32 -0
  45. package/docs/AuthResponseData.md +36 -0
  46. package/docs/BalanceApi.md +73 -0
  47. package/docs/BalanceResponse.md +45 -0
  48. package/docs/ConfirmRequest.md +32 -0
  49. package/docs/CreateApiKeyRequest.md +32 -0
  50. package/docs/DocumentationApi.md +65 -0
  51. package/docs/EnrolledResource.md +59 -0
  52. package/docs/ErrorResponse.md +33 -0
  53. package/docs/Fetch403Response.md +44 -0
  54. package/docs/FetchApi.md +91 -0
  55. package/docs/FetchBalanceSnapshot.md +41 -0
  56. package/docs/FetchErrorResponse.md +41 -0
  57. package/docs/FetchRequest.md +46 -0
  58. package/docs/FetchRequestBody.md +31 -0
  59. package/docs/FetchResponse.md +49 -0
  60. package/docs/InsufficientScopeResponse.md +37 -0
  61. package/docs/MeApiKey.md +40 -0
  62. package/docs/MeApiKeyCreator.md +37 -0
  63. package/docs/MeResponse.md +32 -0
  64. package/docs/MeResponseData.md +56 -0
  65. package/docs/MessageResponse.md +32 -0
  66. package/docs/MessageResponseData.md +32 -0
  67. package/docs/ModelError.md +39 -0
  68. package/docs/Pagination.md +38 -0
  69. package/docs/PasswordResetRequest.md +32 -0
  70. package/docs/PasswordUpdateRequest.md +36 -0
  71. package/docs/Payment.md +56 -0
  72. package/docs/PaymentListResponse.md +34 -0
  73. package/docs/PaymentResponse.md +32 -0
  74. package/docs/PaymentsApi.md +154 -0
  75. package/docs/PrincipalWallet.md +34 -0
  76. package/docs/PromoBalance.md +37 -0
  77. package/docs/Purchase.md +58 -0
  78. package/docs/PurchaseArtifact.md +38 -0
  79. package/docs/PurchaseListResponse.md +34 -0
  80. package/docs/PurchaseResponse.md +32 -0
  81. package/docs/PurchasesApi.md +157 -0
  82. package/docs/RateLimitResponse.md +34 -0
  83. package/docs/ResendConfirmationRequest.md +32 -0
  84. package/docs/ResourceEnrollmentRequest.md +39 -0
  85. package/docs/ResourceEnrollmentResponse.md +32 -0
  86. package/docs/ResourceStats.md +45 -0
  87. package/docs/ResourcesApi.md +77 -0
  88. package/docs/SearchAccessMethod.md +52 -0
  89. package/docs/SearchApi.md +86 -0
  90. package/docs/SearchCapabilityRef.md +34 -0
  91. package/docs/SearchCuratedCallability.md +34 -0
  92. package/docs/SearchCuratedExecution.md +32 -0
  93. package/docs/SearchCuratedOperation.md +36 -0
  94. package/docs/SearchCuratedService.md +34 -0
  95. package/docs/SearchCuratedSource.md +36 -0
  96. package/docs/SearchEndpointCall.md +39 -0
  97. package/docs/SearchEndpointHit.md +74 -0
  98. package/docs/SearchEndpointPrice.md +41 -0
  99. package/docs/SearchErrorResponse.md +35 -0
  100. package/docs/SearchExecutionModeFilter.md +34 -0
  101. package/docs/SearchFilterSpec.md +49 -0
  102. package/docs/SearchMethodFilter.md +34 -0
  103. package/docs/SearchPaymentOffer.md +47 -0
  104. package/docs/SearchPriceAtomicFilter.md +41 -0
  105. package/docs/SearchPriceUsdFilter.md +41 -0
  106. package/docs/SearchProtocolFilter.md +35 -0
  107. package/docs/SearchProviderRef.md +36 -0
  108. package/docs/SearchRequest.md +36 -0
  109. package/docs/SearchResourceTypeFilter.md +35 -0
  110. package/docs/SearchResponse.md +67 -0
  111. package/docs/SearchResponseWarningsInner.md +38 -0
  112. package/docs/SearchResult.md +38 -0
  113. package/docs/SearchStringSetFilter.md +34 -0
  114. package/docs/SearchWeftFetchCompatibility.md +36 -0
  115. package/docs/SignInRequest.md +34 -0
  116. package/docs/SignUpRequest.md +36 -0
  117. package/docs/SpendingPolicy.md +36 -0
  118. package/docs/User.md +36 -0
  119. package/docs/UserPrincipal.md +49 -0
  120. package/docs/Wallet.md +42 -0
  121. package/examples/charge-api-hono.mjs +40 -0
  122. package/examples/charge-api.mjs +43 -0
  123. package/examples/quickstart.mjs +16 -0
  124. package/package.json +105 -0
package/README.md ADDED
@@ -0,0 +1,499 @@
1
+ # `@weftlabs/sdk`
2
+
3
+ The supported TypeScript client for building buyer applications on Weft.
4
+
5
+ ## Get a buyer API key
6
+
7
+ Sign in at [weft.network](https://weft.network), then create a key in
8
+ [Dashboard → API keys](https://weft.network/dashboard/buyer/api_keys). Copy the
9
+ one-time `wk_*` value and store it as `WEFT_API_KEY` in your secret manager or
10
+ shell. Do not put it in source code, command arguments, or logs.
11
+
12
+ ```sh
13
+ export WEFT_API_KEY="wk_..."
14
+ ```
15
+
16
+ ## Install
17
+
18
+ ```sh
19
+ npm install @weftlabs/sdk @x402/core
20
+ ```
21
+
22
+ Node.js 18 or newer is required. The client uses
23
+ `https://weft.network` unless `baseUrl` is explicitly supplied.
24
+
25
+ ## OAuth access tokens
26
+
27
+ User-facing applications can pass an OAuth bearer token instead of an API key:
28
+
29
+ ```js
30
+ const weft = new WeftClient({ accessToken });
31
+ ```
32
+
33
+ Provide exactly one of `apiKey` or `accessToken`. The SDK sends either value as
34
+ bearer authentication. Your application owns OAuth registration, PKCE,
35
+ redirects, refresh, and secure session storage; never expose the token to
36
+ browser JavaScript.
37
+
38
+ ## First authenticated search
39
+
40
+ ```js
41
+ import { WeftClient } from "@weftlabs/sdk";
42
+
43
+ const apiKey = process.env.WEFT_API_KEY;
44
+ if (!apiKey) throw new Error("Set WEFT_API_KEY to a buyer wk_* API key");
45
+
46
+ const weft = new WeftClient({ apiKey });
47
+
48
+ const account = await weft.me();
49
+ const search = await weft.search({ query: "weather data API" });
50
+
51
+ console.log({ account: account.data, results: search.results });
52
+ ```
53
+
54
+ The complete example is shipped as [`examples/quickstart.mjs`](examples/quickstart.mjs)
55
+ and is executed from the packed npm artifact in CI.
56
+
57
+ ## Bounded paid fetch
58
+
59
+ Every paid fetch needs an explicit spending ceiling. Supply an idempotency key
60
+ and reuse that same key when retrying after a timeout or uncertain response.
61
+
62
+ ```js
63
+ import { randomUUID } from "node:crypto";
64
+ import { WeftClient } from "@weftlabs/sdk";
65
+
66
+ const apiKey = process.env.WEFT_API_KEY;
67
+ if (!apiKey) throw new Error("Set WEFT_API_KEY to a buyer wk_* API key");
68
+
69
+ const weft = new WeftClient({ apiKey });
70
+ const idempotencyKey = randomUUID();
71
+
72
+ const artifact = await weft.fetch(
73
+ {
74
+ url: "https://merchant.example/data",
75
+ maxCostUsd: "0.05",
76
+ },
77
+ { idempotencyKey },
78
+ );
79
+
80
+ console.log({ idempotencyKey, artifact });
81
+ ```
82
+
83
+ Do not create a new key for a retry of the same logical purchase. The CLI
84
+ generates a key automatically and returns it in its success envelope.
85
+
86
+ ## CLI
87
+
88
+ The `weft` executable is published separately as `@weftlabs/cli`.
89
+
90
+ ```sh
91
+ npx --package @weftlabs/cli weft me
92
+ npx --package @weftlabs/cli weft balance
93
+ npx --package @weftlabs/cli weft search "weather data API" --max-results 5
94
+ npx --package @weftlabs/cli weft fetch "https://merchant.example/data" \
95
+ --max-cost-usd 0.05
96
+ ```
97
+
98
+ The CLI accepts credentials from `--api-key-stdin`, `WEFT_API_KEY`, or its
99
+ protected local stored OAuth/bootstrap credentials. It never accepts a key in
100
+ process arguments. Every response is a versioned JSON envelope suitable for
101
+ scripts. See the [CLI guide](../cli/README.md) for credential-free bootstrap,
102
+ human claim, and automatic Skill installation.
103
+
104
+ ## Error handling
105
+
106
+ The generated transport throws `ResponseError` for non-2xx responses. Inspect
107
+ the status and the structured response body, and retain the server request ID
108
+ when asking for support.
109
+
110
+ ```js
111
+ import { WeftClient, WeftError } from "@weftlabs/sdk";
112
+
113
+ const apiKey = process.env.WEFT_API_KEY;
114
+ if (!apiKey) throw new Error("Set WEFT_API_KEY to a buyer wk_* API key");
115
+
116
+ const weft = new WeftClient({ apiKey });
117
+
118
+ try {
119
+ await weft.search({ query: "weather data API" });
120
+ } catch (error) {
121
+ if (error instanceof WeftError) {
122
+ console.error({
123
+ status: error.status,
124
+ code: error.code,
125
+ requestId: error.requestId,
126
+ retryable: error.retryable,
127
+ details: error.details,
128
+ });
129
+ }
130
+ throw error;
131
+ }
132
+ ```
133
+
134
+ - `401`: confirm that `WEFT_API_KEY` contains a current buyer `wk_*` key.
135
+ - `403`: inspect `code` and `details` for an insufficient balance or spending
136
+ policy denial before changing the request.
137
+ - `409` (`IDEMPOTENCY_CONFLICT`): this buyer already used the supplied
138
+ idempotency key for a different fetch request. Generate a new key for the
139
+ new operation; the original operation retries unchanged with its own key.
140
+ `retryable` is `false` — resending the conflicting request will conflict
141
+ again.
142
+ - `429`: honor `Retry-After` and back off.
143
+ - `5xx`: retry transient failures with backoff; reuse the idempotency key for a
144
+ paid fetch.
145
+ - `status: 0` (`NETWORK_ERROR`): the request failed before any Weft response,
146
+ so the outcome is uncertain. `retryable` is `true`; retry with backoff and
147
+ reuse the idempotency key for a paid fetch.
148
+
149
+ ## Advanced generated APIs
150
+
151
+ `WeftClient` is the stable application entrypoint. For operations it does not
152
+ yet wrap, the generated OpenAPI classes remain exported:
153
+
154
+ ```js
155
+ import { Configuration, SearchApi } from "@weftlabs/sdk";
156
+
157
+ const configuration = new Configuration({
158
+ accessToken: process.env.WEFT_API_KEY,
159
+ basePath: "https://weft.network",
160
+ });
161
+ const searchApi = new SearchApi(configuration);
162
+ const result = await searchApi.search({
163
+ searchRequest: { query: "weather data API" },
164
+ });
165
+ ```
166
+
167
+ See the [API reference](https://weft.network/docs) and
168
+ [OpenAPI document](https://weft.network/docs/openapi.yaml) for the full
169
+ contract.
170
+
171
+ ## Facilitator integration
172
+
173
+ Seller infrastructure can import the separately exported facilitator helpers:
174
+
175
+ ```js
176
+ import { createFacilitatorClient, getFeeInfo } from "@weftlabs/sdk/facilitator";
177
+
178
+ const facilitator = createFacilitatorClient();
179
+ const fee = await getFeeInfo();
180
+ ```
181
+
182
+ The default facilitator URL is `https://x402.weft.network`; override it through
183
+ the helper configuration or `X402_FACILITATOR_URL`.
184
+
185
+ ## Charging for your own API
186
+
187
+ The payment middleware asks unpaid callers to pay and lets paid callers
188
+ through. The money settles to your wallet; Weft never holds it.
189
+
190
+ Selling needs a **seller** key, not the buyer `wk_*` key above. Create one in
191
+ [Dashboard → Seller → API keys](https://weft.network/dashboard/seller/api_keys).
192
+ It starts with `ax_live_` and is shown once. Store it as
193
+ `WEFT_SELLER_API_KEY`, and put the wallet that gets paid in `WEFT_PAY_TO`.
194
+
195
+ A seller installs a scheme package too. The middleware carries the x402
196
+ plumbing; the scheme prices the route and shapes the payment:
197
+
198
+ ```sh
199
+ npm install @weftlabs/sdk @x402/core @x402/evm express
200
+ ```
201
+
202
+ <!-- example:charge-api.mjs:start -->
203
+
204
+ ```js
205
+ import express from "express";
206
+ import { ExactEvmScheme } from "@x402/evm/exact/server";
207
+ import { weftPaymentMiddleware } from "@weftlabs/sdk/facilitator/middleware";
208
+
209
+ const apiKey = process.env.WEFT_SELLER_API_KEY;
210
+ if (!apiKey) {
211
+ throw new Error("Set WEFT_SELLER_API_KEY to a seller ax_live_* API key");
212
+ }
213
+
214
+ const payTo = process.env.WEFT_PAY_TO;
215
+ if (!payTo) {
216
+ throw new Error("Set WEFT_PAY_TO to the wallet address that gets paid");
217
+ }
218
+
219
+ const network = process.env.WEFT_NETWORK ?? "eip155:8453";
220
+
221
+ const app = express();
222
+
223
+ app.use(
224
+ weftPaymentMiddleware(
225
+ {
226
+ "GET /v1/quote": {
227
+ accepts: { scheme: "exact", network, payTo, price: "$0.01" },
228
+ },
229
+ },
230
+ {
231
+ apiKey,
232
+ name: "Acme Pricing API",
233
+ type: "api",
234
+ tags: ["finance", "pricing"],
235
+ schemes: [{ network, server: new ExactEvmScheme() }],
236
+ },
237
+ ),
238
+ );
239
+
240
+ app.get("/v1/quote", (_req, res) => {
241
+ res.json({ symbol: "ACME", price: "12.34", currency: "USD" });
242
+ });
243
+
244
+ const port = Number(process.env.PORT ?? 3000);
245
+ app.listen(port, () => {
246
+ console.log(JSON.stringify({ listening: port }));
247
+ });
248
+ ```
249
+
250
+ <!-- example:charge-api.mjs:end -->
251
+
252
+ `weftPaymentMiddlewareHono` is the Hono equivalent and takes the same
253
+ configuration:
254
+
255
+ <!-- example:charge-api-hono.mjs:start -->
256
+
257
+ ```js
258
+ import { Hono } from "hono";
259
+ import { ExactEvmScheme } from "@x402/evm/exact/server";
260
+ import { weftPaymentMiddlewareHono } from "@weftlabs/sdk/facilitator/middleware";
261
+
262
+ const apiKey = process.env.WEFT_SELLER_API_KEY;
263
+ if (!apiKey) {
264
+ throw new Error("Set WEFT_SELLER_API_KEY to a seller ax_live_* API key");
265
+ }
266
+
267
+ const payTo = process.env.WEFT_PAY_TO;
268
+ if (!payTo) {
269
+ throw new Error("Set WEFT_PAY_TO to the wallet address that gets paid");
270
+ }
271
+
272
+ const network = process.env.WEFT_NETWORK ?? "eip155:8453";
273
+
274
+ const app = new Hono();
275
+
276
+ app.use(
277
+ weftPaymentMiddlewareHono(
278
+ {
279
+ "GET /v1/quote": {
280
+ accepts: { scheme: "exact", network, payTo, price: "$0.01" },
281
+ },
282
+ },
283
+ {
284
+ apiKey,
285
+ name: "Acme Pricing API",
286
+ type: "api",
287
+ tags: ["finance", "pricing"],
288
+ schemes: [{ network, server: new ExactEvmScheme() }],
289
+ },
290
+ ),
291
+ );
292
+
293
+ app.get("/v1/quote", (c) =>
294
+ c.json({ symbol: "ACME", price: "12.34", currency: "USD" }),
295
+ );
296
+
297
+ export default app;
298
+ ```
299
+
300
+ <!-- example:charge-api-hono.mjs:end -->
301
+
302
+ Both blocks are the shipped `examples/charge-api.mjs` and
303
+ `examples/charge-api-hono.mjs` verbatim. `tests/readme-examples.test.ts` fails
304
+ if they drift. The root artifact test runs both from the packed package against
305
+ a stub facilitator.
306
+
307
+ ### What the facilitator does per paid request
308
+
309
+ The middleware calls the facilitator twice. `/verify` checks the buyer's
310
+ payment before your handler runs. `/settle` moves the money after your handler
311
+ returns success.
312
+
313
+ `/settle` rejects a call without your seller key — every settlement 401s and
314
+ you are never paid. `/verify` works without the key but reads it when present,
315
+ and the facilitator uses it to attribute verification attempts to your
316
+ product. Set `apiKey` once and both are covered.
317
+
318
+ The direct REST contract for `/supported`, `/verify` and `/settle` is the
319
+ advanced path. Sellers do not need it to charge for a route.
320
+
321
+ ### Declaring your product
322
+
323
+ `name`, `type`, `tags` and `iconUrl` describe the product once and apply to
324
+ every protected route. They travel on the 402 challenge, are copied onto the
325
+ buyer's payment, and arrive with the settlement — so your product appears in
326
+ the Weft dashboard already named and categorised, with no form to fill in.
327
+
328
+ | Field | Meaning |
329
+ |---|---|
330
+ | `name` | Display name, e.g. `"Acme Pricing API"`. |
331
+ | `type` | `"api"`, `"agent"` or `"mcp"`. |
332
+ | `tags` | Up to four free-text tags, or five if you omit `type`. |
333
+ | `iconUrl` | Absolute `http`/`https` URL of an icon. |
334
+ | `productId` | Identifier of the dashboard product this deployment claims to be. |
335
+ | `manifestHash` | Hash of the product manifest this deployment was built from. |
336
+
337
+ `type`, `productId` and `manifestHash` additionally travel in the x402
338
+ extensions channel as `extensions["weft.product"]` — `{info, schema}`, with
339
+ `info` holding `kind`, `product_id` and `manifest_hash` and `schema` the JSON
340
+ Schema describing it. Buyers on `@x402/core` echo the declaration onto their
341
+ payment, so it arrives with the settlement; fields you do not declare are
342
+ omitted, never sent empty. A route that declares its own
343
+ `extensions["weft.product"]` keeps it.
344
+
345
+ `apiKey` is the one secret in the config: the API key minted with your product
346
+ in the Weft dashboard. It does two jobs. It **authenticates settlement** — the
347
+ facilitator requires it on every settle call that credits your wallet, so
348
+ without it (and without your own `facilitator.createAuthHeaders`) paid requests
349
+ fail at the money step. And it **announces the product** at boot — one
350
+ authenticated call carrying the identity above, so the dashboard shows your
351
+ product connected, named and typed before the first payment arrives. If you
352
+ supply your own `createAuthHeaders`, yours wins; `apiKey` never overrides it.
353
+ The announcement can never block or break your server: a facilitator that is
354
+ down or slow at boot costs you nothing, and payment-protected routes recover on
355
+ their own once it is reachable again.
356
+
357
+ Setting any of these on an individual route overrides the product-level value
358
+ for that route only — `type` included, so an API with an MCP endpoint beside it
359
+ can say so per route:
360
+
361
+ ```js
362
+ weftPaymentMiddleware(
363
+ {
364
+ "GET /v1/search": { accepts },
365
+ "POST /mcp": { accepts, type: "mcp" },
366
+ },
367
+ { name: "Acme Pricing API", type: "api" },
368
+ );
369
+ ```
370
+
371
+ `type` has no field of its own in the x402 protocol, so the SDK sends it as one
372
+ reserved tag — `weft:type:api`, `weft:type:agent`, `weft:type:mcp`. That is why
373
+ `tags` carries four of your own values rather than five: the protocol allows
374
+ five in total. Any `weft:type:*` value you put in `tags` yourself is dropped
375
+ with a warning — declare `type` instead.
376
+
377
+ #### Stamping the request on the payment
378
+
379
+ `extensions` on a route carries structured metadata to the buyer and on to the
380
+ settlement, alongside the `weft.product` declaration above. Any key in it may
381
+ be a **callback instead of a static value**, evaluated where `price` is
382
+ evaluated — while the challenge for that request is built:
383
+
384
+ ```js
385
+ weftPaymentMiddleware(
386
+ {
387
+ "POST /v1/generate": {
388
+ accepts: [{ scheme: "exact", network, payTo, price: quoteFromBody }],
389
+ extensions: {
390
+ "weft.request": async (context) => {
391
+ const { model, max_tokens } = await context.adapter.getBody();
392
+ return { model, max_tokens };
393
+ },
394
+ },
395
+ },
396
+ },
397
+ { apiKey, name: "Acme Image API", type: "api" },
398
+ );
399
+ ```
400
+
401
+ That is how a seller who prices per request — from a model name, a token
402
+ budget, a page count — can also say what the buyer asked for. The buyer's
403
+ client echoes the resolved value onto the payment, and the facilitator relays
404
+ it onto the settlement, so it is there to display next to the amount.
405
+
406
+ `weft.request` is the key to reach for. Under it the SDK owns the envelope: the
407
+ callback returns the `info` payload alone and a published JSON Schema is
408
+ stamped beside it, the same way `weft.product` is assembled from the fields you
409
+ declare. That schema is deliberately open — what a request asked for is your
410
+ vocabulary, not ours — so what it publishes is a posture rather than a field
411
+ list: seller-authored, display-only, key nothing on it.
412
+
413
+ `weft.product` says what is being sold and is fixed at boot. `weft.request`
414
+ says what this one call asked for. Any *other* key works too and ships your
415
+ value verbatim, envelope and all — the escape hatch if you need your own
416
+ namespace on the wire. Prefer the named key: x402 extensions are named
417
+ capabilities with published contracts, and a key each seller invents is
418
+ readable by nobody but Weft.
419
+
420
+ `await context.adapter.getBody()` rather than `context.adapter.getBody()`: the
421
+ body is a promise on Hono and a plain value on Express.
422
+
423
+ Two rules:
424
+
425
+ - **Be deterministic for one request.** The challenge is built twice — once for
426
+ the unpaid request, once for the buyer's paid retry — and the buyer's echo is
427
+ checked against the second one. A clock, a counter or a random value in here
428
+ fails that check and costs you the sale, not just the blob. Derive the value
429
+ from the request.
430
+ - **It is display-only.** By the time it reaches a consumer it is
431
+ unauthenticated buyer input, like every echoed extension. Attribution keys on
432
+ the API key that settled the payment, never on this.
433
+
434
+ Return plain JSON data. The value travels as JSON and is advertised exactly as
435
+ JSON round-trips it, so a `NaN` arrives as `null` and a function-valued field
436
+ does not arrive at all — the challenge and the buyer's echo always agree.
437
+
438
+ Anything the callback cannot deliver costs the blob and never the payment: a
439
+ callback that throws, returns a non-object, returns something JSON cannot carry,
440
+ or pushes the challenge's whole extensions object past the facilitator's 16 KiB
441
+ relay cap has its key dropped from the challenge, with one `[weft]` line saying
442
+ which and why. The rest of the declaration, `weft.product` included, ships as
443
+ normal. Return `undefined` to skip a request deliberately; that one is silent.
444
+
445
+ #### Declaring what revenue can be sliced by
446
+
447
+ `dimensions` names the fields inside `weft.request` that revenue may be
448
+ broken down by:
449
+
450
+ ```js
451
+ weftPaymentMiddleware(routes, {
452
+ apiKey,
453
+ name: "Acme Image API",
454
+ dimensions: ["model", "tier"],
455
+ });
456
+ ```
457
+
458
+ Declare that, and "how much did this endpoint earn this month with
459
+ `model = gpt-5.6`?" becomes a question the dashboard can answer.
460
+
461
+ It travels on the boot handshake and nowhere else — never on the 402
462
+ challenge. That is the whole point. The values on a payment are echoed back
463
+ by the buyer, so an aggregate over a field you never declared would be
464
+ summing input the buyer controlled. This call is authenticated with your API
465
+ key, so a field named here is one you vouched for.
466
+
467
+ **Declare dimensions, not payloads.** Low cardinality, enumerable: `model`,
468
+ `tier`, `size`, `region`. Never prompt text, a user id, or a document body —
469
+ those cannot be indexed, are useless as a breakdown, and put your users'
470
+ content somewhere it does not belong.
471
+
472
+ At most eight travel, each at most 64 characters and shaped like a field name.
473
+ Anything else is dropped with a `[weft]` line, and the handshake still goes.
474
+
475
+ #### What the SDK trims, and why
476
+
477
+ The x402 protocol's `ResourceInfo` is narrow, and a buyer that validates the
478
+ challenge rejects **the whole challenge** over one out-of-bounds field: you
479
+ would not lose your product name, you would lose the sale. So the SDK keeps
480
+ what it emits inside the protocol's bounds, and tells you at startup — one
481
+ `[weft]` line per problem — what changed. Nothing is reported per request, and
482
+ nothing throws; a payment server should not refuse to boot over a display name.
483
+
484
+ | Limit | What the SDK does |
485
+ |---|---|
486
+ | `name` over 32 characters | Truncates it to 32. `"Acme Real Estate Property Records API"` ships as `"Acme Real Estate Property Record"`. 32 is tight for real product names, so check what yours becomes. |
487
+ | A tag over 32 characters | Drops that tag, keeps the rest. |
488
+ | More tags than the protocol carries | Drops the extras; a declared `type` always survives. |
489
+ | A `name` or tag that is not printable ASCII | Drops it. `"Acme Café"` does not travel. |
490
+ | An `iconUrl` that is not an absolute `http`/`https` URL, or is over 2048 characters | Drops it. A dashboard renders this URL, so no other scheme is relayed. |
491
+ | A `type` outside `api`/`agent`/`mcp`, or any field of the wrong type | Ignores it and says so. Your other tags are unaffected. |
492
+
493
+ The ASCII restriction is the protocol's, not Weft's — the Weft facilitator
494
+ relays a name in any script. The SDK enforces it anyway, because the challenge
495
+ reaches buyers before it reaches any facilitator and a buyer running the
496
+ published schema throws the whole challenge out. That cost falls on sellers
497
+ whose names are not expressible in ASCII. The fix belongs in the x402 schema;
498
+ until it lands, spell `name` in ASCII and put the rest in the route's
499
+ `description`.
@@ -0,0 +1,11 @@
1
+ import { FacilitatorConfig, HTTPFacilitatorClient } from '@x402/core/server';
2
+
3
+ declare const X402_FACILITATOR_URL = "https://x402.weft.network";
4
+ interface WeftFacilitatorConfig {
5
+ url?: string;
6
+ createAuthHeaders?: FacilitatorConfig["createAuthHeaders"];
7
+ }
8
+ type AuthHeadersByPath = Awaited<ReturnType<NonNullable<FacilitatorConfig["createAuthHeaders"]>>>;
9
+ declare function createFacilitatorClient(config?: WeftFacilitatorConfig, weftAuthHeaders?: AuthHeadersByPath): HTTPFacilitatorClient;
10
+
11
+ export { type WeftFacilitatorConfig as W, X402_FACILITATOR_URL as X, createFacilitatorClient as c };
@@ -0,0 +1,11 @@
1
+ import { FacilitatorConfig, HTTPFacilitatorClient } from '@x402/core/server';
2
+
3
+ declare const X402_FACILITATOR_URL = "https://x402.weft.network";
4
+ interface WeftFacilitatorConfig {
5
+ url?: string;
6
+ createAuthHeaders?: FacilitatorConfig["createAuthHeaders"];
7
+ }
8
+ type AuthHeadersByPath = Awaited<ReturnType<NonNullable<FacilitatorConfig["createAuthHeaders"]>>>;
9
+ declare function createFacilitatorClient(config?: WeftFacilitatorConfig, weftAuthHeaders?: AuthHeadersByPath): HTTPFacilitatorClient;
10
+
11
+ export { type WeftFacilitatorConfig as W, X402_FACILITATOR_URL as X, createFacilitatorClient as c };
@@ -0,0 +1,18 @@
1
+ export * from '@x402/core';
2
+ export * from '@x402/core/types';
3
+ import { W as WeftFacilitatorConfig } from '../client-C9_zh3Qg.mjs';
4
+ export { X as X402_FACILITATOR_URL, c as createFacilitatorClient } from '../client-C9_zh3Qg.mjs';
5
+ import '@x402/core/server';
6
+
7
+ interface FeeInfo {
8
+ amount: string;
9
+ asset: string;
10
+ network: string;
11
+ }
12
+ interface FeeCacheConfig {
13
+ ttl?: number;
14
+ }
15
+ declare function invalidateFeeCache(): void;
16
+ declare function getFeeInfo(config?: WeftFacilitatorConfig, cacheConfig?: FeeCacheConfig): Promise<FeeInfo>;
17
+
18
+ export { type FeeCacheConfig, type FeeInfo, WeftFacilitatorConfig, getFeeInfo, invalidateFeeCache };
@@ -0,0 +1,18 @@
1
+ export * from '@x402/core';
2
+ export * from '@x402/core/types';
3
+ import { W as WeftFacilitatorConfig } from '../client-C9_zh3Qg.js';
4
+ export { X as X402_FACILITATOR_URL, c as createFacilitatorClient } from '../client-C9_zh3Qg.js';
5
+ import '@x402/core/server';
6
+
7
+ interface FeeInfo {
8
+ amount: string;
9
+ asset: string;
10
+ network: string;
11
+ }
12
+ interface FeeCacheConfig {
13
+ ttl?: number;
14
+ }
15
+ declare function invalidateFeeCache(): void;
16
+ declare function getFeeInfo(config?: WeftFacilitatorConfig, cacheConfig?: FeeCacheConfig): Promise<FeeInfo>;
17
+
18
+ export { type FeeCacheConfig, type FeeInfo, WeftFacilitatorConfig, getFeeInfo, invalidateFeeCache };