atmn 2.0.140 → 2.0.142

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/bin.js CHANGED
@@ -853,6 +853,12 @@ var API_ROUTES = [
853
853
  required: false,
854
854
  description: "Whether this feature is consumable. A consumable feature is one that periodically resets and is consumed rather than allocated (like credits, API requests, etc.). Applicable only for 'metered' features."
855
855
  },
856
+ {
857
+ name: "combine",
858
+ type: "string",
859
+ required: false,
860
+ description: "How grants of this feature from several plans combine. 'sum' (default) adds them up. 'max' or 'min' keeps only the highest or lowest grant among plan items with no price; priced items always add on top. Metered and credit system features only."
861
+ },
856
862
  {
857
863
  name: "display",
858
864
  type: "json",
@@ -944,6 +950,12 @@ var API_ROUTES = [
944
950
  required: false,
945
951
  description: "Whether this feature is consumable. A consumable feature is one that periodically resets and is consumed rather than allocated (like credits, API requests, etc.). Applicable only for 'metered' features."
946
952
  },
953
+ {
954
+ name: "combine",
955
+ type: "string",
956
+ required: false,
957
+ description: "How grants of this feature from several plans combine. 'sum' (default) adds them up. 'max' or 'min' keeps only the highest or lowest grant among plan items with no price; priced items always add on top. Metered and credit system features only."
958
+ },
947
959
  {
948
960
  name: "display",
949
961
  type: "json",
@@ -1073,7 +1085,7 @@ var API_ROUTES = [
1073
1085
  name: "proration_behavior",
1074
1086
  type: "string",
1075
1087
  required: false,
1076
- description: "How to handle proration when updating an existing subscription. 'prorate_immediately' charges/credits prorated amounts now, 'none' skips creating any charges, 'bill_difference' charges/credits the full-period price difference now without changing the billing cycle."
1088
+ description: "How to handle proration when updating an existing subscription. 'prorate_immediately' charges/credits prorated amounts now, 'none' skips creating any charges, 'bill_difference' bills the larger of the full-period price difference and the prorated amount (full difference on upgrades, prorated credit on downgrades) now without changing the billing cycle."
1077
1089
  },
1078
1090
  {
1079
1091
  name: "redirect_mode",
@@ -1304,7 +1316,7 @@ var API_ROUTES = [
1304
1316
  name: "billing_behavior",
1305
1317
  type: "string",
1306
1318
  required: false,
1307
- description: "Whether to prorate the immediate phase. 'none' skips proration charges and credits, 'bill_difference' charges/credits the full-period price difference."
1319
+ description: "Whether to prorate the immediate phase. 'none' skips proration charges and credits, 'bill_difference' bills the larger of the full-period price difference and the prorated amount (full difference on upgrades, prorated credit on downgrades)."
1308
1320
  },
1309
1321
  {
1310
1322
  name: "no_billing_changes",
@@ -1403,7 +1415,7 @@ var API_ROUTES = [
1403
1415
  name: "billing_behavior",
1404
1416
  type: "string",
1405
1417
  required: false,
1406
- description: "How to handle billing. 'prorate_immediately' charges/credits prorated amounts now, 'none' does not charge/credit anything, 'bill_difference' charges/credits the full-period price difference now without changing the billing cycle."
1418
+ description: "How to handle billing. 'prorate_immediately' charges/credits prorated amounts now, 'none' does not charge/credit anything, 'bill_difference' bills the larger of the full-period price difference and the prorated amount (full difference on upgrades, prorated credit on downgrades) now without changing the billing cycle."
1407
1419
  },
1408
1420
  {
1409
1421
  name: "billing_cycle_anchor",
@@ -1512,7 +1524,7 @@ var API_ROUTES = [
1512
1524
  name: "proration_behavior",
1513
1525
  type: "string",
1514
1526
  required: false,
1515
- description: "How to handle proration when updating an existing subscription. 'prorate_immediately' charges/credits prorated amounts now, 'none' skips creating any charges, 'bill_difference' charges/credits the full-period price difference now without changing the billing cycle."
1527
+ description: "How to handle proration when updating an existing subscription. 'prorate_immediately' charges/credits prorated amounts now, 'none' skips creating any charges, 'bill_difference' bills the larger of the full-period price difference and the prorated amount (full difference on upgrades, prorated credit on downgrades) now without changing the billing cycle."
1516
1528
  },
1517
1529
  {
1518
1530
  name: "redirect_mode",
@@ -1737,7 +1749,7 @@ var API_ROUTES = [
1737
1749
  name: "billing_behavior",
1738
1750
  type: "string",
1739
1751
  required: false,
1740
- description: "How to handle billing. 'prorate_immediately' charges/credits prorated amounts now, 'none' does not charge/credit anything, 'bill_difference' charges/credits the full-period price difference now without changing the billing cycle."
1752
+ description: "How to handle billing. 'prorate_immediately' charges/credits prorated amounts now, 'none' does not charge/credit anything, 'bill_difference' bills the larger of the full-period price difference and the prorated amount (full difference on upgrades, prorated credit on downgrades) now without changing the billing cycle."
1741
1753
  },
1742
1754
  {
1743
1755
  name: "billing_cycle_anchor",
@@ -1846,7 +1858,7 @@ var API_ROUTES = [
1846
1858
  name: "proration_behavior",
1847
1859
  type: "string",
1848
1860
  required: false,
1849
- description: "How to handle proration when updating an existing subscription. 'prorate_immediately' charges/credits prorated amounts now, 'none' skips creating any charges, 'bill_difference' charges/credits the full-period price difference now without changing the billing cycle."
1861
+ description: "How to handle proration when updating an existing subscription. 'prorate_immediately' charges/credits prorated amounts now, 'none' skips creating any charges, 'bill_difference' bills the larger of the full-period price difference and the prorated amount (full difference on upgrades, prorated credit on downgrades) now without changing the billing cycle."
1850
1862
  },
1851
1863
  {
1852
1864
  name: "redirect_mode",
@@ -1999,7 +2011,7 @@ var API_ROUTES = [
1999
2011
  name: "proration_behavior",
2000
2012
  type: "string",
2001
2013
  required: false,
2002
- description: "How to handle proration when updating an existing subscription. 'prorate_immediately' charges/credits prorated amounts now, 'none' skips creating any charges, 'bill_difference' charges/credits the full-period price difference now without changing the billing cycle."
2014
+ description: "How to handle proration when updating an existing subscription. 'prorate_immediately' charges/credits prorated amounts now, 'none' skips creating any charges, 'bill_difference' bills the larger of the full-period price difference and the prorated amount (full difference on upgrades, prorated credit on downgrades) now without changing the billing cycle."
2003
2015
  },
2004
2016
  {
2005
2017
  name: "redirect_mode",
@@ -2251,7 +2263,7 @@ var API_ROUTES = [
2251
2263
  name: "proration_behavior",
2252
2264
  type: "string",
2253
2265
  required: false,
2254
- description: "How to handle proration when updating an existing subscription. 'prorate_immediately' charges/credits prorated amounts now, 'none' skips creating any charges, 'bill_difference' charges/credits the full-period price difference now without changing the billing cycle."
2266
+ description: "How to handle proration when updating an existing subscription. 'prorate_immediately' charges/credits prorated amounts now, 'none' skips creating any charges, 'bill_difference' bills the larger of the full-period price difference and the prorated amount (full difference on upgrades, prorated credit on downgrades) now without changing the billing cycle."
2255
2267
  },
2256
2268
  {
2257
2269
  name: "subscription_id",
@@ -4468,6 +4480,9 @@ var LINT_RULES = {
4468
4480
  type: {
4469
4481
  enum: ["boolean", "metered", "credit_system", "ai_credit_system"]
4470
4482
  },
4483
+ combine: {
4484
+ enum: ["sum", "max", "min"]
4485
+ },
4471
4486
  defaultMarkup: {
4472
4487
  minimum: -100
4473
4488
  },
@@ -7756,7 +7771,7 @@ var createClient = (options) => ({
7756
7771
  });
7757
7772
 
7758
7773
  // src/version.ts
7759
- var version = "2.0.140";
7774
+ var version = "2.0.142";
7760
7775
 
7761
7776
  // src/http/autumnFetch.ts
7762
7777
  var runtime = typeof Bun !== "undefined" ? `bun ${Bun.version}` : `node ${process.versions.node}`;
@@ -8701,6 +8716,7 @@ var COLLECTIONS = {
8701
8716
  "name",
8702
8717
  "type",
8703
8718
  "consumable",
8719
+ "combine",
8704
8720
  "creditSchema",
8705
8721
  "modelMarkups",
8706
8722
  "defaultMarkup",
@@ -8730,6 +8746,7 @@ var COLLECTIONS = {
8730
8746
  ],
8731
8747
  paths: [
8732
8748
  "archived",
8749
+ "combine",
8733
8750
  "consumable",
8734
8751
  "creditSchema",
8735
8752
  "creditSchema.billingUnits",
@@ -10014,16 +10031,16 @@ import { dirname as dirname4, join as join7 } from "node:path";
10014
10031
  import chalk3 from "chalk";
10015
10032
 
10016
10033
  // src/generated/skills.ts
10017
- var SKILLS_VERSION = "2.0.139";
10034
+ var SKILLS_VERSION = "2.0.141";
10018
10035
  var SKILLS = [
10019
10036
  {
10020
10037
  name: "autumn-setup",
10021
10038
  description: "First-time Autumn setup — install the atmn CLI, connect to an org (sign in, or keyless with no account), turn the user's pricing into autumn.config.ts, and push it to a sandbox org. Use when the user is new to Autumn, pastes an Autumn setup prompt, or asks to set up Autumn, add billing, monetization, subscriptions, usage-based billing, pricing, or plans. If Autumn is already set up, use autumn-catalog instead.",
10022
- version: "2.0.139",
10039
+ version: "2.0.141",
10023
10040
  markdown: `---
10024
10041
  name: autumn-setup
10025
10042
  description: First-time Autumn setup — install the atmn CLI, connect to an org (sign in, or keyless with no account), turn the user's pricing into autumn.config.ts, and push it to a sandbox org. Use when the user is new to Autumn, pastes an Autumn setup prompt, or asks to set up Autumn, add billing, monetization, subscriptions, usage-based billing, pricing, or plans. If Autumn is already set up, use autumn-catalog instead.
10026
- version: 2.0.139
10043
+ version: 2.0.141
10027
10044
  ---
10028
10045
 
10029
10046
  # Setup
@@ -10181,11 +10198,11 @@ Then stop. Don't keep building, don't tour the dashboard, don't deploy anything.
10181
10198
  {
10182
10199
  name: "autumn-catalog",
10183
10200
  description: "Modeling a user's pricing into an Autumn catalog — deciding the structure (plans, variants, add-ons, licenses, credit systems, pooled balances) before writing config, then filling in the numbers. Use when the user describes their pricing or asks to model, change, or push a catalog.",
10184
- version: "2.0.139",
10201
+ version: "2.0.141",
10185
10202
  markdown: `---
10186
10203
  name: autumn-catalog
10187
10204
  description: Modeling a user's pricing into an Autumn catalog — deciding the structure (plans, variants, add-ons, licenses, credit systems, pooled balances) before writing config, then filling in the numbers. Use when the user describes their pricing or asks to model, change, or push a catalog.
10188
- version: 2.0.139
10205
+ version: 2.0.141
10189
10206
  ---
10190
10207
 
10191
10208
  # Catalog
@@ -14029,11 +14046,11 @@ Pitch: "Pro $20/mo or $200/yr — 1,000 messages per month either way."
14029
14046
  {
14030
14047
  name: "autumn-integrate",
14031
14048
  description: "Integrating Autumn into an app's backend — creating customers, billing flows (attach, checkout, upgrade, cancel), gating features with check, recording usage with track, and exposing billing data to the frontend. Use when the user asks to integrate Autumn, add billing/payments to their app, gate features, meter usage, or build checkout/upgrade flows. Requires a pushed catalog; use autumn-catalog first if plans don't exist yet.",
14032
- version: "2.0.139",
14049
+ version: "2.0.141",
14033
14050
  markdown: `---
14034
14051
  name: autumn-integrate
14035
14052
  description: Integrating Autumn into an app's backend — creating customers, billing flows (attach, checkout, upgrade, cancel), gating features with check, recording usage with track, and exposing billing data to the frontend. Use when the user asks to integrate Autumn, add billing/payments to their app, gate features, meter usage, or build checkout/upgrade flows. Requires a pushed catalog; use autumn-catalog first if plans don't exist yet.
14036
- version: 2.0.139
14053
+ version: 2.0.141
14037
14054
  ---
14038
14055
 
14039
14056
  # Integrate
@@ -16424,6 +16441,8 @@ curl -X POST 'https://api.useautumn.com/v1/check' \\
16424
16441
 
16425
16442
  You can also use \`check\` to [gate boolean features](/documentation/customers/check#checking-boolean-features) (non-metered features), such as access to "premium AI models".
16426
16443
 
16444
+ Each \`check\` and \`track\` is a network call to Autumn. If they sit on a latency-sensitive path, you can deploy the [edge cache](/documentation/edge-cache) to handle both from inside your own cloud.
16445
+
16427
16446
  ### Tracking usage
16428
16447
 
16429
16448
  After the user has successfully used a chatbot message, you can record the usage in Autumn. This will decrement the user's message balance.
@@ -16496,6 +16515,8 @@ When you create a plan in Autumn, you define what features your customers on tha
16496
16515
 
16497
16516
  The \`check\` method returns the \`allowed\` field in real-time to check if a customer should have access to a feature. You can use this to block access and prompt an upsell.
16498
16517
 
16518
+ On a latency-sensitive path? The [edge cache](/documentation/edge-cache) answers \`check\` from inside your own cloud.
16519
+
16499
16520
  ## The \`allowed\` field
16500
16521
 
16501
16522
  The \`allowed\` field will return \`true\` for a given feature if:
@@ -17738,7 +17759,7 @@ Billing controls tie into three webhook events that fire automatically based on
17738
17759
  |-------|---------------|
17739
17760
  | [\`balances.limit_reached\`](/api-reference/webhooks/balancesLimitReached) | Customer transitions from allowed to not allowed on a feature (included allowance exhausted, max purchase hit, spend limit reached, or usage limit hit). The payload's \`limit_type\` distinguishes \`included\`, \`max_purchase\`, \`spend_limit\`, and \`usage_limit\`. |
17740
17761
  | [\`balances.usage_alert_triggered\`](/api-reference/webhooks/balancesUsageAlertTriggered) | Customer's usage crosses a configured alert threshold |
17741
- | [\`customer.products.updated\`](/documentation/webhooks#customerproductsupdated) | Customer's subscription changes (new, upgrade, downgrade, cancel, etc.) |
17762
+ | [\`billing.updated\`](/api-reference/webhooks/billingUpdated) | Customer's plans change (new subscription, upgrade, downgrade, cancellation, expiry, etc.) |
17742
17763
 
17743
17764
  For webhook setup and security details, see [Webhooks](/documentation/webhooks).`
17744
17765
  },
@@ -18614,7 +18635,7 @@ While the SDK gracefully handles outages for read-path calls, write operations l
18614
18635
  <Step>
18615
18636
  ### Set up webhooks (if applicable)
18616
18637
 
18617
- If you're listening for Autumn [webhook events](/documentation/webhooks) (e.g. \`customer.products.updated\`), make sure your production webhook endpoint is configured in the dashboard. Verify you can receive a test event.
18638
+ If you're listening for Autumn [webhook events](/documentation/webhooks) (e.g. \`billing.updated\`), make sure your production webhook endpoint is configured in the dashboard. Verify you can receive a test event.
18618
18639
 
18619
18640
  </Step>
18620
18641
 
@@ -18648,6 +18669,8 @@ As a billing engine, Autumn sits in the critical path of your application. If yo
18648
18669
 
18649
18670
  Fail-open ensures your users are never blocked by an Autumn outage. Usage events may be lost during the outage, but your customers stay unaffected.
18650
18671
 
18672
+ If you run the [edge cache](/documentation/edge-cache), checks keep working through an outage, answered from the last balances Autumn sent it.
18673
+
18651
18674
  ## Behavior
18652
18675
 
18653
18676
  Fail-open is **enabled by default**. When Autumn is unreachable (network errors, timeouts, or server errors returning 5XX status codes), the SDK returns safe defaults:
@@ -18730,11 +18753,11 @@ Monitor your logs for this message. If you see it, check [status.useautumn.com](
18730
18753
  {
18731
18754
  name: "autumn-concepts",
18732
18755
  description: "Understanding Autumn's billing objects before acting on them — what a plan version is and what active means, when a trial starts or ends billing, how entity- and seat-scoped subscriptions differ from the customer's own, what a customize diff actually changes, and how caps, overage, and top-ups behave. Load before versioning, editing, or drafting a plan, or when a customer's state does not match the plain reading of their plan.",
18733
- version: "2.0.139",
18756
+ version: "2.0.141",
18734
18757
  markdown: `---
18735
18758
  name: autumn-concepts
18736
18759
  description: Understanding Autumn's billing objects before acting on them — what a plan version is and what active means, when a trial starts or ends billing, how entity- and seat-scoped subscriptions differ from the customer's own, what a customize diff actually changes, and how caps, overage, and top-ups behave. Load before versioning, editing, or drafting a plan, or when a customer's state does not match the plain reading of their plan.
18737
- version: 2.0.139
18760
+ version: 2.0.141
18738
18761
  ---
18739
18762
 
18740
18763
  # Concepts
package/dist/index.js CHANGED
@@ -77,6 +77,9 @@ var LINT_RULES = {
77
77
  type: {
78
78
  enum: ["boolean", "metered", "credit_system", "ai_credit_system"]
79
79
  },
80
+ combine: {
81
+ enum: ["sum", "max", "min"]
82
+ },
80
83
  defaultMarkup: {
81
84
  minimum: -100
82
85
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atmn",
3
- "version": "2.0.140",
3
+ "version": "2.0.142",
4
4
  "license": "MIT",
5
5
  "description": "The CLI for Autumn — define your pricing in code, sync it to Autumn, and keep everything in version control.",
6
6
  "bin": {
@@ -49,5 +49,5 @@
49
49
  "open": "10.2.0",
50
50
  "yaml": "2.9.0"
51
51
  },
52
- "gitHead": "b8c06f86e266cbcd7733d2fbdc6870be84d21b5d"
52
+ "gitHead": "4033394192b77db80f8704095f37f89b1b6d9542"
53
53
  }
@@ -813,6 +813,13 @@ export const API_ROUTES: readonly ApiRoute[] = [
813
813
  description:
814
814
  "Whether this feature is consumable. A consumable feature is one that periodically resets and is consumed rather than allocated (like credits, API requests, etc.). Applicable only for 'metered' features.",
815
815
  },
816
+ {
817
+ name: "combine",
818
+ type: "string",
819
+ required: false,
820
+ description:
821
+ "How grants of this feature from several plans combine. 'sum' (default) adds them up. 'max' or 'min' keeps only the highest or lowest grant among plan items with no price; priced items always add on top. Metered and credit system features only.",
822
+ },
816
823
  {
817
824
  name: "display",
818
825
  type: "json",
@@ -911,6 +918,13 @@ export const API_ROUTES: readonly ApiRoute[] = [
911
918
  description:
912
919
  "Whether this feature is consumable. A consumable feature is one that periodically resets and is consumed rather than allocated (like credits, API requests, etc.). Applicable only for 'metered' features.",
913
920
  },
921
+ {
922
+ name: "combine",
923
+ type: "string",
924
+ required: false,
925
+ description:
926
+ "How grants of this feature from several plans combine. 'sum' (default) adds them up. 'max' or 'min' keeps only the highest or lowest grant among plan items with no price; priced items always add on top. Metered and credit system features only.",
927
+ },
914
928
  {
915
929
  name: "display",
916
930
  type: "json",
@@ -1053,7 +1067,7 @@ export const API_ROUTES: readonly ApiRoute[] = [
1053
1067
  type: "string",
1054
1068
  required: false,
1055
1069
  description:
1056
- "How to handle proration when updating an existing subscription. 'prorate_immediately' charges/credits prorated amounts now, 'none' skips creating any charges, 'bill_difference' charges/credits the full-period price difference now without changing the billing cycle.",
1070
+ "How to handle proration when updating an existing subscription. 'prorate_immediately' charges/credits prorated amounts now, 'none' skips creating any charges, 'bill_difference' bills the larger of the full-period price difference and the prorated amount (full difference on upgrades, prorated credit on downgrades) now without changing the billing cycle.",
1057
1071
  },
1058
1072
  {
1059
1073
  name: "redirect_mode",
@@ -1316,7 +1330,7 @@ export const API_ROUTES: readonly ApiRoute[] = [
1316
1330
  type: "string",
1317
1331
  required: false,
1318
1332
  description:
1319
- "Whether to prorate the immediate phase. 'none' skips proration charges and credits, 'bill_difference' charges/credits the full-period price difference.",
1333
+ "Whether to prorate the immediate phase. 'none' skips proration charges and credits, 'bill_difference' bills the larger of the full-period price difference and the prorated amount (full difference on upgrades, prorated credit on downgrades).",
1320
1334
  },
1321
1335
  {
1322
1336
  name: "no_billing_changes",
@@ -1426,7 +1440,7 @@ export const API_ROUTES: readonly ApiRoute[] = [
1426
1440
  type: "string",
1427
1441
  required: false,
1428
1442
  description:
1429
- "How to handle billing. 'prorate_immediately' charges/credits prorated amounts now, 'none' does not charge/credit anything, 'bill_difference' charges/credits the full-period price difference now without changing the billing cycle.",
1443
+ "How to handle billing. 'prorate_immediately' charges/credits prorated amounts now, 'none' does not charge/credit anything, 'bill_difference' bills the larger of the full-period price difference and the prorated amount (full difference on upgrades, prorated credit on downgrades) now without changing the billing cycle.",
1430
1444
  },
1431
1445
  {
1432
1446
  name: "billing_cycle_anchor",
@@ -1546,7 +1560,7 @@ export const API_ROUTES: readonly ApiRoute[] = [
1546
1560
  type: "string",
1547
1561
  required: false,
1548
1562
  description:
1549
- "How to handle proration when updating an existing subscription. 'prorate_immediately' charges/credits prorated amounts now, 'none' skips creating any charges, 'bill_difference' charges/credits the full-period price difference now without changing the billing cycle.",
1563
+ "How to handle proration when updating an existing subscription. 'prorate_immediately' charges/credits prorated amounts now, 'none' skips creating any charges, 'bill_difference' bills the larger of the full-period price difference and the prorated amount (full difference on upgrades, prorated credit on downgrades) now without changing the billing cycle.",
1550
1564
  },
1551
1565
  {
1552
1566
  name: "redirect_mode",
@@ -1802,7 +1816,7 @@ export const API_ROUTES: readonly ApiRoute[] = [
1802
1816
  type: "string",
1803
1817
  required: false,
1804
1818
  description:
1805
- "How to handle billing. 'prorate_immediately' charges/credits prorated amounts now, 'none' does not charge/credit anything, 'bill_difference' charges/credits the full-period price difference now without changing the billing cycle.",
1819
+ "How to handle billing. 'prorate_immediately' charges/credits prorated amounts now, 'none' does not charge/credit anything, 'bill_difference' bills the larger of the full-period price difference and the prorated amount (full difference on upgrades, prorated credit on downgrades) now without changing the billing cycle.",
1806
1820
  },
1807
1821
  {
1808
1822
  name: "billing_cycle_anchor",
@@ -1923,7 +1937,7 @@ export const API_ROUTES: readonly ApiRoute[] = [
1923
1937
  type: "string",
1924
1938
  required: false,
1925
1939
  description:
1926
- "How to handle proration when updating an existing subscription. 'prorate_immediately' charges/credits prorated amounts now, 'none' skips creating any charges, 'bill_difference' charges/credits the full-period price difference now without changing the billing cycle.",
1940
+ "How to handle proration when updating an existing subscription. 'prorate_immediately' charges/credits prorated amounts now, 'none' skips creating any charges, 'bill_difference' bills the larger of the full-period price difference and the prorated amount (full difference on upgrades, prorated credit on downgrades) now without changing the billing cycle.",
1927
1941
  },
1928
1942
  {
1929
1943
  name: "redirect_mode",
@@ -2097,7 +2111,7 @@ export const API_ROUTES: readonly ApiRoute[] = [
2097
2111
  type: "string",
2098
2112
  required: false,
2099
2113
  description:
2100
- "How to handle proration when updating an existing subscription. 'prorate_immediately' charges/credits prorated amounts now, 'none' skips creating any charges, 'bill_difference' charges/credits the full-period price difference now without changing the billing cycle.",
2114
+ "How to handle proration when updating an existing subscription. 'prorate_immediately' charges/credits prorated amounts now, 'none' skips creating any charges, 'bill_difference' bills the larger of the full-period price difference and the prorated amount (full difference on upgrades, prorated credit on downgrades) now without changing the billing cycle.",
2101
2115
  },
2102
2116
  {
2103
2117
  name: "redirect_mode",
@@ -2380,7 +2394,7 @@ export const API_ROUTES: readonly ApiRoute[] = [
2380
2394
  type: "string",
2381
2395
  required: false,
2382
2396
  description:
2383
- "How to handle proration when updating an existing subscription. 'prorate_immediately' charges/credits prorated amounts now, 'none' skips creating any charges, 'bill_difference' charges/credits the full-period price difference now without changing the billing cycle.",
2397
+ "How to handle proration when updating an existing subscription. 'prorate_immediately' charges/credits prorated amounts now, 'none' skips creating any charges, 'bill_difference' bills the larger of the full-period price difference and the prorated amount (full difference on upgrades, prorated credit on downgrades) now without changing the billing cycle.",
2384
2398
  },
2385
2399
  {
2386
2400
  name: "subscription_id",
@@ -48189,6 +48189,8 @@ name: string;
48189
48189
  type: "boolean" | "metered" | "credit_system" | "ai_credit_system";
48190
48190
  /** For metered features: true if usage resets periodically (API calls, credits), false if allocated persistently (seats, storage). */
48191
48191
  consumable: boolean;
48192
+ /** How grants of this feature from several plans combine: 'sum' adds them up, 'max' / 'min' keep the highest / lowest grant among plan items with no price. Omitted when 'sum'. */
48193
+ combine?: "sum" | "max" | "min";
48192
48194
  /** Event names that trigger this feature's balance. Allows multiple features to respond to a single event. */
48193
48195
  eventNames?: Array<string>;
48194
48196
  /** For classic credit systems: maps metered features to flat or graduated credit costs. */
@@ -50510,6 +50512,8 @@ name: string;
50510
50512
  type: "boolean" | "metered" | "credit_system" | "ai_credit_system";
50511
50513
  /** For metered features: true if usage resets periodically (API calls, credits), false if allocated persistently (seats, storage). */
50512
50514
  consumable: boolean;
50515
+ /** How grants of this feature from several plans combine: 'sum' adds them up, 'max' / 'min' keep the highest / lowest grant among plan items with no price. Omitted when 'sum'. */
50516
+ combine?: "sum" | "max" | "min";
50513
50517
  /** Event names that trigger this feature's balance. Allows multiple features to respond to a single event. */
50514
50518
  eventNames?: Array<string>;
50515
50519
  /** For classic credit systems: maps metered features to flat or graduated credit costs. */
@@ -17,6 +17,7 @@ export const COLLECTIONS: Readonly<Record<string, CollectionSpec>> = {
17
17
  "name",
18
18
  "type",
19
19
  "consumable",
20
+ "combine",
20
21
  "creditSchema",
21
22
  "modelMarkups",
22
23
  "defaultMarkup",
@@ -46,6 +47,7 @@ export const COLLECTIONS: Readonly<Record<string, CollectionSpec>> = {
46
47
  ],
47
48
  paths: [
48
49
  "archived",
50
+ "combine",
49
51
  "consumable",
50
52
  "creditSchema",
51
53
  "creditSchema.billingUnits",
@@ -8,6 +8,8 @@ export type Feature = {
8
8
  type: "boolean" | "metered" | "credit_system" | "ai_credit_system";
9
9
  /** Whether this feature is consumable. A consumable feature is one that periodically resets and is consumed rather than allocated (like credits, API requests, etc.). Applicable only for 'metered' features. */
10
10
  consumable?: boolean;
11
+ /** How grants of this feature from several plans combine. 'sum' (default) adds them up. 'max' or 'min' keeps only the highest or lowest grant among plan items with no price; priced items always add on top. Metered and credit system features only. */
12
+ combine?: "sum" | "max" | "min";
11
13
  /** A schema that maps metered feature IDs to flat or graduated credit costs. For classic credit systems only — AI credit systems use model_markups instead. */
12
14
  creditSchema?: Array<
13
15
  | {
@@ -12,6 +12,9 @@ export const LINT_RULES: LintRules = {
12
12
  type: {
13
13
  enum: ["boolean", "metered", "credit_system", "ai_credit_system"],
14
14
  },
15
+ combine: {
16
+ enum: ["sum", "max", "min"],
17
+ },
15
18
  defaultMarkup: {
16
19
  minimum: -100,
17
20
  },
@@ -14,16 +14,16 @@ export type BundledSkill = {
14
14
  }[];
15
15
  };
16
16
 
17
- export const SKILLS_VERSION = "2.0.139";
17
+ export const SKILLS_VERSION = "2.0.141";
18
18
 
19
19
  export const SKILLS: readonly BundledSkill[] = [
20
20
  {
21
21
  name: "autumn-setup",
22
22
  description:
23
23
  "First-time Autumn setup — install the atmn CLI, connect to an org (sign in, or keyless with no account), turn the user's pricing into autumn.config.ts, and push it to a sandbox org. Use when the user is new to Autumn, pastes an Autumn setup prompt, or asks to set up Autumn, add billing, monetization, subscriptions, usage-based billing, pricing, or plans. If Autumn is already set up, use autumn-catalog instead.",
24
- version: "2.0.139",
24
+ version: "2.0.141",
25
25
  markdown:
26
- "---\nname: autumn-setup\ndescription: First-time Autumn setup — install the atmn CLI, connect to an org (sign in, or keyless with no account), turn the user's pricing into autumn.config.ts, and push it to a sandbox org. Use when the user is new to Autumn, pastes an Autumn setup prompt, or asks to set up Autumn, add billing, monetization, subscriptions, usage-based billing, pricing, or plans. If Autumn is already set up, use autumn-catalog instead.\nversion: 2.0.139\n---\n\n# Setup\n\nTake the user from \"I want billing\" to pricing that is live in a sandbox org and working in their app — with as little between those two points as possible. You run the flow; two other skills do the heavy parts. `autumn-catalog` turns their pricing into `autumn.config.ts`. `autumn-integrate` puts the first calls in their code. Never do either of those jobs yourself.\n\n## Ground rules\n\nThese apply the whole time, not just in one phase.\n\n- CLI-first: everything happens in `autumn.config.ts` and `atmn`. The only browser moments are signing in, or claiming a keyless org later. Never send the user to the dashboard to do the work.\n- Never invent a price, limit, or plan name. A missing number is a question, never a guess.\n- Push only after the user approves the pricing (Phase 4), or when they already told you to go ahead without a review.\n- Sandbox by default: `AUTUMN_SECRET_KEY` is the sandbox key. Don't touch production during setup.\n- Keys: check that a key exists by its name only. Never read, print, or ask the user to paste a key into the chat. Same for a keyless org's claim token. A one-time email code is not a key — that one does come through the chat.\n- Two tries max to fix any failing command, then stop and show the error.\n- Never run `atmn reset`, and never remove existing plans unless the user clearly asked.\n- If a step is already done, say so in one line (\"Already signed in — skipping login\") and move on.\n\n## How to talk\n\nYou are a competent engineer pairing with the user, not an installer wizard and not a marketer.\n\n- Simple, everyday words: \"plans\", \"what's included\", \"extra usage\". No jargon in chat — schema words like `consumable`, `prepaid`, `usage_based` stay in the config. If a simpler word says the same thing, use it.\n- 1–3 sentences per message. The pricing summary is the only large thing you send.\n- One message, one purpose: a status, a question, or the pricing summary.\n- Don't ask permission for harmless work — reading the repo, drafting the config, building the summary. Ask only for decisions and approvals.\n- Say what's about to happen before it does: one line before the browser opens, one before the push.\n- When you need input, ask at most three short numbered questions — the ones that unblock you, nothing more. If your platform has a built-in way to ask questions with options, use it. Never re-ask something they answered.\n- Always end a message with something: the question, what you're doing next, or that you're done.\n- Don't paste the config or command output into chat; name the file and summarize. Errors are the exception — quote those exactly.\n- If you're stuck, send three lines: what failed, the exact error, what you need to continue.\n- No emoji, no hype, no \"Great question\". Plain and concrete: \"Connected to Acme (sandbox).\"\n\n## Progress\n\nCopy this checklist into your first message and keep it up to date. If you skip an item, say why in one line.\n\n- [ ] 1 Skills installed; checked for an existing config, key, and pricing\n- [ ] 2 Connected to Autumn — `atmn` installed, signed in or keyless\n- [ ] 3 Got the user's pricing — rough plans and prices to build from\n- [ ] 4 Pricing modeled and approved (the `autumn-catalog` skill runs this part)\n- [ ] 5 Pushed to Autumn and verified\n- [ ] 6 Working in the app — one plan bought, one feature gated (the `autumn-integrate` skill runs this part)\n- [ ] 7 Account linked (keyless only — drop this line if they signed in)\n- [ ] 8 Done\n\nFor items 4 and 6, another skill owns the conversation and its checklist replaces this one for the duration — show theirs, not this one. Come back here when they're finished.\n\n## Phase 1 — Check the project (silent)\n\nDon't message the user yet — just find out where things stand.\n\nFour skills share this job, and they ship inside the `atmn` CLI: `atmn init` writes them next to the config and prints the `npx skills add <that folder> -y` line that registers them with your agent, and `atmn skills update` refreshes them when `push` or `pull` say they are older than the CLI. If any is missing here, that is Phase 2's job — never fetch them from anywhere else.\n\n`autumn-setup` (this file) is the flow. `autumn-catalog` is how to build the pricing, plus the exact `atmn` commands — load it in Phase 4. `autumn-integrate` is how the app calls Autumn — load it in Phase 6. `autumn-concepts` explains Autumn's objects — the other two load it themselves.\n\nThen check three things:\n\n- `autumn.config.ts` exists → Autumn is already set up here. Say so, treat the file as the truth, and use `autumn-catalog` for the changes; come back at Phase 5 (push) when there's something to push.\n- An `AUTUMN_SECRET_KEY` exists (shell env, `.env`, `.env.local`) → already connected; skip the connect step in Phase 2.\n- They already told you the pricing (their message, a pricing page, the README) → Phase 3 is a quick confirm, not a list of questions.\n\n## Phase 2 — Introduce and connect\n\nStart with two or three sentences: what's going to happen (connect this project to an Autumn org → write the pricing into `autumn.config.ts` → approve the pricing → push). Something like this, in your own words:\n\n> Setting up Autumn. I'll connect this project to an Autumn org, write your pricing into `autumn.config.ts`, and show it to you to approve before anything is pushed.\n\nThen:\n\n1. Run `atmn init` from the project root with the user's package manager (`bunx atmn init`, `pnpm exec atmn init`, `yarn atmn init`, `npx atmn init` — read it off the lockfile). Every `atmn …` command below means that run command. One command does the whole connect step: it adds `atmn` as a dependency, places the config in `autumn/` (its own package in a monorepo — it asks where, or takes `--path` and `--name`), pulls whatever the org already holds, and installs these skills beside the config. Each run prints what it did and, when it needs an answer, the flag to pass; run it again with the flag.\n2. Key already there → `init` says who it's connected to and moves on. Say so in one line.\n3. No key → `init` stops and asks how to connect. Ask the user the same thing, one question, two options, plain words:\n\n > Two ways to start: sign in to an Autumn account (I'll open a browser), or go keyless — I set up a sandbox for you right now and you link an account later. Which do you want?\n\n Always ask this one, even if the user told you to handle everything yourself — a keyless org has no owner until they link it, and that is their call, not yours. Pick keyless without asking only when there is genuinely nobody in the chat to answer (an unattended run), and say in one line that you did.\n4. Connect the way they chose, by running `init` again with the flag:\n\n - **Sign in** → say a browser window is coming, then `atmn init --login`. It opens the browser to sign in and create or pick an org, prints the sign-in URL, and waits — that's normal, it's not stuck. If the browser doesn't open (SSH, sandbox), that's not a failure: send the user the printed URL as-is and wait. Keys get saved to `.env`. Fails → retry once, then stop and tell the user what failed. Offer the ways forward — go keyless, or they copy their own sandbox key from app.useautumn.com into `.env` as `AUTUMN_SECRET_KEY` — and wait for their answer. Never switch to keyless on your own.\n - **Keyless** → `atmn init --keyless`. It provisions a sandbox org and saves its key to `.env` as `AUTUMN_SECRET_KEY`. No account, no browser, nothing for the user to do. The org is a real one: pushing, customers, and billing all work the same. It has no owner until Phase 7 links one, and the key doesn't change when that happens. For what provisioning and linking do underneath, and their limits, read `references/keyless.md`.\n\n5. `init` pulled the org's catalog into the config. If plans showed up (\"Pulled N entries\"), say so and go through them with the user before changing anything. A brand-new or keyless org is empty; the config is a scaffold for Phase 4 to fill.\n\nDone when `init` finished and you know whether the org already has plans. Say so in one line — including whether it's keyless, since that decides how you finish.\n\n## Phase 3 — Get a starting point\n\nYou need the pricing in the user's own words — not the details, just what to build.\n\n- They already told you (their message, a pricing page, the README) → repeat it back in one or two sentences and move on. If they gave you everything, ask nothing.\n- They haven't → ask what they're building and what they want to charge. A pricing page, or a competitor's page they like, is a full answer too.\n\nDon't dig into details here — what to ask, what to assume, and how to handle unclear pricing is `autumn-catalog`'s job, next phase.\n\nDone when you have rough plans and prices to build from.\n\n## Phase 4 — Model, write, approve\n\nLoad `autumn-catalog` now — from here it owns the conversation: its Shape/Fill flow, its checklist (shown instead of this skill's), its questions, its catalog display, and the approval. Your speaking rules above still apply to its questions.\n\nThis whole phase is the catalog skill's: structure agreed → config written and valid → pricing shown in its format → user approved. Only come back here when that's done; don't push yet.\n\n## Phase 5 — Push\n\nOnly after the yes. Push with `atmn` using `autumn-catalog`'s atmn reference — it has the exact commands, flags, and versioning choices. If the push asks for decisions (new version of a live plan, deleting things), bring them to the user; never decide alone. Check the push output shows every plan and feature made it — a push that errored is not done, and after two failed fixes you stop and show the error.\n\n## Phase 6 — Get it working in the app\n\nPricing in a sandbox is invisible. The user believes Autumn works when their own app creates a customer, blocks something, and takes money — so go there next, in one short pass, before anything else gets built.\n\nSay what's now live (plans and features, which org, sandbox) and where the config file is, then offer it:\n\n> Plans are live. Want me to wire up the basics now — customers created on signup, buying a plan, one feature gated / tracked — so you can see it running in your app?\n\nThey say no → skip to Phase 7. They say yes, or already asked for the integration up front → this phase runs. Either way it's their call; never start editing app code unasked.\n\nScope it before handing over. The first pass is the thinnest thing that proves the loop, and nothing else:\n\n- a customer created where the app already knows who the user is\n- buying a plan: the main free → paid move, so real money moves once\n- `check` and `track` on the **one** feature that matters most — ask which if it isn't obvious, don't gate everything\n\nAnything else — every remaining feature, entities, seats, billing controls, the billing page — waits for a second pass. Say that out loud when you scope, so the user doesn't read a small first pass as a small integration.\n\nLoad `autumn-integrate` now with that scope — from here it owns the conversation and the code: its order of operations, its checklist, its verification. Your speaking rules above still apply.\n\nCome back when its verification passes: a customer that exists in sandbox, a check that denied, usage that landed, a plan that attached.\n\n## Phase 7 — Link the account (keyless only)\n\nSkip this entirely if the user signed in — they already own their org.\n\nOffer once, right after they've seen the integration work, because that's when there's something worth logging in to look at:\n\n> Want to link this to your account? I’ll create a secure sign-in link so you can claim the org and open everything that just ran in the dashboard.\n\nNo → fine, drop it and say the offer stands whenever. Yes → ask which email to send the link to, run `atmn login --claim <email>`, and give them the returned claim URL. Autumn also emails that same URL — say so, naming the address, so they know where to find it later (\"Also sent to you@example.com\"). Whoever opens the link and signs in becomes the owner — the email is only where the link is delivered, so treat the link like the key and give it to the user alone. Linking makes them the owner: same key, same plans, same customers, plus the dashboard. An account that already exists works the same way; the new org is added beside their existing ones and becomes active after confirmation.\n\nThere's no deadline for linking, so it can wait until the user is ready. If linking fails, nothing is lost: the key keeps working and they can try again, or sign up normally and push the same config. What the commands do underneath: `references/keyless.md`.\n\n## Phase 8 — Done\n\nThree lines: what's live and working, where it lives (`autumn.config.ts`, and the handlers if Phase 6 ran), and the obvious next thing — the rest of the features, or going to production when they're ready. A linked or signed-in user can see it all at app.useautumn.com; a keyless user who declined can't, so don't send them there.\n\nThen stop. Don't keep building, don't tour the dashboard, don't deploy anything.",
26
+ "---\nname: autumn-setup\ndescription: First-time Autumn setup — install the atmn CLI, connect to an org (sign in, or keyless with no account), turn the user's pricing into autumn.config.ts, and push it to a sandbox org. Use when the user is new to Autumn, pastes an Autumn setup prompt, or asks to set up Autumn, add billing, monetization, subscriptions, usage-based billing, pricing, or plans. If Autumn is already set up, use autumn-catalog instead.\nversion: 2.0.141\n---\n\n# Setup\n\nTake the user from \"I want billing\" to pricing that is live in a sandbox org and working in their app — with as little between those two points as possible. You run the flow; two other skills do the heavy parts. `autumn-catalog` turns their pricing into `autumn.config.ts`. `autumn-integrate` puts the first calls in their code. Never do either of those jobs yourself.\n\n## Ground rules\n\nThese apply the whole time, not just in one phase.\n\n- CLI-first: everything happens in `autumn.config.ts` and `atmn`. The only browser moments are signing in, or claiming a keyless org later. Never send the user to the dashboard to do the work.\n- Never invent a price, limit, or plan name. A missing number is a question, never a guess.\n- Push only after the user approves the pricing (Phase 4), or when they already told you to go ahead without a review.\n- Sandbox by default: `AUTUMN_SECRET_KEY` is the sandbox key. Don't touch production during setup.\n- Keys: check that a key exists by its name only. Never read, print, or ask the user to paste a key into the chat. Same for a keyless org's claim token. A one-time email code is not a key — that one does come through the chat.\n- Two tries max to fix any failing command, then stop and show the error.\n- Never run `atmn reset`, and never remove existing plans unless the user clearly asked.\n- If a step is already done, say so in one line (\"Already signed in — skipping login\") and move on.\n\n## How to talk\n\nYou are a competent engineer pairing with the user, not an installer wizard and not a marketer.\n\n- Simple, everyday words: \"plans\", \"what's included\", \"extra usage\". No jargon in chat — schema words like `consumable`, `prepaid`, `usage_based` stay in the config. If a simpler word says the same thing, use it.\n- 1–3 sentences per message. The pricing summary is the only large thing you send.\n- One message, one purpose: a status, a question, or the pricing summary.\n- Don't ask permission for harmless work — reading the repo, drafting the config, building the summary. Ask only for decisions and approvals.\n- Say what's about to happen before it does: one line before the browser opens, one before the push.\n- When you need input, ask at most three short numbered questions — the ones that unblock you, nothing more. If your platform has a built-in way to ask questions with options, use it. Never re-ask something they answered.\n- Always end a message with something: the question, what you're doing next, or that you're done.\n- Don't paste the config or command output into chat; name the file and summarize. Errors are the exception — quote those exactly.\n- If you're stuck, send three lines: what failed, the exact error, what you need to continue.\n- No emoji, no hype, no \"Great question\". Plain and concrete: \"Connected to Acme (sandbox).\"\n\n## Progress\n\nCopy this checklist into your first message and keep it up to date. If you skip an item, say why in one line.\n\n- [ ] 1 Skills installed; checked for an existing config, key, and pricing\n- [ ] 2 Connected to Autumn — `atmn` installed, signed in or keyless\n- [ ] 3 Got the user's pricing — rough plans and prices to build from\n- [ ] 4 Pricing modeled and approved (the `autumn-catalog` skill runs this part)\n- [ ] 5 Pushed to Autumn and verified\n- [ ] 6 Working in the app — one plan bought, one feature gated (the `autumn-integrate` skill runs this part)\n- [ ] 7 Account linked (keyless only — drop this line if they signed in)\n- [ ] 8 Done\n\nFor items 4 and 6, another skill owns the conversation and its checklist replaces this one for the duration — show theirs, not this one. Come back here when they're finished.\n\n## Phase 1 — Check the project (silent)\n\nDon't message the user yet — just find out where things stand.\n\nFour skills share this job, and they ship inside the `atmn` CLI: `atmn init` writes them next to the config and prints the `npx skills add <that folder> -y` line that registers them with your agent, and `atmn skills update` refreshes them when `push` or `pull` say they are older than the CLI. If any is missing here, that is Phase 2's job — never fetch them from anywhere else.\n\n`autumn-setup` (this file) is the flow. `autumn-catalog` is how to build the pricing, plus the exact `atmn` commands — load it in Phase 4. `autumn-integrate` is how the app calls Autumn — load it in Phase 6. `autumn-concepts` explains Autumn's objects — the other two load it themselves.\n\nThen check three things:\n\n- `autumn.config.ts` exists → Autumn is already set up here. Say so, treat the file as the truth, and use `autumn-catalog` for the changes; come back at Phase 5 (push) when there's something to push.\n- An `AUTUMN_SECRET_KEY` exists (shell env, `.env`, `.env.local`) → already connected; skip the connect step in Phase 2.\n- They already told you the pricing (their message, a pricing page, the README) → Phase 3 is a quick confirm, not a list of questions.\n\n## Phase 2 — Introduce and connect\n\nStart with two or three sentences: what's going to happen (connect this project to an Autumn org → write the pricing into `autumn.config.ts` → approve the pricing → push). Something like this, in your own words:\n\n> Setting up Autumn. I'll connect this project to an Autumn org, write your pricing into `autumn.config.ts`, and show it to you to approve before anything is pushed.\n\nThen:\n\n1. Run `atmn init` from the project root with the user's package manager (`bunx atmn init`, `pnpm exec atmn init`, `yarn atmn init`, `npx atmn init` — read it off the lockfile). Every `atmn …` command below means that run command. One command does the whole connect step: it adds `atmn` as a dependency, places the config in `autumn/` (its own package in a monorepo — it asks where, or takes `--path` and `--name`), pulls whatever the org already holds, and installs these skills beside the config. Each run prints what it did and, when it needs an answer, the flag to pass; run it again with the flag.\n2. Key already there → `init` says who it's connected to and moves on. Say so in one line.\n3. No key → `init` stops and asks how to connect. Ask the user the same thing, one question, two options, plain words:\n\n > Two ways to start: sign in to an Autumn account (I'll open a browser), or go keyless — I set up a sandbox for you right now and you link an account later. Which do you want?\n\n Always ask this one, even if the user told you to handle everything yourself — a keyless org has no owner until they link it, and that is their call, not yours. Pick keyless without asking only when there is genuinely nobody in the chat to answer (an unattended run), and say in one line that you did.\n4. Connect the way they chose, by running `init` again with the flag:\n\n - **Sign in** → say a browser window is coming, then `atmn init --login`. It opens the browser to sign in and create or pick an org, prints the sign-in URL, and waits — that's normal, it's not stuck. If the browser doesn't open (SSH, sandbox), that's not a failure: send the user the printed URL as-is and wait. Keys get saved to `.env`. Fails → retry once, then stop and tell the user what failed. Offer the ways forward — go keyless, or they copy their own sandbox key from app.useautumn.com into `.env` as `AUTUMN_SECRET_KEY` — and wait for their answer. Never switch to keyless on your own.\n - **Keyless** → `atmn init --keyless`. It provisions a sandbox org and saves its key to `.env` as `AUTUMN_SECRET_KEY`. No account, no browser, nothing for the user to do. The org is a real one: pushing, customers, and billing all work the same. It has no owner until Phase 7 links one, and the key doesn't change when that happens. For what provisioning and linking do underneath, and their limits, read `references/keyless.md`.\n\n5. `init` pulled the org's catalog into the config. If plans showed up (\"Pulled N entries\"), say so and go through them with the user before changing anything. A brand-new or keyless org is empty; the config is a scaffold for Phase 4 to fill.\n\nDone when `init` finished and you know whether the org already has plans. Say so in one line — including whether it's keyless, since that decides how you finish.\n\n## Phase 3 — Get a starting point\n\nYou need the pricing in the user's own words — not the details, just what to build.\n\n- They already told you (their message, a pricing page, the README) → repeat it back in one or two sentences and move on. If they gave you everything, ask nothing.\n- They haven't → ask what they're building and what they want to charge. A pricing page, or a competitor's page they like, is a full answer too.\n\nDon't dig into details here — what to ask, what to assume, and how to handle unclear pricing is `autumn-catalog`'s job, next phase.\n\nDone when you have rough plans and prices to build from.\n\n## Phase 4 — Model, write, approve\n\nLoad `autumn-catalog` now — from here it owns the conversation: its Shape/Fill flow, its checklist (shown instead of this skill's), its questions, its catalog display, and the approval. Your speaking rules above still apply to its questions.\n\nThis whole phase is the catalog skill's: structure agreed → config written and valid → pricing shown in its format → user approved. Only come back here when that's done; don't push yet.\n\n## Phase 5 — Push\n\nOnly after the yes. Push with `atmn` using `autumn-catalog`'s atmn reference — it has the exact commands, flags, and versioning choices. If the push asks for decisions (new version of a live plan, deleting things), bring them to the user; never decide alone. Check the push output shows every plan and feature made it — a push that errored is not done, and after two failed fixes you stop and show the error.\n\n## Phase 6 — Get it working in the app\n\nPricing in a sandbox is invisible. The user believes Autumn works when their own app creates a customer, blocks something, and takes money — so go there next, in one short pass, before anything else gets built.\n\nSay what's now live (plans and features, which org, sandbox) and where the config file is, then offer it:\n\n> Plans are live. Want me to wire up the basics now — customers created on signup, buying a plan, one feature gated / tracked — so you can see it running in your app?\n\nThey say no → skip to Phase 7. They say yes, or already asked for the integration up front → this phase runs. Either way it's their call; never start editing app code unasked.\n\nScope it before handing over. The first pass is the thinnest thing that proves the loop, and nothing else:\n\n- a customer created where the app already knows who the user is\n- buying a plan: the main free → paid move, so real money moves once\n- `check` and `track` on the **one** feature that matters most — ask which if it isn't obvious, don't gate everything\n\nAnything else — every remaining feature, entities, seats, billing controls, the billing page — waits for a second pass. Say that out loud when you scope, so the user doesn't read a small first pass as a small integration.\n\nLoad `autumn-integrate` now with that scope — from here it owns the conversation and the code: its order of operations, its checklist, its verification. Your speaking rules above still apply.\n\nCome back when its verification passes: a customer that exists in sandbox, a check that denied, usage that landed, a plan that attached.\n\n## Phase 7 — Link the account (keyless only)\n\nSkip this entirely if the user signed in — they already own their org.\n\nOffer once, right after they've seen the integration work, because that's when there's something worth logging in to look at:\n\n> Want to link this to your account? I’ll create a secure sign-in link so you can claim the org and open everything that just ran in the dashboard.\n\nNo → fine, drop it and say the offer stands whenever. Yes → ask which email to send the link to, run `atmn login --claim <email>`, and give them the returned claim URL. Autumn also emails that same URL — say so, naming the address, so they know where to find it later (\"Also sent to you@example.com\"). Whoever opens the link and signs in becomes the owner — the email is only where the link is delivered, so treat the link like the key and give it to the user alone. Linking makes them the owner: same key, same plans, same customers, plus the dashboard. An account that already exists works the same way; the new org is added beside their existing ones and becomes active after confirmation.\n\nThere's no deadline for linking, so it can wait until the user is ready. If linking fails, nothing is lost: the key keeps working and they can try again, or sign up normally and push the same config. What the commands do underneath: `references/keyless.md`.\n\n## Phase 8 — Done\n\nThree lines: what's live and working, where it lives (`autumn.config.ts`, and the handlers if Phase 6 ran), and the obvious next thing — the rest of the features, or going to production when they're ready. A linked or signed-in user can see it all at app.useautumn.com; a keyless user who declined can't, so don't send them there.\n\nThen stop. Don't keep building, don't tour the dashboard, don't deploy anything.",
27
27
  references: [
28
28
  {
29
29
  path: "references/keyless.md",
@@ -36,9 +36,9 @@ export const SKILLS: readonly BundledSkill[] = [
36
36
  name: "autumn-catalog",
37
37
  description:
38
38
  "Modeling a user's pricing into an Autumn catalog — deciding the structure (plans, variants, add-ons, licenses, credit systems, pooled balances) before writing config, then filling in the numbers. Use when the user describes their pricing or asks to model, change, or push a catalog.",
39
- version: "2.0.139",
39
+ version: "2.0.141",
40
40
  markdown:
41
- '---\nname: autumn-catalog\ndescription: Modeling a user\'s pricing into an Autumn catalog — deciding the structure (plans, variants, add-ons, licenses, credit systems, pooled balances) before writing config, then filling in the numbers. Use when the user describes their pricing or asks to model, change, or push a catalog.\nversion: 2.0.139\n---\n\n# Catalog\n\nBefore using this skill, first load the `autumn-concepts` skill — it defines Autumn\'s data model — features, plans, plan items, balances — which every modeling decision builds on.\n\n## STRICT RULES — re-read before every config write\n\n1. **Amounts are in major units (e.g. dollars), never minor units (cents).** $180/month is `amount: 180`. $0.01 per credit is `amount: 0.01`. If any amount you wrote is 100× the user\'s number, it is wrong.\n2. **Never invent a price, limit, or plan name** — a missing number is a question.\n3. **One definition per real thing.** One feature per resource, one child plan per license pattern (parents customize their license, never get their own copy), one add-on per offer (sizes are tiers, not plans).\n\n**Building, or iterating on a live catalog?** What matters is whether customers are on these plans — not whether a config file exists. Still setting up (even across sessions, with a half-built `autumn.config.ts`) → the workflow below; edit the draft freely. Already running Autumn with customers, now changing prices/plans → that\'s an update with real stakes (versioning, migrations, grandfathering) — read `references/catalog-update.md` first. Unsure → check for customers (`atmn pull` / the org) or ask.\n\nTurning pricing into a catalog is two jobs:\n\n- **Shape** — decide the structure: which plans exist, what\'s a variant, what\'s an add-on, where balances live. Decided by relationships in their pricing, not by amounts.\n- **Fill** — put in the numbers and per-item details, then write and validate the config.\n\nDo Shape fully before Fill. Take numbers whenever the user mentions them, but never chase numbers during Shape — the one exception is "is this the same on every plan?", which is a structure question.\n\n## Progress (what the user sees)\n\nCopy this checklist into your first message and keep it up to date. It is the **only** structure the user ever sees — never say "step", "pass", "decide", or "fork" to them. If you skip an item, say why in one line.\n\n- [ ] 1 Your plans and prices\n- [ ] 2 What\'s included in each plan\n- [ ] 3 How billing behaves (signup, trials, limits)\n- [ ] 4 Licenses — paid seats, workspaces, projects (if any)\n- [ ] 5 Structure agreed\n- [ ] 6 Config written and checked\n\n## How to ask\n\n- One topic per message. Two questions at most, and only if both belong to that topic. Never mix topics in one message.\n- If one ambiguity changes which other questions apply, resolve it first before asking those.\n- Attach your guess to each question — "does the trial need a card? I\'d guess no" — a wrong guess gets corrected faster than a blank gets answered.\n- Assert the obvious instead of asking. "500 messages a month" resets monthly — state it as an assumption, don\'t ask. Questions are only for facts that change the structure and can\'t be guessed.\n- Skip anything they already told you. A good message to answer is a nod, not homework.\n- Never announce what you\'ll do next ("once I get those three, I\'ll restate…") — just work the current topic.\n- The restate (step 5) is the safety net: wrong assumptions get caught there cheaply, which is what makes fewer questions safe.\n\n## Shape: collect, then decide\n\nWork the four steps below in order, one at a time — each says when it\'s done. While collecting, make only the small calls each step allows; leave the big ones (marked ↦ Decide) for after.\n\n### Step 1 — Plans\n\n- What are the plans? ("Free, Pro $20/mo, Growth $50/mo")\n- Free tier? → a plan with no price that every new customer starts on automatically.\n- Anything bought *alongside* a plan rather than instead of it (packs, extra storage)? → note it ↦ Decide (add-on).\n- Enterprise tier? Ask. If it exists, model the base enterprise plan in the catalog, and tell the user: custom terms per customer (special prices, custom limits) are applied later when attaching, not modeled here.\n\nDone when you can list every plan they sell, including free, add-ons, and enterprise.\n\n### Step 2 — What each plan includes\n\nHave them describe what they charge for or limit, in their own words. Pick a type per feature:\n\n| They say | Feature type |\n|---|---|\n| "Pro has SSO" | boolean (on/off) |\n| "500 messages a month" | metered, resets |\n| "10 team members" | metered, no reset (held, not used up) |\n| "credits" / "tokens" / "wallet" | credit system — always, even if only one action uses it today. They will add more actions; a single mapped action is fine. Their app tracks the actions (chat, image), never the credit balance itself. |\n\nNote top-ups ("buy more when you run out", "auto-recharge") ↦ Decide (top-up placement).\n\n### Step 3 — How billing behaves\n\nThis step usually means explaining Autumn to the user in plain words. Do.\n\n- **Signup**: every new customer automatically gets the default plan — usually the free one. One default per group. A default can carry no prices at all — a "$0" plan with paid items (per-seat charges, prepaid packs) is a paid plan, not a default. If every plan bills something, there is no default; customers subscribe.\n- **Trials**: ask "does the trial need a card?" (guess from their motion — PLG usually no). Card → `free_trial` on the paid plan. No card → a **separate free trial plan** (`pro_trial`: no price, the paid plan\'s items, auto-enabled when everyone starts on it); the paid plan stays untouched, since a default plan can never be paid. For trial modeling details — trial-behavior on plans, read `references/plan.md` in the `autumn-concepts` skill.\n- **What is a plan attached to?** The customer, or each thing they own (workspace, project, site)? "Pro is $200 per workspace" → attached per entity. Pin this down — it changes everything downstream.\n- **Who uses each metered feature?** The customer as a whole, or each entity? If entities: one shared balance or separate ones? "Shared across…" → shared ↦ Decide (balances).\n\nDone when you know what attaches where, who consumes what, and how trials and signup work.\n\n### Step 4 — Licenses\n\nTriggered whenever the customer pays per unit of some entity — seats, workspaces, projects, sites, members: "each X is $10/month", "comes with 3 X". When you see one, always ask: **what does one X come with?** Never skip this because the user didn\'t say "seat". For modeling a license — the concept: child plan, license link, customize, read `references/licenses.md` in the `autumn-concepts` skill.\n\n- Nothing of its own ("$10 per seat", just a count) → a per-unit priced item on the plan. No entities. The common case.\n- The unit gets something of its own ("each seat gets 100 credits", "every workspace has its own allowance") → a **license**: a small plan of its own that the parent plan hands out per unit.\n- Units must be assigned, reassigned, or sit empty → also licenses. Rare — confirm they need it.\n\nFor deciding between a per-unit item, licenses, and entity-attached plans for a countable paid unit, read `references/fork-licenses.md`.\n\n### Before deciding: restate\n\nOnly after your questions are answered — never announce it in advance, and never restate facts nobody has confirmed yet. One short message: the plans, what\'s metered, what attaches where, who shares what. Let the user correct it. A wrong fact here is much cheaper than a wrong structure later.\n\nIf the user gave you everything up front and you asked nothing, skip the separate restate — the structure message (checklist item 5) does that job.\n\n## Decide\n\nResolve these with all facts in hand. Each has a default — when the facts genuinely don\'t settle it, show both options in one line each and ask.\n\n**Variant or separate plan?**\nA variant can change the price, swap items in or out, and change the trial — nothing else.\n\n- "Pro monthly / Pro annual, same features" → variant. (Annual usually still resets allowances monthly — billing and reset intervals are independent. Confirm.)\n- Volume buckets/tiers — one question decides: **is each bucket the subscription itself (→ a variant per bucket) or a purchase on top of one (→ a prepaid item)?**\n- Different features per tier → separate plans. One plan per tier is normal, not a smell.\n\nFor deciding whether volume buckets/tiers are variants of the plan or one prepaid volume-tiered item — the tells, the trap, when variants are forced, read `references/fork-variants.md`.\n\n**Add-on, or part of the plan?** Two independent questions: is the purchase priced/bundled per plan (→ item on each plan) or one offer across plans (→ one add-on plan, sizes as tiers on its prepaid item)? And does a plan exist at the level its balance is shared at (→ item there) or are all plans entity-attached (→ a customer-level add-on is forced)? Auto-recharge needs the prepaid item to exist — add it in Shape, it\'s structural.\n\nFor modeling packs, top-ups, or any purchase bought on top of a plan — add-on vs plan item, and what level it sits at, read `references/fork-addon.md`.\n\n**Where do balances and purchases live?**\nThe rule: **purchases and balance at the customer; usage tracking and caps at the entity.** Grants "shared across…" entities → pooled (`pooled: true` on the item); separate per-entity balances → no pooling; overage stays on the entity\'s plan either way.\n\nFor deciding where balances and purchases live — pooled grants, per-entity balances, customer-level packs, overage placement, read `references/fork-pooled.md`.\n\n**Groups?**\nCan one customer hold two plans at once from different lines (a support plan AND a sales plan)? → one group per line. Within a group, attaching a plan replaces the current one; that\'s what makes upgrades work.\n\n**One-off?**\n"Lifetime deal", "one-time pack" → a plan with a one-off price: single invoice, no subscription, balances never reset.\n\n## Check\n\nCompare the structure against the shapes in `references/cases.md`. If their pricing matches a known shape but your structure differs, either say why or fix it.\n\nShortcuts that are usually wrong — catch yourself before Show:\n\n| You\'re thinking | Check first |\n|---|---|\n| "seats + credits → licenses" | Are the credits per seat, or one shared pot? Shared → per-unit seats + pooled balance, no licenses. |\n| "annual pricing → separate plan" | Same features? → variant. |\n| "tiers → one plan with tiers" | Does overage or anything else differ per tier? → plan per tier. |\n| "they said credits but it\'s one action → plain meter" | Credits are always a credit system. |\n| "packs belong on the plan" | Are they shared across entities or plans? → add-on. |\n| "everyone starts on a Pro trial → trial + auto-enable on Pro" | A default plan can never be paid. → separate free `pro_trial` plan (Pro\'s items, no price, auto-enabled); Pro untouched. |\n| "several top-up sizes → one add-on plan per size" | Do the sizes differ only in quantity and price? → volume tiers on one prepaid item, one add-on plan. |\n| "the pack is priced per plan → an add-on per plan" | Per-plan pricing IS plan differentiation → a prepaid item on each base plan, no add-ons. Add-ons are for one offer shared across plans, or when no plan exists at the shared level. |\n| "the seat differs per plan → one seat plan per parent" | ONE child plan carrying the mainline take; each differing parent\'s license carries its own diff via `customize`. Never a `<parent>_seat` plan per parent. |\n\nFor checking the derived structure against known-good shapes, read `references/cases.md`.\n\n## Show\n\nPresent the structure in the "Showing the catalog" format below — the same one used for every catalog display, catalog inside a fenced code block. Numbers you don\'t have yet stay open, never invented: write the line without them ("AI messages per month — amount TBD"). Structure notes go in parentheses on the line they describe. The whole message:\n\n````\nHere\'s the structure I\'d build:\n\n```\nFeatures: AI credits (credit system — chat and image messages draw from it) · SSO (on/off)\n\nFree — no price, everyone starts here\n - 100 AI credits per month\nPro — $20/month, or annual (same features)\n - AI credits per month — amount TBD\n - SSO\nCredit pack (add-on) — price TBD, shared across all workspaces\n```\n\nI assumed: credits reset monthly, no rollover. Anything wrong?\n````\n\nList every assumption. Get a clear yes — "sounds good" without reading is not a yes. But if the user already told you to go ahead without review ("no need to ask", "just build it"), show the structure and keep going — don\'t stop to wait. If a late fact changes the structure, redo the affected decision, update the structure, and show what changed in one line.\n\n## Fill\n\nStructure agreed — now finish it. Four moves, in order.\n\n**1 — Fill in what\'s known.** Everything the user already said goes straight into the draft. Never re-ask a confirmed fact.\n\n**2 — Ask for missing essentials.** Values with no sane default: base prices, included amounts, per-unit prices, tier boundaries, trial length. Never invent one — a missing number is a question. Batch per the how-to-ask rules.\n\n**3 — Sweep the options.** Ask each of these **once for the whole catalog**, in plain words — never item by item. The answer distributes to every item it touches ("carry over on both plans, or just Growth?" only if they hint at a difference). Raise a bucket only when the structure makes it relevant; skip anything already answered.\n\n- **Carry-over** — any allowance that resets: "Should unused messages carry over month to month, or reset clean?"\n- **Running out** — any metered feature: "When they run out — hard stop, or keep going and bill the extra?" (often settled in Shape; skip if so)\n- **Top-ups** — credits present or buying-more mentioned: "Can they buy more before the reset?" → a one-off prepaid item\n- **Guardrails** — any plan with overage or heavy usage: "Any caps on how fast or how far usage can run — like a daily limit, or a ceiling on overage? Want customers warned as they approach limits?" → plan-level billing controls\n- **Anything else on/off** — always, it\'s cheap: "Any other on/off differences between plans — SSO, priority support, API access?"\n\nThe sweep exists so the user hears what\'s configurable without being marched through every item. Behind it, check every knob yourself — this list is internal, never show it:\n\n- per plan: base price · trial (length, unit, card — all explicit) · default plan · group · billing controls (guardrails answered → `billingControls` on the plan)\n- per item: billing method · included · price or tiers (tier behavior explicit) · reset · rollover · pooled (if Shape chose shared balances) · purchase caps · the one-off item auto-recharge needs\n\nLanding a guardrail answer means picking the right control — windowed cap vs overage ceiling vs alert vs allow-past-balance vs auto-recharge are different knobs with different fields. What each one is, the three overage knobs, and the plan→customer inheritance are the `autumn-concepts` skill\'s billing-controls reference — read it before writing `billingControls`. The flow rules here:\n\n- Plan-level controls are defaults every subscriber inherits — the right home for anything true of the whole plan ("free users max 20 emails/day"). Per-customer exceptions are a billing/customer operation, not catalog work.\n- A cap stated alongside an allowance ("1,000 a month but never more than 20 a day") is a usage limit on the plan, not a second item or a smaller allowance.\n- "Track it but don\'t bill it" / "let them run over, we\'ll invoice manually" → overage knobs on the plan, not a $0 price.\n- Auto-recharge needs its one-off prepaid item (already on the per-item list) AND the `autoTopups` control.\n\n**4 — Propose, then finalize.** One message: the full catalog in the format below, then "I assumed:" listing every knob you defaulted. Fold corrections in. Then write the config — and before saving, re-read every amount in it: dollars, never cents ($600 is `600`, not `60000`). Validate with `atmn push` (a preview; nothing is applied until `--yes`), fix what it flags, and show the final catalog — same format, no assumptions list. **Done means the config is written and valid — a summary is not done.**\n\n### Showing the catalog\n\nUse **exactly this format** every time you show the catalog — the Show structure message, the Fill proposal, and the done message alike. Never a markdown table, never a different layout per message. Send it as a fenced code block (```) so the indentation survives markdown rendering — plan names on bare lines otherwise get folded into the previous plan\'s list. It\'s the same grammar the dashboard renders. Features first with their kind, then plans, one line per item:\n\n```\nFeatures: AI messages (usage, resets) · Seats (held, not used up) · Credits (credit system — 1 message = 1 credit) · SSO (on/off)\n\nPro — $20/month\n - 500 AI messages per month\n · unused carry over, up to 500, for 1 month\n - 500 AI messages per month, then $0.01 per message\n - $10 per 1,000 credits\n - 5,000 credits for $50 per month\n - 3 seats included, then $10 per seat\n - $18 - $15 per seat\n - Unlimited projects\n - SSO\n - 100 credits per seat per month\nTeam — $500/month\n - 5 seats included, then $40 per seat per month\n each seat gets:\n · 100 summaries per month\n · SSO\nCredit pack (add-on) — $10 for 1,000 credits, buy anytime\n```\n\nThe Features line names every feature and its kind in plain words: `(usage, resets)` for consumable meters, `(held, not used up)` for non-consumable ones like seats, `(credit system — …)` with its action mappings, `(on/off)` for boolean. Item lines, top to bottom: plain allowance · allowance with overage · pure usage price (per billing unit) · prepaid bucket · included + prepaid per-unit · tiered rate shown first-to-last · unlimited · boolean (name only, never "enabled") · per-entity grant · one-off add-on. Numbers get commas; "then …" says what happens after the included runs out; annual variants go inline ("or $200/year — messages still reset monthly").\n\nConfigured item properties — carry-over, purchase caps, top-up behavior — are never their own `-` lines: indent them under the item they belong to as `·` lines, one behavior each, so the hierarchy is visible. Only show what\'s configured; defaults (like usage simply stopping when no overage price is set) get no line.\n\nBilling controls follow the same rule: `·` lines under the item they guard, in plain words — `· max 200 per day` · `· overage capped at 20% over` · `· overage tracked, not billed` · `· warned at 80%` · `· auto-buys 1,000 credits when below 100`. Never the schema names (usage_limits, spend_limits) in user-facing text.\n\n### Config gotchas\n\n- Amounts are plain dollars everywhere — base prices, tier `flatAmount`s, unit prices: $20 is `20`, never `2000`, and a $100 tier is `flatAmount: 100`, never `10000`. Re-check every number before writing; cents is the most common wrong config.\n- A default/auto-enabled plan can\'t be paid: no base price, no paid items.\n- A prepaid quantity **includes** the included amount, and so does each tier\'s `to` — the first tier\'s `to` must exceed `included`.\n- `billingUnits` rounds usage **up** when billing.\n- Set explicitly, never lean on defaults: `billingMethod` and `interval` on every item price, `tierBehavior` on tiered prices, and every trial field (`durationLength`, `durationType`, `cardRequired`, `onEnd`). Explicit defaults cause no spurious diffs.\n- Volume tiers on usage-based items bill only usage past the included amount, at the rate of the tier total usage reaches, plus its flat fee. On prepaid items they charge every unit (included ones too) at the reached tier\'s rate and flat amount. Graduated (the default) sums across brackets. Volume can\'t be combined with threshold billing, and allocated volume can\'t have a tier-1 flat amount.\n- Rollover needs a resetting allowance; `max` and `maxPercentage` are mutually exclusive; `expiryDurationType` is required.\n- `billingControls` is a plain object on the plan with camelCase fields like the rest of the config (`featureId`, `overageLimit`); each control list replaces wholesale on update.\n- Plan `config` (`ignorePastDue`, `anchorToMonthStart`) is unmanaged when omitted. Once stated it is the whole set: a flag left out is off, so `config: {}` turns every flag off.\n- Pooled balances are config: `pooled: true` on the entity plan\'s item. Concluding "shared across workspaces" in Shape and then omitting the flag is the classic miss.\n- Pooled grant + overage = two items on the plan: the pooled grant carries no price; a separate usage-priced item (`included: 0`) carries the overage. A pooled item can\'t itself be usage-priced.\n- Don\'t write `proration` — leave it out and take server defaults.\n- Trial end behavior is `freeTrial.onEnd`: `"bill"` (default) charges when the trial ends, `"revert"` expires it and restores the previous plan.\n- Every plan row carries `versionSlug` and `active`, and every variant row and license link `versionSlug`. A plan\'s rows are its versions — exactly one `active: true` — and a version row left out of `plans` is deleted. How rows express versions, renames and drafts: `references/atmn.md`.\n\nThe config uses the builders `feature`, `plan`, `variant`, `license` as plain function calls with object arguments; items are plain objects inside a plan, and the file\'s default export is `atmn({...})` naming every collection. Never guess other functions or fields; the full shapes are in `references/atmn.md`.\n\n```ts\nimport { atmn, feature, plan } from "atmn";\n\nexport const credits = feature({\n featureId: "credits",\n name: "Credits",\n type: "credit_system",\n creditSchema: [{ meteredFeatureId: "messages", creditCost: 1 }],\n});\n\nexport const pro = plan({\n planId: "pro",\n versionSlug: "v1",\n active: true,\n name: "Pro",\n price: { amount: 20, interval: "month" },\n items: [\n { featureId: credits.featureId, included: 500, reset: { interval: "month" } },\n ],\n});\n\nexport default atmn({ features: [credits], plans: [pro] });\n```\n\nPattern deep-dives, split one file per pattern under `references/` — read the matching one when filling that pattern\'s details:\n\nFor usage overage pricing, read `references/usage-based-pricing.md`.\nFor prepaid quantities (seats, packs, buckets), read `references/prepaid-pricing.md`.\nFor volume-tiered prices, read `references/volume-based-tiers.md`.\nFor $X per unit, read `references/per-unit-pricing.md`.\nFor billing vs reset intervals, read `references/recurring.md`.\nFor one-off purchases / top-ups, read `references/one-off-purchases.md`.\nFor auto-recharge, read `references/auto-top-ups.md`.\nFor carry-over, read `references/rollovers.md`.\nFor trial details, read `references/trials.md`.\nFor entity plans and license attach flows, read `references/entity-plans.md`.\nFor credit schemas and mappings, read `references/credit-systems.md`.\nFor default plans / auto-enable, read `references/free-plans.md`.\nFor add-on balance stacking, read `references/add-ons.md`.\nFor what variants can change, read `references/plan-variants.md`.\n\n## Conduct\n\n- In an existing config, match its patterns: if sibling plans carry their prepaid purchases as items, the new plan does too — don\'t introduce a different structure for the same kind of thing.\n- Stable lowercase IDs with underscores: `pro_plan`, `chat_messages`.\n- `entityFeatureId` is deprecated. Never mention or use it unless the user\'s existing config already has it.\n- Per-unit pricing pairs a base fee with the per-unit item ("$X/seat" plans still have a base price, even $0).\n- Speak plainly: "plans", "what\'s included", "extra usage". Schema words stay in the config — say "carry over" not "rollover", "shared across workspaces" not "pooled", "paid upfront" / "billed at month end" not "prepaid" / "usage_based".\n- Never volunteer what Autumn can or can\'t do. Don\'t offer options Autumn can\'t model, and don\'t explain limitations unprompted — only address one when the user directly asks to model that specific thing, and even then lead with the closest thing that works.\n- Start simple: the most important features first, confirm before adding more.\n\nBefore finishing: re-check the STRICT RULES at the top against the config you wrote.\n\n## Catalog operations\n\nFor using atmn, autumn.config.ts, or headless push flows, read `references/atmn.md`.\n\nFor changing an existing catalog: previewing, versioning, migrations, variant propagation, read `references/catalog-update.md`.\n\nFor creating or changing coupons, promo codes, feature grants, or referral programs, read `references/rewards.md`.',
41
+ '---\nname: autumn-catalog\ndescription: Modeling a user\'s pricing into an Autumn catalog — deciding the structure (plans, variants, add-ons, licenses, credit systems, pooled balances) before writing config, then filling in the numbers. Use when the user describes their pricing or asks to model, change, or push a catalog.\nversion: 2.0.141\n---\n\n# Catalog\n\nBefore using this skill, first load the `autumn-concepts` skill — it defines Autumn\'s data model — features, plans, plan items, balances — which every modeling decision builds on.\n\n## STRICT RULES — re-read before every config write\n\n1. **Amounts are in major units (e.g. dollars), never minor units (cents).** $180/month is `amount: 180`. $0.01 per credit is `amount: 0.01`. If any amount you wrote is 100× the user\'s number, it is wrong.\n2. **Never invent a price, limit, or plan name** — a missing number is a question.\n3. **One definition per real thing.** One feature per resource, one child plan per license pattern (parents customize their license, never get their own copy), one add-on per offer (sizes are tiers, not plans).\n\n**Building, or iterating on a live catalog?** What matters is whether customers are on these plans — not whether a config file exists. Still setting up (even across sessions, with a half-built `autumn.config.ts`) → the workflow below; edit the draft freely. Already running Autumn with customers, now changing prices/plans → that\'s an update with real stakes (versioning, migrations, grandfathering) — read `references/catalog-update.md` first. Unsure → check for customers (`atmn pull` / the org) or ask.\n\nTurning pricing into a catalog is two jobs:\n\n- **Shape** — decide the structure: which plans exist, what\'s a variant, what\'s an add-on, where balances live. Decided by relationships in their pricing, not by amounts.\n- **Fill** — put in the numbers and per-item details, then write and validate the config.\n\nDo Shape fully before Fill. Take numbers whenever the user mentions them, but never chase numbers during Shape — the one exception is "is this the same on every plan?", which is a structure question.\n\n## Progress (what the user sees)\n\nCopy this checklist into your first message and keep it up to date. It is the **only** structure the user ever sees — never say "step", "pass", "decide", or "fork" to them. If you skip an item, say why in one line.\n\n- [ ] 1 Your plans and prices\n- [ ] 2 What\'s included in each plan\n- [ ] 3 How billing behaves (signup, trials, limits)\n- [ ] 4 Licenses — paid seats, workspaces, projects (if any)\n- [ ] 5 Structure agreed\n- [ ] 6 Config written and checked\n\n## How to ask\n\n- One topic per message. Two questions at most, and only if both belong to that topic. Never mix topics in one message.\n- If one ambiguity changes which other questions apply, resolve it first before asking those.\n- Attach your guess to each question — "does the trial need a card? I\'d guess no" — a wrong guess gets corrected faster than a blank gets answered.\n- Assert the obvious instead of asking. "500 messages a month" resets monthly — state it as an assumption, don\'t ask. Questions are only for facts that change the structure and can\'t be guessed.\n- Skip anything they already told you. A good message to answer is a nod, not homework.\n- Never announce what you\'ll do next ("once I get those three, I\'ll restate…") — just work the current topic.\n- The restate (step 5) is the safety net: wrong assumptions get caught there cheaply, which is what makes fewer questions safe.\n\n## Shape: collect, then decide\n\nWork the four steps below in order, one at a time — each says when it\'s done. While collecting, make only the small calls each step allows; leave the big ones (marked ↦ Decide) for after.\n\n### Step 1 — Plans\n\n- What are the plans? ("Free, Pro $20/mo, Growth $50/mo")\n- Free tier? → a plan with no price that every new customer starts on automatically.\n- Anything bought *alongside* a plan rather than instead of it (packs, extra storage)? → note it ↦ Decide (add-on).\n- Enterprise tier? Ask. If it exists, model the base enterprise plan in the catalog, and tell the user: custom terms per customer (special prices, custom limits) are applied later when attaching, not modeled here.\n\nDone when you can list every plan they sell, including free, add-ons, and enterprise.\n\n### Step 2 — What each plan includes\n\nHave them describe what they charge for or limit, in their own words. Pick a type per feature:\n\n| They say | Feature type |\n|---|---|\n| "Pro has SSO" | boolean (on/off) |\n| "500 messages a month" | metered, resets |\n| "10 team members" | metered, no reset (held, not used up) |\n| "credits" / "tokens" / "wallet" | credit system — always, even if only one action uses it today. They will add more actions; a single mapped action is fine. Their app tracks the actions (chat, image), never the credit balance itself. |\n\nNote top-ups ("buy more when you run out", "auto-recharge") ↦ Decide (top-up placement).\n\n### Step 3 — How billing behaves\n\nThis step usually means explaining Autumn to the user in plain words. Do.\n\n- **Signup**: every new customer automatically gets the default plan — usually the free one. One default per group. A default can carry no prices at all — a "$0" plan with paid items (per-seat charges, prepaid packs) is a paid plan, not a default. If every plan bills something, there is no default; customers subscribe.\n- **Trials**: ask "does the trial need a card?" (guess from their motion — PLG usually no). Card → `free_trial` on the paid plan. No card → a **separate free trial plan** (`pro_trial`: no price, the paid plan\'s items, auto-enabled when everyone starts on it); the paid plan stays untouched, since a default plan can never be paid. For trial modeling details — trial-behavior on plans, read `references/plan.md` in the `autumn-concepts` skill.\n- **What is a plan attached to?** The customer, or each thing they own (workspace, project, site)? "Pro is $200 per workspace" → attached per entity. Pin this down — it changes everything downstream.\n- **Who uses each metered feature?** The customer as a whole, or each entity? If entities: one shared balance or separate ones? "Shared across…" → shared ↦ Decide (balances).\n\nDone when you know what attaches where, who consumes what, and how trials and signup work.\n\n### Step 4 — Licenses\n\nTriggered whenever the customer pays per unit of some entity — seats, workspaces, projects, sites, members: "each X is $10/month", "comes with 3 X". When you see one, always ask: **what does one X come with?** Never skip this because the user didn\'t say "seat". For modeling a license — the concept: child plan, license link, customize, read `references/licenses.md` in the `autumn-concepts` skill.\n\n- Nothing of its own ("$10 per seat", just a count) → a per-unit priced item on the plan. No entities. The common case.\n- The unit gets something of its own ("each seat gets 100 credits", "every workspace has its own allowance") → a **license**: a small plan of its own that the parent plan hands out per unit.\n- Units must be assigned, reassigned, or sit empty → also licenses. Rare — confirm they need it.\n\nFor deciding between a per-unit item, licenses, and entity-attached plans for a countable paid unit, read `references/fork-licenses.md`.\n\n### Before deciding: restate\n\nOnly after your questions are answered — never announce it in advance, and never restate facts nobody has confirmed yet. One short message: the plans, what\'s metered, what attaches where, who shares what. Let the user correct it. A wrong fact here is much cheaper than a wrong structure later.\n\nIf the user gave you everything up front and you asked nothing, skip the separate restate — the structure message (checklist item 5) does that job.\n\n## Decide\n\nResolve these with all facts in hand. Each has a default — when the facts genuinely don\'t settle it, show both options in one line each and ask.\n\n**Variant or separate plan?**\nA variant can change the price, swap items in or out, and change the trial — nothing else.\n\n- "Pro monthly / Pro annual, same features" → variant. (Annual usually still resets allowances monthly — billing and reset intervals are independent. Confirm.)\n- Volume buckets/tiers — one question decides: **is each bucket the subscription itself (→ a variant per bucket) or a purchase on top of one (→ a prepaid item)?**\n- Different features per tier → separate plans. One plan per tier is normal, not a smell.\n\nFor deciding whether volume buckets/tiers are variants of the plan or one prepaid volume-tiered item — the tells, the trap, when variants are forced, read `references/fork-variants.md`.\n\n**Add-on, or part of the plan?** Two independent questions: is the purchase priced/bundled per plan (→ item on each plan) or one offer across plans (→ one add-on plan, sizes as tiers on its prepaid item)? And does a plan exist at the level its balance is shared at (→ item there) or are all plans entity-attached (→ a customer-level add-on is forced)? Auto-recharge needs the prepaid item to exist — add it in Shape, it\'s structural.\n\nFor modeling packs, top-ups, or any purchase bought on top of a plan — add-on vs plan item, and what level it sits at, read `references/fork-addon.md`.\n\n**Where do balances and purchases live?**\nThe rule: **purchases and balance at the customer; usage tracking and caps at the entity.** Grants "shared across…" entities → pooled (`pooled: true` on the item); separate per-entity balances → no pooling; overage stays on the entity\'s plan either way.\n\nFor deciding where balances and purchases live — pooled grants, per-entity balances, customer-level packs, overage placement, read `references/fork-pooled.md`.\n\n**Groups?**\nCan one customer hold two plans at once from different lines (a support plan AND a sales plan)? → one group per line. Within a group, attaching a plan replaces the current one; that\'s what makes upgrades work.\n\n**One-off?**\n"Lifetime deal", "one-time pack" → a plan with a one-off price: single invoice, no subscription, balances never reset.\n\n## Check\n\nCompare the structure against the shapes in `references/cases.md`. If their pricing matches a known shape but your structure differs, either say why or fix it.\n\nShortcuts that are usually wrong — catch yourself before Show:\n\n| You\'re thinking | Check first |\n|---|---|\n| "seats + credits → licenses" | Are the credits per seat, or one shared pot? Shared → per-unit seats + pooled balance, no licenses. |\n| "annual pricing → separate plan" | Same features? → variant. |\n| "tiers → one plan with tiers" | Does overage or anything else differ per tier? → plan per tier. |\n| "they said credits but it\'s one action → plain meter" | Credits are always a credit system. |\n| "packs belong on the plan" | Are they shared across entities or plans? → add-on. |\n| "everyone starts on a Pro trial → trial + auto-enable on Pro" | A default plan can never be paid. → separate free `pro_trial` plan (Pro\'s items, no price, auto-enabled); Pro untouched. |\n| "several top-up sizes → one add-on plan per size" | Do the sizes differ only in quantity and price? → volume tiers on one prepaid item, one add-on plan. |\n| "the pack is priced per plan → an add-on per plan" | Per-plan pricing IS plan differentiation → a prepaid item on each base plan, no add-ons. Add-ons are for one offer shared across plans, or when no plan exists at the shared level. |\n| "the seat differs per plan → one seat plan per parent" | ONE child plan carrying the mainline take; each differing parent\'s license carries its own diff via `customize`. Never a `<parent>_seat` plan per parent. |\n\nFor checking the derived structure against known-good shapes, read `references/cases.md`.\n\n## Show\n\nPresent the structure in the "Showing the catalog" format below — the same one used for every catalog display, catalog inside a fenced code block. Numbers you don\'t have yet stay open, never invented: write the line without them ("AI messages per month — amount TBD"). Structure notes go in parentheses on the line they describe. The whole message:\n\n````\nHere\'s the structure I\'d build:\n\n```\nFeatures: AI credits (credit system — chat and image messages draw from it) · SSO (on/off)\n\nFree — no price, everyone starts here\n - 100 AI credits per month\nPro — $20/month, or annual (same features)\n - AI credits per month — amount TBD\n - SSO\nCredit pack (add-on) — price TBD, shared across all workspaces\n```\n\nI assumed: credits reset monthly, no rollover. Anything wrong?\n````\n\nList every assumption. Get a clear yes — "sounds good" without reading is not a yes. But if the user already told you to go ahead without review ("no need to ask", "just build it"), show the structure and keep going — don\'t stop to wait. If a late fact changes the structure, redo the affected decision, update the structure, and show what changed in one line.\n\n## Fill\n\nStructure agreed — now finish it. Four moves, in order.\n\n**1 — Fill in what\'s known.** Everything the user already said goes straight into the draft. Never re-ask a confirmed fact.\n\n**2 — Ask for missing essentials.** Values with no sane default: base prices, included amounts, per-unit prices, tier boundaries, trial length. Never invent one — a missing number is a question. Batch per the how-to-ask rules.\n\n**3 — Sweep the options.** Ask each of these **once for the whole catalog**, in plain words — never item by item. The answer distributes to every item it touches ("carry over on both plans, or just Growth?" only if they hint at a difference). Raise a bucket only when the structure makes it relevant; skip anything already answered.\n\n- **Carry-over** — any allowance that resets: "Should unused messages carry over month to month, or reset clean?"\n- **Running out** — any metered feature: "When they run out — hard stop, or keep going and bill the extra?" (often settled in Shape; skip if so)\n- **Top-ups** — credits present or buying-more mentioned: "Can they buy more before the reset?" → a one-off prepaid item\n- **Guardrails** — any plan with overage or heavy usage: "Any caps on how fast or how far usage can run — like a daily limit, or a ceiling on overage? Want customers warned as they approach limits?" → plan-level billing controls\n- **Anything else on/off** — always, it\'s cheap: "Any other on/off differences between plans — SSO, priority support, API access?"\n\nThe sweep exists so the user hears what\'s configurable without being marched through every item. Behind it, check every knob yourself — this list is internal, never show it:\n\n- per plan: base price · trial (length, unit, card — all explicit) · default plan · group · billing controls (guardrails answered → `billingControls` on the plan)\n- per item: billing method · included · price or tiers (tier behavior explicit) · reset · rollover · pooled (if Shape chose shared balances) · purchase caps · the one-off item auto-recharge needs\n\nLanding a guardrail answer means picking the right control — windowed cap vs overage ceiling vs alert vs allow-past-balance vs auto-recharge are different knobs with different fields. What each one is, the three overage knobs, and the plan→customer inheritance are the `autumn-concepts` skill\'s billing-controls reference — read it before writing `billingControls`. The flow rules here:\n\n- Plan-level controls are defaults every subscriber inherits — the right home for anything true of the whole plan ("free users max 20 emails/day"). Per-customer exceptions are a billing/customer operation, not catalog work.\n- A cap stated alongside an allowance ("1,000 a month but never more than 20 a day") is a usage limit on the plan, not a second item or a smaller allowance.\n- "Track it but don\'t bill it" / "let them run over, we\'ll invoice manually" → overage knobs on the plan, not a $0 price.\n- Auto-recharge needs its one-off prepaid item (already on the per-item list) AND the `autoTopups` control.\n\n**4 — Propose, then finalize.** One message: the full catalog in the format below, then "I assumed:" listing every knob you defaulted. Fold corrections in. Then write the config — and before saving, re-read every amount in it: dollars, never cents ($600 is `600`, not `60000`). Validate with `atmn push` (a preview; nothing is applied until `--yes`), fix what it flags, and show the final catalog — same format, no assumptions list. **Done means the config is written and valid — a summary is not done.**\n\n### Showing the catalog\n\nUse **exactly this format** every time you show the catalog — the Show structure message, the Fill proposal, and the done message alike. Never a markdown table, never a different layout per message. Send it as a fenced code block (```) so the indentation survives markdown rendering — plan names on bare lines otherwise get folded into the previous plan\'s list. It\'s the same grammar the dashboard renders. Features first with their kind, then plans, one line per item:\n\n```\nFeatures: AI messages (usage, resets) · Seats (held, not used up) · Credits (credit system — 1 message = 1 credit) · SSO (on/off)\n\nPro — $20/month\n - 500 AI messages per month\n · unused carry over, up to 500, for 1 month\n - 500 AI messages per month, then $0.01 per message\n - $10 per 1,000 credits\n - 5,000 credits for $50 per month\n - 3 seats included, then $10 per seat\n - $18 - $15 per seat\n - Unlimited projects\n - SSO\n - 100 credits per seat per month\nTeam — $500/month\n - 5 seats included, then $40 per seat per month\n each seat gets:\n · 100 summaries per month\n · SSO\nCredit pack (add-on) — $10 for 1,000 credits, buy anytime\n```\n\nThe Features line names every feature and its kind in plain words: `(usage, resets)` for consumable meters, `(held, not used up)` for non-consumable ones like seats, `(credit system — …)` with its action mappings, `(on/off)` for boolean. Item lines, top to bottom: plain allowance · allowance with overage · pure usage price (per billing unit) · prepaid bucket · included + prepaid per-unit · tiered rate shown first-to-last · unlimited · boolean (name only, never "enabled") · per-entity grant · one-off add-on. Numbers get commas; "then …" says what happens after the included runs out; annual variants go inline ("or $200/year — messages still reset monthly").\n\nConfigured item properties — carry-over, purchase caps, top-up behavior — are never their own `-` lines: indent them under the item they belong to as `·` lines, one behavior each, so the hierarchy is visible. Only show what\'s configured; defaults (like usage simply stopping when no overage price is set) get no line.\n\nBilling controls follow the same rule: `·` lines under the item they guard, in plain words — `· max 200 per day` · `· overage capped at 20% over` · `· overage tracked, not billed` · `· warned at 80%` · `· auto-buys 1,000 credits when below 100`. Never the schema names (usage_limits, spend_limits) in user-facing text.\n\n### Config gotchas\n\n- Amounts are plain dollars everywhere — base prices, tier `flatAmount`s, unit prices: $20 is `20`, never `2000`, and a $100 tier is `flatAmount: 100`, never `10000`. Re-check every number before writing; cents is the most common wrong config.\n- A default/auto-enabled plan can\'t be paid: no base price, no paid items.\n- A prepaid quantity **includes** the included amount, and so does each tier\'s `to` — the first tier\'s `to` must exceed `included`.\n- `billingUnits` rounds usage **up** when billing.\n- Set explicitly, never lean on defaults: `billingMethod` and `interval` on every item price, `tierBehavior` on tiered prices, and every trial field (`durationLength`, `durationType`, `cardRequired`, `onEnd`). Explicit defaults cause no spurious diffs.\n- Volume tiers on usage-based items bill only usage past the included amount, at the rate of the tier total usage reaches, plus its flat fee. On prepaid items they charge every unit (included ones too) at the reached tier\'s rate and flat amount. Graduated (the default) sums across brackets. Volume can\'t be combined with threshold billing, and allocated volume can\'t have a tier-1 flat amount.\n- Rollover needs a resetting allowance; `max` and `maxPercentage` are mutually exclusive; `expiryDurationType` is required.\n- `billingControls` is a plain object on the plan with camelCase fields like the rest of the config (`featureId`, `overageLimit`); each control list replaces wholesale on update.\n- Plan `config` (`ignorePastDue`, `anchorToMonthStart`) is unmanaged when omitted. Once stated it is the whole set: a flag left out is off, so `config: {}` turns every flag off.\n- Pooled balances are config: `pooled: true` on the entity plan\'s item. Concluding "shared across workspaces" in Shape and then omitting the flag is the classic miss.\n- Pooled grant + overage = two items on the plan: the pooled grant carries no price; a separate usage-priced item (`included: 0`) carries the overage. A pooled item can\'t itself be usage-priced.\n- Don\'t write `proration` — leave it out and take server defaults.\n- Trial end behavior is `freeTrial.onEnd`: `"bill"` (default) charges when the trial ends, `"revert"` expires it and restores the previous plan.\n- Every plan row carries `versionSlug` and `active`, and every variant row and license link `versionSlug`. A plan\'s rows are its versions — exactly one `active: true` — and a version row left out of `plans` is deleted. How rows express versions, renames and drafts: `references/atmn.md`.\n\nThe config uses the builders `feature`, `plan`, `variant`, `license` as plain function calls with object arguments; items are plain objects inside a plan, and the file\'s default export is `atmn({...})` naming every collection. Never guess other functions or fields; the full shapes are in `references/atmn.md`.\n\n```ts\nimport { atmn, feature, plan } from "atmn";\n\nexport const credits = feature({\n featureId: "credits",\n name: "Credits",\n type: "credit_system",\n creditSchema: [{ meteredFeatureId: "messages", creditCost: 1 }],\n});\n\nexport const pro = plan({\n planId: "pro",\n versionSlug: "v1",\n active: true,\n name: "Pro",\n price: { amount: 20, interval: "month" },\n items: [\n { featureId: credits.featureId, included: 500, reset: { interval: "month" } },\n ],\n});\n\nexport default atmn({ features: [credits], plans: [pro] });\n```\n\nPattern deep-dives, split one file per pattern under `references/` — read the matching one when filling that pattern\'s details:\n\nFor usage overage pricing, read `references/usage-based-pricing.md`.\nFor prepaid quantities (seats, packs, buckets), read `references/prepaid-pricing.md`.\nFor volume-tiered prices, read `references/volume-based-tiers.md`.\nFor $X per unit, read `references/per-unit-pricing.md`.\nFor billing vs reset intervals, read `references/recurring.md`.\nFor one-off purchases / top-ups, read `references/one-off-purchases.md`.\nFor auto-recharge, read `references/auto-top-ups.md`.\nFor carry-over, read `references/rollovers.md`.\nFor trial details, read `references/trials.md`.\nFor entity plans and license attach flows, read `references/entity-plans.md`.\nFor credit schemas and mappings, read `references/credit-systems.md`.\nFor default plans / auto-enable, read `references/free-plans.md`.\nFor add-on balance stacking, read `references/add-ons.md`.\nFor what variants can change, read `references/plan-variants.md`.\n\n## Conduct\n\n- In an existing config, match its patterns: if sibling plans carry their prepaid purchases as items, the new plan does too — don\'t introduce a different structure for the same kind of thing.\n- Stable lowercase IDs with underscores: `pro_plan`, `chat_messages`.\n- `entityFeatureId` is deprecated. Never mention or use it unless the user\'s existing config already has it.\n- Per-unit pricing pairs a base fee with the per-unit item ("$X/seat" plans still have a base price, even $0).\n- Speak plainly: "plans", "what\'s included", "extra usage". Schema words stay in the config — say "carry over" not "rollover", "shared across workspaces" not "pooled", "paid upfront" / "billed at month end" not "prepaid" / "usage_based".\n- Never volunteer what Autumn can or can\'t do. Don\'t offer options Autumn can\'t model, and don\'t explain limitations unprompted — only address one when the user directly asks to model that specific thing, and even then lead with the closest thing that works.\n- Start simple: the most important features first, confirm before adding more.\n\nBefore finishing: re-check the STRICT RULES at the top against the config you wrote.\n\n## Catalog operations\n\nFor using atmn, autumn.config.ts, or headless push flows, read `references/atmn.md`.\n\nFor changing an existing catalog: previewing, versioning, migrations, variant propagation, read `references/catalog-update.md`.\n\nFor creating or changing coupons, promo codes, feature grants, or referral programs, read `references/rewards.md`.',
42
42
  references: [
43
43
  {
44
44
  path: "references/usage-based-pricing.md",
@@ -156,9 +156,9 @@ export const SKILLS: readonly BundledSkill[] = [
156
156
  name: "autumn-integrate",
157
157
  description:
158
158
  "Integrating Autumn into an app's backend — creating customers, billing flows (attach, checkout, upgrade, cancel), gating features with check, recording usage with track, and exposing billing data to the frontend. Use when the user asks to integrate Autumn, add billing/payments to their app, gate features, meter usage, or build checkout/upgrade flows. Requires a pushed catalog; use autumn-catalog first if plans don't exist yet.",
159
- version: "2.0.139",
159
+ version: "2.0.141",
160
160
  markdown:
161
- "---\nname: autumn-integrate\ndescription: Integrating Autumn into an app's backend — creating customers, billing flows (attach, checkout, upgrade, cancel), gating features with check, recording usage with track, and exposing billing data to the frontend. Use when the user asks to integrate Autumn, add billing/payments to their app, gate features, meter usage, or build checkout/upgrade flows. Requires a pushed catalog; use autumn-catalog first if plans don't exist yet.\nversion: 2.0.139\n---\n\n# Integrate\n\nBefore using this skill, first load the `autumn-concepts` skill — it defines the objects these calls operate on — customers, plans, balances, entities.\n\n## STRICT RULES\n\n1. **Everything is backend.** All Autumn calls run in the app's server code with `AUTUMN_SECRET_KEY` — never in the browser. The frontend gets billing data only through the app's own endpoints.\n2. **The old frontend layer is deprecated — never use or suggest it**: `autumnHandler` mounts, React hooks (`useCustomer`, `useEntity`, `AutumnProvider`), shadcn components. If an app already uses them, don't rip them out unasked — but build new work backend-only.\n3. **Resolve the customer before anything else.** Every handler that checks, tracks, or attaches must serve a brand-new user: `customers.getOrCreate` with the app's own stable id (user or org id from auth — whoever pays — never an email). Never assume the customer exists.\n4. **An error response is a failure.** A 4xx/5xx from any Autumn call is never \"working\" — read the error, fix the call.\n\n## Order of operations\n\nWork these in order; each step names what to settle and where the details live. Read the referenced doc before implementing the step — the snippets and parameters are there, not here.\n\n**1 — Discover the app.** Framework, server entry points, where auth lives, which existing routes handle the actions being billed. Settle the customer id: the app's stable user or org id — whoever pays. If the product bills per workspace/project/seat, those are entities under one customer, not separate customers.\n\n**2 — Settle the level.** Before writing any flow, ask the user how their plans and features actually work: what does a plan get bought for — the whole account, or each workspace/deployment/seat under it? And where does each feature's balance live — shared across the account, or per unit? The catalog hints (entity-scoped items, license plans) but doesn't decide — a plan can attach per deployment while credit packs stay account-wide. The answers flow through everything: `billing.attach` takes `entityId` for entity-level plans, `check`/`track` take `entityId` for entity-scoped balances, billing controls set at the matching level. Mixing levels silently misbills — an account-wide check against a per-seat balance always passes. For entities, entity-scoped balances, when to use which level, read `references/feature-entities.md`. For plans that attach per entity, read `references/entity-plans.md`.\n\n**3 — Install and connect.** `autumn-js` (or the platform's SDK) + `AUTUMN_SECRET_KEY` in server env. One shared client in server code. For install, client setup, the end-to-end happy path, read `references/setup.md`.\n\n**4 — Customer creation.** `customers.getOrCreate` where the app knows who the user is — signup/login hook, or lazily in billing handlers. Pass name/email when available. A default (auto-enable) plan attaches on creation — no attach call for the free tier. If step 2 settled anything at entity level, mirror this for entities: when the app creates its unit (a workspace, a deployment, a seat), create the entity then — usually in the same handler, right before the entity-level attach or check that needs it. For B2B invoicing, tax details (address, VAT IDs, reverse charge, a PO number on invoices) go in `billing_details` on create or update — written to Stripe, read back with `expand: [\"billing_details\"]`. For getOrCreate parameters, Stripe linking, pre-creating, read `references/creating-customers.md`. For billing_details fields, tax ID add/remove, replace semantics, read `references/managing-customers-billing-details.md`. For creating entities, entity parameters, read `references/feature-entities.md`.\n\n**5 — Purchase and plan changes.** All through two calls, split by intent (pass `entityId` when the plan attaches at entity level — step 2):\n - `billing.attach` — getting on a plan, AND upgrades/downgrades within a plan group: attach the new plan; a higher (monthly-normalized) price applies immediately with proration, a lower one schedules for period end. `planSchedule` overrides. Default attach charges a saved card in place and returns `checkout_url` only when payment is actually needed — handle both outcomes. Force `redirectMode: \"always\"` only when the product wants hosted checkout unconditionally; custom flows preview first. For hosted vs custom checkout, redirectMode, previewAttach, read `references/payment-flow.md`. For upgrade/downgrade behavior, scheduled downgrades, carry-over on upgrade, read `references/subscription-lifecycle.md`.\n - `billing.update` — changing the subscription you're on: prepaid quantities (seats), `cancelAction` (`cancel_end_of_cycle` / `cancel_immediately` / `uncancel`), customization. It never switches plans — that's attach's job. Preview twins exist for both calls — use them wherever the app shows a confirmation screen. For quantities, cancelAction, previewUpdate, prorationBehavior, read `references/updating-subscriptions.md`.\n - When several plans move together — a plan plus its add-on in one purchase, or tied cancellation — use the multi twins: `billing.multiAttach` (one checkout / one payment covering every plan) and `billing.multiUpdate` (one atomic call applying a `cancelAction` per plan). Two sequential attach/update calls mean two checkouts or a half-canceled customer, and concurrent ones trip the per-customer lock.\n - Payment snags are response **state, not exceptions**: `required_action` (3DS, payment method, failure) plus `payment_url` — handle the states, surface the URL. For 3DS, payment failures, required_action codes, read `references/edge-cases.md`.\n - For deep parameter detail on attach, update, and the other billing actions (createSchedule, discounts, custom terms), the `autumn-billing` skill is the reference — it's written for operating billing directly, but its parameter and edge-case knowledge applies when implementing these calls in code.\n\n**6 — Check and track.** In the billed action's own handler: `check` before the work, `track` after it succeeds. Gate on `allowed` — don't re-derive access from balances. Pass `requiredBalance` when an action costs more than 1, and `entityId` when the balance lives on an entity (step 2) — check and track must agree on the level. For the check → act → track pattern, read `references/gating.md`. For check parameters, allowed semantics, credit systems, entity checks, read `references/check.md`. For track and setUsage, read `references/tracking-usage.md`.\n\n**7 — Billing controls.** Per-customer policy on top of the plan: set via `customers.update` with `billing_controls` (`spend_limits`, `usage_limits`, `usage_alerts`, `auto_topups`, `overage_allowed`), or at purchase time by passing `billing_controls` on `billing.attach`. The flagship shape is a user-facing \"overage billing on/off\" toggle: off maps to a `spend_limits` entry with `skip_overage_billing: true` — overage still accrues but is never invoiced — plus an `overage_limit` (use `limit_type: \"usage_percentage\"`, e.g. `20` = overage up to 20% of the plan allowance) so the user is blocked at the cap instead of running up unbilled usage forever; on means clearing or replacing the entry (`skip_overage_billing: false`). Whether overage needs enabling at all depends on the plan: items with a usage price already allow it (controls only modulate the billing); pure-grant items hard-stop at zero and need `overage_allowed` per customer — which permits the overage but doesn't bill it. Alerts (\"email at 80%\") are `usage_alerts` with `threshold_type: \"usage_percentage\"`; prepaid balances self-replenish via `auto_topups`. For all five controls, field shapes, customer vs entity level, plan defaults, read `references/billing-controls.md`. For spend limit semantics, skip_overage_billing, usage alerts, read `references/spend-limits.md`. For auto top-up setup and requirements, read `references/auto-top-ups.md`.\n\n**8 — Billing data for the frontend.** The app exposes its own endpoints calling `customers.get` / `plans.list` server-side; the UI reads those. For customer payload, plans list with eligibility, portal, read `references/display-billing.md`.\n\n**9 — Verify.** Call the app's routes as a fresh user; confirm in sandbox that the customer exists, checks gated, usage recorded, purchase reached checkout. `customers.get` is the oracle. **Done means verified behavior — compiling is not done.** Then undo what verifying created — release the license assignments, delete the entities, and reverse the usage your test recorded (a negative `track` or `setUsage` back), and nothing more. Never delete the customer or any state that existed before you started; pre-existing subscriptions and assignments are real. Verification also re-runs flows, so billing endpoints must tolerate repeats: joining a plan you're already on returns success, not a crash.\n\n## Gotchas the docs don't cover\n\n- **Concurrent billing mutations on one customer return 429** — attach/update share a per-customer lock; serialize instead of retrying blindly.\n- **A denied check should say what ran out** — include the feature and its balance state in the response, not a bare unexplained 403. What the caller does with it is the app's call: an API returns a clear error code, a dashboard might prompt an upgrade.\n\n## Conduct\n\n- Follow the app's existing patterns — router style, error handling, response shapes. Autumn code should look like the app wrote it.\n- Use catalog ids exactly; never invent feature or plan ids. Missing from the catalog → say so; that's an `autumn-catalog` change, not a workaround.\n- Smallest working integration first: one gated action end to end, verified, before spreading wider.\n\nGoing to production: For live keys, production checklist, read `references/deploy.md`. For fail-open behavior during Autumn outages, read `references/fail-open.md`.",
161
+ "---\nname: autumn-integrate\ndescription: Integrating Autumn into an app's backend — creating customers, billing flows (attach, checkout, upgrade, cancel), gating features with check, recording usage with track, and exposing billing data to the frontend. Use when the user asks to integrate Autumn, add billing/payments to their app, gate features, meter usage, or build checkout/upgrade flows. Requires a pushed catalog; use autumn-catalog first if plans don't exist yet.\nversion: 2.0.141\n---\n\n# Integrate\n\nBefore using this skill, first load the `autumn-concepts` skill — it defines the objects these calls operate on — customers, plans, balances, entities.\n\n## STRICT RULES\n\n1. **Everything is backend.** All Autumn calls run in the app's server code with `AUTUMN_SECRET_KEY` — never in the browser. The frontend gets billing data only through the app's own endpoints.\n2. **The old frontend layer is deprecated — never use or suggest it**: `autumnHandler` mounts, React hooks (`useCustomer`, `useEntity`, `AutumnProvider`), shadcn components. If an app already uses them, don't rip them out unasked — but build new work backend-only.\n3. **Resolve the customer before anything else.** Every handler that checks, tracks, or attaches must serve a brand-new user: `customers.getOrCreate` with the app's own stable id (user or org id from auth — whoever pays — never an email). Never assume the customer exists.\n4. **An error response is a failure.** A 4xx/5xx from any Autumn call is never \"working\" — read the error, fix the call.\n\n## Order of operations\n\nWork these in order; each step names what to settle and where the details live. Read the referenced doc before implementing the step — the snippets and parameters are there, not here.\n\n**1 — Discover the app.** Framework, server entry points, where auth lives, which existing routes handle the actions being billed. Settle the customer id: the app's stable user or org id — whoever pays. If the product bills per workspace/project/seat, those are entities under one customer, not separate customers.\n\n**2 — Settle the level.** Before writing any flow, ask the user how their plans and features actually work: what does a plan get bought for — the whole account, or each workspace/deployment/seat under it? And where does each feature's balance live — shared across the account, or per unit? The catalog hints (entity-scoped items, license plans) but doesn't decide — a plan can attach per deployment while credit packs stay account-wide. The answers flow through everything: `billing.attach` takes `entityId` for entity-level plans, `check`/`track` take `entityId` for entity-scoped balances, billing controls set at the matching level. Mixing levels silently misbills — an account-wide check against a per-seat balance always passes. For entities, entity-scoped balances, when to use which level, read `references/feature-entities.md`. For plans that attach per entity, read `references/entity-plans.md`.\n\n**3 — Install and connect.** `autumn-js` (or the platform's SDK) + `AUTUMN_SECRET_KEY` in server env. One shared client in server code. For install, client setup, the end-to-end happy path, read `references/setup.md`.\n\n**4 — Customer creation.** `customers.getOrCreate` where the app knows who the user is — signup/login hook, or lazily in billing handlers. Pass name/email when available. A default (auto-enable) plan attaches on creation — no attach call for the free tier. If step 2 settled anything at entity level, mirror this for entities: when the app creates its unit (a workspace, a deployment, a seat), create the entity then — usually in the same handler, right before the entity-level attach or check that needs it. For B2B invoicing, tax details (address, VAT IDs, reverse charge, a PO number on invoices) go in `billing_details` on create or update — written to Stripe, read back with `expand: [\"billing_details\"]`. For getOrCreate parameters, Stripe linking, pre-creating, read `references/creating-customers.md`. For billing_details fields, tax ID add/remove, replace semantics, read `references/managing-customers-billing-details.md`. For creating entities, entity parameters, read `references/feature-entities.md`.\n\n**5 — Purchase and plan changes.** All through two calls, split by intent (pass `entityId` when the plan attaches at entity level — step 2):\n - `billing.attach` — getting on a plan, AND upgrades/downgrades within a plan group: attach the new plan; a higher (monthly-normalized) price applies immediately with proration, a lower one schedules for period end. `planSchedule` overrides. Default attach charges a saved card in place and returns `checkout_url` only when payment is actually needed — handle both outcomes. Force `redirectMode: \"always\"` only when the product wants hosted checkout unconditionally; custom flows preview first. For hosted vs custom checkout, redirectMode, previewAttach, read `references/payment-flow.md`. For upgrade/downgrade behavior, scheduled downgrades, carry-over on upgrade, read `references/subscription-lifecycle.md`.\n - `billing.update` — changing the subscription you're on: prepaid quantities (seats), `cancelAction` (`cancel_end_of_cycle` / `cancel_immediately` / `uncancel`), customization. It never switches plans — that's attach's job. Preview twins exist for both calls — use them wherever the app shows a confirmation screen. For quantities, cancelAction, previewUpdate, prorationBehavior, read `references/updating-subscriptions.md`.\n - When several plans move together — a plan plus its add-on in one purchase, or tied cancellation — use the multi twins: `billing.multiAttach` (one checkout / one payment covering every plan) and `billing.multiUpdate` (one atomic call applying a `cancelAction` per plan). Two sequential attach/update calls mean two checkouts or a half-canceled customer, and concurrent ones trip the per-customer lock.\n - Payment snags are response **state, not exceptions**: `required_action` (3DS, payment method, failure) plus `payment_url` — handle the states, surface the URL. For 3DS, payment failures, required_action codes, read `references/edge-cases.md`.\n - For deep parameter detail on attach, update, and the other billing actions (createSchedule, discounts, custom terms), the `autumn-billing` skill is the reference — it's written for operating billing directly, but its parameter and edge-case knowledge applies when implementing these calls in code.\n\n**6 — Check and track.** In the billed action's own handler: `check` before the work, `track` after it succeeds. Gate on `allowed` — don't re-derive access from balances. Pass `requiredBalance` when an action costs more than 1, and `entityId` when the balance lives on an entity (step 2) — check and track must agree on the level. For the check → act → track pattern, read `references/gating.md`. For check parameters, allowed semantics, credit systems, entity checks, read `references/check.md`. For track and setUsage, read `references/tracking-usage.md`.\n\n**7 — Billing controls.** Per-customer policy on top of the plan: set via `customers.update` with `billing_controls` (`spend_limits`, `usage_limits`, `usage_alerts`, `auto_topups`, `overage_allowed`), or at purchase time by passing `billing_controls` on `billing.attach`. The flagship shape is a user-facing \"overage billing on/off\" toggle: off maps to a `spend_limits` entry with `skip_overage_billing: true` — overage still accrues but is never invoiced — plus an `overage_limit` (use `limit_type: \"usage_percentage\"`, e.g. `20` = overage up to 20% of the plan allowance) so the user is blocked at the cap instead of running up unbilled usage forever; on means clearing or replacing the entry (`skip_overage_billing: false`). Whether overage needs enabling at all depends on the plan: items with a usage price already allow it (controls only modulate the billing); pure-grant items hard-stop at zero and need `overage_allowed` per customer — which permits the overage but doesn't bill it. Alerts (\"email at 80%\") are `usage_alerts` with `threshold_type: \"usage_percentage\"`; prepaid balances self-replenish via `auto_topups`. For all five controls, field shapes, customer vs entity level, plan defaults, read `references/billing-controls.md`. For spend limit semantics, skip_overage_billing, usage alerts, read `references/spend-limits.md`. For auto top-up setup and requirements, read `references/auto-top-ups.md`.\n\n**8 — Billing data for the frontend.** The app exposes its own endpoints calling `customers.get` / `plans.list` server-side; the UI reads those. For customer payload, plans list with eligibility, portal, read `references/display-billing.md`.\n\n**9 — Verify.** Call the app's routes as a fresh user; confirm in sandbox that the customer exists, checks gated, usage recorded, purchase reached checkout. `customers.get` is the oracle. **Done means verified behavior — compiling is not done.** Then undo what verifying created — release the license assignments, delete the entities, and reverse the usage your test recorded (a negative `track` or `setUsage` back), and nothing more. Never delete the customer or any state that existed before you started; pre-existing subscriptions and assignments are real. Verification also re-runs flows, so billing endpoints must tolerate repeats: joining a plan you're already on returns success, not a crash.\n\n## Gotchas the docs don't cover\n\n- **Concurrent billing mutations on one customer return 429** — attach/update share a per-customer lock; serialize instead of retrying blindly.\n- **A denied check should say what ran out** — include the feature and its balance state in the response, not a bare unexplained 403. What the caller does with it is the app's call: an API returns a clear error code, a dashboard might prompt an upgrade.\n\n## Conduct\n\n- Follow the app's existing patterns — router style, error handling, response shapes. Autumn code should look like the app wrote it.\n- Use catalog ids exactly; never invent feature or plan ids. Missing from the catalog → say so; that's an `autumn-catalog` change, not a workaround.\n- Smallest working integration first: one gated action end to end, verified, before spreading wider.\n\nGoing to production: For live keys, production checklist, read `references/deploy.md`. For fail-open behavior during Autumn outages, read `references/fail-open.md`.",
162
162
  references: [
163
163
  {
164
164
  path: "references/feature-entities.md",
@@ -213,12 +213,12 @@ export const SKILLS: readonly BundledSkill[] = [
213
213
  {
214
214
  path: "references/gating.md",
215
215
  contents:
216
- '## Checking and tracking\n\nTypically, your users should get access to different features and usage limits, depending on their plan.\n\nAutumn handles your customer\'s payments and grants them the features defined in your plan configuration. There are 2 functions you need to enforce limits and gating:\n\n- `check` for feature access, before allowing a user to do something\n- `track` the usage in Autumn afterwards (if needed)\n\nThis example will continue from before: a 2-tier pricing model for a chatbot.\n\nThis guide shows an asynchronous approach to checking and tracking. You can also [check and reserve](/documentation/customers/check#checking-and-reserving-usage) balance in a single, atomic API call for concurrent events.\n\n### Checking feature access\n\nCheck if a user has enough remaining balance of messages, before executing the action. The `feature_id` used here is defined by you when you create the feature in Autumn.\n\n<CodeGroup>\n\n```typescript TypeScript\nimport { Autumn } from "autumn-js";\n\nconst autumn = new Autumn({\n secretKey: \'am_sk_42424242\',\n});\n\n// Check if user can send 1 message\nconst { allowed } = await autumn.check({\n customerId: "user_or_org_id_from_auth",\n featureId: "messages",\n requiredBalance: 1,\n});\n\nif (!allowed) {\n console.log("User has run out of messages");\n return;\n}\n```\n\n```python Python\nimport asyncio\nfrom autumn_sdk import Autumn\n\nautumn = Autumn(\'am_sk_42424242\')\n\nasync def main():\n # Check feature access\n response = await autumn.check(\n customer_id=\'user_or_org_id_from_auth\',\n feature_id=\'messages\',\n required_balance=1,\n )\n\nasyncio.run(main())\n```\n\n```bash cURL\n# Check feature access\ncurl -X POST \'https://api.useautumn.com/v1/check\' \\\n -H \'Authorization: Bearer am_sk_42424242\' \\\n -H \'Content-Type: application/json\' \\\n -d \'{\n "customer_id": "user_123",\n "feature_id": "messages",\n "required_balance": 1\n }\'\n```\n\n</CodeGroup>\n\nYou can also use `check` to [gate boolean features](/documentation/customers/check#checking-boolean-features) (non-metered features), such as access to "premium AI models".\n\n### Tracking usage\n\nAfter the user has successfully used a chatbot message, you can record the usage in Autumn. This will decrement the user\'s message balance.\n\n<CodeGroup>\n\n```typescript TypeScript\n// Your own function to send the chat message\n\n// Then record 1 message used\nawait autumn.track({\n customerId: "user_or_org_id_from_auth",\n featureId: "messages",\n value: 1,\n});\n```\n\n```python Python\nimport asyncio\nfrom autumn_sdk import Autumn\n\nautumn = Autumn(\'am_sk_42424242\')\n\n# Your own function to send the chat message\n\n# Then record 1 message used\nasync def main():\n await autumn.track(\n customer_id=\'user_or_org_id_from_auth\',\n feature_id=\'messages\',\n value=1,\n )\n\nasyncio.run(main())\n```\n\n```bash cURL\n# Your own function to send the chat message\n\n# Then record 1 message used\ncurl -X POST \'https://api.useautumn.com/v1/track\' \\\n -H \'Authorization: Bearer am_sk_42424242\' \\\n -H \'Content-Type: application/json\' \\\n -d \'{\n "customer_id": "user_or_org_id_from_auth",\n "feature_id": "messages",\n "value": 1\n }\'\n```\n\n</CodeGroup>\n\nOnce you send usage events, you can verify their receipt in the Autumn dashboard, on the [customer](https://app.useautumn.com/customers) detail page.\n\nYou should always handle access checks and usage tracking server-side for security. Users can manipulate client-side code using devtools.\n\n--- \n\n**Next: Build your billing page**\n\nNow, whenever your customers change their plan, they will automatically have the correct access and limits. Next, build a billing page for your customers.\n\nDisplay plan, balance and usage information to your customers using Autumn\'s `customer` state',
216
+ '## Checking and tracking\n\nTypically, your users should get access to different features and usage limits, depending on their plan.\n\nAutumn handles your customer\'s payments and grants them the features defined in your plan configuration. There are 2 functions you need to enforce limits and gating:\n\n- `check` for feature access, before allowing a user to do something\n- `track` the usage in Autumn afterwards (if needed)\n\nThis example will continue from before: a 2-tier pricing model for a chatbot.\n\nThis guide shows an asynchronous approach to checking and tracking. You can also [check and reserve](/documentation/customers/check#checking-and-reserving-usage) balance in a single, atomic API call for concurrent events.\n\n### Checking feature access\n\nCheck if a user has enough remaining balance of messages, before executing the action. The `feature_id` used here is defined by you when you create the feature in Autumn.\n\n<CodeGroup>\n\n```typescript TypeScript\nimport { Autumn } from "autumn-js";\n\nconst autumn = new Autumn({\n secretKey: \'am_sk_42424242\',\n});\n\n// Check if user can send 1 message\nconst { allowed } = await autumn.check({\n customerId: "user_or_org_id_from_auth",\n featureId: "messages",\n requiredBalance: 1,\n});\n\nif (!allowed) {\n console.log("User has run out of messages");\n return;\n}\n```\n\n```python Python\nimport asyncio\nfrom autumn_sdk import Autumn\n\nautumn = Autumn(\'am_sk_42424242\')\n\nasync def main():\n # Check feature access\n response = await autumn.check(\n customer_id=\'user_or_org_id_from_auth\',\n feature_id=\'messages\',\n required_balance=1,\n )\n\nasyncio.run(main())\n```\n\n```bash cURL\n# Check feature access\ncurl -X POST \'https://api.useautumn.com/v1/check\' \\\n -H \'Authorization: Bearer am_sk_42424242\' \\\n -H \'Content-Type: application/json\' \\\n -d \'{\n "customer_id": "user_123",\n "feature_id": "messages",\n "required_balance": 1\n }\'\n```\n\n</CodeGroup>\n\nYou can also use `check` to [gate boolean features](/documentation/customers/check#checking-boolean-features) (non-metered features), such as access to "premium AI models".\n\nEach `check` and `track` is a network call to Autumn. If they sit on a latency-sensitive path, you can deploy the [edge cache](/documentation/edge-cache) to handle both from inside your own cloud.\n\n### Tracking usage\n\nAfter the user has successfully used a chatbot message, you can record the usage in Autumn. This will decrement the user\'s message balance.\n\n<CodeGroup>\n\n```typescript TypeScript\n// Your own function to send the chat message\n\n// Then record 1 message used\nawait autumn.track({\n customerId: "user_or_org_id_from_auth",\n featureId: "messages",\n value: 1,\n});\n```\n\n```python Python\nimport asyncio\nfrom autumn_sdk import Autumn\n\nautumn = Autumn(\'am_sk_42424242\')\n\n# Your own function to send the chat message\n\n# Then record 1 message used\nasync def main():\n await autumn.track(\n customer_id=\'user_or_org_id_from_auth\',\n feature_id=\'messages\',\n value=1,\n )\n\nasyncio.run(main())\n```\n\n```bash cURL\n# Your own function to send the chat message\n\n# Then record 1 message used\ncurl -X POST \'https://api.useautumn.com/v1/track\' \\\n -H \'Authorization: Bearer am_sk_42424242\' \\\n -H \'Content-Type: application/json\' \\\n -d \'{\n "customer_id": "user_or_org_id_from_auth",\n "feature_id": "messages",\n "value": 1\n }\'\n```\n\n</CodeGroup>\n\nOnce you send usage events, you can verify their receipt in the Autumn dashboard, on the [customer](https://app.useautumn.com/customers) detail page.\n\nYou should always handle access checks and usage tracking server-side for security. Users can manipulate client-side code using devtools.\n\n--- \n\n**Next: Build your billing page**\n\nNow, whenever your customers change their plan, they will automatically have the correct access and limits. Next, build a billing page for your customers.\n\nDisplay plan, balance and usage information to your customers using Autumn\'s `customer` state',
217
217
  },
218
218
  {
219
219
  path: "references/check.md",
220
220
  contents:
221
- '## Checking access\n\nWhen you create a plan in Autumn, you define what features your customers on that plan get access to.\n\nThe `check` method returns the `allowed` field in real-time to check if a customer should have access to a feature. You can use this to block access and prompt an upsell.\n\n## The `allowed` field\n\nThe `allowed` field will return `true` for a given feature if:\n- The customer has an active plan with this feature\n- The customer has an active plan with a `credit_system` that grants this feature\n- The plan feature is <Badge>included</Badge> or <Badge>prepaid</Badge>, and the current balance is greater than the `required_balance` parameter\n- The plan feature is <Badge>usage-based</Badge>, and the user has not exceeded their max spend limit\n- The plan feature is <Badge>unlimited</Badge> or a <Badge>boolean</Badge> feature\n\nUnder these conditions, you should allow your customer to use the feature. You can then [record the usage event](/documentation/customers/tracking-usage/) so Autumn can update the `allowed` field as necessary.\n\nThe customer must already exist before calling `check`. If the `customer_id` doesn\'t match an existing customer, the API returns a `customer_not_found` error. Create customers using [`customers.getOrCreate`](/documentation/customers/creating-customers) during signup or login.\n\n## Checking metered features\n\nBefore your customer uses a feature, you can check if the customer is allowed to use it and their current usage.\n\n**Example**\n\nLet\'s imagine you have a free plan for a chatbot that allows 5 messages per month. Before your customer sends an AI message, you can check if they have any left.\n\nIf they still have messages remaining, they\'ll be allowed to send an AI message.\n\n<CodeGroup>\n\n```typescript TypeScript\nimport { Autumn } from "autumn-js";\n\nconst autumn = new Autumn({ secretKey: "am_sk_test_1234" });\n\nconst response = await autumn.check({\n customerId: "user_123",\n featureId: "messages",\n});\n\nconsole.log(response.allowed);\n```\n\n```python Python\nfrom autumn_sdk import Autumn\n\nautumn = Autumn("am_sk_test_1234")\n\nresponse = await autumn.check(\n customer_id="user_123",\n feature_id="messages",\n)\nprint(response.allowed)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/check" \\\n -H "Authorization: Bearer am_sk_test_1234" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "customer_id": "user_123",\n "feature_id": "messages"\n }\'\n```\n\n</CodeGroup>\n\n```json\n{\n "allowed": true,\n "customerId": "user_123",\n "requiredBalance": 1,\n "balance": {\n "featureId": "messages",\n "granted": 5,\n "remaining": 5,\n "usage": 0,\n "unlimited": false,\n "overageAllowed": false,\n "nextResetAt": 1767610960519\n }\n}\n```\n\nEven if your product doesn\'t have usage limits (ie your feature is purely usage-based), you can still use the above method to prevent usage if a customer\'s payment fails.\n\n## Checking for a required balance\n\nIf you know the balance a user will consume in advance, you can specify it with the `requiredBalance` parameter. This means you can prevent a user from starting a process that would consume more than their current balance.\n\nBy default, `requiredBalance` is 1, so not passing this parameter will return `allowed: true` as long as the customer has a feature balance of 1 or more.\n\n<CodeGroup>\n\n```typescript TypeScript\nimport { Autumn } from "autumn-js";\n\nconst autumn = new Autumn({ secretKey: "am_sk_test_1234" });\n\nconst response = await autumn.check({\n customerId: "user_123",\n featureId: "messages",\n requiredBalance: 3,\n});\n\nconsole.log(response.allowed);\n```\n\n```python Python\nfrom autumn_sdk import Autumn\n\nautumn = Autumn("am_sk_test_1234")\n\nresponse = await autumn.check(\n customer_id="user_123",\n feature_id="messages",\n required_balance=3,\n)\nprint(response.allowed)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/check" \\\n -H "Authorization: Bearer am_sk_test_1234" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "customer_id": "user_123",\n "feature_id": "messages",\n "required_balance": 3\n }\'\n```\n\n</CodeGroup>\n\n## Checking and reserving usage\n\nWhen you don\'t know the final cost upfront — like AI completions or long-running jobs — reserve balance with a `lock` on the check call, then finalize it once the operation completes.\n\n<CodeGroup>\n\n```typescript TypeScript\nimport { Autumn } from "autumn-js";\n\nconst autumn = new Autumn({ secretKey: "am_sk_test_1234" });\n\nconst response = await autumn.check({\n customerId: "user_123",\n featureId: "api_calls",\n requiredBalance: 3,\n sendEvent: true,\n lock: {\n enabled: true,\n lockId: "request_abc123",\n expiresAt: Date.now() + 5 * 60 * 1000, // 5 minutes\n },\n});\n\nconsole.log(response.allowed);\n```\n\n```python Python\nfrom autumn_sdk import Autumn\n\nautumn = Autumn("am_sk_test_1234")\n\nresponse = await autumn.check(\n customer_id="user_123",\n feature_id="api_calls",\n required_balance=3,\n send_event=True,\n lock={\n "enabled": True,\n "lock_id": "request_abc123",\n "expires_at": int(time.time() * 1000) + 5 * 60 * 1000,\n },\n)\nprint(response.allowed)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/check" \\\n -H "Authorization: Bearer am_sk_test_1234" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "customer_id": "user_123",\n "feature_id": "api_calls",\n "required_balance": 3,\n "send_event": true,\n "lock": {\n "enabled": true,\n "lock_id": "request_abc123",\n "expires_at": 1735689600000\n }\n }\'\n```\n\n</CodeGroup>\n\nOnce the operation completes, finalize the lock — `confirm` it to keep the deduction, or `release` it if the operation failed.\n\n<CodeGroup>\n\n```typescript TypeScript\nawait autumn.balances.finalize({\n lockId: "request_abc123",\n action: "confirm",\n});\n```\n\n```python Python\nawait autumn.balances.finalize(\n lock_id="request_abc123",\n action="confirm",\n)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/balances.finalize" \\\n -H "Authorization: Bearer am_sk_test_1234" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "lock_id": "request_abc123",\n "action": "confirm"\n }\'\n```\n\n</CodeGroup>\n\nSee [Balance Locking](/documentation/customers/balance-locking) for the full guide, including releasing a lock and adjusting the final amount when actual usage differs from what you reserved.\n\n## Checking boolean features\n\nFor simple on/off features, use the check method to determine if a customer has access:\n\n```typescript TypeScript\nimport { Autumn } from "autumn-js";\n\nconst autumn = new Autumn({ secretKey: "am_sk_test_1234" });\n\nconst response = await autumn.check({\n customerId: "user_123",\n featureId: "premium-dashboard",\n});\n\nif (response.allowed) {\n // Show premium dashboard\n}\n```\n\n### Feature flags in customer responses\n\nBoolean features are also returned as a `flags` object on customer and entity API responses. This lets you check on/off feature access directly from the customer object without calling the `check` endpoint separately.\n\n```json\n{\n "balances": {\n "credits": { "featureId": "credits", "granted": 1000, "remaining": 800, "usage": 200 }\n },\n "flags": {\n "premiumDashboard": {\n "id": "cus_ent_abc123",\n "planId": "pro_plan",\n "expiresAt": null,\n "featureId": "premiumDashboard"\n }\n }\n}\n```\n\n- **Flags** are separate from **balances** — boolean features appear under `flags`, while metered and credit system features remain under `balances`\n- Each flag shows which `planId` it originates from and when it expires\n- Use `expand: ["flags.feature"]` to include the full feature object on each flag\n\n<CodeGroup>\n\n```typescript TypeScript\nconst customer = await autumn.customers.getOrCreate({\n customerId: "user_123",\n expand: ["flags.feature"],\n});\n\nif (customer.flags?.["premiumDashboard"]) {\n // Customer has the premium dashboard feature\n}\n```\n\n```python Python\ncustomer = await autumn.customers.get_or_create(\n customer_id="user_123",\n expand=["flags.feature"],\n)\n\nif customer.flags and "premium_dashboard" in customer.flags:\n # Customer has the premium dashboard feature\n pass\n```\n\n</CodeGroup>',
221
+ '## Checking access\n\nWhen you create a plan in Autumn, you define what features your customers on that plan get access to.\n\nThe `check` method returns the `allowed` field in real-time to check if a customer should have access to a feature. You can use this to block access and prompt an upsell.\n\nOn a latency-sensitive path? The [edge cache](/documentation/edge-cache) answers `check` from inside your own cloud.\n\n## The `allowed` field\n\nThe `allowed` field will return `true` for a given feature if:\n- The customer has an active plan with this feature\n- The customer has an active plan with a `credit_system` that grants this feature\n- The plan feature is <Badge>included</Badge> or <Badge>prepaid</Badge>, and the current balance is greater than the `required_balance` parameter\n- The plan feature is <Badge>usage-based</Badge>, and the user has not exceeded their max spend limit\n- The plan feature is <Badge>unlimited</Badge> or a <Badge>boolean</Badge> feature\n\nUnder these conditions, you should allow your customer to use the feature. You can then [record the usage event](/documentation/customers/tracking-usage/) so Autumn can update the `allowed` field as necessary.\n\nThe customer must already exist before calling `check`. If the `customer_id` doesn\'t match an existing customer, the API returns a `customer_not_found` error. Create customers using [`customers.getOrCreate`](/documentation/customers/creating-customers) during signup or login.\n\n## Checking metered features\n\nBefore your customer uses a feature, you can check if the customer is allowed to use it and their current usage.\n\n**Example**\n\nLet\'s imagine you have a free plan for a chatbot that allows 5 messages per month. Before your customer sends an AI message, you can check if they have any left.\n\nIf they still have messages remaining, they\'ll be allowed to send an AI message.\n\n<CodeGroup>\n\n```typescript TypeScript\nimport { Autumn } from "autumn-js";\n\nconst autumn = new Autumn({ secretKey: "am_sk_test_1234" });\n\nconst response = await autumn.check({\n customerId: "user_123",\n featureId: "messages",\n});\n\nconsole.log(response.allowed);\n```\n\n```python Python\nfrom autumn_sdk import Autumn\n\nautumn = Autumn("am_sk_test_1234")\n\nresponse = await autumn.check(\n customer_id="user_123",\n feature_id="messages",\n)\nprint(response.allowed)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/check" \\\n -H "Authorization: Bearer am_sk_test_1234" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "customer_id": "user_123",\n "feature_id": "messages"\n }\'\n```\n\n</CodeGroup>\n\n```json\n{\n "allowed": true,\n "customerId": "user_123",\n "requiredBalance": 1,\n "balance": {\n "featureId": "messages",\n "granted": 5,\n "remaining": 5,\n "usage": 0,\n "unlimited": false,\n "overageAllowed": false,\n "nextResetAt": 1767610960519\n }\n}\n```\n\nEven if your product doesn\'t have usage limits (ie your feature is purely usage-based), you can still use the above method to prevent usage if a customer\'s payment fails.\n\n## Checking for a required balance\n\nIf you know the balance a user will consume in advance, you can specify it with the `requiredBalance` parameter. This means you can prevent a user from starting a process that would consume more than their current balance.\n\nBy default, `requiredBalance` is 1, so not passing this parameter will return `allowed: true` as long as the customer has a feature balance of 1 or more.\n\n<CodeGroup>\n\n```typescript TypeScript\nimport { Autumn } from "autumn-js";\n\nconst autumn = new Autumn({ secretKey: "am_sk_test_1234" });\n\nconst response = await autumn.check({\n customerId: "user_123",\n featureId: "messages",\n requiredBalance: 3,\n});\n\nconsole.log(response.allowed);\n```\n\n```python Python\nfrom autumn_sdk import Autumn\n\nautumn = Autumn("am_sk_test_1234")\n\nresponse = await autumn.check(\n customer_id="user_123",\n feature_id="messages",\n required_balance=3,\n)\nprint(response.allowed)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/check" \\\n -H "Authorization: Bearer am_sk_test_1234" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "customer_id": "user_123",\n "feature_id": "messages",\n "required_balance": 3\n }\'\n```\n\n</CodeGroup>\n\n## Checking and reserving usage\n\nWhen you don\'t know the final cost upfront — like AI completions or long-running jobs — reserve balance with a `lock` on the check call, then finalize it once the operation completes.\n\n<CodeGroup>\n\n```typescript TypeScript\nimport { Autumn } from "autumn-js";\n\nconst autumn = new Autumn({ secretKey: "am_sk_test_1234" });\n\nconst response = await autumn.check({\n customerId: "user_123",\n featureId: "api_calls",\n requiredBalance: 3,\n sendEvent: true,\n lock: {\n enabled: true,\n lockId: "request_abc123",\n expiresAt: Date.now() + 5 * 60 * 1000, // 5 minutes\n },\n});\n\nconsole.log(response.allowed);\n```\n\n```python Python\nfrom autumn_sdk import Autumn\n\nautumn = Autumn("am_sk_test_1234")\n\nresponse = await autumn.check(\n customer_id="user_123",\n feature_id="api_calls",\n required_balance=3,\n send_event=True,\n lock={\n "enabled": True,\n "lock_id": "request_abc123",\n "expires_at": int(time.time() * 1000) + 5 * 60 * 1000,\n },\n)\nprint(response.allowed)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/check" \\\n -H "Authorization: Bearer am_sk_test_1234" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "customer_id": "user_123",\n "feature_id": "api_calls",\n "required_balance": 3,\n "send_event": true,\n "lock": {\n "enabled": true,\n "lock_id": "request_abc123",\n "expires_at": 1735689600000\n }\n }\'\n```\n\n</CodeGroup>\n\nOnce the operation completes, finalize the lock — `confirm` it to keep the deduction, or `release` it if the operation failed.\n\n<CodeGroup>\n\n```typescript TypeScript\nawait autumn.balances.finalize({\n lockId: "request_abc123",\n action: "confirm",\n});\n```\n\n```python Python\nawait autumn.balances.finalize(\n lock_id="request_abc123",\n action="confirm",\n)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/balances.finalize" \\\n -H "Authorization: Bearer am_sk_test_1234" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "lock_id": "request_abc123",\n "action": "confirm"\n }\'\n```\n\n</CodeGroup>\n\nSee [Balance Locking](/documentation/customers/balance-locking) for the full guide, including releasing a lock and adjusting the final amount when actual usage differs from what you reserved.\n\n## Checking boolean features\n\nFor simple on/off features, use the check method to determine if a customer has access:\n\n```typescript TypeScript\nimport { Autumn } from "autumn-js";\n\nconst autumn = new Autumn({ secretKey: "am_sk_test_1234" });\n\nconst response = await autumn.check({\n customerId: "user_123",\n featureId: "premium-dashboard",\n});\n\nif (response.allowed) {\n // Show premium dashboard\n}\n```\n\n### Feature flags in customer responses\n\nBoolean features are also returned as a `flags` object on customer and entity API responses. This lets you check on/off feature access directly from the customer object without calling the `check` endpoint separately.\n\n```json\n{\n "balances": {\n "credits": { "featureId": "credits", "granted": 1000, "remaining": 800, "usage": 200 }\n },\n "flags": {\n "premiumDashboard": {\n "id": "cus_ent_abc123",\n "planId": "pro_plan",\n "expiresAt": null,\n "featureId": "premiumDashboard"\n }\n }\n}\n```\n\n- **Flags** are separate from **balances** — boolean features appear under `flags`, while metered and credit system features remain under `balances`\n- Each flag shows which `planId` it originates from and when it expires\n- Use `expand: ["flags.feature"]` to include the full feature object on each flag\n\n<CodeGroup>\n\n```typescript TypeScript\nconst customer = await autumn.customers.getOrCreate({\n customerId: "user_123",\n expand: ["flags.feature"],\n});\n\nif (customer.flags?.["premiumDashboard"]) {\n // Customer has the premium dashboard feature\n}\n```\n\n```python Python\ncustomer = await autumn.customers.get_or_create(\n customer_id="user_123",\n expand=["flags.feature"],\n)\n\nif customer.flags and "premium_dashboard" in customer.flags:\n # Customer has the premium dashboard feature\n pass\n```\n\n</CodeGroup>',
222
222
  },
223
223
  {
224
224
  path: "references/tracking-usage.md",
@@ -228,7 +228,7 @@ export const SKILLS: readonly BundledSkill[] = [
228
228
  {
229
229
  path: "references/billing-controls.md",
230
230
  contents:
231
- '## Billing Controls\n\nBilling controls let you manage how individual customers (or [entities](/documentation/customers/feature-entities)) consume usage-based features. You can toggle whether overage is allowed, cap how much overage accumulates, hard-cap how much a feature can be used per time window, get notified when usage crosses a threshold, and automatically replenish prepaid balances — all configured per-customer via the API or viewed in the dashboard.\n\nAll billing controls are set through the `billingControls` field when [updating a customer](/api-reference/customers/updateCustomer) or [updating an entity](/api-reference/entities/updateEntity).\n\n<CodeGroup>\n\n```typescript TypeScript\nawait autumn.customers.update({\n customerId: "user_123",\n billingControls: {\n overageAllowed: [{ featureId: "api_calls", enabled: true }],\n spendLimits: [{ featureId: "api_calls", enabled: true, overageLimit: 5000 }],\n usageLimits: [{ featureId: "api_calls", limit: 50, interval: "day" }],\n usageAlerts: [{ featureId: "api_calls", threshold: 80, thresholdType: "usage_percentage", enabled: true }],\n autoTopups: [{ featureId: "credits", enabled: true, threshold: 500, quantity: 1000 }],\n },\n});\n```\n\n```python Python\nawait autumn.customers.update(\n customer_id="user_123",\n billing_controls={\n "overage_allowed": [{"feature_id": "api_calls", "enabled": True}],\n "spend_limits": [{"feature_id": "api_calls", "enabled": True, "overage_limit": 5000}],\n "usage_limits": [{"feature_id": "api_calls", "limit": 50, "interval": "day"}],\n "usage_alerts": [{"feature_id": "api_calls", "threshold": 80, "threshold_type": "usage_percentage", "enabled": True}],\n "auto_topups": [{"feature_id": "credits", "enabled": True, "threshold": 500, "quantity": 1000}],\n },\n)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/customers/update" \\\n -H "Authorization: Bearer am_sk_..." \\\n -H "Content-Type: application/json" \\\n -d \'{\n "customer_id": "user_123",\n "billing_controls": {\n "overage_allowed": [{"feature_id": "api_calls", "enabled": true}],\n "spend_limits": [{"feature_id": "api_calls", "enabled": true, "overage_limit": 5000}],\n "usage_limits": [{"feature_id": "api_calls", "limit": 50, "interval": "day"}],\n "usage_alerts": [{"feature_id": "api_calls", "threshold": 80, "threshold_type": "usage_percentage", "enabled": true}],\n "auto_topups": [{"feature_id": "credits", "enabled": true, "threshold": 500, "quantity": 1000}]\n }\n }\'\n```\n\n</CodeGroup>\n\n## Overage Allowed\n\nBy default, whether a customer can use a feature beyond their included balance depends on the plan\'s pricing model. Features with [usage-based pricing](/documentation/modelling-pricing/usage-based-pricing) (pay-per-use) automatically allow overage — the customer keeps using and gets billed for the extra. Features without usage-based pricing (like a flat included allowance) block usage once the balance hits zero.\n\nThe `overageAllowed` control lets you override this default per customer or entity.\n\n#### Default behavior (no override)\n\n| Plan item pricing | Overage? |\n|-------------------|----------|\n| Usage-based (pay-per-use) | Allowed — customer is billed for overage |\n| Included / prepaid (no overage price) | Blocked — `check` returns `allowed: false` at zero balance |\n\n#### With `overageAllowed` enabled\n\nSetting `overageAllowed` to `true` on a feature lets a customer consume beyond their included balance **even when the plan doesn\'t have usage-based pricing** for that feature. The balance goes negative, meaning you can track how much overage occurred, though no automatic overage charge is created.\n\n> **Example** <br />\n> A customer is on a free plan with 100 API calls included (no overage pricing). Normally they\'d be blocked at 0 remaining. You set `overageAllowed: true` for `api_calls`. Now they can keep using beyond 100, and you can decide how to handle the overage in your application (prompt an upgrade, bill manually, etc.).\n\n<CodeGroup>\n\n```typescript TypeScript\nawait autumn.customers.update({\n customerId: "user_123",\n billingControls: {\n overageAllowed: [{\n featureId: "api_calls",\n enabled: true,\n }],\n },\n});\n```\n\n```python Python\nawait autumn.customers.update(\n customer_id="user_123",\n billing_controls={\n "overage_allowed": [{\n "feature_id": "api_calls",\n "enabled": True,\n }],\n },\n)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/customers/update" \\\n -H "Authorization: Bearer am_sk_..." \\\n -H "Content-Type: application/json" \\\n -d \'{\n "customer_id": "user_123",\n "billing_controls": {\n "overage_allowed": [{\n "feature_id": "api_calls",\n "enabled": true\n }]\n }\n }\'\n```\n\n</CodeGroup>\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `feature_id` | string | The feature to override overage behavior for |\n| `enabled` | boolean | `true` to allow overage, `false` to block it |\n\n#### Disabling overage on a pay-per-use feature\n\nYou can also use `overageAllowed` to **block** overage on a feature that would normally allow it. Setting `enabled: false` forces a hard cap at the included balance — even if the plan has usage-based pricing for that feature.\n\n> **Example** <br />\n> A customer\'s plan includes 1,000 API calls with pay-per-use overage at $1/1,000 calls. You set `overageAllowed: false` for `api_calls`. The customer is now blocked at 1,000 total calls — no overage charges will occur.\n\nSetting `overageAllowed: false` is a hard override. It takes precedence over usage-based pricing on the plan. The customer will be blocked at zero remaining balance regardless of whether overage pricing exists.\n\n#### How it interacts with spend limits\n\n`overageAllowed` and [spend limits](#spend-limits) are complementary:\n\n- **`overageAllowed`** answers: *can* usage go beyond the included balance?\n- **Spend limits** answer: *how far* can overage go?\n\nIf both are set, `overageAllowed` is checked first. If overage is blocked (`enabled: false`), the spend limit is irrelevant. If overage is allowed, the spend limit caps how much overage can accumulate.\n\n## Spend Limits\n\nSpend limits cap how much overage a customer can accumulate on a usage-based feature. Once the cap is reached, `check` returns `allowed: false` and `track` stops deducting.\n\n> **Example** <br />\n> A customer\'s plan includes 1,000 API calls with $1 per 1,000 overage calls. You set a spend limit of 5,000 on `api_calls`. The customer can use up to 6,000 total calls (1,000 included + 5,000 overage), then they\'re blocked.\n\n<CodeGroup>\n\n```typescript TypeScript\nawait autumn.customers.update({\n customerId: "user_123",\n billingControls: {\n spendLimits: [{\n featureId: "api_calls",\n enabled: true,\n overageLimit: 5000,\n }],\n },\n});\n```\n\n```python Python\nawait autumn.customers.update(\n customer_id="user_123",\n billing_controls={\n "spend_limits": [{\n "feature_id": "api_calls",\n "enabled": True,\n "overage_limit": 5000,\n }],\n },\n)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/customers/update" \\\n -H "Authorization: Bearer am_sk_..." \\\n -H "Content-Type: application/json" \\\n -d \'{\n "customer_id": "user_123",\n "billing_controls": {\n "spend_limits": [{\n "feature_id": "api_calls",\n "enabled": true,\n "overage_limit": 5000\n }]\n }\n }\'\n```\n\n</CodeGroup>\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `feature_id` | string | The feature to cap |\n| `enabled` | boolean | Whether the spend limit is active |\n| `overage_limit` | number | Maximum overage units beyond the included amount |\n\nThe `overage_limit` is measured in feature units, not dollars. An `overage_limit` of 5,000 on "API calls" means 5,000 additional API calls beyond the included allowance.\n\nWhen both a spend limit and a plan-level max purchase exist for the same feature, the **spend limit takes precedence**. This lets you use max purchase as a default for all customers, then selectively raise or lower the cap per-customer.\n\nFor a deeper dive, see [Spend Limits & Usage Alerts](/documentation/modelling-pricing/spend-limits).\n\n## Usage Limits\n\nA usage limit is a **windowed hard cap**: at most `limit` units of a feature per `interval` window (day, week, month, or year), regardless of how much balance the customer has left or how the plan is priced. It\'s a throttle, that you or your customer may set.\n\nOnce usage reaches the cap inside the active window, `check` returns `allowed: false` and `track` stops deducting.\n\nUsage limits cap *total usage* of a feature within a period: a sub-limit within an existing balance. They sit on top of the plan\'s allowance and apply even when there\'s balance remaining and even when the feature has no overage pricing.\n\n`set_usage` sets a feature\'s balance but never touches usage-window counters. A capped feature stays capped: after `set_usage`, a customer whose window is exhausted still gets `allowed: false` until the window resets. To count usage against a window (including limits with conditions, which match on event properties), record usage normally with `track`.\n\nThe consumed counter belongs to the window, not to the limit\'s configuration — editing the limit never resets what\'s already been counted. To unblock a capped customer mid-window: **disable** the limit (`enabled: false`) to stop it gating while keeping its configuration, **raise** the limit (headroom becomes the new limit minus usage already counted), or **delete** it.\n\n> **Example** <br />\n> A customer\'s plan includes **300 credits per month**, but you want to stop any single day from burning through them. Set a usage limit of **50 on `credits` with a `day` interval**. The customer still gets their 300 monthly credits, but can never spend more than 50 in a day.\n\n<CodeGroup>\n\n```typescript TypeScript\nawait autumn.customers.update({\n customerId: "user_123",\n billingControls: {\n usageLimits: [{\n featureId: "credits",\n limit: 50,\n interval: "day",\n }],\n },\n});\n```\n\n```python Python\nawait autumn.customers.update(\n customer_id="user_123",\n billing_controls={\n "usage_limits": [{\n "feature_id": "credits",\n "limit": 50,\n "interval": "day",\n }],\n },\n)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/customers/update" \\\n -H "Authorization: Bearer am_sk_..." \\\n -H "Content-Type: application/json" \\\n -d \'{\n "customer_id": "user_123",\n "billing_controls": {\n "usage_limits": [{\n "feature_id": "credits",\n "limit": 50,\n "interval": "day"\n }]\n }\n }\'\n```\n\n</CodeGroup>\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `feature_id` | string | The feature to cap |\n| `limit` | number | Maximum units allowed per window |\n| `interval` | string | Window length: `"day"`, `"week"`, `"month"`, or `"year"`. Cannot be `one_off`. |\n\nEach customer/entity feature in a `get` response carries a `usage_limits` array where every entry also reports the `usage` consumed in the current window, so you can show "12 / 50 used today" without a separate call.\n\n### Window reset and plan changes\n\nEach window aligns to the **customer\'s billing cycle**, not the UTC calendar. A `day` cap rolls at the customer\'s billing time-of-day; a `month` cap rolls on their billing-cycle anchor. When there\'s no billing cycle to anchor to (e.g. a feature with no backing plan), the window falls back to UTC calendar alignment — daily windows roll at UTC midnight, monthly on the 1st.\n\nBecause the window is tied to the feature\'s reset cycle, **a plan change that restarts that cycle also restarts the window.** If a customer upgrades mid-month and their billing anchor moves, the usage limit\'s window re-anchors to the new cycle and the consumed counter starts fresh.\n\n### Caps on credit systems\n\nWhen a feature is part of a [credit system](/documentation/modelling-pricing/credit-systems), you can cap usage at either level:\n\n- **Cap the credit balance** — e.g. limit total `credits` spend per window across every feature that draws from it.\n- **Cap an individual feature** — e.g. give a credit system shared by features A, B, and C, but limit how many units of B specifically can be used per window. The per-feature cap is converted into credits using B\'s credit cost, so both caps are enforced together.\n\n> **Example — per-feature cap inside a credit system** <br />\n> Your `credits` system is spent by `images`, `transcriptions`, and `exports`. Customers can spend credits freely across all three, but you cap `exports` at **10 per day** so one feature can\'t drain the whole balance. A check or track on `exports` is blocked at 10/day even if plenty of credits remain.\n\nWhen more than one cap applies to a check (the cap on the evaluated feature and a cap on its parent credit system), Autumn enforces the **tightest** one — the remaining headroom is the minimum across all armed caps.\n\n## Usage Alerts\n\nUsage alerts fire a webhook when a customer\'s usage crosses a threshold. They don\'t block usage — they notify, so you can take action like sending a warning email or prompting an upgrade.\n\nThere are two threshold types:\n\n- **`usage`** — fires when absolute usage reaches a specific count\n- **`usage_percentage`** — fires when usage reaches a percentage of the included allowance\n\n<CodeGroup>\n\n```typescript TypeScript\nawait autumn.customers.update({\n customerId: "user_123",\n billingControls: {\n usageAlerts: [\n {\n featureId: "api_calls",\n threshold: 80,\n thresholdType: "usage_percentage",\n enabled: true,\n name: "80% usage warning",\n },\n {\n featureId: "api_calls",\n threshold: 900,\n thresholdType: "usage",\n enabled: true,\n name: "Approaching limit",\n },\n ],\n },\n});\n```\n\n```python Python\nawait autumn.customers.update(\n customer_id="user_123",\n billing_controls={\n "usage_alerts": [\n {\n "feature_id": "api_calls",\n "threshold": 80,\n "threshold_type": "usage_percentage",\n "enabled": True,\n "name": "80% usage warning",\n },\n {\n "feature_id": "api_calls",\n "threshold": 900,\n "threshold_type": "usage",\n "enabled": True,\n "name": "Approaching limit",\n },\n ],\n },\n)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/customers/update" \\\n -H "Authorization: Bearer am_sk_..." \\\n -H "Content-Type: application/json" \\\n -d \'{\n "customer_id": "user_123",\n "billing_controls": {\n "usage_alerts": [\n {\n "feature_id": "api_calls",\n "threshold": 80,\n "threshold_type": "usage_percentage",\n "enabled": true,\n "name": "80% usage warning"\n },\n {\n "feature_id": "api_calls",\n "threshold": 900,\n "threshold_type": "usage",\n "enabled": true,\n "name": "Approaching limit"\n }\n ]\n }\n }\'\n```\n\n</CodeGroup>\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `feature_id` | string | The feature to monitor |\n| `threshold` | number | Trigger value — absolute count or percentage (0–100) |\n| `threshold_type` | string | `"usage"` for absolute count, `"usage_percentage"` for percentage of included allowance |\n| `enabled` | boolean | Whether the alert is active (defaults to `true`) |\n| `name` | string (optional) | A label to distinguish multiple alerts |\n\nEach alert fires **once** per threshold crossing. It won\'t re-fire unless usage drops below the threshold and crosses it again.\n\nWhen triggered, Autumn sends a `balances.usage_alert_triggered` [webhook](/documentation/webhooks). See the [webhook schema](/api-reference/webhooks/balancesUsageAlertTriggered) for the full payload.\n\nFor more details and examples, see [Spend Limits & Usage Alerts](/documentation/modelling-pricing/spend-limits#usage-alerts).\n\n## Auto Top-Ups\n\nAuto top-ups automatically replenish a customer\'s prepaid balance when it drops below a configured threshold. This prevents service interruptions for customers who don\'t want to manually manage their credits.\n\n> **Example** <br />\n> A customer gets 5,000 credits per month. When their balance drops below 500, Autumn automatically purchases 1,000 more credits using the plan\'s one-off prepaid price.\n\nAuto top-ups require a plan with a [one-off prepaid](/documentation/modelling-pricing/one-off-purchases) item for the feature, and the customer must have a payment method on file.\n\n<CodeGroup>\n\n```typescript TypeScript\nawait autumn.customers.update({\n customerId: "user_123",\n billingControls: {\n autoTopups: [{\n featureId: "credits",\n enabled: true,\n threshold: 500,\n quantity: 1000,\n }],\n },\n});\n```\n\n```python Python\nawait autumn.customers.update(\n customer_id="user_123",\n billing_controls={\n "auto_topups": [{\n "feature_id": "credits",\n "enabled": True,\n "threshold": 500,\n "quantity": 1000,\n }],\n },\n)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/customers/update" \\\n -H "Authorization: Bearer am_sk_..." \\\n -H "Content-Type: application/json" \\\n -d \'{\n "customer_id": "user_123",\n "billing_controls": {\n "auto_topups": [{\n "feature_id": "credits",\n "enabled": true,\n "threshold": 500,\n "quantity": 1000\n }]\n }\n }\'\n```\n\n</CodeGroup>\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `feature_id` | string | The feature (credit balance) to monitor |\n| `enabled` | boolean | Whether auto top-up is active |\n| `threshold` | number | Balance level that triggers a top-up |\n| `quantity` | number | Units to purchase each time |\n| `purchase_limit` | object (optional) | Rate limit on how often top-ups can occur |\n\nTo prevent runaway spending, you can set a purchase limit:\n\n```json\n{\n "purchase_limit": {\n "interval": "month",\n "interval_count": 1,\n "limit": 5\n }\n}\n```\n\nThis limits the customer to 5 auto top-ups per month. Supported intervals: `hour`, `day`, `week`, `month`.\n\nFor setup instructions and how it works end-to-end, see [Auto Top-Ups](/documentation/modelling-pricing/auto-top-ups).\n\n## Customer vs Entity Controls\n\nBilling controls can be set at two levels:\n\n| Control | Customer-level | Entity-level |\n|---------|---------------|--------------|\n| **Overage allowed** | Yes | Yes |\n| **Spend limits** | Yes | Yes |\n| **Usage limits** | Yes | Yes |\n| **Usage alerts** | Yes | Yes |\n| **Auto top-ups** | Yes | No |\n\n**Entity-level** controls are configured by updating the entity instead of the customer. Entity-level controls override customer-level controls for that entity — for example, an entity overage override takes precedence over the customer-level setting, and entity spend limits override the customer-level limit.\n\nOverrides are resolved **per feature**: an entity\'s own entry for a feature wins, and the customer\'s entries fill in any features the entity doesn\'t set. For usage limits specifically, an inherited (customer-level) cap counts usage against the **shared customer window** — it\'s the same aggregate cap, not a separate per-entity copy. An entity-level usage limit, by contrast, gets its own per-entity window and counter.\n\n> **Example — per-entity cap** <br />\n> An org (the customer) has 1,000 monthly API calls shared across its workspaces (entities). You set a customer-level usage limit of 1,000/month so the org can\'t exceed its plan, and an entity-level limit of 200/day on a noisy workspace so it can\'t starve the others. The workspace is blocked at 200/day; the org is blocked at 1,000/month.\n\n<CodeGroup>\n\n```typescript TypeScript\nawait autumn.entities.update({\n customerId: "org_123",\n entityId: "workspace_a",\n billingControls: {\n overageAllowed: [{\n featureId: "api_calls",\n enabled: true,\n }],\n spendLimits: [{\n featureId: "api_calls",\n enabled: true,\n overageLimit: 2000,\n }],\n usageLimits: [{\n featureId: "api_calls",\n limit: 200,\n interval: "day",\n }],\n usageAlerts: [{\n featureId: "api_calls",\n threshold: 90,\n thresholdType: "usage_percentage",\n enabled: true,\n }],\n },\n});\n```\n\n```python Python\nawait autumn.entities.update(\n customer_id="org_123",\n entity_id="workspace_a",\n billing_controls={\n "overage_allowed": [{\n "feature_id": "api_calls",\n "enabled": True,\n }],\n "spend_limits": [{\n "feature_id": "api_calls",\n "enabled": True,\n "overage_limit": 2000,\n }],\n "usage_limits": [{\n "feature_id": "api_calls",\n "limit": 200,\n "interval": "day",\n }],\n "usage_alerts": [{\n "feature_id": "api_calls",\n "threshold": 90,\n "threshold_type": "usage_percentage",\n "enabled": True,\n }],\n },\n)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/entities/update" \\\n -H "Authorization: Bearer am_sk_..." \\\n -H "Content-Type: application/json" \\\n -d \'{\n "customer_id": "org_123",\n "entity_id": "workspace_a",\n "billing_controls": {\n "overage_allowed": [{\n "feature_id": "api_calls",\n "enabled": true\n }],\n "spend_limits": [{\n "feature_id": "api_calls",\n "enabled": true,\n "overage_limit": 2000\n }],\n "usage_limits": [{\n "feature_id": "api_calls",\n "limit": 200,\n "interval": "day"\n }],\n "usage_alerts": [{\n "feature_id": "api_calls",\n "threshold": 90,\n "threshold_type": "usage_percentage",\n "enabled": true\n }]\n }\n }\'\n```\n\n</CodeGroup>\n\nAuto top-ups are customer-level only because they create invoices and charge a payment method, which is tied to the customer account — not individual entities.\n\n## Plan-Level Defaults\n\nEvery billing control can also be defined on a **plan** (in the dashboard under plan settings → billing controls, via the `billingControls` field when creating or updating a plan, or in the [CLI config](/cli/config#plans) with the same field). Plan-level controls act as defaults for every customer on that plan — set a daily usage limit once on your free tier instead of on each customer.\n\n```ts autumn.config.ts\nexport const free = plan({\n planId: "free",\n versionSlug: "v1",\n active: true,\n name: "Free",\n autoEnable: true,\n items: [{ featureId: "emails", included: 200, reset: { interval: "month" } }],\n billingControls: {\n usageLimits: [{ featureId: "emails", limit: 200, interval: "day" }],\n },\n});\n```\n\nResolution when a customer is on one or more plans:\n\n- A **customer-level entry shadows the plan\'s entry** for the same feature (for usage limits, the same feature *and* conditions). Setting a control on the customer is how you override the plan default — and a disabled customer entry still shadows the plan\'s, it doesn\'t resurface it.\n- With **multiple attached plans** defining the same control, the most restrictive wins (auto top-ups: the most recently attached plan\'s config).\n\n### Reading effective controls\n\nFrom API version `2.3.0`, [fetching a customer](/api-reference/customers/getCustomer) returns the **effective** controls: plan defaults are merged into `billing_controls`, and every entry carries a `source` field so you can tell overrides from inherited defaults. Inherited usage limits include the live window `usage`, so you can render "X of 200 used today" without extra calls.\n\n```json\n"billing_controls": {\n "usage_limits": [\n {\n "feature_id": "emails",\n "enabled": true,\n "limit": 200,\n "interval": "day",\n "usage": 37,\n "source": "plan"\n }\n ]\n}\n```\n\nOn earlier API versions, `billing_controls` contains only customer-level entries. Entity responses always show the entity\'s own controls.\n\n## Related Webhooks\n\nBilling controls tie into three webhook events that fire automatically based on usage:\n\n| Event | When it fires |\n|-------|---------------|\n| [`balances.limit_reached`](/api-reference/webhooks/balancesLimitReached) | Customer transitions from allowed to not allowed on a feature (included allowance exhausted, max purchase hit, spend limit reached, or usage limit hit). The payload\'s `limit_type` distinguishes `included`, `max_purchase`, `spend_limit`, and `usage_limit`. |\n| [`balances.usage_alert_triggered`](/api-reference/webhooks/balancesUsageAlertTriggered) | Customer\'s usage crosses a configured alert threshold |\n| [`customer.products.updated`](/documentation/webhooks#customerproductsupdated) | Customer\'s subscription changes (new, upgrade, downgrade, cancel, etc.) |\n\nFor webhook setup and security details, see [Webhooks](/documentation/webhooks).',
231
+ '## Billing Controls\n\nBilling controls let you manage how individual customers (or [entities](/documentation/customers/feature-entities)) consume usage-based features. You can toggle whether overage is allowed, cap how much overage accumulates, hard-cap how much a feature can be used per time window, get notified when usage crosses a threshold, and automatically replenish prepaid balances — all configured per-customer via the API or viewed in the dashboard.\n\nAll billing controls are set through the `billingControls` field when [updating a customer](/api-reference/customers/updateCustomer) or [updating an entity](/api-reference/entities/updateEntity).\n\n<CodeGroup>\n\n```typescript TypeScript\nawait autumn.customers.update({\n customerId: "user_123",\n billingControls: {\n overageAllowed: [{ featureId: "api_calls", enabled: true }],\n spendLimits: [{ featureId: "api_calls", enabled: true, overageLimit: 5000 }],\n usageLimits: [{ featureId: "api_calls", limit: 50, interval: "day" }],\n usageAlerts: [{ featureId: "api_calls", threshold: 80, thresholdType: "usage_percentage", enabled: true }],\n autoTopups: [{ featureId: "credits", enabled: true, threshold: 500, quantity: 1000 }],\n },\n});\n```\n\n```python Python\nawait autumn.customers.update(\n customer_id="user_123",\n billing_controls={\n "overage_allowed": [{"feature_id": "api_calls", "enabled": True}],\n "spend_limits": [{"feature_id": "api_calls", "enabled": True, "overage_limit": 5000}],\n "usage_limits": [{"feature_id": "api_calls", "limit": 50, "interval": "day"}],\n "usage_alerts": [{"feature_id": "api_calls", "threshold": 80, "threshold_type": "usage_percentage", "enabled": True}],\n "auto_topups": [{"feature_id": "credits", "enabled": True, "threshold": 500, "quantity": 1000}],\n },\n)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/customers/update" \\\n -H "Authorization: Bearer am_sk_..." \\\n -H "Content-Type: application/json" \\\n -d \'{\n "customer_id": "user_123",\n "billing_controls": {\n "overage_allowed": [{"feature_id": "api_calls", "enabled": true}],\n "spend_limits": [{"feature_id": "api_calls", "enabled": true, "overage_limit": 5000}],\n "usage_limits": [{"feature_id": "api_calls", "limit": 50, "interval": "day"}],\n "usage_alerts": [{"feature_id": "api_calls", "threshold": 80, "threshold_type": "usage_percentage", "enabled": true}],\n "auto_topups": [{"feature_id": "credits", "enabled": true, "threshold": 500, "quantity": 1000}]\n }\n }\'\n```\n\n</CodeGroup>\n\n## Overage Allowed\n\nBy default, whether a customer can use a feature beyond their included balance depends on the plan\'s pricing model. Features with [usage-based pricing](/documentation/modelling-pricing/usage-based-pricing) (pay-per-use) automatically allow overage — the customer keeps using and gets billed for the extra. Features without usage-based pricing (like a flat included allowance) block usage once the balance hits zero.\n\nThe `overageAllowed` control lets you override this default per customer or entity.\n\n#### Default behavior (no override)\n\n| Plan item pricing | Overage? |\n|-------------------|----------|\n| Usage-based (pay-per-use) | Allowed — customer is billed for overage |\n| Included / prepaid (no overage price) | Blocked — `check` returns `allowed: false` at zero balance |\n\n#### With `overageAllowed` enabled\n\nSetting `overageAllowed` to `true` on a feature lets a customer consume beyond their included balance **even when the plan doesn\'t have usage-based pricing** for that feature. The balance goes negative, meaning you can track how much overage occurred, though no automatic overage charge is created.\n\n> **Example** <br />\n> A customer is on a free plan with 100 API calls included (no overage pricing). Normally they\'d be blocked at 0 remaining. You set `overageAllowed: true` for `api_calls`. Now they can keep using beyond 100, and you can decide how to handle the overage in your application (prompt an upgrade, bill manually, etc.).\n\n<CodeGroup>\n\n```typescript TypeScript\nawait autumn.customers.update({\n customerId: "user_123",\n billingControls: {\n overageAllowed: [{\n featureId: "api_calls",\n enabled: true,\n }],\n },\n});\n```\n\n```python Python\nawait autumn.customers.update(\n customer_id="user_123",\n billing_controls={\n "overage_allowed": [{\n "feature_id": "api_calls",\n "enabled": True,\n }],\n },\n)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/customers/update" \\\n -H "Authorization: Bearer am_sk_..." \\\n -H "Content-Type: application/json" \\\n -d \'{\n "customer_id": "user_123",\n "billing_controls": {\n "overage_allowed": [{\n "feature_id": "api_calls",\n "enabled": true\n }]\n }\n }\'\n```\n\n</CodeGroup>\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `feature_id` | string | The feature to override overage behavior for |\n| `enabled` | boolean | `true` to allow overage, `false` to block it |\n\n#### Disabling overage on a pay-per-use feature\n\nYou can also use `overageAllowed` to **block** overage on a feature that would normally allow it. Setting `enabled: false` forces a hard cap at the included balance — even if the plan has usage-based pricing for that feature.\n\n> **Example** <br />\n> A customer\'s plan includes 1,000 API calls with pay-per-use overage at $1/1,000 calls. You set `overageAllowed: false` for `api_calls`. The customer is now blocked at 1,000 total calls — no overage charges will occur.\n\nSetting `overageAllowed: false` is a hard override. It takes precedence over usage-based pricing on the plan. The customer will be blocked at zero remaining balance regardless of whether overage pricing exists.\n\n#### How it interacts with spend limits\n\n`overageAllowed` and [spend limits](#spend-limits) are complementary:\n\n- **`overageAllowed`** answers: *can* usage go beyond the included balance?\n- **Spend limits** answer: *how far* can overage go?\n\nIf both are set, `overageAllowed` is checked first. If overage is blocked (`enabled: false`), the spend limit is irrelevant. If overage is allowed, the spend limit caps how much overage can accumulate.\n\n## Spend Limits\n\nSpend limits cap how much overage a customer can accumulate on a usage-based feature. Once the cap is reached, `check` returns `allowed: false` and `track` stops deducting.\n\n> **Example** <br />\n> A customer\'s plan includes 1,000 API calls with $1 per 1,000 overage calls. You set a spend limit of 5,000 on `api_calls`. The customer can use up to 6,000 total calls (1,000 included + 5,000 overage), then they\'re blocked.\n\n<CodeGroup>\n\n```typescript TypeScript\nawait autumn.customers.update({\n customerId: "user_123",\n billingControls: {\n spendLimits: [{\n featureId: "api_calls",\n enabled: true,\n overageLimit: 5000,\n }],\n },\n});\n```\n\n```python Python\nawait autumn.customers.update(\n customer_id="user_123",\n billing_controls={\n "spend_limits": [{\n "feature_id": "api_calls",\n "enabled": True,\n "overage_limit": 5000,\n }],\n },\n)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/customers/update" \\\n -H "Authorization: Bearer am_sk_..." \\\n -H "Content-Type: application/json" \\\n -d \'{\n "customer_id": "user_123",\n "billing_controls": {\n "spend_limits": [{\n "feature_id": "api_calls",\n "enabled": true,\n "overage_limit": 5000\n }]\n }\n }\'\n```\n\n</CodeGroup>\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `feature_id` | string | The feature to cap |\n| `enabled` | boolean | Whether the spend limit is active |\n| `overage_limit` | number | Maximum overage units beyond the included amount |\n\nThe `overage_limit` is measured in feature units, not dollars. An `overage_limit` of 5,000 on "API calls" means 5,000 additional API calls beyond the included allowance.\n\nWhen both a spend limit and a plan-level max purchase exist for the same feature, the **spend limit takes precedence**. This lets you use max purchase as a default for all customers, then selectively raise or lower the cap per-customer.\n\nFor a deeper dive, see [Spend Limits & Usage Alerts](/documentation/modelling-pricing/spend-limits).\n\n## Usage Limits\n\nA usage limit is a **windowed hard cap**: at most `limit` units of a feature per `interval` window (day, week, month, or year), regardless of how much balance the customer has left or how the plan is priced. It\'s a throttle, that you or your customer may set.\n\nOnce usage reaches the cap inside the active window, `check` returns `allowed: false` and `track` stops deducting.\n\nUsage limits cap *total usage* of a feature within a period: a sub-limit within an existing balance. They sit on top of the plan\'s allowance and apply even when there\'s balance remaining and even when the feature has no overage pricing.\n\n`set_usage` sets a feature\'s balance but never touches usage-window counters. A capped feature stays capped: after `set_usage`, a customer whose window is exhausted still gets `allowed: false` until the window resets. To count usage against a window (including limits with conditions, which match on event properties), record usage normally with `track`.\n\nThe consumed counter belongs to the window, not to the limit\'s configuration — editing the limit never resets what\'s already been counted. To unblock a capped customer mid-window: **disable** the limit (`enabled: false`) to stop it gating while keeping its configuration, **raise** the limit (headroom becomes the new limit minus usage already counted), or **delete** it.\n\n> **Example** <br />\n> A customer\'s plan includes **300 credits per month**, but you want to stop any single day from burning through them. Set a usage limit of **50 on `credits` with a `day` interval**. The customer still gets their 300 monthly credits, but can never spend more than 50 in a day.\n\n<CodeGroup>\n\n```typescript TypeScript\nawait autumn.customers.update({\n customerId: "user_123",\n billingControls: {\n usageLimits: [{\n featureId: "credits",\n limit: 50,\n interval: "day",\n }],\n },\n});\n```\n\n```python Python\nawait autumn.customers.update(\n customer_id="user_123",\n billing_controls={\n "usage_limits": [{\n "feature_id": "credits",\n "limit": 50,\n "interval": "day",\n }],\n },\n)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/customers/update" \\\n -H "Authorization: Bearer am_sk_..." \\\n -H "Content-Type: application/json" \\\n -d \'{\n "customer_id": "user_123",\n "billing_controls": {\n "usage_limits": [{\n "feature_id": "credits",\n "limit": 50,\n "interval": "day"\n }]\n }\n }\'\n```\n\n</CodeGroup>\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `feature_id` | string | The feature to cap |\n| `limit` | number | Maximum units allowed per window |\n| `interval` | string | Window length: `"day"`, `"week"`, `"month"`, or `"year"`. Cannot be `one_off`. |\n\nEach customer/entity feature in a `get` response carries a `usage_limits` array where every entry also reports the `usage` consumed in the current window, so you can show "12 / 50 used today" without a separate call.\n\n### Window reset and plan changes\n\nEach window aligns to the **customer\'s billing cycle**, not the UTC calendar. A `day` cap rolls at the customer\'s billing time-of-day; a `month` cap rolls on their billing-cycle anchor. When there\'s no billing cycle to anchor to (e.g. a feature with no backing plan), the window falls back to UTC calendar alignment — daily windows roll at UTC midnight, monthly on the 1st.\n\nBecause the window is tied to the feature\'s reset cycle, **a plan change that restarts that cycle also restarts the window.** If a customer upgrades mid-month and their billing anchor moves, the usage limit\'s window re-anchors to the new cycle and the consumed counter starts fresh.\n\n### Caps on credit systems\n\nWhen a feature is part of a [credit system](/documentation/modelling-pricing/credit-systems), you can cap usage at either level:\n\n- **Cap the credit balance** — e.g. limit total `credits` spend per window across every feature that draws from it.\n- **Cap an individual feature** — e.g. give a credit system shared by features A, B, and C, but limit how many units of B specifically can be used per window. The per-feature cap is converted into credits using B\'s credit cost, so both caps are enforced together.\n\n> **Example — per-feature cap inside a credit system** <br />\n> Your `credits` system is spent by `images`, `transcriptions`, and `exports`. Customers can spend credits freely across all three, but you cap `exports` at **10 per day** so one feature can\'t drain the whole balance. A check or track on `exports` is blocked at 10/day even if plenty of credits remain.\n\nWhen more than one cap applies to a check (the cap on the evaluated feature and a cap on its parent credit system), Autumn enforces the **tightest** one — the remaining headroom is the minimum across all armed caps.\n\n## Usage Alerts\n\nUsage alerts fire a webhook when a customer\'s usage crosses a threshold. They don\'t block usage — they notify, so you can take action like sending a warning email or prompting an upgrade.\n\nThere are two threshold types:\n\n- **`usage`** — fires when absolute usage reaches a specific count\n- **`usage_percentage`** — fires when usage reaches a percentage of the included allowance\n\n<CodeGroup>\n\n```typescript TypeScript\nawait autumn.customers.update({\n customerId: "user_123",\n billingControls: {\n usageAlerts: [\n {\n featureId: "api_calls",\n threshold: 80,\n thresholdType: "usage_percentage",\n enabled: true,\n name: "80% usage warning",\n },\n {\n featureId: "api_calls",\n threshold: 900,\n thresholdType: "usage",\n enabled: true,\n name: "Approaching limit",\n },\n ],\n },\n});\n```\n\n```python Python\nawait autumn.customers.update(\n customer_id="user_123",\n billing_controls={\n "usage_alerts": [\n {\n "feature_id": "api_calls",\n "threshold": 80,\n "threshold_type": "usage_percentage",\n "enabled": True,\n "name": "80% usage warning",\n },\n {\n "feature_id": "api_calls",\n "threshold": 900,\n "threshold_type": "usage",\n "enabled": True,\n "name": "Approaching limit",\n },\n ],\n },\n)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/customers/update" \\\n -H "Authorization: Bearer am_sk_..." \\\n -H "Content-Type: application/json" \\\n -d \'{\n "customer_id": "user_123",\n "billing_controls": {\n "usage_alerts": [\n {\n "feature_id": "api_calls",\n "threshold": 80,\n "threshold_type": "usage_percentage",\n "enabled": true,\n "name": "80% usage warning"\n },\n {\n "feature_id": "api_calls",\n "threshold": 900,\n "threshold_type": "usage",\n "enabled": true,\n "name": "Approaching limit"\n }\n ]\n }\n }\'\n```\n\n</CodeGroup>\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `feature_id` | string | The feature to monitor |\n| `threshold` | number | Trigger value — absolute count or percentage (0–100) |\n| `threshold_type` | string | `"usage"` for absolute count, `"usage_percentage"` for percentage of included allowance |\n| `enabled` | boolean | Whether the alert is active (defaults to `true`) |\n| `name` | string (optional) | A label to distinguish multiple alerts |\n\nEach alert fires **once** per threshold crossing. It won\'t re-fire unless usage drops below the threshold and crosses it again.\n\nWhen triggered, Autumn sends a `balances.usage_alert_triggered` [webhook](/documentation/webhooks). See the [webhook schema](/api-reference/webhooks/balancesUsageAlertTriggered) for the full payload.\n\nFor more details and examples, see [Spend Limits & Usage Alerts](/documentation/modelling-pricing/spend-limits#usage-alerts).\n\n## Auto Top-Ups\n\nAuto top-ups automatically replenish a customer\'s prepaid balance when it drops below a configured threshold. This prevents service interruptions for customers who don\'t want to manually manage their credits.\n\n> **Example** <br />\n> A customer gets 5,000 credits per month. When their balance drops below 500, Autumn automatically purchases 1,000 more credits using the plan\'s one-off prepaid price.\n\nAuto top-ups require a plan with a [one-off prepaid](/documentation/modelling-pricing/one-off-purchases) item for the feature, and the customer must have a payment method on file.\n\n<CodeGroup>\n\n```typescript TypeScript\nawait autumn.customers.update({\n customerId: "user_123",\n billingControls: {\n autoTopups: [{\n featureId: "credits",\n enabled: true,\n threshold: 500,\n quantity: 1000,\n }],\n },\n});\n```\n\n```python Python\nawait autumn.customers.update(\n customer_id="user_123",\n billing_controls={\n "auto_topups": [{\n "feature_id": "credits",\n "enabled": True,\n "threshold": 500,\n "quantity": 1000,\n }],\n },\n)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/customers/update" \\\n -H "Authorization: Bearer am_sk_..." \\\n -H "Content-Type: application/json" \\\n -d \'{\n "customer_id": "user_123",\n "billing_controls": {\n "auto_topups": [{\n "feature_id": "credits",\n "enabled": true,\n "threshold": 500,\n "quantity": 1000\n }]\n }\n }\'\n```\n\n</CodeGroup>\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `feature_id` | string | The feature (credit balance) to monitor |\n| `enabled` | boolean | Whether auto top-up is active |\n| `threshold` | number | Balance level that triggers a top-up |\n| `quantity` | number | Units to purchase each time |\n| `purchase_limit` | object (optional) | Rate limit on how often top-ups can occur |\n\nTo prevent runaway spending, you can set a purchase limit:\n\n```json\n{\n "purchase_limit": {\n "interval": "month",\n "interval_count": 1,\n "limit": 5\n }\n}\n```\n\nThis limits the customer to 5 auto top-ups per month. Supported intervals: `hour`, `day`, `week`, `month`.\n\nFor setup instructions and how it works end-to-end, see [Auto Top-Ups](/documentation/modelling-pricing/auto-top-ups).\n\n## Customer vs Entity Controls\n\nBilling controls can be set at two levels:\n\n| Control | Customer-level | Entity-level |\n|---------|---------------|--------------|\n| **Overage allowed** | Yes | Yes |\n| **Spend limits** | Yes | Yes |\n| **Usage limits** | Yes | Yes |\n| **Usage alerts** | Yes | Yes |\n| **Auto top-ups** | Yes | No |\n\n**Entity-level** controls are configured by updating the entity instead of the customer. Entity-level controls override customer-level controls for that entity — for example, an entity overage override takes precedence over the customer-level setting, and entity spend limits override the customer-level limit.\n\nOverrides are resolved **per feature**: an entity\'s own entry for a feature wins, and the customer\'s entries fill in any features the entity doesn\'t set. For usage limits specifically, an inherited (customer-level) cap counts usage against the **shared customer window** — it\'s the same aggregate cap, not a separate per-entity copy. An entity-level usage limit, by contrast, gets its own per-entity window and counter.\n\n> **Example — per-entity cap** <br />\n> An org (the customer) has 1,000 monthly API calls shared across its workspaces (entities). You set a customer-level usage limit of 1,000/month so the org can\'t exceed its plan, and an entity-level limit of 200/day on a noisy workspace so it can\'t starve the others. The workspace is blocked at 200/day; the org is blocked at 1,000/month.\n\n<CodeGroup>\n\n```typescript TypeScript\nawait autumn.entities.update({\n customerId: "org_123",\n entityId: "workspace_a",\n billingControls: {\n overageAllowed: [{\n featureId: "api_calls",\n enabled: true,\n }],\n spendLimits: [{\n featureId: "api_calls",\n enabled: true,\n overageLimit: 2000,\n }],\n usageLimits: [{\n featureId: "api_calls",\n limit: 200,\n interval: "day",\n }],\n usageAlerts: [{\n featureId: "api_calls",\n threshold: 90,\n thresholdType: "usage_percentage",\n enabled: true,\n }],\n },\n});\n```\n\n```python Python\nawait autumn.entities.update(\n customer_id="org_123",\n entity_id="workspace_a",\n billing_controls={\n "overage_allowed": [{\n "feature_id": "api_calls",\n "enabled": True,\n }],\n "spend_limits": [{\n "feature_id": "api_calls",\n "enabled": True,\n "overage_limit": 2000,\n }],\n "usage_limits": [{\n "feature_id": "api_calls",\n "limit": 200,\n "interval": "day",\n }],\n "usage_alerts": [{\n "feature_id": "api_calls",\n "threshold": 90,\n "threshold_type": "usage_percentage",\n "enabled": True,\n }],\n },\n)\n```\n\n```bash cURL\ncurl -X POST "https://api.useautumn.com/v1/entities/update" \\\n -H "Authorization: Bearer am_sk_..." \\\n -H "Content-Type: application/json" \\\n -d \'{\n "customer_id": "org_123",\n "entity_id": "workspace_a",\n "billing_controls": {\n "overage_allowed": [{\n "feature_id": "api_calls",\n "enabled": true\n }],\n "spend_limits": [{\n "feature_id": "api_calls",\n "enabled": true,\n "overage_limit": 2000\n }],\n "usage_limits": [{\n "feature_id": "api_calls",\n "limit": 200,\n "interval": "day"\n }],\n "usage_alerts": [{\n "feature_id": "api_calls",\n "threshold": 90,\n "threshold_type": "usage_percentage",\n "enabled": true\n }]\n }\n }\'\n```\n\n</CodeGroup>\n\nAuto top-ups are customer-level only because they create invoices and charge a payment method, which is tied to the customer account — not individual entities.\n\n## Plan-Level Defaults\n\nEvery billing control can also be defined on a **plan** (in the dashboard under plan settings → billing controls, via the `billingControls` field when creating or updating a plan, or in the [CLI config](/cli/config#plans) with the same field). Plan-level controls act as defaults for every customer on that plan — set a daily usage limit once on your free tier instead of on each customer.\n\n```ts autumn.config.ts\nexport const free = plan({\n planId: "free",\n versionSlug: "v1",\n active: true,\n name: "Free",\n autoEnable: true,\n items: [{ featureId: "emails", included: 200, reset: { interval: "month" } }],\n billingControls: {\n usageLimits: [{ featureId: "emails", limit: 200, interval: "day" }],\n },\n});\n```\n\nResolution when a customer is on one or more plans:\n\n- A **customer-level entry shadows the plan\'s entry** for the same feature (for usage limits, the same feature *and* conditions). Setting a control on the customer is how you override the plan default — and a disabled customer entry still shadows the plan\'s, it doesn\'t resurface it.\n- With **multiple attached plans** defining the same control, the most restrictive wins (auto top-ups: the most recently attached plan\'s config).\n\n### Reading effective controls\n\nFrom API version `2.3.0`, [fetching a customer](/api-reference/customers/getCustomer) returns the **effective** controls: plan defaults are merged into `billing_controls`, and every entry carries a `source` field so you can tell overrides from inherited defaults. Inherited usage limits include the live window `usage`, so you can render "X of 200 used today" without extra calls.\n\n```json\n"billing_controls": {\n "usage_limits": [\n {\n "feature_id": "emails",\n "enabled": true,\n "limit": 200,\n "interval": "day",\n "usage": 37,\n "source": "plan"\n }\n ]\n}\n```\n\nOn earlier API versions, `billing_controls` contains only customer-level entries. Entity responses always show the entity\'s own controls.\n\n## Related Webhooks\n\nBilling controls tie into three webhook events that fire automatically based on usage:\n\n| Event | When it fires |\n|-------|---------------|\n| [`balances.limit_reached`](/api-reference/webhooks/balancesLimitReached) | Customer transitions from allowed to not allowed on a feature (included allowance exhausted, max purchase hit, spend limit reached, or usage limit hit). The payload\'s `limit_type` distinguishes `included`, `max_purchase`, `spend_limit`, and `usage_limit`. |\n| [`balances.usage_alert_triggered`](/api-reference/webhooks/balancesUsageAlertTriggered) | Customer\'s usage crosses a configured alert threshold |\n| [`billing.updated`](/api-reference/webhooks/billingUpdated) | Customer\'s plans change (new subscription, upgrade, downgrade, cancellation, expiry, etc.) |\n\nFor webhook setup and security details, see [Webhooks](/documentation/webhooks).',
232
232
  },
233
233
  {
234
234
  path: "references/spend-limits.md",
@@ -248,12 +248,12 @@ export const SKILLS: readonly BundledSkill[] = [
248
248
  {
249
249
  path: "references/deploy.md",
250
250
  contents:
251
- "## Deploy to production\n\nOnce you've tested your integration in sandbox, follow this checklist to go live with real payments.\n\n<Steps>\n<Step>\n### Connect your live Stripe account\n\nIn the Autumn dashboard, open the **Deploy to Production** dialog from the sidebar.\nConnect your live Stripe account via OAuth — this links Autumn to your real Stripe environment.\n\nYour sandbox uses a shared Stripe test account by default. Production requires your own Stripe account.\n\n</Step>\n\n<Step>\n### Push your plans to production\n\nIf you're using the [CLI](/cli/getting-started), add `-p` to target production. It uses `AUTUMN_PROD_SECRET_KEY`. `push -p` only previews the change. Add `--yes` to apply it:\n\n```bash\nbunx atmn push -p\nbunx atmn push -p --yes\n```\n\n`atmn login` writes `AUTUMN_PROD_SECRET_KEY` next to your sandbox key, so you can use both.\n\nAlternatively, the Deploy dialog in the dashboard can copy your sandbox plans to production for you.\n\n</Step>\n\n<Step>\n### Swap your API key\n\nPoint your server-side code at a live secret key. Create a production key from [Developer Settings](https://app.useautumn.com/production/dev?tab=api_keys), and set it in your production environment:\n\n```bash .env\nAUTUMN_SECRET_KEY=am_sk_live_...\n```\n\nOnly set the live key in your production environment. Keep your local `AUTUMN_SECRET_KEY` as the sandbox key. CLI commands with `-p` read `AUTUMN_PROD_SECRET_KEY`, so you never need to overwrite it.\n\nDouble check that:\n- Your **server-side** code uses the live secret key (`am_sk_live_*`)\n- If you're using a publishable key client-side, it's the live one (`am_pk_live_*`)\n\nThe key prefix determines the environment automatically — `_test_` routes to sandbox, `_live_` routes to production. There's no separate \"environment\" config to flip.\n\n</Step>\n\n<Step>\n### Verify fail-open behavior\n\nAutumn's SDK is **fail-open by default** — if Autumn is unreachable, `check`, `track`, and customer fetches return safe dummy responses instead of throwing errors. This means Autumn can never take your app down.\n\nYou should verify this before going live. The easiest way is to point the SDK at a non-existent URL and exercise your app's core flows:\n\n<CodeGroup>\n\n```typescript TypeScript\nconst autumn = new Autumn({\n secretKey: process.env.AUTUMN_SECRET_KEY,\n serverURL: \"https://localhost:9999\", // simulate outage\n});\n```\n\n```python Python\nautumn = Autumn(\n secret_key=os.environ[\"AUTUMN_SECRET_KEY\"],\n server_url=\"https://localhost:9999\", # simulate outage\n)\n```\n\n</CodeGroup>\n\nWith this in place:\n\n1. Trigger actions that call `check` — they should return `allowed: true`\n2. Trigger actions that call `track` — they should not crash\n3. Confirm your core user flows work as normal\n4. Remove the `serverURL` override when done\n\nWhile the SDK gracefully handles outages for read-path calls, write operations like `attach` (which initiate checkout or subscription changes) will still fail when Autumn is unreachable.\n\n</Step>\n\n<Step>\n### Set up webhooks (if applicable)\n\nIf you're listening for Autumn [webhook events](/documentation/webhooks) (e.g. `customer.products.updated`), make sure your production webhook endpoint is configured in the dashboard. Verify you can receive a test event.\n\n</Step>\n\n<Step>\n### Monitor your first users\n\nAfter deploying, keep an eye on the [Customers](https://app.useautumn.com/production/customers) page in the Autumn dashboard. Verify that:\n\n- New customers are created correctly\n- Subscriptions are attached as expected\n- Usage is being tracked\n- Invoices are generated in Stripe\n\n</Step>\n\n</Steps>\n\n---\n\nOnce you've completed this checklist, you're live. Your sandbox environment remains available for testing new plans and pricing changes before pushing them to production.",
251
+ "## Deploy to production\n\nOnce you've tested your integration in sandbox, follow this checklist to go live with real payments.\n\n<Steps>\n<Step>\n### Connect your live Stripe account\n\nIn the Autumn dashboard, open the **Deploy to Production** dialog from the sidebar.\nConnect your live Stripe account via OAuth — this links Autumn to your real Stripe environment.\n\nYour sandbox uses a shared Stripe test account by default. Production requires your own Stripe account.\n\n</Step>\n\n<Step>\n### Push your plans to production\n\nIf you're using the [CLI](/cli/getting-started), add `-p` to target production. It uses `AUTUMN_PROD_SECRET_KEY`. `push -p` only previews the change. Add `--yes` to apply it:\n\n```bash\nbunx atmn push -p\nbunx atmn push -p --yes\n```\n\n`atmn login` writes `AUTUMN_PROD_SECRET_KEY` next to your sandbox key, so you can use both.\n\nAlternatively, the Deploy dialog in the dashboard can copy your sandbox plans to production for you.\n\n</Step>\n\n<Step>\n### Swap your API key\n\nPoint your server-side code at a live secret key. Create a production key from [Developer Settings](https://app.useautumn.com/production/dev?tab=api_keys), and set it in your production environment:\n\n```bash .env\nAUTUMN_SECRET_KEY=am_sk_live_...\n```\n\nOnly set the live key in your production environment. Keep your local `AUTUMN_SECRET_KEY` as the sandbox key. CLI commands with `-p` read `AUTUMN_PROD_SECRET_KEY`, so you never need to overwrite it.\n\nDouble check that:\n- Your **server-side** code uses the live secret key (`am_sk_live_*`)\n- If you're using a publishable key client-side, it's the live one (`am_pk_live_*`)\n\nThe key prefix determines the environment automatically — `_test_` routes to sandbox, `_live_` routes to production. There's no separate \"environment\" config to flip.\n\n</Step>\n\n<Step>\n### Verify fail-open behavior\n\nAutumn's SDK is **fail-open by default** — if Autumn is unreachable, `check`, `track`, and customer fetches return safe dummy responses instead of throwing errors. This means Autumn can never take your app down.\n\nYou should verify this before going live. The easiest way is to point the SDK at a non-existent URL and exercise your app's core flows:\n\n<CodeGroup>\n\n```typescript TypeScript\nconst autumn = new Autumn({\n secretKey: process.env.AUTUMN_SECRET_KEY,\n serverURL: \"https://localhost:9999\", // simulate outage\n});\n```\n\n```python Python\nautumn = Autumn(\n secret_key=os.environ[\"AUTUMN_SECRET_KEY\"],\n server_url=\"https://localhost:9999\", # simulate outage\n)\n```\n\n</CodeGroup>\n\nWith this in place:\n\n1. Trigger actions that call `check` — they should return `allowed: true`\n2. Trigger actions that call `track` — they should not crash\n3. Confirm your core user flows work as normal\n4. Remove the `serverURL` override when done\n\nWhile the SDK gracefully handles outages for read-path calls, write operations like `attach` (which initiate checkout or subscription changes) will still fail when Autumn is unreachable.\n\n</Step>\n\n<Step>\n### Set up webhooks (if applicable)\n\nIf you're listening for Autumn [webhook events](/documentation/webhooks) (e.g. `billing.updated`), make sure your production webhook endpoint is configured in the dashboard. Verify you can receive a test event.\n\n</Step>\n\n<Step>\n### Monitor your first users\n\nAfter deploying, keep an eye on the [Customers](https://app.useautumn.com/production/customers) page in the Autumn dashboard. Verify that:\n\n- New customers are created correctly\n- Subscriptions are attached as expected\n- Usage is being tracked\n- Invoices are generated in Stripe\n\n</Step>\n\n</Steps>\n\n---\n\nOnce you've completed this checklist, you're live. Your sandbox environment remains available for testing new plans and pricing changes before pushing them to production.",
252
252
  },
253
253
  {
254
254
  path: "references/fail-open.md",
255
255
  contents:
256
- '## Fail-Open Defaults\n\nAutumn\'s SDKs include a **fail-open** mechanism that prevents your application from going down if Autumn is temporarily unreachable. When enabled, critical SDK methods return safe default responses instead of throwing errors.\n\n## Why fail-open?\n\nAs a billing engine, Autumn sits in the critical path of your application. If your app calls `check()` to gate access to a feature, and Autumn is unreachable, that call would throw an error -- effectively blocking your users from accessing your product.\n\nFail-open ensures your users are never blocked by an Autumn outage. Usage events may be lost during the outage, but your customers stay unaffected.\n\n## Behavior\n\nFail-open is **enabled by default**. When Autumn is unreachable (network errors, timeouts, or server errors returning 5XX status codes), the SDK returns safe defaults:\n\n| Method | Default response | Effect |\n|--------|-----------------|--------|\n| `check()` | `{ allowed: true }` | Users retain access to features |\n| `track()` | `{ value: 0, balance: null }` | Usage event is silently dropped |\n\nCustomer and entity operations, including `customers.getOrCreate()`, `customers.get()`, `entities.get()`, and `entities.create()`, propagate errors even when fail-open is enabled. Only `check()` and `track()` return fail-open defaults.\n\nWhen fail-open triggers, the SDK logs a prominent error to your console so you\'re immediately aware of the issue.\n\n## When fail-open triggers\n\nFail-open activates when the SDK encounters:\n\n- **Server errors** -- Autumn returns a 5XX status code (500, 502, 503, etc.)\n- **Network failures** -- DNS resolution failure, connection refused, connection reset\n- **Timeouts** -- The request to Autumn times out\n\nFail-open does **not** activate for client errors (4XX). If you receive a 400 (bad request), 401 (unauthorized), or 404 (not found), those errors propagate normally since they indicate a problem with your integration, not an Autumn outage.\n\n## Configuration\n\n### Disabling fail-open\n\nIf you prefer strict error handling and want all Autumn errors to propagate:\n\n<CodeGroup>\n```typescript TypeScript\nimport { Autumn } from "@useautumn/sdk";\n\nconst autumn = new Autumn({\n secretKey: "sk_...",\n failOpen: false,\n});\n```\n\n```python Python\n# Coming soon\n```\n</CodeGroup>\n\n### Detecting fail-open responses\n\nWhen `check()` fails open, the response will have `allowed: true` with empty values:\n\n```typescript\nconst result = await autumn.check({\n customerId: "cus_123",\n featureId: "messages",\n});\n\n// Normal response: allowed is based on actual balance\n// Fail-open response: allowed is always true, customerId is ""\n```\n\n## Console output\n\nWhen fail-open triggers, you\'ll see this in your server logs:\n\n```\nFATAL AUTUMN ERROR DETECTED; FAILING OPEN; LEARN MORE: https://docs.useautumn.com/docs/fail-open; STATUS PAGE: status.useautumn.com\n Operation: check | Status: 503 | Error: Server error\n```\n\nMonitor your logs for this message. If you see it, check [status.useautumn.com](https://status.useautumn.com) for ongoing incidents.\n\n## SDK support\n\n| SDK | Fail-open support |\n|-----|------------------|\n| `@useautumn/sdk` (TypeScript) | Supported |\n| `autumn-python` (Python) | Coming soon |\n| `autumn-js` (framework SDK) | Coming soon |',
256
+ '## Fail-Open Defaults\n\nAutumn\'s SDKs include a **fail-open** mechanism that prevents your application from going down if Autumn is temporarily unreachable. When enabled, critical SDK methods return safe default responses instead of throwing errors.\n\n## Why fail-open?\n\nAs a billing engine, Autumn sits in the critical path of your application. If your app calls `check()` to gate access to a feature, and Autumn is unreachable, that call would throw an error -- effectively blocking your users from accessing your product.\n\nFail-open ensures your users are never blocked by an Autumn outage. Usage events may be lost during the outage, but your customers stay unaffected.\n\nIf you run the [edge cache](/documentation/edge-cache), checks keep working through an outage, answered from the last balances Autumn sent it.\n\n## Behavior\n\nFail-open is **enabled by default**. When Autumn is unreachable (network errors, timeouts, or server errors returning 5XX status codes), the SDK returns safe defaults:\n\n| Method | Default response | Effect |\n|--------|-----------------|--------|\n| `check()` | `{ allowed: true }` | Users retain access to features |\n| `track()` | `{ value: 0, balance: null }` | Usage event is silently dropped |\n\nCustomer and entity operations, including `customers.getOrCreate()`, `customers.get()`, `entities.get()`, and `entities.create()`, propagate errors even when fail-open is enabled. Only `check()` and `track()` return fail-open defaults.\n\nWhen fail-open triggers, the SDK logs a prominent error to your console so you\'re immediately aware of the issue.\n\n## When fail-open triggers\n\nFail-open activates when the SDK encounters:\n\n- **Server errors** -- Autumn returns a 5XX status code (500, 502, 503, etc.)\n- **Network failures** -- DNS resolution failure, connection refused, connection reset\n- **Timeouts** -- The request to Autumn times out\n\nFail-open does **not** activate for client errors (4XX). If you receive a 400 (bad request), 401 (unauthorized), or 404 (not found), those errors propagate normally since they indicate a problem with your integration, not an Autumn outage.\n\n## Configuration\n\n### Disabling fail-open\n\nIf you prefer strict error handling and want all Autumn errors to propagate:\n\n<CodeGroup>\n```typescript TypeScript\nimport { Autumn } from "@useautumn/sdk";\n\nconst autumn = new Autumn({\n secretKey: "sk_...",\n failOpen: false,\n});\n```\n\n```python Python\n# Coming soon\n```\n</CodeGroup>\n\n### Detecting fail-open responses\n\nWhen `check()` fails open, the response will have `allowed: true` with empty values:\n\n```typescript\nconst result = await autumn.check({\n customerId: "cus_123",\n featureId: "messages",\n});\n\n// Normal response: allowed is based on actual balance\n// Fail-open response: allowed is always true, customerId is ""\n```\n\n## Console output\n\nWhen fail-open triggers, you\'ll see this in your server logs:\n\n```\nFATAL AUTUMN ERROR DETECTED; FAILING OPEN; LEARN MORE: https://docs.useautumn.com/docs/fail-open; STATUS PAGE: status.useautumn.com\n Operation: check | Status: 503 | Error: Server error\n```\n\nMonitor your logs for this message. If you see it, check [status.useautumn.com](https://status.useautumn.com) for ongoing incidents.\n\n## SDK support\n\n| SDK | Fail-open support |\n|-----|------------------|\n| `@useautumn/sdk` (TypeScript) | Supported |\n| `autumn-python` (Python) | Coming soon |\n| `autumn-js` (framework SDK) | Coming soon |',
257
257
  },
258
258
  ],
259
259
  },
@@ -261,9 +261,9 @@ export const SKILLS: readonly BundledSkill[] = [
261
261
  name: "autumn-concepts",
262
262
  description:
263
263
  "Understanding Autumn's billing objects before acting on them — what a plan version is and what active means, when a trial starts or ends billing, how entity- and seat-scoped subscriptions differ from the customer's own, what a customize diff actually changes, and how caps, overage, and top-ups behave. Load before versioning, editing, or drafting a plan, or when a customer's state does not match the plain reading of their plan.",
264
- version: "2.0.139",
264
+ version: "2.0.141",
265
265
  markdown:
266
- "---\nname: autumn-concepts\ndescription: Understanding Autumn's billing objects before acting on them — what a plan version is and what active means, when a trial starts or ends billing, how entity- and seat-scoped subscriptions differ from the customer's own, what a customize diff actually changes, and how caps, overage, and top-ups behave. Load before versioning, editing, or drafting a plan, or when a customer's state does not match the plain reading of their plan.\nversion: 2.0.139\n---\n\n# Concepts\n\nAutumn is a database for your application billing state: features, plans, customers, subscriptions, purchases, balances, flags, and billing controls. It helps you iterate on pricing, manage credit access and usage, control overage, and keep billing behavior connected to product access.\n\nAutumn is a layer above Stripe; Stripe still handles subscription management, invoicing, and payment processing. Autumn provisions and updates Stripe subscriptions, schedules, invoices, and related billing objects for you.\n\n## Object graph\n\n```txt\nOrganization\n- features[] -> Feature\n- plans[] -> Plan\n- customers[] -> Customer\n\nConfiguration model:\nFeature\n- referenced by -> Plan Item\n- credit_system type: maps actions to credit costs (flat or tiered)\n\nPlan\n- items[] -> Plan Item\n - feature_id -> Feature\n - optional price -> usage_based or prepaid feature price\n- price -> base recurring or one-off price\n- versions[] -> parallel definitions of this plan; ONE is active\n- variants[] -> Plan (a derived plan storing only its differences)\n- licenses[] -> Plan (a seat plan this plan hands out per seat)\n- aliases -> old plan ids that still resolve after a rename\n\nPlan Item\n- feature_id -> Feature\n- optional price -> usage_based or prepaid feature price\n- pooled? -> entity grants combine into one shared customer balance\n\nRuntime model:\nCustomer\n- subscriptions[] -> Subscription -> Plan (a specific version of it)\n- purchases[] -> Purchase -> Plan\n- balances[feature_id] -> Balance -> Feature\n - pooled balance: fed by entity grants, spent by any entity\n - rollover, expiry, usage windows live here\n- licenses[] -> CustomerLicense -> seats granted / in use / remaining\n- flags[feature_id] -> Flag -> Feature\n- billing_controls -> customer-level usage controls\n- entities[] -> Entity -> same runtime shape scoped under Customer\n\nEntity\n- belongs to -> Customer\n- subscriptions[] -> Subscription -> Plan\n- purchases[] -> Purchase -> Plan\n- balances[feature_id] -> Balance -> Feature\n- flags[feature_id] -> Flag -> Feature\n- license assignment -> holds one seat from the customer's pool\n\nFrom config to customer state:\nPlan + Customer --billing.attach--> Subscription or Purchase\nPlan + Customer + entity_id --billing.attach--> Entity-scoped Subscription or Purchase\nParent plan's licenses --licenses.attach--> seat assigned to an Entity\nSubscription/Purchase -> Balance or Flag provisioning\n```\n\nTwo relationships changed recently — worth stating plainly because older docs describe the old way:\n\n**Versions are groups of customers, not history.** A plan's versions used to be numbered steps in time, and the newest was always live. Now each version is a definition that some group of customers lives on, and exactly one is **active** — the one attach uses when no version is named. Which changes are edits and which are new versions, drafts, and how customers move: the plan definition below.\n\n**Plans connect to other plans.** A plan can have variants (an annual twin storing only its differences), and it can offer licenses (a small seat plan it hands out per seat). So plans form a graph, not a flat list.\n\nUse these definitions as the mental model when designing or changing Autumn\npricing. Reason in terms of features, plans, plan items, customers/entities, and\nbilling controls before writing any config or calling the API — most modeling\nmistakes come from conflating a feature with a plan item, or a plan-level price\nwith a per-feature price.\n\n## Definitions\n\nLoad the matching definition when reasoning about that object.\n\nFor defining a feature — the atomic unit Autumn gates, tracks, or bills, and its types, read `references/feature.md`.\n\nFor defining a plan — the attachable package of items and pricing, read `references/plan.md`.\n\nFor modeling plan items, or when you need concrete API request-body examples (included usage, prepaid, usage-based, tiers), read `references/plan-items.md`.\n\nFor using customize, reading plan update previews, or representing a small diff from a base plan, read `references/customize.md`.\n\nFor reasoning about free trials and when billing begins, read `references/trials.md`.\n\nFor distinguishing a customer from an entity (seats, sub-accounts) and their runtime billing state, read `references/customer-entity.md`.\n\nFor reasoning about billing controls — runtime caps, alerts, overage, and top-ups, read `references/billing-controls.md`.\n\nFor setting or reading a customer's billing address, tax IDs, tax exemption, or invoice custom fields, read `references/billing-details.md`.\n\nFor reasoning about licenses — seat plans a parent plan hands out, seat pools, assigning and releasing seats, read `references/licenses.md`.",
266
+ "---\nname: autumn-concepts\ndescription: Understanding Autumn's billing objects before acting on them — what a plan version is and what active means, when a trial starts or ends billing, how entity- and seat-scoped subscriptions differ from the customer's own, what a customize diff actually changes, and how caps, overage, and top-ups behave. Load before versioning, editing, or drafting a plan, or when a customer's state does not match the plain reading of their plan.\nversion: 2.0.141\n---\n\n# Concepts\n\nAutumn is a database for your application billing state: features, plans, customers, subscriptions, purchases, balances, flags, and billing controls. It helps you iterate on pricing, manage credit access and usage, control overage, and keep billing behavior connected to product access.\n\nAutumn is a layer above Stripe; Stripe still handles subscription management, invoicing, and payment processing. Autumn provisions and updates Stripe subscriptions, schedules, invoices, and related billing objects for you.\n\n## Object graph\n\n```txt\nOrganization\n- features[] -> Feature\n- plans[] -> Plan\n- customers[] -> Customer\n\nConfiguration model:\nFeature\n- referenced by -> Plan Item\n- credit_system type: maps actions to credit costs (flat or tiered)\n\nPlan\n- items[] -> Plan Item\n - feature_id -> Feature\n - optional price -> usage_based or prepaid feature price\n- price -> base recurring or one-off price\n- versions[] -> parallel definitions of this plan; ONE is active\n- variants[] -> Plan (a derived plan storing only its differences)\n- licenses[] -> Plan (a seat plan this plan hands out per seat)\n- aliases -> old plan ids that still resolve after a rename\n\nPlan Item\n- feature_id -> Feature\n- optional price -> usage_based or prepaid feature price\n- pooled? -> entity grants combine into one shared customer balance\n\nRuntime model:\nCustomer\n- subscriptions[] -> Subscription -> Plan (a specific version of it)\n- purchases[] -> Purchase -> Plan\n- balances[feature_id] -> Balance -> Feature\n - pooled balance: fed by entity grants, spent by any entity\n - rollover, expiry, usage windows live here\n- licenses[] -> CustomerLicense -> seats granted / in use / remaining\n- flags[feature_id] -> Flag -> Feature\n- billing_controls -> customer-level usage controls\n- entities[] -> Entity -> same runtime shape scoped under Customer\n\nEntity\n- belongs to -> Customer\n- subscriptions[] -> Subscription -> Plan\n- purchases[] -> Purchase -> Plan\n- balances[feature_id] -> Balance -> Feature\n- flags[feature_id] -> Flag -> Feature\n- license assignment -> holds one seat from the customer's pool\n\nFrom config to customer state:\nPlan + Customer --billing.attach--> Subscription or Purchase\nPlan + Customer + entity_id --billing.attach--> Entity-scoped Subscription or Purchase\nParent plan's licenses --licenses.attach--> seat assigned to an Entity\nSubscription/Purchase -> Balance or Flag provisioning\n```\n\nTwo relationships changed recently — worth stating plainly because older docs describe the old way:\n\n**Versions are groups of customers, not history.** A plan's versions used to be numbered steps in time, and the newest was always live. Now each version is a definition that some group of customers lives on, and exactly one is **active** — the one attach uses when no version is named. Which changes are edits and which are new versions, drafts, and how customers move: the plan definition below.\n\n**Plans connect to other plans.** A plan can have variants (an annual twin storing only its differences), and it can offer licenses (a small seat plan it hands out per seat). So plans form a graph, not a flat list.\n\nUse these definitions as the mental model when designing or changing Autumn\npricing. Reason in terms of features, plans, plan items, customers/entities, and\nbilling controls before writing any config or calling the API — most modeling\nmistakes come from conflating a feature with a plan item, or a plan-level price\nwith a per-feature price.\n\n## Definitions\n\nLoad the matching definition when reasoning about that object.\n\nFor defining a feature — the atomic unit Autumn gates, tracks, or bills, and its types, read `references/feature.md`.\n\nFor defining a plan — the attachable package of items and pricing, read `references/plan.md`.\n\nFor modeling plan items, or when you need concrete API request-body examples (included usage, prepaid, usage-based, tiers), read `references/plan-items.md`.\n\nFor using customize, reading plan update previews, or representing a small diff from a base plan, read `references/customize.md`.\n\nFor reasoning about free trials and when billing begins, read `references/trials.md`.\n\nFor distinguishing a customer from an entity (seats, sub-accounts) and their runtime billing state, read `references/customer-entity.md`.\n\nFor reasoning about billing controls — runtime caps, alerts, overage, and top-ups, read `references/billing-controls.md`.\n\nFor setting or reading a customer's billing address, tax IDs, tax exemption, or invoice custom fields, read `references/billing-details.md`.\n\nFor reasoning about licenses — seat plans a parent plan hands out, seat pools, assigning and releasing seats, read `references/licenses.md`.",
267
267
  references: [
268
268
  {
269
269
  path: "references/feature.md",