@solvapay/server 2.7.0 → 2.8.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.
@@ -879,6 +879,9 @@ function classifyPaywallState(limits) {
879
879
  if (limits.activationRequired === true || limits.paywallReason === "activation_required") {
880
880
  return { kind: "activation_required" };
881
881
  }
882
+ if (limits.paywallReason === "limit_reached") {
883
+ return { kind: "limit_reached" };
884
+ }
882
885
  if (limits.paywallReason === "topup_required") {
883
886
  return { kind: "topup_required" };
884
887
  }
@@ -942,9 +945,12 @@ function formatCreditsWithMoney(credits, gate) {
942
945
  }
943
946
  return `${amount} credits`;
944
947
  }
948
+ function isFreeMeter(name) {
949
+ return typeof name === "string" && name.startsWith("free-");
950
+ }
945
951
  function meterLabel(gate) {
946
952
  if (!gate.meterName) return "units";
947
- return gate.meterName.replace(/_/g, " ");
953
+ return gate.meterName.replace(/[_-]/g, " ");
948
954
  }
949
955
  function namedCheckoutMarkdown(url, label = "Open checkout") {
950
956
  return `[${label}](${url})`;
@@ -980,6 +986,12 @@ function buildGateMessage(state, gate) {
980
986
  switch (state.kind) {
981
987
  case "limit_reached": {
982
988
  const included = gate.included;
989
+ if (isFreeMeter(gate.meterName)) {
990
+ const total = included?.total;
991
+ const usedLine2 = total !== void 0 ? `You've used all ${total} ${meterLabel(gate)} for this period.` : `You've used all ${meterLabel(gate)} for this period.`;
992
+ const switchLine2 = ladder ? ` Pick a plan to keep going: ${ladder}. ${callViewer("account").replace(/^c/, "C")} for usage and recovery.` : recoverClause(url, "keep going", "checkout");
993
+ return `${usedLine2}${switchLine2}`;
994
+ }
983
995
  const price = gate.unitPriceMinor != null && gate.currency ? formatMinor(gate.unitPriceMinor, gate.currency) : null;
984
996
  const usedLine = included ? `You've used ${included.used} of ${included.total} included ${meterLabel(gate)} this period.` : `You've reached the included usage for this period.`;
985
997
  const nextLine = price ? ` The next call is ${price}.` : "";
@@ -1282,6 +1294,7 @@ function createRequestDeduplicator(options = {}) {
1282
1294
  }
1283
1295
 
1284
1296
  // src/paywall.ts
1297
+ var import_core5 = require("@solvapay/core");
1285
1298
  var PaywallError = class extends Error {
1286
1299
  /**
1287
1300
  * Creates a new PaywallError instance.
@@ -1332,6 +1345,15 @@ var sharedLimitsFetchDeduplicator = createRequestDeduplicator({
1332
1345
  });
1333
1346
  var sharedLimitsFetchClaims = /* @__PURE__ */ new Map();
1334
1347
  var EXTRA_FORWARD_KEY = "__solvapayExtra";
1348
+ function trackUsageExtra(metadata, outcome, consequence) {
1349
+ if (metadata.freeLimit) {
1350
+ return { meterName: metadata.freeLimit.meter };
1351
+ }
1352
+ if (outcome === "success") {
1353
+ return { usageClass: consequence === "overage" ? "overage" : "included" };
1354
+ }
1355
+ return void 0;
1356
+ }
1335
1357
  var SolvaPayPaywall = class {
1336
1358
  constructor(apiClient, options = {}) {
1337
1359
  this.apiClient = apiClient;
@@ -1391,10 +1413,19 @@ var SolvaPayPaywall = class {
1391
1413
  */
1392
1414
  async decide(args, metadata = {}, getCustomerRef) {
1393
1415
  const product = this.resolveProduct(metadata);
1394
- const usageType = metadata.meterName || metadata.usageType || "requests";
1416
+ const usageType = metadata.freeLimit?.meter || metadata.meterName || metadata.usageType || "requests";
1395
1417
  const requestId = this.generateRequestId();
1396
1418
  const startTime = Date.now();
1397
1419
  const inputCustomerRef = getCustomerRef ? getCustomerRef(args) : args.auth?.customer_ref || "anonymous";
1420
+ if (metadata.freeLimit) {
1421
+ const resolved = typeof inputCustomerRef === "string" ? inputCustomerRef.trim() : "";
1422
+ if (!resolved || resolved === "anonymous") {
1423
+ throw new import_core5.SolvaPayError("identity required", {
1424
+ status: 401,
1425
+ code: "identity_required"
1426
+ });
1427
+ }
1428
+ }
1398
1429
  let backendCustomerRef;
1399
1430
  if (inputCustomerRef.startsWith("cus_")) {
1400
1431
  backendCustomerRef = inputCustomerRef;
@@ -1445,7 +1476,8 @@ var SolvaPayPaywall = class {
1445
1476
  // `checkLimitsCore`, which powers the React `useLimits` hook)
1446
1477
  // leave this unset and the backend skips the session-creation
1447
1478
  // side effect.
1448
- includeCheckoutSession: true
1479
+ includeCheckoutSession: true,
1480
+ ...metadata.freeLimit ? { freeAllowance: metadata.freeLimit } : {}
1449
1481
  });
1450
1482
  }
1451
1483
  );
@@ -1489,7 +1521,8 @@ var SolvaPayPaywall = class {
1489
1521
  "paywall",
1490
1522
  requestId,
1491
1523
  latencyMs,
1492
- metadata.toolName
1524
+ metadata.toolName,
1525
+ metadata.freeLimit ? { meterName: metadata.freeLimit.meter } : void 0
1493
1526
  ).catch(() => void 0);
1494
1527
  const gate = buildPaywallGate(
1495
1528
  product,
@@ -1536,7 +1569,7 @@ var SolvaPayPaywall = class {
1536
1569
  */
1537
1570
  async runAllow(decision, handler, metadata, args) {
1538
1571
  const product = this.resolveProduct(metadata);
1539
- const usageType = metadata.meterName || metadata.usageType || "requests";
1572
+ const usageType = metadata.freeLimit?.meter || metadata.meterName || metadata.usageType || "requests";
1540
1573
  const requestId = decision.requestId;
1541
1574
  const startTime = Date.now();
1542
1575
  const forwardedExtra = args[EXTRA_FORWARD_KEY];
@@ -1555,7 +1588,8 @@ var SolvaPayPaywall = class {
1555
1588
  "success",
1556
1589
  requestId,
1557
1590
  latencyMs,
1558
- metadata.toolName
1591
+ metadata.toolName,
1592
+ trackUsageExtra(metadata, "success", decision.consequence)
1559
1593
  ).catch(() => void 0);
1560
1594
  return result;
1561
1595
  } catch (error) {
@@ -1574,7 +1608,8 @@ var SolvaPayPaywall = class {
1574
1608
  "fail",
1575
1609
  requestId,
1576
1610
  latencyMs,
1577
- metadata.toolName
1611
+ metadata.toolName,
1612
+ trackUsageExtra(metadata, "fail")
1578
1613
  ).catch(() => void 0);
1579
1614
  }
1580
1615
  throw error;
@@ -1765,7 +1800,7 @@ var SolvaPayPaywall = class {
1765
1800
  }
1766
1801
  return backendRef;
1767
1802
  }
1768
- async trackUsage(customerRef, productRef, action, outcome, requestId, actionDuration, toolName) {
1803
+ async trackUsage(customerRef, productRef, action, outcome, requestId, actionDuration, toolName, extra) {
1769
1804
  await withRetry(
1770
1805
  () => this.apiClient.trackUsage({
1771
1806
  customerRef,
@@ -1778,7 +1813,9 @@ var SolvaPayPaywall = class {
1778
1813
  metadata: {
1779
1814
  action: action || "api_requests",
1780
1815
  requestId,
1781
- ...toolName ? { toolName } : {}
1816
+ ...toolName ? { toolName } : {},
1817
+ ...extra?.meterName ? { meterName: extra.meterName } : {},
1818
+ ...extra?.usageClass ? { usageClass: extra.usageClass } : {}
1782
1819
  },
1783
1820
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1784
1821
  }),
@@ -2118,7 +2155,7 @@ var McpAdapter = class {
2118
2155
  };
2119
2156
 
2120
2157
  // src/factory.ts
2121
- var import_core5 = require("@solvapay/core");
2158
+ var import_core6 = require("@solvapay/core");
2122
2159
 
2123
2160
  // src/virtual-tools.ts
2124
2161
  var TOOL_GET_USER_INFO = {
@@ -2359,7 +2396,7 @@ function registerVirtualToolsMcpImpl(server, apiClient, options) {
2359
2396
  function createSolvaPay(config) {
2360
2397
  let resolvedConfig;
2361
2398
  if (!config) {
2362
- const envConfig = (0, import_core5.getSolvaPayConfig)();
2399
+ const envConfig = (0, import_core6.getSolvaPayConfig)();
2363
2400
  resolvedConfig = {
2364
2401
  apiKey: envConfig.apiKey,
2365
2402
  apiBaseUrl: envConfig.apiBaseUrl
@@ -2395,25 +2432,25 @@ function createSolvaPay(config) {
2395
2432
  },
2396
2433
  createPaymentIntent(params) {
2397
2434
  if (!apiClient.createPaymentIntent) {
2398
- throw new import_core5.SolvaPayError("createPaymentIntent is not available on this API client");
2435
+ throw new import_core6.SolvaPayError("createPaymentIntent is not available on this API client");
2399
2436
  }
2400
2437
  return apiClient.createPaymentIntent(params);
2401
2438
  },
2402
2439
  createTopupPaymentIntent(params) {
2403
2440
  if (!apiClient.createTopupPaymentIntent) {
2404
- throw new import_core5.SolvaPayError("createTopupPaymentIntent is not available on this API client");
2441
+ throw new import_core6.SolvaPayError("createTopupPaymentIntent is not available on this API client");
2405
2442
  }
2406
2443
  return apiClient.createTopupPaymentIntent(params);
2407
2444
  },
2408
2445
  processPaymentIntent(params) {
2409
2446
  if (!apiClient.processPaymentIntent) {
2410
- throw new import_core5.SolvaPayError("processPaymentIntent is not available on this API client");
2447
+ throw new import_core6.SolvaPayError("processPaymentIntent is not available on this API client");
2411
2448
  }
2412
2449
  return apiClient.processPaymentIntent(params);
2413
2450
  },
2414
2451
  attachBusinessDetails(params) {
2415
2452
  if (!apiClient.attachBusinessDetails) {
2416
- throw new import_core5.SolvaPayError("attachBusinessDetails is not available on this API client");
2453
+ throw new import_core6.SolvaPayError("attachBusinessDetails is not available on this API client");
2417
2454
  }
2418
2455
  return apiClient.attachBusinessDetails(params);
2419
2456
  },
@@ -2425,13 +2462,13 @@ function createSolvaPay(config) {
2425
2462
  },
2426
2463
  trackUsageBulk(params) {
2427
2464
  if (!apiClient.trackUsageBulk) {
2428
- throw new import_core5.SolvaPayError("trackUsageBulk is not available on this API client");
2465
+ throw new import_core6.SolvaPayError("trackUsageBulk is not available on this API client");
2429
2466
  }
2430
2467
  return apiClient.trackUsageBulk(params);
2431
2468
  },
2432
2469
  createCustomer(params) {
2433
2470
  if (!apiClient.createCustomer) {
2434
- throw new import_core5.SolvaPayError("createCustomer is not available on this API client");
2471
+ throw new import_core6.SolvaPayError("createCustomer is not available on this API client");
2435
2472
  }
2436
2473
  return apiClient.createCustomer({ ...params, metadata: params.metadata ?? {} });
2437
2474
  },
@@ -2440,13 +2477,13 @@ function createSolvaPay(config) {
2440
2477
  },
2441
2478
  assignCredits(params) {
2442
2479
  if (!apiClient.assignCredits) {
2443
- throw new import_core5.SolvaPayError("assignCredits is not available on this API client");
2480
+ throw new import_core6.SolvaPayError("assignCredits is not available on this API client");
2444
2481
  }
2445
2482
  return apiClient.assignCredits(params);
2446
2483
  },
2447
2484
  getCustomerBalance(params) {
2448
2485
  if (!apiClient.getCustomerBalance) {
2449
- throw new import_core5.SolvaPayError("getCustomerBalance is not available on this API client");
2486
+ throw new import_core6.SolvaPayError("getCustomerBalance is not available on this API client");
2450
2487
  }
2451
2488
  return apiClient.getCustomerBalance(params);
2452
2489
  },
@@ -2464,19 +2501,19 @@ function createSolvaPay(config) {
2464
2501
  },
2465
2502
  activatePlan(params) {
2466
2503
  if (!apiClient.activatePlan) {
2467
- throw new import_core5.SolvaPayError("activatePlan is not available on this API client");
2504
+ throw new import_core6.SolvaPayError("activatePlan is not available on this API client");
2468
2505
  }
2469
2506
  return apiClient.activatePlan(params);
2470
2507
  },
2471
2508
  bootstrapMcpProduct(params) {
2472
2509
  if (!apiClient.bootstrapMcpProduct) {
2473
- throw new import_core5.SolvaPayError("bootstrapMcpProduct is not available on this API client");
2510
+ throw new import_core6.SolvaPayError("bootstrapMcpProduct is not available on this API client");
2474
2511
  }
2475
2512
  return apiClient.bootstrapMcpProduct(params);
2476
2513
  },
2477
2514
  configureMcpPlans(productRef, params) {
2478
2515
  if (!apiClient.configureMcpPlans) {
2479
- throw new import_core5.SolvaPayError("configureMcpPlans is not available on this API client");
2516
+ throw new import_core6.SolvaPayError("configureMcpPlans is not available on this API client");
2480
2517
  }
2481
2518
  return apiClient.configureMcpPlans(productRef, params);
2482
2519
  },
@@ -2489,12 +2526,13 @@ function createSolvaPay(config) {
2489
2526
  // Payable API for framework-specific handlers
2490
2527
  payable(options = {}) {
2491
2528
  const product = resolveProductRef(options.productRef || options.product);
2492
- const usageType = options.meterName || options.usageType || "requests";
2529
+ const usageType = options.freeLimit?.meter || options.meterName || options.usageType || "requests";
2493
2530
  const metadata = {
2494
2531
  product,
2495
2532
  meterName: usageType,
2496
2533
  usageType,
2497
- ...options.toolName ? { toolName: options.toolName } : {}
2534
+ ...options.toolName ? { toolName: options.toolName } : {},
2535
+ ...options.freeLimit ? { freeLimit: options.freeLimit } : {}
2498
2536
  };
2499
2537
  return {
2500
2538
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -2583,6 +2621,8 @@ function createSolvaPay(config) {
2583
2621
  const errMeta = opts?.error !== void 0 ? {
2584
2622
  error: opts.error instanceof Error ? opts.error.message : String(opts.error)
2585
2623
  } : {};
2624
+ const isFree = Boolean(decideMetadata.freeLimit);
2625
+ const usageClass = !isFree && outcome === "success" ? decision.consequence === "overage" ? "overage" : "included" : void 0;
2586
2626
  const trackPromise = apiClient.trackUsage({
2587
2627
  customerRef,
2588
2628
  productRef,
@@ -2595,6 +2635,8 @@ function createSolvaPay(config) {
2595
2635
  action: meterName,
2596
2636
  requestId,
2597
2637
  ...decideMetadata.toolName ? { toolName: decideMetadata.toolName } : {},
2638
+ ...isFree && decideMetadata.freeLimit ? { meterName: decideMetadata.freeLimit.meter } : {},
2639
+ ...usageClass ? { usageClass } : {},
2598
2640
  ...errMeta,
2599
2641
  ...opts?.metadata ?? {}
2600
2642
  },
@@ -2667,7 +2709,7 @@ async function getCustomerBalanceCore(request, options = {}) {
2667
2709
  }
2668
2710
 
2669
2711
  // src/helpers/payment.ts
2670
- var import_core6 = require("@solvapay/core");
2712
+ var import_core7 = require("@solvapay/core");
2671
2713
  async function createPaymentIntentCore(request, body, options = {}) {
2672
2714
  try {
2673
2715
  if (!body.planRef || !body.productRef) {
@@ -2850,7 +2892,7 @@ async function createCustomerSessionCore(request, options = {}) {
2850
2892
  }
2851
2893
 
2852
2894
  // src/helpers/renewal.ts
2853
- var import_core7 = require("@solvapay/core");
2895
+ var import_core8 = require("@solvapay/core");
2854
2896
  async function cancelPurchaseCore(request, body, options = {}) {
2855
2897
  try {
2856
2898
  if (!body.purchaseRef) {
@@ -2896,7 +2938,7 @@ async function cancelPurchaseCore(request, body, options = {}) {
2896
2938
  await new Promise((resolve) => setTimeout(resolve, 500));
2897
2939
  return cancelledPurchase;
2898
2940
  } catch (error) {
2899
- if (error instanceof import_core7.SolvaPayError) {
2941
+ if (error instanceof import_core8.SolvaPayError) {
2900
2942
  const errorMessage = error.message;
2901
2943
  if (errorMessage.includes("not found")) {
2902
2944
  return {
@@ -2964,7 +3006,7 @@ async function reactivatePurchaseCore(request, body, options = {}) {
2964
3006
  await new Promise((resolve) => setTimeout(resolve, 500));
2965
3007
  return reactivatedPurchase;
2966
3008
  } catch (error) {
2967
- if (error instanceof import_core7.SolvaPayError) {
3009
+ if (error instanceof import_core8.SolvaPayError) {
2968
3010
  const errorMessage = error.message;
2969
3011
  if (errorMessage.includes("not found")) {
2970
3012
  return {
@@ -3086,7 +3128,7 @@ async function getHistoryCore(request, input, options = {}) {
3086
3128
  }
3087
3129
 
3088
3130
  // src/helpers/plans.ts
3089
- var import_core8 = require("@solvapay/core");
3131
+ var import_core9 = require("@solvapay/core");
3090
3132
  async function listPlansCore(request, options = {}) {
3091
3133
  try {
3092
3134
  const url = new URL(request.url);
@@ -3098,7 +3140,7 @@ async function listPlansCore(request, options = {}) {
3098
3140
  };
3099
3141
  }
3100
3142
  const apiClient = options.solvaPay?.apiClient ?? (() => {
3101
- const config = (0, import_core8.getSolvaPayConfig)();
3143
+ const config = (0, import_core9.getSolvaPayConfig)();
3102
3144
  if (!config.apiKey) return null;
3103
3145
  return createSolvaPayClient({
3104
3146
  apiKey: config.apiKey,
@@ -3128,11 +3170,11 @@ async function listPlansCore(request, options = {}) {
3128
3170
  }
3129
3171
 
3130
3172
  // src/helpers/merchant.ts
3131
- var import_core9 = require("@solvapay/core");
3173
+ var import_core10 = require("@solvapay/core");
3132
3174
  async function getMerchantCore(_request, options = {}) {
3133
3175
  try {
3134
3176
  const apiClient = options.solvaPay?.apiClient ?? (() => {
3135
- const config = (0, import_core9.getSolvaPayConfig)();
3177
+ const config = (0, import_core10.getSolvaPayConfig)();
3136
3178
  if (!config.apiKey) return null;
3137
3179
  return createSolvaPayClient({
3138
3180
  apiKey: config.apiKey,
@@ -3159,7 +3201,7 @@ async function getMerchantCore(_request, options = {}) {
3159
3201
  }
3160
3202
 
3161
3203
  // src/helpers/product.ts
3162
- var import_core10 = require("@solvapay/core");
3204
+ var import_core11 = require("@solvapay/core");
3163
3205
  async function getProductCore(request, options = {}) {
3164
3206
  try {
3165
3207
  const url = new URL(request.url);
@@ -3171,7 +3213,7 @@ async function getProductCore(request, options = {}) {
3171
3213
  };
3172
3214
  }
3173
3215
  const apiClient = options.solvaPay?.apiClient ?? (() => {
3174
- const config = (0, import_core10.getSolvaPayConfig)();
3216
+ const config = (0, import_core11.getSolvaPayConfig)();
3175
3217
  if (!config.apiKey) return null;
3176
3218
  return createSolvaPayClient({
3177
3219
  apiKey: config.apiKey,
@@ -3254,7 +3296,7 @@ async function checkPurchaseCore(request, options = {}) {
3254
3296
  }
3255
3297
 
3256
3298
  // src/helpers/usage.ts
3257
- var import_core11 = require("@solvapay/core");
3299
+ var import_core12 = require("@solvapay/core");
3258
3300
  async function trackUsageCore(request, body, options = {}) {
3259
3301
  try {
3260
3302
  const userResult = await getAuthenticatedUserCore(request);
@@ -3283,7 +3325,7 @@ async function trackUsageCore(request, body, options = {}) {
3283
3325
  }
3284
3326
 
3285
3327
  // src/edge.ts
3286
- var import_core12 = require("@solvapay/core");
3328
+ var import_core13 = require("@solvapay/core");
3287
3329
  function timingSafeEqual(a, b) {
3288
3330
  if (a.length !== b.length) return false;
3289
3331
  let mismatch = 0;
@@ -3298,22 +3340,22 @@ async function verifyWebhook({
3298
3340
  secret
3299
3341
  }) {
3300
3342
  const toleranceSec = 300;
3301
- if (!signature) throw new import_core12.SolvaPayError("Missing webhook signature");
3343
+ if (!signature) throw new import_core13.SolvaPayError("Missing webhook signature");
3302
3344
  const parts = signature.split(",");
3303
3345
  const tPart = parts.find((p) => p.startsWith("t="));
3304
3346
  const v1Part = parts.find((p) => p.startsWith("v1="));
3305
3347
  if (!tPart || !v1Part) {
3306
- throw new import_core12.SolvaPayError("Malformed webhook signature");
3348
+ throw new import_core13.SolvaPayError("Malformed webhook signature");
3307
3349
  }
3308
3350
  const timestamp = parseInt(tPart.slice(2), 10);
3309
3351
  const receivedHmac = v1Part.slice(3);
3310
3352
  if (Number.isNaN(timestamp) || !receivedHmac) {
3311
- throw new import_core12.SolvaPayError("Malformed webhook signature");
3353
+ throw new import_core13.SolvaPayError("Malformed webhook signature");
3312
3354
  }
3313
3355
  if (toleranceSec > 0) {
3314
3356
  const age = Math.abs(Math.floor(Date.now() / 1e3) - timestamp);
3315
3357
  if (age > toleranceSec) {
3316
- throw new import_core12.SolvaPayError("Webhook signature timestamp too old");
3358
+ throw new import_core13.SolvaPayError("Webhook signature timestamp too old");
3317
3359
  }
3318
3360
  }
3319
3361
  const enc = new TextEncoder();
@@ -3327,12 +3369,12 @@ async function verifyWebhook({
3327
3369
  const sigBuf = await crypto.subtle.sign("HMAC", key, enc.encode(`${timestamp}.${body}`));
3328
3370
  const expectedHmac = Array.from(new Uint8Array(sigBuf)).map((b) => b.toString(16).padStart(2, "0")).join("");
3329
3371
  if (!timingSafeEqual(expectedHmac, receivedHmac)) {
3330
- throw new import_core12.SolvaPayError("Invalid webhook signature");
3372
+ throw new import_core13.SolvaPayError("Invalid webhook signature");
3331
3373
  }
3332
3374
  try {
3333
3375
  return JSON.parse(body);
3334
3376
  } catch {
3335
- throw new import_core12.SolvaPayError("Invalid webhook payload: body is not valid JSON");
3377
+ throw new import_core13.SolvaPayError("Invalid webhook payload: body is not valid JSON");
3336
3378
  }
3337
3379
  }
3338
3380
 
@@ -251,6 +251,15 @@ interface components {
251
251
  };
252
252
  CheckLimitRequest: {
253
253
  customerRef: string;
254
+ /** @description SDK-only free-tool allowance. Authenticated by the provider secret key, so `cap` is the provider's own declaration, not customer input. */
255
+ freeAllowance?: {
256
+ cap?: number;
257
+ /** @description Free meter name. Must match `/^free-[a-z0-9-]+$/`. */
258
+ meter?: string;
259
+ /** @enum {string} */
260
+ scope?: 'rolling_window' | 'lifetime';
261
+ windowDays?: number;
262
+ };
254
263
  includeCheckoutSession?: boolean;
255
264
  meterName?: string;
256
265
  productRef: string;
@@ -906,7 +915,7 @@ interface components {
906
915
  * Authoritative paywall classification shared with Managed MCP. Present on denial responses only.
907
916
  * @enum {string}
908
917
  */
909
- paywallReason?: 'activation_required' | 'topup_required' | 'payment_required';
918
+ paywallReason?: 'activation_required' | 'topup_required' | 'payment_required' | 'limit_reached';
910
919
  /** @description Display name of the active or default plan */
911
920
  planName?: string;
912
921
  /** @description Active plan reference when the customer already holds a purchase */
@@ -251,6 +251,15 @@ interface components {
251
251
  };
252
252
  CheckLimitRequest: {
253
253
  customerRef: string;
254
+ /** @description SDK-only free-tool allowance. Authenticated by the provider secret key, so `cap` is the provider's own declaration, not customer input. */
255
+ freeAllowance?: {
256
+ cap?: number;
257
+ /** @description Free meter name. Must match `/^free-[a-z0-9-]+$/`. */
258
+ meter?: string;
259
+ /** @enum {string} */
260
+ scope?: 'rolling_window' | 'lifetime';
261
+ windowDays?: number;
262
+ };
254
263
  includeCheckoutSession?: boolean;
255
264
  meterName?: string;
256
265
  productRef: string;
@@ -906,7 +915,7 @@ interface components {
906
915
  * Authoritative paywall classification shared with Managed MCP. Present on denial responses only.
907
916
  * @enum {string}
908
917
  */
909
- paywallReason?: 'activation_required' | 'topup_required' | 'payment_required';
918
+ paywallReason?: 'activation_required' | 'topup_required' | 'payment_required' | 'limit_reached';
910
919
  /** @description Display name of the active or default plan */
911
920
  planName?: string;
912
921
  /** @description Active plan reference when the customer already holds a purchase */