docs more

This commit is contained in:
Ayush Rodrigues
2026-03-12 16:59:18 +00:00
parent ad1ca1653d
commit 8758ddf7b5
9 changed files with 517 additions and 288 deletions

View File

@@ -90,17 +90,25 @@
]
},
{
"group": "Manage Customers",
"group": "Billing & Subscriptions",
"pages": [
"documentation/customers/payment-flow",
"documentation/customers/subscription-lifecycle",
"documentation/customers/managing-balances",
"documentation/customers/check",
"documentation/customers/tracking-usage",
"documentation/customers/feature-entities",
"documentation/customers/updating-subscriptions",
"documentation/customers/custom-plans",
"documentation/customers/versioning"
]
},
{
"group": "Customers",
"pages": [
"documentation/customers/creating-customers",
"documentation/customers/managing-customers",
"documentation/customers/versioning"
"documentation/customers/check",
"documentation/customers/tracking-usage",
"documentation/customers/balance-locking",
"documentation/customers/managing-balances",
"documentation/customers/feature-entities"
]
},
{

View File

@@ -1,223 +0,0 @@
---
title: "Attaching Plans"
description: "How to attach plans to customers and handle upgrades and downgrades"
---
Attaching a plan to a customer will:
- Grant the customer access to the features in the plan
- If the plan has prices, generate a checkout URL for payment
## Basic Usage
Call `billing.attach` to attach a plan to a customer. This always returns a `paymentUrl` that the customer should be redirected to.
<CodeGroup>
```typescript TypeScript
import { Autumn } from "autumn-js";
const autumn = new Autumn({
secretKey: "am_sk_..."
});
const response = await autumn.billing.attach({
customerId: "user_123",
planId: "pro",
});
// Redirect customer to complete payment or confirm plan change
redirect(response.paymentUrl);
```
```tsx React
import { useCustomer } from "autumn-js/react";
const { attach } = useCustomer();
// Hook automatically redirects to checkout
await attach({ planId: "pro" });
```
```python Python
import asyncio
from autumn_sdk import Autumn
autumn = Autumn("am_sk_...")
async def main():
response = await autumn.billing.attach(
customer_id="user_123",
plan_id="pro",
)
# Redirect customer to response.payment_url
asyncio.run(main())
```
```bash cURL
curl -X POST 'https://api.useautumn.com/v1/attach' \
-H 'Authorization: Bearer am_sk_...' \
-H 'Content-Type: application/json' \
-d '{
"customer_id": "user_123",
"plan_id": "pro"
}'
```
</CodeGroup>
<Note>
**Default behavior:**
- **New subscriptions:** `paymentUrl` points to **Stripe Checkout** for payment collection
- **Plan changes (upgrades/downgrades):** `paymentUrl` points to **Autumn Checkout** where the customer can review prorations before confirming
</Note>
This handles any plan change scenario: new subscriptions, upgrades, downgrades, add-ons, and renewals.
- **Upgrades** happen immediately with prorated charges
- **Downgrades** are scheduled for the end of the current billing cycle
## Building Your Own Checkout UI
Autumn's checkout page is designed to get you off the ground quickly, but often you'll want full control over the styling and user experience. For plan changes, you can build your own checkout UI using a two-step flow:
### Step 1: Preview the change
Call `billing.previewAttach` to see exactly what will be charged before making any changes. This returns line items, totals, and next cycle information that you can display in your own UI.
<CodeGroup>
```typescript TypeScript
const preview = await autumn.billing.previewAttach({
customerId: "user_123",
planId: "pro",
});
// Display to user:
// - preview.lineItems (array of charges/credits)
// - preview.total (amount in cents)
// - preview.currency (e.g., "usd")
// - preview.nextCycle (next billing cycle info)
```
```tsx React
import { useCustomer } from "autumn-js/react";
const { previewAttach } = useCustomer();
const preview = await previewAttach({ planId: "pro" });
// Use preview data to render your custom checkout UI
```
```python Python
preview = await autumn.billing.preview_attach(
customer_id="user_123",
plan_id="pro",
)
# preview.line_items, preview.total, preview.currency
```
```bash cURL
curl -X POST 'https://api.useautumn.com/v1/billing/preview-attach' \
-H 'Authorization: Bearer am_sk_...' \
-H 'Content-Type: application/json' \
-d '{
"customer_id": "user_123",
"plan_id": "pro"
}'
```
</CodeGroup>
<Expandable title="Example response">
```json
{
"customerId": "user_123",
"lineItems": [
{
"title": "Pro Plan",
"description": "Monthly subscription",
"amount": 20
},
{
"title": "Credit for Free Plan",
"description": "Unused time on current plan",
"amount": -5
}
],
"total": 15,
"currency": "usd",
"nextCycle": {
"startsAt": 1735689600000,
"total": 20
}
}
```
</Expandable>
### Step 2: Execute the change
Once the customer confirms, call `billing.attach` with `redirectMode: "if_required"`. This will:
- **Charge the customer automatically** if they have a payment method on file
- **Return a Stripe Checkout URL** only if no payment method exists (for new customers)
<CodeGroup>
```typescript TypeScript
const response = await autumn.billing.attach({
customerId: "user_123",
planId: "pro",
redirectMode: "if_required",
});
if (response.paymentUrl) {
// No payment method on file — redirect to Stripe Checkout
redirect(response.paymentUrl);
} else {
// Plan attached and charged successfully
showSuccessMessage();
}
```
```tsx React
const { attach } = useCustomer();
const response = await attach({
planId: "pro",
redirectMode: "if_required",
});
// If no redirect happened, the plan was attached successfully
```
```python Python
response = await autumn.billing.attach(
customer_id="user_123",
plan_id="pro",
redirect_mode="if_required",
)
if response.payment_url:
# Redirect to Stripe Checkout
pass
else:
# Plan attached successfully
pass
```
```bash cURL
curl -X POST 'https://api.useautumn.com/v1/attach' \
-H 'Authorization: Bearer am_sk_...' \
-H 'Content-Type: application/json' \
-d '{
"customer_id": "user_123",
"plan_id": "pro",
"redirect_mode": "if_required"
}'
```
</CodeGroup>

View File

@@ -0,0 +1,287 @@
---
title: "Balance Locking"
description: "Reserve balance upfront with locks, then confirm or release when the operation completes"
---
For operations where you don't know the final cost upfront — like AI completions, batch processing, or long-running jobs — you can **reserve** balance before the work starts, then **finalize** the reservation when it's done.
This is a three-step flow:
1. **Check with lock** — atomically check access and hold balance
2. **Do work** — run your operation
3. **Finalize** — confirm the deduction, adjust it, or release the hold
```mermaid
sequenceDiagram
participant App
participant Autumn
App->>Autumn: check (lock: { enabled: true, lock_id })
Autumn-->>App: allowed: true (balance held)
App->>App: Do work (e.g. AI completion)
App->>Autumn: balances.finalize (lock_id, action)
Autumn-->>App: success: true
```
## Step 1: Check with lock
Pass the `lock` parameter to the check endpoint. This atomically checks if the customer has enough balance and reserves it in a single call.
<CodeGroup>
```typescript TypeScript
const response = await autumn.check({
customerId: "user_123",
featureId: "ai-tokens",
requiredBalance: 1000,
sendEvent: true,
lock: {
enabled: true,
lockId: "completion_abc123",
expiresAt: Date.now() + 5 * 60 * 1000, // 5 minutes
},
});
if (!response.allowed) {
// Customer doesn't have enough balance
}
// Balance is now held — proceed with the operation
```
```python Python
response = await autumn.check(
customer_id="user_123",
feature_id="ai-tokens",
required_balance=1000,
send_event=True,
lock={
"enabled": True,
"lock_id": "completion_abc123",
"expires_at": int(time.time() * 1000) + 5 * 60 * 1000,
},
)
if not response.allowed:
# Customer doesn't have enough balance
pass
# Balance is now held — proceed with the operation
```
```bash cURL
curl -X POST "https://api.useautumn.com/v1/check" \
-H "Authorization: Bearer am_sk_..." \
-H "Content-Type: application/json" \
-d '{
"customer_id": "user_123",
"feature_id": "ai-tokens",
"required_balance": 1000,
"send_event": true,
"lock": {
"enabled": true,
"lock_id": "completion_abc123",
"expires_at": 1735689600000
}
}'
```
</CodeGroup>
### Lock parameters
| Parameter | Type | Description |
|---|---|---|
| `enabled` | `boolean` | Must be `true` to enable locking |
| `lock_id` | `string` | A unique identifier for this lock. You'll use this to finalize later. If omitted, Autumn generates one. |
| `expires_at` | `number` | Unix timestamp (ms) when the lock auto-expires and releases the held balance. Max 24 hours from now. |
<Warning>
Always set an `expires_at` to prevent balance from being held indefinitely if your finalize call fails. If a lock expires, the held balance is automatically released back to the customer.
</Warning>
## Step 2: Do your work
Run whatever operation you reserved balance for. The held balance is guaranteed to be available — no other concurrent request can consume it.
## Step 3: Finalize the lock
When the operation completes, call `balances.finalize` to resolve the held balance.
### Confirm the full amount
If the operation used exactly the amount you reserved, confirm the lock:
<CodeGroup>
```typescript TypeScript
await autumn.balances.finalize({
lockId: "completion_abc123",
action: "confirm",
});
```
```python Python
await autumn.balances.finalize(
lock_id="completion_abc123",
action="confirm",
)
```
```bash cURL
curl -X POST "https://api.useautumn.com/v1/balances.finalize" \
-H "Authorization: Bearer am_sk_..." \
-H "Content-Type: application/json" \
-d '{
"lock_id": "completion_abc123",
"action": "confirm"
}'
```
</CodeGroup>
### Release the hold
If the operation failed or was canceled, release the lock to return the held balance:
<CodeGroup>
```typescript TypeScript
await autumn.balances.finalize({
lockId: "completion_abc123",
action: "release",
});
```
```python Python
await autumn.balances.finalize(
lock_id="completion_abc123",
action="release",
)
```
```bash cURL
curl -X POST "https://api.useautumn.com/v1/balances.finalize" \
-H "Authorization: Bearer am_sk_..." \
-H "Content-Type: application/json" \
-d '{
"lock_id": "completion_abc123",
"action": "release"
}'
```
</CodeGroup>
### Adjust the final amount
If the actual usage differs from the reserved amount (common with AI tokens), pass `overrideValue` to adjust:
<CodeGroup>
```typescript TypeScript
// Reserved 1000 tokens, but only used 743
await autumn.balances.finalize({
lockId: "completion_abc123",
action: "confirm",
overrideValue: 743,
});
```
```python Python
# Reserved 1000 tokens, but only used 743
await autumn.balances.finalize(
lock_id="completion_abc123",
action="confirm",
override_value=743,
)
```
```bash cURL
curl -X POST "https://api.useautumn.com/v1/balances.finalize" \
-H "Authorization: Bearer am_sk_..." \
-H "Content-Type: application/json" \
-d '{
"lock_id": "completion_abc123",
"action": "confirm",
"override_value": 743
}'
```
</CodeGroup>
Autumn will reconcile the difference — returning the unused 257 tokens back to the customer's balance.
## Use cases
### AI completions
Reserve a token budget before starting generation, then finalize with the actual token count:
```typescript
const lockId = `completion_${generateId()}`;
const { allowed } = await autumn.check({
customerId: "user_123",
featureId: "ai-tokens",
requiredBalance: 4000, // max_tokens
sendEvent: true,
lock: {
enabled: true,
lockId,
expiresAt: Date.now() + 60_000,
},
});
if (!allowed) return showUpgradePrompt();
const completion = await openai.chat.completions.create({
model: "gpt-4",
max_tokens: 4000,
messages: [{ role: "user", content: prompt }],
});
await autumn.balances.finalize({
lockId,
action: "confirm",
overrideValue: completion.usage.total_tokens,
});
```
### Long-running jobs
Reserve credits before queuing a job, release if the job fails:
```typescript
const lockId = `job_${jobId}`;
const { allowed } = await autumn.check({
customerId: "user_123",
featureId: "compute-credits",
requiredBalance: 10,
sendEvent: true,
lock: {
enabled: true,
lockId,
expiresAt: Date.now() + 30 * 60_000, // 30 min timeout
},
});
if (!allowed) throw new Error("Insufficient credits");
try {
await runJob(jobId);
await autumn.balances.finalize({ lockId, action: "confirm" });
} catch (error) {
await autumn.balances.finalize({ lockId, action: "release" });
throw error;
}
```
## Compared to check + track
For simple operations where you know the cost upfront and the operation is fast, [check with `sendEvent`](/documentation/customers/check#checking-and-reserving-usage) is simpler — it deducts immediately in one call.
Use reservations when:
- The final usage amount is unknown at check time (e.g., AI token counts)
- The operation can fail after balance is deducted
- The operation takes significant time and you don't want another request to consume the same balance

View File

@@ -158,7 +158,7 @@ curl -X POST "https://api.useautumn.com/v1/check" \
</CodeGroup>
## Checking and reserving usage
## Checking and tracking atomically
You can check access and record usage in a single, atomic API call using the `sendEvent` field. The usage value deducted from the balance will be the `requiredBalance` parameter. This is useful for concurrent events.

View File

@@ -1,20 +1,19 @@
---
title: "Creating Customers"
description: "Learn about customers in Autumn and how to create them"
description: "Create customers via API or dashboard, link to Stripe, and pre-create for enterprise deals"
---
Customers represent the entities, usually users or organizations, of your application that can use and pay for your products.
Customers represent the entities usually users or organizations of your application that can use and pay for your products.
For each customer, Autumn will:
- Keep record of the products they've purchased
- Track the features they've used
- Track the features they have access to
- Track the features they've used and have access to
- Bill them through Stripe for the prices you've set
## Creating a customer via API
You can create a customer with the `customers.getOrCreate` method. This will create the customer if they don't exist, or return the existing customer if they do.
Use the `customers.getOrCreate` method to create a customer. This is idempotent — it creates the customer if they don't exist, or returns the existing one if they do.
<CodeGroup>
@@ -55,40 +54,42 @@ curl -X POST "https://api.useautumn.com/v1/customers" \
</CodeGroup>
Only the `customerId` field is required - this should be your unique identifier for the customer that you'll use to reference them in future API calls.
Only the `customerId` field is required this should be your unique identifier for the customer that you'll use in all future API calls.
<Note>
An Autumn customer does not map to a Stripe customer by default. A Stripe
customer will only be created when an `attach` request is made.
</Note>
<Tip>
A common pattern is to call `customers.getOrCreate` on every login or signup in your application, so Autumn always has the latest customer information.
</Tip>
## Creating a customer via the Autumn dashboard
## Pre-creating customers via the dashboard
You can also create a customer via the Autumn dashboard. This is useful if you want to "pre-create" a customer for a user who hasn't interacted with your application yet.
You can create a customer in the Autumn dashboard before they've ever interacted with your application. This is useful for enterprise or sales-led deals where you want to provision access before the customer signs up.
1. Navigate to the [Customers page](https://app.useautumn.com/customers)
2. Click the "Create Customer" button
3. Fill in the customer's details, such as name and email. <br/>
**Leave the `id` field blank**, as this will come from your application.
2. Click "Create Customer"
3. Fill in the customer's details (name, email). **Leave the `id` field blank** — it will be assigned when the customer first logs in.
4. Click "Create Customer"
When the customer logs in for the first time, Autumn will match the user's `email` to the customer email you provided above.
Once the customer is created, you can enable products and configure their features from the customer details page. When the customer eventually signs up in your application, Autumn will match them by email and link the pre-created customer record.
<Warning>
Make sure the email you provide is the same email that the customer will use
to log in to your application. You can update this from the customer details
page.
The email you provide must match the email the customer will use to sign up or log in. You can update the email from the customer details page if needed.
</Warning>
You can enable a product and set their properties in advance, so that when they do interact with your application, they're already have the right features available.
<Info>
**Example: Enterprise onboarding**
This is especially useful for larger or enterprise deals where payment happens separately via invoice.
You've closed an enterprise deal with Acme Corp. Before their team starts using your product:
## Customer Properties
1. Create a customer in the dashboard with the billing contact's email
2. Enable a custom Enterprise plan with negotiated pricing
3. When the Acme team signs up, Autumn matches the email and they immediately have their plan active — no checkout needed
</Info>
## Customer properties
#### Customer ID
This is your unique identifier for the customer. You'll use this ID in all future API calls to reference this customer. It could be:
Your unique identifier for the customer. This is the only required field. It could be:
- Your database ID for the user
- Their email address
@@ -96,19 +97,27 @@ This is your unique identifier for the customer. You'll use this ID in all futur
#### Name and Email
Optional fields to help identify the customer in the Autumn dashboard and on invoices.
Optional fields that help identify the customer in the Autumn dashboard and on Stripe invoices.
## Stripe Integration
## Stripe integration
By default, Autumn does not create a Stripe customer when you create an Autumn customer. A Stripe customer is only created on the first billing call (like `billing.attach` or `billing.openCustomerPortal`).
By default, Autumn does **not** create a Stripe customer when you create an Autumn customer. A Stripe customer is created lazily — only when the first billing operation needs one (like `billing.attach`, `billing.openCustomerPortal`, or `billing.setupPayment`).
You can change this behavior with the following options:
```mermaid
flowchart LR
A["Your app<br/><code>user_123</code>"] -->|customers.getOrCreate| B["Autumn Customer<br/><code>user_123</code>"]
B -->|on first billing call| C["Stripe Customer<br/><code>cus_abc123</code>"]
```
You can change this behavior:
#### Create in Stripe immediately
Pass `createInStripe: true` to create the Stripe customer at the same time as the Autumn customer:
Pass `createInStripe: true` to create the Stripe customer at the same time as the Autumn customer. This is useful if you need the Stripe customer ID upfront (e.g., for your own Stripe integration).
```typescript
<CodeGroup>
```typescript TypeScript
await autumn.customers.getOrCreate({
customerId: "user_123",
name: "John Doe",
@@ -117,13 +126,63 @@ await autumn.customers.getOrCreate({
});
```
```python Python
await autumn.customers.get_or_create(
customer_id="user_123",
name="John Doe",
email="john@example.com",
create_in_stripe=True,
)
```
```bash cURL
curl -X POST "https://api.useautumn.com/v1/customers" \
-H "Authorization: Bearer am_sk_test_1234" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "user_123",
"name": "John Doe",
"email": "john@example.com",
"create_in_stripe": true
}'
```
</CodeGroup>
#### Link to an existing Stripe customer
If you already have a Stripe customer, pass `stripeId` to link it to the Autumn customer:
If you already have a Stripe customer (e.g., you're migrating to Autumn), pass `stripeId` to link it instead of creating a new one:
```typescript
<CodeGroup>
```typescript TypeScript
await autumn.customers.getOrCreate({
customerId: "user_123",
stripeId: "cus_abc123", // Your existing Stripe customer ID
stripeId: "cus_abc123",
});
```
```python Python
await autumn.customers.get_or_create(
customer_id="user_123",
stripe_id="cus_abc123",
)
```
```bash cURL
curl -X POST "https://api.useautumn.com/v1/customers" \
-H "Authorization: Bearer am_sk_test_1234" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "user_123",
"stripe_id": "cus_abc123"
}'
```
</CodeGroup>
Once linked, the mapping is bidirectional — the Stripe customer ID is stored on the Autumn customer, and the Autumn customer ID is stored in the Stripe customer's metadata.
<Note>
For more details on how Autumn and Stripe work together, see [Stripe Sync](/documentation/concepts/stripe).
</Note>

View File

@@ -0,0 +1,55 @@
---
title: "Custom Plans"
description: "Create one-off plan variations for individual customers"
---
Sometimes a customer needs a plan that doesn't match any of your standard offerings — extra credits, different pricing, or access to features not in their current tier. Custom plans let you create a one-off variation of any product for a specific customer, without changing the product for everyone else.
## When to use custom plans
Custom plans are useful when:
- An enterprise customer negotiates a different price or feature set
- You want to give a customer extra allowance as a one-time accommodation
- A customer needs access to a feature that isn't in their current plan
- You're closing a deal that doesn't fit neatly into your standard tiers
## Creating a custom plan via the dashboard
1. Navigate to the [Customer details page](https://app.useautumn.com/customers) for the customer
2. Click "Enable Product" (or click the existing product to modify it)
3. Make changes to the product items before enabling:
- Adjust feature allowances (e.g., increase included credits from 100 to 500)
- Add or remove features
- Change pricing (fixed or usage-based)
4. Click "Enable"
This creates a custom version of the product — only this customer will have it. The original product remains unchanged for all other customers.
## What happens when you enable a custom plan
The behavior depends on what you changed:
| Change type | Behavior |
|---|---|
| **Features only** (allowances, adding/removing features) | Takes effect immediately. New balances are provisioned while keeping existing reset dates. |
| **Pricing changes** (higher price) | Treated as an upgrade — prorated charges apply for the remainder of the billing cycle. |
| **Pricing changes** (lower price) | Treated as a downgrade — scheduled for end of current billing cycle. |
## Custom plans vs editing balances
If you just need a temporary adjustment (e.g., granting a customer extra credits this month), you can [edit their balance directly](/documentation/customers/managing-customers#editing-feature-balances) instead. Balance edits reset at the next billing cycle.
Custom plans are better when you want the change to persist across billing cycles.
<Info>
**Example**
A customer on your Pro plan (100 credits/month, $50/month) needs 250 credits/month at the same price. Create a custom plan with 250 included credits. They'll get 250 credits every month going forward, while all other Pro customers continue getting 100.
</Info>
## Custom plans and versioning
Custom plans are tracked as separate versions of the product. When you [update the base product](/documentation/customers/versioning), customers on custom plans are not affected — they stay on their custom version.
If you want to migrate a customer off a custom plan and onto the latest standard version, you can do so from the [customer details page](https://app.useautumn.com/customers) by re-enabling the standard product.

View File

@@ -3,14 +3,14 @@ title: "Entities"
description: "Learn how to use feature entities to track balances per separate entity, such as a user or a workspace"
---
Feature entities are used to keep track of sub-balances for a feature. For example, you may have a product that allows 500 credits **per user** per month.
Entities are sub accounts of a customer. For example, you may have a product that allows 500 credits **per user** per month.
## Product setup
There are two ways you can handle entities depending on your use case.
1. **Entity product items**: this is the simplest way of handling entities, and keeps everything under 1 subscription.
2. **Attaching a product to an entity**: if an entity can have its own subscription tiers, then you can attach a product directly at the entity level by passing the [entity ID](/api-reference/billing/billingAttach#body-entity-id) into an attach function. Each will be under its own subscription in Stripe with the billing cycles synced.
1. **Entity-level balances**: this is the simplest way of handling entities, and keeps everything under 1 subscription. This is useful when all entities get the same features and limits.
2. **Entity-level subscriptions**: if an entity can have its own subscription tiers (ie basic seats, pro seats), then you can attach a product directly at the entity level by passing the [entity ID](/api-reference/billing/billingAttach#body-entity-id) into an attach function. Each will be under its own subscription in Stripe with the billing cycles synced.
<Info>
**Example**

View File

@@ -12,21 +12,6 @@ The customer details page shows:
- **Invoices**: See billing history, payment status, amounts and hosted invoice pages
- **Events**: View feature usage events that have been tracked for the customer
## Enabling a Product
You can enable a product for a customer either via the API using the `billing.attach` method or via the dashboard.
If you're enabling a product for a customer that already has a product, Autumn will handle the upgrade or downgrade between the two products.
#### Custom Product Versions
When attaching a product via the Dashboard, you can optionally make changes to the product items before enabling it. This will create a custom version of the product.
When enabling a custom product, what happens will depend on the changes you made:
- **Changing features only**: this will take effect immediately, provisioning the new features while keeping the same reset dates
- **Changing pricing**: this will follow Autumn's upgrade and downgrade logic, depending on whether the pricing is higher or lower than the existing product
## Editing Feature Balances
You can directly edit a customer's feature balances via the dashboard, to give them additional allowance or alter how much they'll be charged for their next invoice (typically in case of errors).

View File

@@ -121,6 +121,63 @@ curl -X POST "https://api.useautumn.com/v1/checkout" \
</CodeGroup>
## One-off prices within a subscription
A subscription plan can include both recurring and one-off prices. When it does, Autumn splits them at checkout:
- **Recurring prices** bill every cycle as part of the Stripe subscription
- **One-off prices** are charged once on the first invoice only
This is useful for setup fees, one-time credit grants, or any charge that should happen once when the customer subscribes.
> **Example** <br />
> A Pro plan charges \$20/month plus a one-time \$50 setup fee. The customer's first invoice is \$70, and subsequent invoices are \$20.
<Tabs>
<Tab title="CLI">
Add a non-consumable feature for the setup fee, then include it as a separate one-off item alongside the recurring base price:
```ts autumn.config.ts expandable
import { feature, item, plan } from 'atmn';
export const setupFee = feature({
id: 'setup_fee',
name: 'Setup Fee',
type: 'metered',
consumable: false,
});
export const pro = plan({
id: 'pro',
name: 'Pro',
price: { amount: 20, interval: 'month' },
items: [
item({
featureId: setupFee.id,
price: {
amount: 50,
billingMethod: 'prepaid',
interval: 'one_off',
},
}),
],
});
```
When you attach the plan, you can select a quantity for the setup fee. The \$20/month base price recurs on every invoice. The setup fee item is charged once on the first invoice only.
</Tab>
<Tab title="Dashboard">
1. Create a **boolean** feature for the setup fee (e.g., `setup_fee`)
2. Create a plan with a **recurring** base price (e.g., $20/month)
3. Add the setup fee feature as an item and set its price interval to **One-off**
4. The recurring charge will bill every cycle; the one-off charge applies to the first invoice only
</Tab>
</Tabs>
## Balance stacking
One-off balances stack with existing balances from subscriptions. Autumn uses [deduction order](/documentation/concepts/balances#deduction-order) to ensure shorter-interval balances (e.g., monthly) are used before one-off (lifetime) balances.
@@ -132,3 +189,4 @@ One-off balances stack with existing balances from subscriptions. Autumn uses [d
| Credit top-up | Prepaid price, add-on, no base price |
| Lifetime plan | One-off base price, features with no reset |
| One-time fee | One-off base price, no features |
| Setup fee + subscription | Recurring base price, one-off item price on same plan |