Architecture

Billing & plates

OpsDash gates access with a plate ladder, not a flat tier list. The rungs are freestudiosalesgrowth full_loopagency (defined in packages/config/src/plates.ts). Three flat-rate add-on packs stack on top — AI Pack ($25), Advanced Analytics ($19), and e-Invoicing ($29). Billing is account-level (anchored to a personal workspace), limits are enforced server-side, and Stripe is the single billing source of truth.

The Billing surface itself is not plate-gated — Stripe is the entitlement source, so every plate can open Settings → Billing. The sales rung is dormant (folded into Growth by strict nesting), so it is in the ladder but never sold as its own public column.

Plate overview

PlateTargetMonthlyYearly
FreeTry the loop — contacts, a few projects and forms$0$0
StudioBill it — proposals, invoices, recurring & a portal$24$240
GrowthWin work and deliver it — pipeline, projects, contracts$59$590
Full LoopThe whole loop — lead to profit, one database$99$990
AgencyScale it — SSO, white-label, e-invoicing, priority support$179 — “Contact us”$1,790

A no-card 7-day plate trial is offered from the first-run plate-decision popup; at trial end with no payment method Stripe cancels the subscription and the org lapses to Free — no surprise charge. All modules are always visible in the sidebar on every plate; sub-features are gated with progressive disclosure — upgrade prompts appear contextually, not as locked nav items.

Workspace limits

Columns are the four public self-serve plates. The dormant sales rung and the “Contact us” agency rung are omitted — see packages/config/src/plates.ts for their full caps.

ResourceFreeStudioGrowthFull Loop
Workspaces1135
Included seats2258
Storage500 MB5 GB25 GB100 GB
API calls / month1,0005,00050,000250,000

Sub-feature gating by module

CRM

Feature keyFreeStudioGrowthFull Loop
crm:contacts25050015,00025,000
crm:companies501003,0005,000
crm:deals
crm:leads
crm:lead-scoring
crm:deal-stages
crm:quotes
crm:csv-import
crm:contact-filters
crm:lead-filters
crm:email-integration
crm:export

Projects

Feature keyFreeStudioGrowthFull Loop
projects:crud33100500
projects:tasks10010010,00050,000
projects:kanban
projects:calendar
projects:subtasks
projects:milestones
projects:gantt
projects:time-tracking-log
projects:budget

Platform & module surfaces

Feature keyFreeStudioGrowthFull Loop
platform:audit-logs
platform:api-access
platform:webhooks
reports:page
invoicing:page
ap:page

platform:custom-branding, platform:sso-saml, platform:scim-provisioning, platform:audit-logs-export, and custom roles sit on the Agency rung (not shown as a column above).

Stripe integration

Stripe is the single billing source of truth. Custom logic outside webhook handlers is prohibited.

  • Webhook: POST /api/webhooks/stripe handles all subscription lifecycle events (Connect events land on POST /api/webhooks/stripe/connect)
  • Entitlement: an org’s plate is resolved solely from subscriptions.plan (resolveOrgPlate in packages/config/src/plates.ts); a feature is enabled when the plate’s rung is at or above the feature’s minPlate in packages/config/src/defaults.ts
  • Price IDs: follow STRIPE_PRICE_{PLATE}_{INTERVAL} (e.g. STRIPE_PRICE_STUDIO_MONTHLY)
  • Billing portal: Stripe Customer Portal for subscription management (no custom UI for plan changes)

Distribution modes

  • saas → billing provider stripe; the full plate ladder + Checkout/Portal is live
  • selfHosted / whitelabel → billing provider none by default; gating is driven by the signed license’s planCeiling (a plate id) instead of Stripe, and per-request subscription reads short-circuit

See Configuration for Stripe env vars and setup steps.

Billing & plates — OpsDash Docs | OpsDash