Architecture

Billing & plans

Quattora gates access with a plan ladder defined once in packages/config/src/pricing/: a catalogue of plans, a versioned price book, promotions, a cost model and the invariants that hold them together. packages/config/src/plates.ts composes the runtime platesCatalog from it. The public columns are Free → Studio → Team → Firm, with Dedicated sold in a conversation. Legacy rungs stay in the ladder for their subscribers, hidden from every buyer-facing surface and honoured as their successor plan at the entitlement boundary. Billing is account-level (anchored to a personal workspace), limits are enforced server-side, and Stripe is the single billing source of truth.

The ladder (price book v1)

PlanForMonthlyYearly (per month)Yearly
FreeFor one person trying it out — two seats, a few projects, a few invoices.$0$0$0
StudioFor a studio of up to three — clients, pipeline, proposals, invoices and the money arriving.$35$29$348
TeamFor an agency of up to ten — lead to profit in one record. The plan most teams pick.$119$99$1,188
FirmFor a firm of up to twenty-five — everything included, plus custom roles, single sign-on, provisioning and audit export.$299$249$2,988
DedicatedYour own instance, your own domain, your region — for thirty to sixty people. Talk to us.$499$449$5,388

Add-on packs, monthly, on top of any plan they are available from: AI Pack ($29), e-Invoicing / Peppol ($29). Policy: a 14-day trial without a card, a 24-month price lock from the first payment, 90 days’ notice of any change, 30-day money-back on yearly billing, promotions capped at 25% of the yearly price for the first term.

Seats, allowances and limits

A full seat is a member who can change things; viewers and client-portal users are light seats and never billed on a paid plan. The seat price is one number for the whole ladder and every hand-over lands exactly on the next plan’s price.

LimitFreeStudioTeamFirmDedicated
Full seats included23102550
Extra full seat—$12/month · $120/year$12/month · $120/year$12/month · $120/year$12/month · $120/year
Viewers & client-portal usersnonefree, unlimitedfree, unlimitedfree, unlimitedfree, unlimited
Accountants2 free, then an ordinary seat2 free, then an ordinary seat2 free, then an ordinary seat2 free, then an ordinary seat2 free, then an ordinary seat
AI actions / month10503001,0002,000
Online-payment platform fee1%0%0%0%0%
Workspaces1131025
Storage500 MB10 GB100 GB250 GB250 GB
Emails / month1001,0005,00020,00025,000
API calls / month1,00025,000250,0001,000,0001,000,000
Automation runs / month050010,00025,000100,000
Contacts1002,00025,000100,000250,000
Projects3155002,0005,000
Open invoices52002,00010,00025,000

Feature gating by plan

A feature is on when the plan’s rung is at or above its minPlate in packages/config/src/defaults.ts. A sample of keys per module; the full map is the file itself.

CRM

Feature keyFreeStudioTeamFirmDedicated
crm:contacts✓✓✓✓✓
crm:companies✓✓✓✓✓
crm:leads—✓✓✓✓
crm:deals—✓✓✓✓
crm:quotes—✓✓✓✓
crm:sequences——✓✓✓
crm:csv-import✓✓✓✓✓
crm:export✓✓✓✓✓

Projects

Feature keyFreeStudioTeamFirmDedicated
projects:crud✓✓✓✓✓
projects:tasks✓✓✓✓✓
projects:kanban✓✓✓✓✓
projects:milestones——✓✓✓
projects:gantt——✓✓✓
projects:time-tracking-log——✓✓✓
projects:budget——✓✓✓
resource-planning:page——✓✓✓

Money & platform

Feature keyFreeStudioTeamFirmDedicated
invoicing:page✓✓✓✓✓
invoicing:payments✓✓✓✓✓
invoicing:recurring—✓✓✓✓
ap:page——✓✓✓
reports:page——✓✓✓
platform:api-access——✓✓✓
platform:webhooks——✓✓✓
platform:audit-logs——✓✓✓
platform:sso-saml———✓✓
platform:rbac-custom-roles———✓✓

Stripe integration

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

  • Catalogue sync: scripts/billing/sync-stripe-catalog.mjs diffs the price book against the account — products by identity metadata, prices by lookup_key, EUR at parity as currency_options, coupons from the promotions table — and prints the env block. Dry run by default; --apply executes; scripts/check-stripe-prices.mjs proves the numbers match.
  • Webhook: POST /api/webhooks/stripe handles the subscription lifecycle and writes the commercial terms (price_book_version, price_locked_until, included_full_seats); Connect events land on POST /api/webhooks/stripe/connect.
  • Entitlement: an org’s plan is resolved solely from subscriptions.plan (resolveOrgPlate); a legacy plan resolves to its successor.
  • Price ids: NEXT_PUBLIC_STRIPE_PRICE_PLATE_{PLAN}_{INTERVAL}, plus the seat, add-on, partner and licence keys the sync prints. A superseded price still resolves through the plate metadata the sync stamps on it, so a re-price never downgrades a grandfathered customer.
  • Billing portal: Stripe Customer Portal for payment methods and invoices; plan changes, seats and cancellation happen in Settings → Billing.

Distribution modes

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

See Configuration for Stripe env vars and setup steps.