Billing
Set up and manage billing with Autumn for per-seat pricing
Billing is already integrated with Autumn for per-seat pricing. Organizations are automatically charged based on the number of members on the Pro plan.
Billing is organization-scoped. Each organization has its own subscription and is billed per user. Autumn can be removed if you don't need billing.
Overview
Unstack Pro uses Autumn for billing:
- Per-seat pricing: Charge per organization member
- Automatic billing: Adding members adjusts billing automatically
- Organization-scoped: Each organization has its own subscription
- Self-service: Users manage billing through Autumn's portal
How It Works
Billing Flow
- User creates an organization
- Organization starts on Free tier (no members allowed)
- Owner upgrades to Pro plan via Autumn
- Pro plan enables adding organization members
- Each member added increases the subscription cost
- Billing is handled automatically by Autumn
Plans
| Plan | Members | Features |
|---|---|---|
| Free | Owner only | Basic organization features |
| Pro | Unlimited | Full member management, teams, custom roles |
Organizations cannot add members until they are on the Pro plan.
Setting Up Autumn
Create an Autumn Account
- Go to useautumn.com
- Sign up for an account
- Create a new project
Get Your API Key
- In Autumn dashboard, go to Settings → API Keys
- Create a new API key
- Copy the key (starts with
am_sk_)
Configure Environment Variables
AUTUMN_SECRET_KEY is the only variable billing needs. How many places it goes
depends on your edition:
One place. The billing routes and the Better Auth organization hooks both run
inside Next.js, so both read it from .env.local:
AUTUMN_SECRET_KEY="am_sk_your_api_key_here"Two places. The Next.js billing routes read it from .env.local; the auth hooks
run on the Convex deployment and read it there:
bunx convex env set AUTUMN_SECRET_KEY "am_sk_your_api_key_here"AUTUMN_SECRET_KEY="am_sk_your_api_key_here"Setting it in only one of the two breaks billing: the checkout / portal routes 500 in the Next.js runtime, or seat syncing fails in the Convex hooks. Set it in both.
Push Your Products
Plans are defined as code in autumn.config.ts (a free_plan and a
seat-metered pro plan). Push them to your Autumn account with the Autumn CLI:
bunx atmn login # authenticate the Autumn CLI
bunx atmn push # create the products from autumn.config.tsEdit autumn.config.ts to change the plans, price, or billing interval, then
bunx atmn push again.
Managing Subscriptions
Organization Billing Page
Organization owners and admins can manage billing at /organizations/[slug]/billing:
View Subscription:
- Current plan status
- Number of seats/members
- Price per seat
- Next billing date
- Total monthly cost
Manage Subscription:
- Upgrade to Pro
- Access Autumn billing portal
- View order history
- Download invoices
Autumn Customer Portal
Users are redirected to Autumn's hosted portal for:
- Updating payment method
- Viewing invoices
- Managing subscription
- Canceling subscription
// Example: Redirect to billing portal
const handleManageBilling = async () => {
const portalUrl = await getBillingPortalUrl(organizationId);
window.open(portalUrl, "_blank");
};Per-Seat Billing Logic
Adding Members
When a member is added to an organization:
- System checks organization has Pro plan
- If on Pro, member is added
- Autumn automatically updates seat count
- Next invoice reflects new seat
Removing Members
When a member is removed:
- Member is removed from organization
- Seat count decreases
- Prorated credit applied (depending on Autumn settings)
Example Pricing
| Members | Monthly Cost (at $10/seat) |
|---|---|
| 1 (owner) | $10 |
| 5 | $50 |
| 10 | $100 |
| 50 | $500 |
How It's Wired Up
Billing is integrated through Better Auth organization hooks, not a webhook endpoint. The key pieces:
autumn.config.ts— thefree_plan/proproduct definitions, pushed withbunx atmn push.app/api/autumn/[...all]/route.ts— the Next.js billing route (checkout, portal); it readsAUTUMN_SECRET_KEYat runtime in the Node runtime and is gated to org owners/admins.
The organizationHooks do the same work either way:
afterCreateOrganizationinitialises the Autumn customer for the org.afterAcceptInvitation/afterRemoveMembersync the seat count.beforeCreateInvitation/beforeAddMember/beforeAcceptInvitationgate on a paid plan and throwAPIError("BAD_REQUEST", …)if the org lacks one.
What differs is which file carries them, and which Autumn client they call:
lib/auth.ts— the Better Auth config,organizationHooksincluded.lib/autumn.ts— theautumn-jsSDK, imported directly by those hooks.
convex/betterAuth/auth.ts— the Better Auth config,organizationHooksincluded.lib/autumn-server.ts— an SDK-free REST client the hooks import from the Convex runtime (theautumn-jsSDK is too large for Convex's 64 MB isolate).lib/autumn.ts— the fullautumn-jsSDK, usable from Node server pages.
Subscriptions are organization-scoped and billing is keyed per URL organization
via the x-organization-slug header. If you add a flow that grows seat count or
invites members, hook it into organizationHooks in the auth config file listed
above for your edition.
Billing Route
Organization owners and admins manage billing at
/organizations/[organizationSlug]/billing (plan status, seats, upgrade, and the
Autumn portal link).
Removing Billing
If you don't need billing:
Remove Environment Variable
Remove AUTUMN_SECRET_KEY from .env.local.
Remove AUTUMN_SECRET_KEY from .env.local and from the Convex deployment:
bunx convex env remove AUTUMN_SECRET_KEYUpdate Organization Logic
In the auth config file for your edition, delete the plan gate from the
beforeCreateInvitation, beforeAddMember and beforeAcceptInvitation
organization hooks, and drop the afterCreateOrganization,
afterAcceptInvitation and afterRemoveMember calls into the Autumn client.
Members can then be added regardless of plan.
Remove Billing UI Components
Remove billing-related components and references from:
- Organization sidebar
- Organization settings
- Dashboard cards
Troubleshooting
Subscription Not Updating
Check:
AUTUMN_SECRET_KEYis correct, and set everywhere your edition needs itbunx atmn pushhas run, sofree_planandproexist in your Autumn account- The organization id matches the Autumn customer id — the org id is the customer id
Payment Failed
User should:
- Access billing portal
- Update payment method
- Retry payment
System should:
- Send notification to organization owner
- Gracefully handle expired subscriptions
- Provide clear upgrade path
Can't Add Members
Verify:
- Organization is on Pro plan
- Subscription is active (not canceled)
- No payment failures
Best Practices
- Clear pricing: Show per-seat cost clearly
- Upgrade prompts: Guide users to upgrade when needed
- Graceful degradation: Handle expired subscriptions gracefully
- Email notifications: Notify users of billing events
- Audit trail: Log billing-related actions
- Test in sandbox: Use Autumn's test mode before production