Unstack Pro Docs

Billing

Set up and manage billing with Autumn for per-seat pricing

Billing Integration

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

  1. User creates an organization
  2. Organization starts on Free tier (no members allowed)
  3. Owner upgrades to Pro plan via Autumn
  4. Pro plan enables adding organization members
  5. Each member added increases the subscription cost
  6. Billing is handled automatically by Autumn

Plans

PlanMembersFeatures
FreeOwner onlyBasic organization features
ProUnlimitedFull member management, teams, custom roles

Organizations cannot add members until they are on the Pro plan.

Setting Up Autumn

Create an Autumn Account

  1. Go to useautumn.com
  2. Sign up for an account
  3. Create a new project

Get Your API Key

  1. In Autumn dashboard, go to SettingsAPI Keys
  2. Create a new API key
  3. Copy the key (starts with am_sk_)

Configure Environment Variables

Set AUTUMN_SECRET_KEY in both environments — the Next.js billing routes read it at runtime, and the Convex auth hooks read it too:

# Convex deployment
bunx convex env set AUTUMN_SECRET_KEY "am_sk_your_api_key_here"
.env.local
AUTUMN_SECRET_KEY="am_sk_your_api_key_here"

Setting AUTUMN_SECRET_KEY in only one environment 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.ts

Edit 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:

  1. System checks organization has Pro plan
  2. If on Pro, member is added
  3. Autumn automatically updates seat count
  4. Next invoice reflects new seat

Removing Members

When a member is removed:

  1. Member is removed from organization
  2. Seat count decreases
  3. Prorated credit applied (depending on Autumn settings)

Example Pricing

MembersMonthly 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 — the free_plan / pro product definitions, pushed with bunx atmn push.
  • convex/betterAuth/auth.tsorganizationHooks call into the billing integration:
    • afterCreateOrganization initialises the Autumn customer for the org.
    • afterAcceptInvitation / afterRemoveMember sync the seat count.
    • beforeCreateInvitation / beforeAddMember / beforeAcceptInvitation gate on a paid plan and throw APIError("BAD_REQUEST", …) if the org lacks one.
  • lib/autumn-server.ts — an SDK-free REST client used from the Convex runtime (the autumn-js SDK is too large for Convex's 64 MB isolate).
  • lib/autumn.ts — the full autumn-js SDK, usable from Node server pages.
  • app/api/autumn/[...all]/route.ts — the Next.js billing route (checkout, portal); it reads AUTUMN_SECRET_KEY at runtime in the Node runtime and is gated to org owners/admins.

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 convex/betterAuth/auth.ts.

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 both the Convex deployment and .env.local.

Delete Billing Routes

Remove the billing directory:

app/organizations/[organizationSlug]/billing/

Update Organization Logic

Modify member addition to not check for Pro plan:

// Remove the plan check in add member logic
export async function addMember(organizationId: string, userId: string) {
  // Skip: const canAdd = await canAddMembers(organizationId);
  
  await db.insert("members", {
    organizationId,
    userId,
    role: "member",
    createdAt: Date.now(),
  });
}

Remove Billing UI Components

Remove billing-related components and references from:

  • Organization sidebar
  • Organization settings
  • Dashboard cards

Troubleshooting

Subscription Not Updating

Check:

  • Autumn API key is correct
  • Webhook endpoint is configured
  • Organization ID matches Autumn customer ID

Payment Failed

User should:

  1. Access billing portal
  2. Update payment method
  3. Retry payment

System should:

  1. Send notification to organization owner
  2. Gracefully handle expired subscriptions
  3. Provide clear upgrade path

Can't Add Members

Verify:

  • Organization is on Pro plan
  • Subscription is active (not canceled)
  • No payment failures

Best Practices

  1. Clear pricing: Show per-seat cost clearly
  2. Upgrade prompts: Guide users to upgrade when needed
  3. Graceful degradation: Handle expired subscriptions gracefully
  4. Email notifications: Notify users of billing events
  5. Audit trail: Log billing-related actions
  6. Test in sandbox: Use Autumn's test mode before production

Resources

Next Steps

On this page