docs more
This commit is contained in:
@@ -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"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
287
apps/docs/mintlify/documentation/customers/balance-locking.mdx
Normal file
287
apps/docs/mintlify/documentation/customers/balance-locking.mdx
Normal 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
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
55
apps/docs/mintlify/documentation/customers/custom-plans.mdx
Normal file
55
apps/docs/mintlify/documentation/customers/custom-plans.mdx
Normal 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.
|
||||
@@ -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**
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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 |
|
||||
|
||||
Reference in New Issue
Block a user