autumn
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAutumn Billing Integration Guide
Autumn计费集成指南
Reference Repositories
参考仓库
- Autumn — Usage-based billing platform
- Autumn TypeScript SDK + CLI — SDK and
autumn-jsCLIatmn - Autumn Docs
- Autumn — 用量驱动的计费平台
- Autumn TypeScript SDK + CLI — SDK 和
autumn-jsCLI工具atmn - Autumn 官方文档
When to Apply This Skill
适用场景
Use this when you need to:
- Define or modify features, credit systems, or plans in .
autumn.config.ts - Add credit checks or usage tracking via the SDK.
autumn-js - Gate API endpoints behind billing (free tier limits, paid plan access).
- Push/pull billing config with the CLI.
atmn - Debug billing issues (insufficient credits, customer sync, refunds).
当你需要完成以下操作时可使用本指南:
- 在中定义或修改功能、额度体系、套餐规则
autumn.config.ts - 通过SDK添加额度校验或使用量追踪能力
autumn-js - 为API接口添加计费管控(免费版额度限制、付费套餐访问权限)
- 通过CLI推送/拉取计费配置
atmn - 排查计费相关问题(额度不足、客户数据同步、退款处理)
Naming Conventions (CRITICAL)
命名规范(重要)
All IDs use . This is Autumn's explicit convention.
snake_caseFeature IDs should be descriptive (not abstract tier numbers) and ecosystem-scoped (not tied to a single app feature like "chat"). The metered features represent model cost tiers that any AI feature can consume.
typescript
// CORRECT — descriptive, ecosystem-scoped
feature({ id: 'ai_fast', ... })
feature({ id: 'ai_standard', ... })
feature({ id: 'ai_premium', ... })
plan({ id: 'pro', ... })
plan({ id: 'credit_top_up', ... })
// WRONG — tied to a single feature ("chat")
feature({ id: 'ai_chat_fast', ... })
// WRONG — abstract tier numbers (Autumn convention prefers descriptive)
feature({ id: 'ai_tier_1', ... })
// WRONG — kebab-case
feature({ id: 'ai-fast', ... })所有ID必须使用命名,这是Autumn明确要求的规范。
snake_case功能ID需要具备描述性(不要使用抽象的层级编号)且全局生态适用(不要绑定单一应用功能比如"chat")。计量功能代表所有AI功能都可消耗的模型成本层级。
typescript
// 正确 — 描述清晰、全局适用
feature({ id: 'ai_fast', ... })
feature({ id: 'ai_standard', ... })
feature({ id: 'ai_premium', ... })
plan({ id: 'pro', ... })
plan({ id: 'credit_top_up', ... })
// 错误 — 绑定了单一功能("chat")
feature({ id: 'ai_chat_fast', ... })
// 错误 — 使用了抽象层级编号(Autumn规范要求使用描述性命名)
feature({ id: 'ai_tier_1', ... })
// 错误 — 使用了kebab-case命名
feature({ id: 'ai-fast', ... })Feature Types
功能类型
| Type | | Use Case | Example |
|---|---|---|---|
| | Usage that resets periodically (messages, API calls) | AI model invocations |
| | Persistent allocation (seats, storage) | Team seats |
| — | Pool that maps to metered features via | AI credits |
| — | Feature flag on/off | Advanced analytics |
Credit systems require linked features with . Each linked feature has a defining how many credits one unit consumes.
meteredconsumable: truecreditCosttypescript
export const aiUsage = feature({
id: 'ai_usage',
name: 'AI Usage',
type: 'metered',
consumable: true,
});
export const aiCredits = feature({
id: 'ai_credits',
name: 'AI Credits',
type: 'credit_system',
creditSchema: [
{ meteredFeatureId: 'ai_usage', creditCost: 1 },
],
});| 类型 | | 适用场景 | 示例 |
|---|---|---|---|
| | 定期重置的用量(消息数、API调用次数) | AI模型调用 |
| | 永久分配的资源(席位、存储空间) | 团队席位 |
| — | 通过 | AI额度 |
| — | 开关型功能标记 | 高级分析功能 |
额度体系需要关联的计量功能,每个关联的功能都需要定义,说明每单位消耗的额度数量。
consumable: truecreditCosttypescript
export const aiUsage = feature({
id: 'ai_usage',
name: 'AI Usage',
type: 'metered',
consumable: true,
});
export const aiCredits = feature({
id: 'ai_credits',
name: 'AI Credits',
type: 'credit_system',
creditSchema: [
{ meteredFeatureId: 'ai_usage', creditCost: 1 },
],
});Proportional Billing
比例计费
Instead of multiple metered features with fixed per tier, use a single metered feature with and vary the at runtime.
creditCostcreditCost: 1requiredBalanceThis gives per-model cost precision without cluttering the Autumn dashboard with dozens of features.
How it works: Autumn's with uses as the deduction amount. With , passing deducts exactly 5 credits from the pool.
check()sendEvent: truerequiredBalancecreditCost: 1requiredBalance: 5typescript
// Runtime cost table (in model-costs.ts, not autumn.config.ts)
const MODEL_CREDITS: Record<string, number> = {
'gpt-4o-mini': 1, // cheap model = 1 credit
'claude-sonnet-4': 5, // mid-range = 5 credits
'claude-opus-4': 30, // expensive = 30 credits
};
// Dynamic deduction
const credits = MODEL_CREDITS[model];
await autumn.check({
customerId,
featureId: 'ai_usage', // single feature for all models
requiredBalance: credits, // varies per model
sendEvent: true,
});Refund on error: Use to refund the exact amount.
track({ featureId: 'ai_usage', value: -credits })Blocking expensive models: Omit them from . Unknown models → returns → 400.
MODEL_CREDITSgetModelCredits()undefined不要创建多个计量功能为每个层级设置固定,而是使用单个计量功能设置,在运行时动态修改的值。
creditCostcreditCost: 1requiredBalance这种方式可以实现按模型精确计费,不会在Autumn控制台中创建大量冗余功能。
实现逻辑:Autumn的方法如果设置了,会将作为扣除金额。当时,传入就会从额度池中精准扣除5个额度。
check()sendEvent: truerequiredBalancecreditCost: 1requiredBalance: 5typescript
// 运行时成本表(存放在model-costs.ts中,不是autumn.config.ts)
const MODEL_CREDITS: Record<string, number> = {
'gpt-4o-mini': 1, // 低成本模型 = 1额度
'claude-sonnet-4': 5, // 中端模型 = 5额度
'claude-opus-4': 30, // 高成本模型 = 30额度
};
// 动态扣除额度
const credits = MODEL_CREDITS[model];
await autumn.check({
customerId,
featureId: 'ai_usage', // 所有模型共用同一个功能ID
requiredBalance: credits, // 按模型动态变化
sendEvent: true,
});错误场景退款:调用来退还对应额度。
track({ featureId: 'ai_usage', value: -credits })限制高成本模型使用:将其从中移除即可,未知模型会导致返回,进而返回400错误。
MODEL_CREDITSgetModelCredits()undefinedPlan Structure
套餐结构
Groups
分组
Plans in the same are mutually exclusive. Subscribing to a new plan in the same group replaces the old one. Autumn handles the Stripe subscription swap automatically.
group- Upgrade (free → pro): Immediate swap with proration.
- Downgrade (pro → free): Scheduled for end of billing cycle.
同一个下的套餐是互斥的,订阅同分组下的新套餐会自动替换旧套餐,Autumn会自动处理Stripe订阅的切换逻辑。
group- 升级(免费版→专业版):立即切换并按使用比例结算
- 降级(专业版→免费版):在当前计费周期结束后生效
Add-ons
附加项
Plans with stack on top of any plan. No group conflict.
addOn: true设置了的套餐可以叠加在任意套餐之上,不会产生分组冲突。
addOn: trueautoEnable
autoEnableautoEnable
autoEnablePlans with are auto-assigned when a customer is created via . Use for free tiers. Only allowed on plans with no .
autoEnable: truecustomers.getOrCreate()price设置了的套餐会在通过创建客户时自动分配,适用于免费套餐,仅可用于无配置的套餐。
autoEnable: truecustomers.getOrCreate()pricePlan items: reset.interval
vs price.interval
reset.intervalprice.interval套餐项:reset.interval
vs price.interval
reset.intervalprice.intervalThe intervals are mutually exclusive, not and themselves. A is one of three variants:
resetpricePlanItemPlanItemWithResetreset.intervalpriceprice.intervalPlanItemWithPriceIntervalprice.intervalresetprice.intervalincludedPlanItemNoResetresettypescript
// Free plan — reset only, no price
// `reset.interval` controls when the 50 included credits refresh
item({ featureId: aiCredits.id, included: 50, reset: { interval: 'month' } })
// Paid plan — price.interval handles both billing AND reset
// The 2000 included credits reset monthly via `price.interval: 'month'`
// Overage beyond 2000 billed at $1/100 credits
item({
featureId: aiCredits.id,
included: 2000,
price: { amount: 1, billingUnits: 100, billingMethod: 'usage_based', interval: 'month' },
})Key insight: For paid plans, + implies monthly reset. The field's Zod description: "Balance resets to this each interval for consumable features." You do NOT need a separate field on paid plan items.
includedprice.intervalincludedreset两个interval参数是互斥的,不是和本身互斥。分为三种类型:
resetpricePlanItemPlanItemWithResetreset.intervalpricepriceintervalPlanItemWithPriceIntervalprice.intervalresetprice.intervalincludedPlanItemNoResetresettypescript
// 免费套餐 — 仅配置reset,无价格
// `reset.interval`控制50个赠送额度每月刷新一次
item({ featureId: aiCredits.id, included: 50, reset: { interval: 'month' } })
// 付费套餐 — price.interval同时处理计费和额度重置
// 2000个赠送额度通过`price.interval: 'month'`每月重置
// 超出2000的部分按每100额度1美元计费
item({
featureId: aiCredits.id,
included: 2000,
price: { amount: 1, billingUnits: 100, billingMethod: 'usage_based', interval: 'month' },
})核心要点:付费套餐中 + 就代表每月重置额度,字段的Zod描述为:"可消耗功能的额度每个周期重置为该值",不需要在付费套餐项中额外配置字段。
includedprice.intervalincludedresetSDK: autumn-js
autumn-jsSDK: autumn-js
autumn-jsInitialization
初始化
typescript
import { Autumn } from 'autumn-js';
const autumn = new Autumn({ secretKey: env.AUTUMN_SECRET_KEY });Stateless—safe to create per-request. No connection pooling needed.
typescript
import { Autumn } from 'autumn-js';
const autumn = new Autumn({ secretKey: env.AUTUMN_SECRET_KEY });无状态,每个请求创建实例也安全,不需要连接池。
Customer Sync (MUST be blocking)
客户同步(必须阻塞执行)
typescript
await autumn.customers.getOrCreate({
customerId: userId,
name: userName ?? undefined,
email: userEmail ?? undefined,
});This call MUST be awaited (blocking). Autumn's endpoint does not auto-create customers. The customer must exist before any call.
/checkcheck()typescript
await autumn.customers.getOrCreate({
customerId: userId,
name: userName ?? undefined,
email: userEmail ?? undefined,
});该调用必须使用await阻塞执行,Autumn的接口不会自动创建客户,任何调用前必须确保客户已存在。
/checkcheck()Credit Check
额度校验
typescript
const credits = getModelCredits(data.model);
const { allowed, balance } = await autumn.check({
customerId: userId,
featureId: 'ai_usage',
requiredBalance: credits,
sendEvent: true,
properties: { model, provider },
});
if (!allowed) {
// Return 402 with balance info
}featureId is always 'ai_usage'. The credit cost varies per model via the dynamic requiredBalance.
typescript
const credits = getModelCredits(data.model);
const { allowed, balance } = await autumn.check({
customerId: userId,
featureId: 'ai_usage',
requiredBalance: credits,
sendEvent: true,
properties: { model, provider },
});
if (!allowed) {
// 返回402状态码和额度信息
}featureId固定为'ai_usage',通过动态的requiredBalance实现按模型差异化扣减额度。
Refund on Error
错误场景退款
typescript
await autumn.track({
customerId: userId,
featureId: 'ai_usage',
value: -credits, // Negative value = refund
});Use when the operation fails after credits were already deducted (e.g., AI stream errors). Typically pushed to an queue to avoid blocking the error response.
afterResponsetypescript
await autumn.track({
customerId: userId,
featureId: 'ai_usage',
value: -credits, // 负值代表退款
});适用于额度扣除后操作失败的场景(比如AI流式响应出错),通常推送到队列处理,避免阻塞错误响应返回。
afterResponseCLI: atmn
atmnCLI: atmn
atmnSetup
环境配置
bash
bunx atmn login # OAuth login, saves keys to .env
bunx atmn env # Verify org and environmentbash
bunx atmn login # OAuth登录,将密钥保存到.env
bunx atmn env # 验证组织和环境配置Config File
配置文件
autumn.config.tsatmntypescript
import { feature, item, plan } from 'atmn';项目根目录下的,使用提供的构建器定义功能和套餐:
autumn.config.tsatmntypescript
import { feature, item, plan } from 'atmn';Push/Pull
配置推送/拉取
bash
bunx atmn preview # Dry run — shows what would change
bunx atmn push # Push to sandbox (interactive confirmation)
bunx atmn push --prod # Push to production
bunx atmn push --yes # Auto-confirm (for CI/CD)
bunx atmn pull # Pull remote config, generate SDK typesbash
bunx atmn preview # dry run — 展示会变更的内容
bunx atmn push # 推送到沙箱环境(需要交互确认)
bunx atmn push --prod # 推送到生产环境
bunx atmn push --yes # 自动确认(适用于CI/CD场景)
bunx atmn pull # 拉取远程配置,生成SDK类型定义Data Inspection
数据查看
bash
bunx atmn customers # Browse customers
bunx atmn plans # Browse plans
bunx atmn features # Browse features
bunx atmn events # Browse usage eventsbash
bunx atmn customers # 查看客户列表
bunx atmn plans # 查看套餐列表
bunx atmn features # 查看功能列表
bunx atmn events # 查看用量事件列表Environment & Secrets
环境与密钥
| Key | Environment | Prefix |
|---|---|---|
| Sandbox (test) | |
| Production | |
Use the same key name in both environments. Let your secrets manager (Infisical, etc.) swap the value per environment. Don't create separate key names for sandbox vs prod.
For Cloudflare Workers:
wrangler secret put AUTUMN_SECRET_KEYFor local dev with Infisical: secrets are auto-injected via
infisical run --path=/api -- wrangler dev| 密钥名称 | 环境 | 前缀 |
|---|---|---|
| 沙箱(测试) | |
| 生产 | |
两个环境使用相同的密钥名称,通过密钥管理工具(Infisical等)按环境切换值即可,不要为沙箱和生产创建不同的密钥名称。
Cloudflare Workers配置:
wrangler secret put AUTUMN_SECRET_KEY本地使用Infisical开发:通过自动注入密钥
infisical run --path=/api -- wrangler devMiddleware Pattern (Cloudflare Workers + Hono)
中间件模式(Cloudflare Workers + Hono)
Ensure Customer Exists
确保客户存在
Run after , before any billing-gated routes:
authGuardtypescript
app.use('/ai/*', async (c, next) => {
const autumn = createAutumn(c.env);
await autumn.customers.getOrCreate({
customerId: c.var.user.id,
name: c.var.user.name ?? undefined,
email: c.var.user.email ?? undefined,
});
await next();
});Why inline? Cloudflare Workers don't expose at module scope. The Autumn client must be created inside the request handler.
env在之后、所有计费管控路由之前执行:
authGuardtypescript
app.use('/ai/*', async (c, next) => {
const autumn = createAutumn(c.env);
await autumn.customers.getOrCreate({
customerId: c.var.user.id,
name: c.var.user.name ?? undefined,
email: c.var.user.email ?? undefined,
});
await next();
});为什么要内联创建? Cloudflare Workers在模块作用域无法获取,必须在请求处理函数内部创建Autumn客户端。
envCredit Gate in Handler
处理函数中的额度管控
typescript
const credits = getModelCredits(data.model);
if (!credits) return c.json(error, 400);
const { allowed, balance } = await autumn.check({
customerId: c.var.user.id,
featureId: 'ai_usage',
requiredBalance: credits,
sendEvent: true,
});
if (!allowed) return c.json(error, 402);typescript
const credits = getModelCredits(data.model);
if (!credits) return c.json(error, 400);
const { allowed, balance } = await autumn.check({
customerId: c.var.user.id,
featureId: 'ai_usage',
requiredBalance: credits,
sendEvent: true,
});
if (!allowed) return c.json(error, 402);Stripe Integration
Stripe集成
- Sandbox: Built-in Stripe test account. No setup needed.
- Production: Connect via Dashboard → Integrations → Stripe (OAuth recommended).
- Autumn creates Stripe products/prices automatically when you .
atmn push - Autumn is the source of truth for customer state; Stripe handles payments.
- 沙箱环境:内置Stripe测试账号,无需额外配置
- 生产环境:通过控制台→集成→Stripe连接(推荐使用OAuth)
- 执行时Autumn会自动创建Stripe商品/价格
atmn push - Autumn是客户状态的唯一可信源,Stripe负责处理支付逻辑
Common Gotchas
常见注意事项
- must be awaited — Fire-and-forget will cause
getOrCreateto fail with "customer not found."check() - in
featureIdis always 'ai_usage' — The credit cost varies per model via dynamiccheck(), not featureId.requiredBalance - and
reset.intervalare mutually exclusive — notprice.intervalandresetthemselves. ApriceCAN have aPlanItemWithReset, but that price cannot have anprice. For paid plans,intervalhandles both billing and balance reset.price.interval - deducts atomically — Don't call
sendEvent: trueseparately for the happy path. Only usetrack()for refunds.track({ value: -1 }) - All IDs are snake_case — Autumn's pricing agent convention. Don't use kebab-case.
- triggers on customer creation — Not on first
autoEnable. Ensure the middleware callscheck()before checking.getOrCreate - Multiple keys per environment — Autumn supports multiple active secret keys for rotation. Generate new key → update secrets → revoke old key.
- Use proportional billing — One metered feature () with
ai_usageand dynamiccreditCost: 1per model. Per-model costs live in model-costs.ts, not autumn.config.ts. This avoids cluttering the dashboard with dozens of features.requiredBalance
- 必须使用await阻塞:如果不等待执行完成,
getOrCreate会报"客户不存在"错误check() - 中的
check()固定为'ai_usage':通过动态featureId实现按模型差异化扣费,不是通过featureId区分requiredBalance - 和
reset.interval互斥:不是price.interval和reset本身互斥,price可以有PlanItemWithReset,但该price不能有price,付费套餐通过interval同时处理计费和额度重置price.interval - 会原子性扣减额度:正常流程不需要单独调用
sendEvent: true,只有退款场景使用track()track({ value: -1 }) - 所有ID使用snake_case命名:这是Autumn定价Agent的规范,不要使用kebab-case
- 在客户创建时触发:不是在第一次
autoEnable时触发,确保中间件在校验前调用check()getOrCreate - 单环境支持多密钥:Autumn支持多个活跃密钥用于轮换,生成新密钥→更新密钥配置→撤销旧密钥即可
- 使用比例计费模式:创建单个计量功能()设置
ai_usage,按模型动态设置creditCost: 1,模型成本存放在model-costs.ts中,不是autumn.config.ts,避免控制台出现大量冗余功能requiredBalance
Project Files
项目文件
| File | Purpose |
|---|---|
| Feature, credit system, and plan definitions |
| |
| Model string → proportional credit cost mapping |
| Credit check + refund logic for AI chat handler |
| Middleware wiring (ensureAutumnCustomer) |
| 文件路径 | 用途 |
|---|---|
| 功能、额度体系、套餐定义 |
| 按请求创建SDK客户端的 |
| 模型名称→比例计费额度映射表 |
| AI聊天处理函数的额度校验+退款逻辑 |
| 中间件配置(ensureAutumnCustomer) |