Loading...
Loading...
Integrate Autumn billing—define features/plans in autumn.config.ts, use autumn-js SDK for credit checks/tracking, manage the atmn CLI for push/pull. Use when working on billing, pricing, credits, plan gating, or metered usage.
npx skill4agent add epicenterhq/epicenter autumnautumn-jsatmnautumn.config.tsautumn-jsatmnsnake_case// 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', ... })| 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 |
meteredconsumable: truecreditCostexport 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 },
],
});creditCostcreditCost: 1requiredBalancecheck()sendEvent: truerequiredBalancecreditCost: 1requiredBalance: 5// 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,
});track({ featureId: 'ai_usage', value: -credits })MODEL_CREDITSgetModelCredits()undefinedgroupaddOn: trueautoEnableautoEnable: truecustomers.getOrCreate()pricereset.intervalprice.intervalresetpricePlanItemPlanItemWithResetreset.intervalpriceprice.intervalPlanItemWithPriceIntervalprice.intervalresetprice.intervalincludedPlanItemNoResetreset// 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' },
})includedprice.intervalincludedresetautumn-jsimport { Autumn } from 'autumn-js';
const autumn = new Autumn({ secretKey: env.AUTUMN_SECRET_KEY });await autumn.customers.getOrCreate({
customerId: userId,
name: userName ?? undefined,
email: userEmail ?? undefined,
});/checkcheck()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
}await autumn.track({
customerId: userId,
featureId: 'ai_usage',
value: -credits, // Negative value = refund
});afterResponseatmnbunx atmn login # OAuth login, saves keys to .env
bunx atmn env # Verify org and environmentautumn.config.tsatmnimport { feature, item, plan } from 'atmn';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 typesbunx atmn customers # Browse customers
bunx atmn plans # Browse plans
bunx atmn features # Browse features
bunx atmn events # Browse usage events| Key | Environment | Prefix |
|---|---|---|
| Sandbox (test) | |
| Production | |
wrangler secret put AUTUMN_SECRET_KEYinfisical run --path=/api -- wrangler devauthGuardapp.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();
});envconst 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);atmn pushgetOrCreatecheck()featureIdcheck()requiredBalancereset.intervalprice.intervalresetpricePlanItemWithResetpriceintervalprice.intervalsendEvent: truetrack()track({ value: -1 })autoEnablecheck()getOrCreateai_usagecreditCost: 1requiredBalance| 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) |