Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions apps/docs/content/docs/en/platform/costs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -331,27 +331,27 @@ Each voice session is billed when it starts. In deployed chat voice mode, each c

Sim has two paid plan tiers - **Pro** and **Max**. Either can be used individually or with a team. Team plans pool credits across all seats in the organization.

| Plan | Price | Credits Included | Daily Refresh |
|------|-------|------------------|---------------|
| Plan | Price | Credits Included | Weekly Refresh |
|------|-------|------------------|----------------|
| **Community** | $0 | 1,000 (one-time) | - |
| **Pro** | $25/mo | 6,000/mo | +50/day |
| **Max** | $100/mo | 25,000/mo | +200/day |
| **Pro** | $25/mo | 6,000/mo | +2,000/week |
| **Max** | $100/mo | 25,000/mo | +4,000/week |
| **Enterprise** | Custom | Custom | - |

To use Pro or Max with a team, select **Get For Team** in subscription settings and choose the tier and number of seats. Credits are pooled across the organization at the per-seat rate (e.g. Max for Teams with 3 seats = 75,000 credits/mo pooled).

Internal organization members use seats and contribute to the team's pooled credit allocation. External workspace members do not join your organization, do not appear in the organization roster, and do not count toward your seat total.

### Daily Refresh Credits
### Weekly Refresh Credits

Paid plans include a small daily credit allowance that does not count toward your plan limit. Each day, usage up to the daily refresh amount is excluded from billable usage. This allowance resets every 24 hours and does not carry over - use it or lose it.
Paid plans include a weekly credit allowance that does not count toward your plan limit. Each week, usage up to the weekly refresh amount is excluded from billable usage. This allowance resets every 7 days from your billing period start and does not carry over - use it or lose it.

| Plan | Daily Refresh |
|------|---------------|
| **Pro** | 50 credits/day ($0.25) |
| **Max** | 200 credits/day ($1.00) |
| Plan | Weekly Refresh |
|------|----------------|
| **Pro** | 2,000 credits/week ($10.00) |
| **Max** | 4,000 credits/week ($20.00) |

For team plans, the daily refresh scales with seats (e.g. Max for Teams with 3 seats = 600 credits/day).
For team plans, the weekly refresh scales with seats (e.g. Max for Teams with 3 seats = 12,000 credits/week).

### Annual Billing

Expand Down Expand Up @@ -573,7 +573,7 @@ import { FAQ } from '@/components/ui/faq'
<FAQ items={[
{ question: "How much does a single workflow run cost?", answer: "Every run incurs a base charge of 1 credit ($0.005). On top of that, any AI model usage is billed based on token consumption. Workflows that do not use AI blocks only pay the base run charge." },
{ question: "What is the credit-to-dollar conversion rate?", answer: "1 credit equals $0.005. All plan limits, usage meters, and billing thresholds in the Sim UI are displayed in credits." },
{ question: "Do unused daily refresh credits carry over?", answer: "No. Daily refresh credits reset every 24 hours and do not accumulate. If you do not use them within the day, they are lost." },
{ question: "Do unused weekly refresh credits carry over?", answer: "No. Weekly refresh credits reset every 7 days and do not accumulate. If you do not use them within the week, they are lost." },
{ question: "What happens when I exceed my plan's credit limit?", answer: "By default, your usage is capped at your plan's included credits and runs will stop. If you enable on-demand billing or manually raise your usage limit in Settings, you can continue running workflows and pay for the overage at the end of the billing period." },
{ question: "How does the 1.1x hosted model multiplier work?", answer: "When you use Sim's hosted API keys (instead of bringing your own), a 1.1x multiplier is applied to the base model pricing for Agent blocks. This covers infrastructure and API management costs. You can avoid this multiplier by using your own API keys via the BYOK feature." },
{ question: "Are there any free options for AI models?", answer: "Yes. If you run local models through Ollama or VLLM, there are no API costs for those model calls. You still pay the base run charge of 1 credit per run." },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { formatDate } from '@sim/utils/formatting'
import { useRouter } from 'next/navigation'
import { useSession, useSubscription } from '@/lib/auth/auth-client'
import { ON_DEMAND_UNLIMITED } from '@/lib/billing/constants'
import { CREDIT_MULTIPLIER } from '@/lib/billing/credits/conversion'
import { CREDIT_MULTIPLIER, formatCreditCost } from '@/lib/billing/credits/conversion'
import {
getCoveredUsage,
getIsOnDemandActive,
Expand All @@ -30,6 +30,7 @@ import {
getDisplayPlanName,
getPlanTierCredits,
getPlanTierDollars,
getPlanWeeklyRefreshDollars,
isEnterprise,
isFree,
isPaid,
Expand Down Expand Up @@ -438,6 +439,10 @@ export function Billing({ scope, organizationId, creditUsageHref }: BillingProps
? organizationBilling?.cancelAtPeriodEnd === true
: subscriptionData?.data?.cancelAtPeriodEnd === true

const weeklyRefreshDollars =
getPlanWeeklyRefreshDollars(subscription.plan) *
(isOrganizationScope ? subscription.seats || 1 : 1)

const invoices = (invoicesData?.invoices ?? []).map((invoice) => ({
id: invoice.id,
date: formatDate(new Date(invoice.created * 1000)),
Expand Down Expand Up @@ -579,6 +584,15 @@ export function Billing({ scope, organizationId, creditUsageHref }: BillingProps
</div>
)}

{subscription.isPaid && weeklyRefreshDollars > 0 && (
<div className='flex items-center justify-between'>
<span className='text-[var(--text-body)] text-small'>Weekly refresh</span>
<span className='text-[var(--text-muted)] text-small'>
+{formatCreditCost(weeklyRefreshDollars)}
</span>
</div>
)}

<div className='flex items-center justify-between'>
<span className='text-[var(--text-body)] text-small'>Payment method</span>
<Chip
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { DEFAULT_BILLING_CONCURRENCY_LIMITS } from '@/lib/billing/concurrency-defaults'
import {
CREDIT_TIERS,
CREDITS_PER_DOLLAR,
DAILY_REFRESH_RATE,
DEFAULT_FREE_CREDITS,
} from '@/lib/billing/constants'
import { CREDIT_TIERS, CREDITS_PER_DOLLAR, DEFAULT_FREE_CREDITS } from '@/lib/billing/constants'

/**
* Looks up a credit tier by name, so a column binds to the right tier even if
Expand All @@ -22,10 +17,6 @@ const MAX_TIER = tierByName('Max')
/** Formats a credit count with thousands separators (e.g. 25000 → "25,000"). */
const formatCredits = (credits: number): string => credits.toLocaleString('en-US')

/** Daily refresh credits for a plan: 1% of plan dollars/day, in credits. */
const dailyRefreshCredits = (dollars: number): number =>
Math.round(dollars * DAILY_REFRESH_RATE * CREDITS_PER_DOLLAR)

/** A brand icon rendered in a cell instead of a check/em-dash/text. */
export interface CellIcon {
/** Icon identifier resolved to a component by the table renderer. */
Expand Down Expand Up @@ -94,11 +85,11 @@ export const COMPARISON_SECTIONS: ComparisonSection[] = [
],
},
{
label: 'Daily refresh',
label: 'Weekly refresh',
values: [
false,
`+${formatCredits(dailyRefreshCredits(PRO_TIER.dollars))}`,
`+${formatCredits(dailyRefreshCredits(MAX_TIER.dollars))}`,
`+${formatCredits(PRO_TIER.weeklyRefreshCredits)}`,
`+${formatCredits(MAX_TIER.weeklyRefreshCredits)}`,
'Custom',
],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export interface UpgradePlanCardProps {
*/
credits?: string
/**
* Daily refresh allocation shown below the credit amount, e.g. `"+50/day refresh"`.
* Weekly refresh allocation shown below the credit amount, e.g. `"+2,000/week refresh"`.
* Only rendered when {@link UpgradePlanCardProps.credits} is also set.
*/
refresh?: string
Expand Down
6 changes: 3 additions & 3 deletions apps/sim/app/workspace/[workspaceId]/upgrade/plan-configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ import { DEFAULT_BILLING_CONCURRENCY_LIMITS } from '@/lib/billing/concurrency-de
export interface PlanCredits {
/** Formatted credits string, e.g. `"6,000 credits/mo"`. */
credits: string
/** Formatted daily-refresh string, e.g. `"+50/day refresh"`. */
/** Formatted weekly-refresh string, e.g. `"+2,000/week refresh"`. */
refresh: string
}

export const PRO_PLAN_CREDITS: PlanCredits = {
credits: '6,000 credits/mo',
refresh: '+50/day refresh',
refresh: '+2,000/week refresh',
}

export const MAX_PLAN_CREDITS: PlanCredits = {
credits: '25,000 credits/mo',
refresh: '+200/day refresh',
refresh: '+4,000/week refresh',
}

export const ENTERPRISE_PLAN_CREDITS: PlanCredits = {
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/components/emails/billing/constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/** Pro plan features shown in billing upgrade emails */
export const proFeatures = [
{ label: '6,000 credits/month', desc: 'included' },
{ label: '+50 daily refresh', desc: 'credits per day' },
{ label: '+2,000 weekly refresh', desc: 'credits per week' },
{ label: '150 runs/min', desc: 'sync executions' },
{ label: '50GB storage', desc: 'for files & assets' },
] as const
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ slug: best-multi-agent-frameworks-2026
title: 'Best Multi-Agent Frameworks for Production in 2026'
description: 'Compare the best multi-agent frameworks for production in 2026 across orchestration, state, observability, licensing, self-hosting, deployment, and pricing.'
date: 2026-08-25
updated: 2026-08-25
updated: 2026-08-26
authors:
- andrew
readingTime: 19
Expand Down Expand Up @@ -163,8 +163,8 @@ That shared graph also makes agent handoffs, state changes, and business rules e
**As of August 2026, [Sim pricing](https://www.sim.ai/pricing) starts at $0 and uses both per-user plan pricing and usage credits.**

- **Free:** $0 with 1,000 monthly credits.
- **Pro:** $25 per user per month with 6,000 monthly credits and a 50-credit daily refresh.
- **Max:** $100 per user per month with 25,000 monthly credits and a 200-credit daily refresh.
- **Pro:** $25 per user per month with 6,000 monthly credits and a 2,000-credit weekly refresh.
- **Max:** $100 per user per month with 25,000 monthly credits and a 4,000-credit weekly refresh.
- **Enterprise:** Custom pricing and custom credits.

According to the [Sim cost documentation](https://docs.sim.ai/platform/costs), one credit equals $0.005. Each run includes a base charge of one credit, with hosted model cost converted to credits. Sim-hosted models use a 1.1× multiplier. BYOK lets customers pay model providers directly at provider pricing with no Sim markup.
Expand Down
28 changes: 14 additions & 14 deletions apps/sim/lib/billing/calculations/usage-monitor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ const {
mockGetOrgMemberUsageLimit,
mockGetUserUsageLimit,
mockIsOrganizationBillingBlocked,
mockComputeBillingPeriodUsageWithDailyRefresh,
mockComputeBillingPeriodUsageWithWeeklyRefresh,
} = vi.hoisted(() => ({
mockGetBillingPeriodUsageCost: vi.fn(),
mockGetOrgMemberUsageForBillingPeriod: vi.fn(),
mockGetOrgMemberUsageLimit: vi.fn(),
mockGetUserUsageLimit: vi.fn(),
mockIsOrganizationBillingBlocked: vi.fn(),
mockComputeBillingPeriodUsageWithDailyRefresh: vi.fn(),
mockComputeBillingPeriodUsageWithWeeklyRefresh: vi.fn(),
}))

vi.mock('@/lib/billing/organizations/member-limits', () => ({
Expand All @@ -39,8 +39,8 @@ vi.mock('@/lib/billing/core/usage-log', () => ({
getBillingPeriodUsageCost: mockGetBillingPeriodUsageCost,
}))

vi.mock('@/lib/billing/credits/daily-refresh', () => ({
computeBillingPeriodUsageWithDailyRefresh: mockComputeBillingPeriodUsageWithDailyRefresh,
vi.mock('@/lib/billing/credits/weekly-refresh', () => ({
computeBillingPeriodUsageWithWeeklyRefresh: mockComputeBillingPeriodUsageWithWeeklyRefresh,
}))

import {
Expand All @@ -64,7 +64,7 @@ describe('checkUsageStatus', () => {
setEnvFlags({ isHosted: true, isBillingEnabled: true })
mockGetUserUsageLimit.mockResolvedValue(500)
mockGetBillingPeriodUsageCost.mockResolvedValue(125)
mockComputeBillingPeriodUsageWithDailyRefresh.mockResolvedValue({
mockComputeBillingPeriodUsageWithWeeklyRefresh.mockResolvedValue({
ledgerUsage: 125,
refreshConsumed: 25,
})
Expand Down Expand Up @@ -121,17 +121,17 @@ describe('checkUsageStatus', () => {
scope: 'user',
})

expect(mockComputeBillingPeriodUsageWithDailyRefresh).toHaveBeenCalledWith({
expect(mockComputeBillingPeriodUsageWithWeeklyRefresh).toHaveBeenCalledWith({
billingEntity: { type: 'user', id: 'user-1' },
billingPeriod: { start: periodStart, end: periodEnd },
refreshPeriodStart: periodStart,
refreshPeriodEnd: periodEnd,
planDollars: 20,
weeklyRefreshDollars: 10,
})
expect(mockGetBillingPeriodUsageCost).not.toHaveBeenCalled()
})

it('preserves the paid daily-refresh clamp for negative effective usage', async () => {
it('preserves the paid weekly-refresh clamp for negative effective usage', async () => {
const periodStart = new Date('2026-06-01T00:00:00.000Z')
const periodEnd = new Date('2026-07-01T00:00:00.000Z')
const subscription = {
Expand All @@ -142,7 +142,7 @@ describe('checkUsageStatus', () => {
periodStart,
periodEnd,
}
mockComputeBillingPeriodUsageWithDailyRefresh.mockResolvedValueOnce({
mockComputeBillingPeriodUsageWithWeeklyRefresh.mockResolvedValueOnce({
ledgerUsage: -1,
refreshConsumed: 1,
})
Expand Down Expand Up @@ -173,7 +173,7 @@ describe('checkUsageStatus', () => {
{ type: 'user', id: 'user-1' },
{ start: periodStart, end: periodEnd }
)
expect(mockComputeBillingPeriodUsageWithDailyRefresh).not.toHaveBeenCalled()
expect(mockComputeBillingPeriodUsageWithWeeklyRefresh).not.toHaveBeenCalled()
})

it('preserves negative ledger-only personal usage', async () => {
Expand All @@ -194,7 +194,7 @@ describe('checkUsageStatus', () => {
scope: 'user',
})

expect(mockComputeBillingPeriodUsageWithDailyRefresh).not.toHaveBeenCalled()
expect(mockComputeBillingPeriodUsageWithWeeklyRefresh).not.toHaveBeenCalled()
})

it('combines paid organization ledger usage with entity-scoped refresh — no roster read', async () => {
Expand All @@ -208,7 +208,7 @@ describe('checkUsageStatus', () => {
periodStart,
periodEnd,
}
mockComputeBillingPeriodUsageWithDailyRefresh.mockResolvedValue({
mockComputeBillingPeriodUsageWithWeeklyRefresh.mockResolvedValue({
ledgerUsage: 100,
refreshConsumed: 10,
})
Expand All @@ -221,7 +221,7 @@ describe('checkUsageStatus', () => {

// Refresh is scoped by the entity stamps alone, so departed members'
// org-attributed rows participate identically to current members'.
expect(mockComputeBillingPeriodUsageWithDailyRefresh).toHaveBeenCalledWith({
expect(mockComputeBillingPeriodUsageWithWeeklyRefresh).toHaveBeenCalledWith({
billingEntity: { type: 'organization', id: 'org-1' },
billingPeriod: expect.objectContaining({
start: periodStart,
Expand All @@ -230,7 +230,7 @@ describe('checkUsageStatus', () => {
}),
refreshPeriodStart: periodStart,
refreshPeriodEnd: periodEnd,
planDollars: expect.any(Number),
weeklyRefreshDollars: expect.any(Number),
seats: 2,
})
expect(mockGetBillingPeriodUsageCost).not.toHaveBeenCalled()
Expand Down
26 changes: 13 additions & 13 deletions apps/sim/lib/billing/calculations/usage-monitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import {
type UsageQueryPeriod,
} from '@/lib/billing/core/usage-log'
import { dollarsToCredits } from '@/lib/billing/credits/conversion'
import { computeBillingPeriodUsageWithDailyRefresh } from '@/lib/billing/credits/daily-refresh'
import { computeBillingPeriodUsageWithWeeklyRefresh } from '@/lib/billing/credits/weekly-refresh'
import {
getOrgMemberUsageForBillingPeriod,
getOrgMemberUsageLimit,
} from '@/lib/billing/organizations/member-limits'
import { getPlanTierDollars, isPaid } from '@/lib/billing/plan-helpers'
import { getPlanWeeklyRefreshDollars, isPaid } from '@/lib/billing/plan-helpers'
import { isOrgScopedSubscription } from '@/lib/billing/subscriptions/utils'
import { isBillingEnabled, isHosted } from '@/lib/core/config/env-flags'

Expand Down Expand Up @@ -61,17 +61,17 @@ async function computePooledOrgUsage(
return getBillingPeriodUsageCost({ type: 'organization', id: organizationId }, billingPeriod)
}

const planDollars = getPlanTierDollars(sub.plan)
if (planDollars <= 0) {
const weeklyRefreshDollars = getPlanWeeklyRefreshDollars(sub.plan)
if (weeklyRefreshDollars <= 0) {
return getBillingPeriodUsageCost({ type: 'organization', id: organizationId }, billingPeriod)
}

const { ledgerUsage, refreshConsumed } = await computeBillingPeriodUsageWithDailyRefresh({
const { ledgerUsage, refreshConsumed } = await computeBillingPeriodUsageWithWeeklyRefresh({
billingEntity: { type: 'organization', id: organizationId },
billingPeriod,
refreshPeriodStart: sub.periodStart,
refreshPeriodEnd: sub.periodEnd ?? null,
planDollars,
weeklyRefreshDollars,
seats: sub.seats || 1,
})

Expand Down Expand Up @@ -134,28 +134,28 @@ export async function checkUsageStatus(
: defaultBillingPeriod())
let ledgerUsage: number
let refreshConsumed = 0
let appliedDailyRefresh = false
let appliedWeeklyRefresh = false
if (sub && isPaid(sub.plan) && sub.periodStart) {
const planDollars = getPlanTierDollars(sub.plan)
if (planDollars > 0) {
const usage = await computeBillingPeriodUsageWithDailyRefresh({
const weeklyRefreshDollars = getPlanWeeklyRefreshDollars(sub.plan)
if (weeklyRefreshDollars > 0) {
const usage = await computeBillingPeriodUsageWithWeeklyRefresh({
billingEntity: { type: 'user', id: userId },
billingPeriod,
refreshPeriodStart: sub.periodStart,
refreshPeriodEnd: sub.periodEnd ?? null,
planDollars,
weeklyRefreshDollars,
})
ledgerUsage = usage.ledgerUsage
refreshConsumed = usage.refreshConsumed
appliedDailyRefresh = true
appliedWeeklyRefresh = true
} else {
ledgerUsage = await getBillingPeriodUsageCost({ type: 'user', id: userId }, billingPeriod)
}
} else {
ledgerUsage = await getBillingPeriodUsageCost({ type: 'user', id: userId }, billingPeriod)
}
const usageBeforeRefresh = ledgerUsage - refreshConsumed
const currentUsage = appliedDailyRefresh ? Math.max(0, usageBeforeRefresh) : usageBeforeRefresh
const currentUsage = appliedWeeklyRefresh ? Math.max(0, usageBeforeRefresh) : usageBeforeRefresh

return buildUsageData({ currentUsage, limit, scope, organizationId })
} catch (error) {
Expand Down
Loading
Loading