Skip to content

Commit 8ce5a1b

Browse files
icecrasher321waleed
authored andcommitted
feat(billing): bill by threshold to prevent cancellation edge case (#1583)
* feat(billing): bill by threshold to prevent cancellation edge case * fix org billing * fix idempotency key issue * small optimization for team checks * remove console log * remove unused type * fix error handling
1 parent 88d2e7b commit 8ce5a1b

File tree

14 files changed

+7575
-42
lines changed

14 files changed

+7575
-42
lines changed

apps/docs/content/docs/en/execution/costs.mdx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,38 @@ Different subscription plans have different usage limits:
166166
| **Team** | $500 (pooled) | 50 sync, 100 async |
167167
| **Enterprise** | Custom | Custom |
168168

169+
## Billing Model
170+
171+
Sim uses a **base subscription + overage** billing model:
172+
173+
### How It Works
174+
175+
**Pro Plan ($20/month):**
176+
- Monthly subscription includes $20 of usage
177+
- Usage under $20 → No additional charges
178+
- Usage over $20 → Pay the overage at month end
179+
- Example: $35 usage = $20 (subscription) + $15 (overage)
180+
181+
**Team Plan ($40/seat/month):**
182+
- Pooled usage across all team members
183+
- Overage calculated from total team usage
184+
- Organization owner receives one bill
185+
186+
**Enterprise Plans:**
187+
- Fixed monthly price, no overages
188+
- Custom usage limits per agreement
189+
190+
### Threshold Billing
191+
192+
When unbilled overage reaches $50, Sim automatically bills the full unbilled amount.
193+
194+
**Example:**
195+
- Day 10: $70 overage → Bill $70 immediately
196+
- Day 15: Additional $35 usage ($105 total) → Already billed, no action
197+
- Day 20: Another $50 usage ($155 total, $85 unbilled) → Bill $85 immediately
198+
199+
This spreads large overage charges throughout the month instead of one large bill at period end.
200+
169201
## Cost Management Best Practices
170202

171203
1. **Monitor Regularly**: Check your usage dashboard frequently to avoid surprises

apps/sim/app/api/billing/update-cost/route.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { userStats } from '@sim/db/schema'
33
import { eq, sql } from 'drizzle-orm'
44
import { type NextRequest, NextResponse } from 'next/server'
55
import { z } from 'zod'
6+
import { checkAndBillOverageThreshold } from '@/lib/billing/threshold-billing'
67
import { checkInternalApiKey } from '@/lib/copilot/utils'
78
import { isBillingEnabled } from '@/lib/environment'
89
import { createLogger } from '@/lib/logs/console/logger'
@@ -148,6 +149,9 @@ export async function POST(req: NextRequest) {
148149
addedTokens: totalTokens,
149150
})
150151

152+
// Check if user has hit overage threshold and bill incrementally
153+
await checkAndBillOverageThreshold(userId)
154+
151155
const duration = Date.now() - startTime
152156

153157
logger.info(`[${requestId}] Cost update completed successfully`, {

apps/sim/app/api/wand-generate/route.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { userStats, workflow } from '@sim/db/schema'
33
import { eq, sql } from 'drizzle-orm'
44
import { type NextRequest, NextResponse } from 'next/server'
55
import OpenAI, { AzureOpenAI } from 'openai'
6+
import { checkAndBillOverageThreshold } from '@/lib/billing/threshold-billing'
67
import { env } from '@/lib/env'
78
import { getCostMultiplier, isBillingEnabled } from '@/lib/environment'
89
import { createLogger } from '@/lib/logs/console/logger'
@@ -133,6 +134,9 @@ async function updateUserStatsForWand(
133134
tokensUsed: totalTokens,
134135
costAdded: costToStore,
135136
})
137+
138+
// Check if user has hit overage threshold and bill incrementally
139+
await checkAndBillOverageThreshold(userId)
136140
} catch (error) {
137141
logger.error(`[${requestId}] Failed to update user stats for wand usage`, error)
138142
}

apps/sim/lib/billing/constants.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,9 @@ export const DEFAULT_ENTERPRISE_TIER_COST_LIMIT = 200
1919
* This charge is applied regardless of whether the workflow uses AI models
2020
*/
2121
export const BASE_EXECUTION_CHARGE = 0.001
22+
23+
/**
24+
* Default threshold (in dollars) for incremental overage billing
25+
* When unbilled overage reaches this amount, an invoice item is created
26+
*/
27+
export const DEFAULT_OVERAGE_THRESHOLD = 50

0 commit comments

Comments
 (0)