Subscription & Billing System
How Ever Works charges for itself, end to end. The behaviour spec is
docs/specs/features/billing/spec.md; the
implementation lives in packages/agent/src/subscriptions/ and
apps/api/src/billing/. Everything sells through the shared "Ever Tech" Stripe
account under the ever_works_* lookup-key convention
(packages/agent/src/subscriptions/billing/stripe-catalog.data.json, applied by
scripts/stripe-sync-catalog.mjs — see the
billing operations runbook).
The three things Ever Works sells
| What | Shape | Where priced |
|---|---|---|
| Plans | Free / Pro $25 / Enterprise $199 per month on cloud (annual $204 / $1,668); self-hosted Pro $49/mo, $408/yr or a $99 one-time perpetual licence; Enterprise Edition $199/mo | catalog + subscription_plans seed |
| Seats | 10 included on paid tiers; +$5 (Pro) / +$10 (Enterprise) per additional seat per month. A seat is an employee OR an agent | catalog seat prices |
| Credits | 1 credit = 1¢ of platform-billed AI usage. Daily allowance 50 on every plan; monthly allowance 3,000 (Pro) / 25,000 (Enterprise) expiring at each allowance-month end; prepaid packs $10/1,000 · $50/5,500 · $200/25,000 (never expire); pay-as-you-go beyond the balance, opt-in, metered and billed in arrears | credit-packs.ts + the catalog payg section |
Plan codes are identities (free / standard / premium) and never change;
standard is displayed as "Pro" and premium as "Enterprise".
Credits pipeline
- Metering — every AI call lands in
plugin_usage_eventswithcostCentsat provider list price (AiFacade.calculateCost). - Settlement — when a run reaches a terminal state,
RunCostSettlementServicesums the run's billable spend (BYOK-exempt plugins removed), stampsagent_runs.costCents, and converts the billable part to credits:ceil(costCents × (CREDITS_PER_DOLLAR/100) × (1 + margin)). The margin defaults to the catalog'screditsMarginPercent(35). That is the defaultCREDITS_SETTLEMENT_MODE=provider_cost. Withprice_list, calls priced by a fixed per-unit entry on the published credit price list (web search, page extraction, screenshot) debit their listed credits instead, and everything else still converts from cost. Usage rows carry the list price in both modes;GET /api/credits/pricingreports the active mode assettlementMode. - Ledger — one
consumptionrow per run (run:{runId}), allocated against credit buckets soonest-expiring first. Balance = available sum of the ledger; see Credits & Billing. - Overflow — if the balance cannot cover the debit and pay-as-you-go is
on, the remainder (up to the monthly cap headroom) becomes a
credit_meter_eventsrow and a Stripe meter event; Stripe rates it under the graduated metered price and invoices in arrears. - Enforcement — the dispatch gate parks new runs for credit-limited plans
with no balance and no pay-as-you-go headroom (
CREDITS_ENFORCEMENTdefaults on iff Stripe is configured).
Seats
A seat is an employee OR an agent — interchangeable, which is the point of the product. Paid plans include 10; extras are billed per seat per month from the catalog ($5 Pro / $10 Enterprise).
- Counted tenant-wide. Access in Ever Works is tenant-wide, so somebody who belongs to three Organizations in one Tenant occupies ONE seat, and an agent built by a team member is capacity exactly like one the owner built. Archiving an agent frees its seat.
- Persisted from the provider.
user_subscriptions.seats/providerSeatItemIdare reconciled from the subscription's items on everysubscription.*delivery (the seat item is the one whose pricelookup_keycarries the_seat_infix), never guessed locally. NULL means "fall back to the plan'sseatsIncluded" — never zero, which would read as "no seats allowed". - Enforced before the write. Inviting a member (at invite AND at accept)
and creating an agent ask
SeatsService.assertSeatAvailablefor the Tenant owner first; a full allowance surfaces as 402 with the counts. The check fails OPEN on everything else: subscriptions disabled, an unbounded plan, or any resolution error never blocks adding a teammate. - Adjustable.
POST /api/billing/seatstakes the TOTAL wanted (a total, not a delta — a delta double-charges on a retry); the server billsmax(0, total − included)from the stored plan row and refuses to drop the allowance below what is already in use.
Stripe integration (the provider seam)
BillingProvider is the vendor-neutral seam; StripeBillingProvider is the
only file that imports the Stripe SDK. It uses:
- Checkout Sessions —
mode: paymentfor credit packs and the perpetual licence,mode: subscriptionfor plans (+ seat line items),mode: setupfor card capture. Every session that CHARGES carries Stripe Tax (automatic_tax+customer_update+tax_id_collection);mode: setupdeliberately does not, because saving a card charges nothing. The pay-as-you-go subscription carriesautomatic_taxtoo. - PaymentIntents (off-session) — threshold auto-recharge.
- Subscriptions — plans, and one usage-only subscription per owner for
pay-as-you-go (
billing_thresholds.amount_gte = $50for mid-cycle invoicing; cancelled immediately withinvoice_nowon disable). - Billing Meters —
ever_works_credits, aggregationsumbystripe_customer_id; meter events are keyedrun:{runId}(identifier = request idempotency key), retried by thecredits-meter-flushcron and given up after 23 hours for manual reconciliation. This stays inside Stripe's 24-hour idempotency window and prevents a late retry from double-counting usage after the request key expires. - Webhooks — signature-verified, normalized to a closed event union;
every ledger write is idempotent on the provider event id. The
pay-as-you-go subscription's lifecycle normalizes to
payg.updatedand can never move a plan tier; its invoices are taggedsubscriptionKind: 'payg'so a payment failure suspends overflow and a payment resumes it. - Customer Portal — the past-due recovery surface.
Scheduled jobs
| Task | Cadence | Does |
|---|---|---|
credits-daily-grant | 00:05 UTC | expire lapsed buckets → daily allowance top-ups → monthly plan-allowance grants |
credits-meter-flush | every 5 min | resend meter events the settlement path could not deliver |
Legacy: per-run pay-per-use (deprecated)
The 2026-05 design (WorkScheduleService billingMode=usage,
UsageLedgerService.recordUsage, PAY_PER_USE_PRICE_USD,
BillingProvider.recordUsageCharge) predates credits. recordUsageCharge is
a no-op on every provider and overagePricePerRun is never billed — the path
only writes usage_ledger_entries rows in pending. It is superseded by
credits + pay-as-you-go, kept only until its removal is confirmed, and must
not be extended.
Configuration
| Env | Meaning |
|---|---|
SUBSCRIPTIONS_ENABLED | master switch for plan resolution/gating (default off) |
STRIPE_SECRET_KEY / STRIPE_WEBHOOK_SECRET | the money path; both blank = manual provider, everything fails closed |
PAYMENTS_ENABLED (web) | shows the live purchase surfaces instead of coming-soon |
CREDITS_ENFORCEMENT | on/off; unset = on iff Stripe is configured |
CREDITS_PER_DOLLAR / CREDITS_MARGIN_PERCENT / CREDITS_DAILY_FREE | conversion knobs; margin defaults to the catalog (35) |
CREDITS_SETTLEMENT_MODE | provider_cost (default) or price_list; see Credits pipeline |
PAYG_MAX_MONTHLY_CAP_CREDITS | ceiling for a self-service pay-as-you-go cap |