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)
| Plan | For | Monthly | Yearly (per month) | Yearly |
|---|---|---|---|---|
| Free | For one person trying it out — two seats, a few projects, a few invoices. | $0 | $0 | $0 |
| Studio | For a studio of up to three — clients, pipeline, proposals, invoices and the money arriving. | $35 | $29 | $348 |
| Team | For an agency of up to ten — lead to profit in one record. The plan most teams pick. | $119 | $99 | $1,188 |
| Firm | For a firm of up to twenty-five — everything included, plus custom roles, single sign-on, provisioning and audit export. | $299 | $249 | $2,988 |
| Dedicated | Your 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.
| Limit | Free | Studio | Team | Firm | Dedicated |
|---|---|---|---|---|---|
| Full seats included | 2 | 3 | 10 | 25 | 50 |
| Extra full seat | — | $12/month · $120/year | $12/month · $120/year | $12/month · $120/year | $12/month · $120/year |
| Viewers & client-portal users | none | free, unlimited | free, unlimited | free, unlimited | free, unlimited |
| Accountants | 2 free, then an ordinary seat | 2 free, then an ordinary seat | 2 free, then an ordinary seat | 2 free, then an ordinary seat | 2 free, then an ordinary seat |
| AI actions / month | 10 | 50 | 300 | 1,000 | 2,000 |
| Online-payment platform fee | 1% | 0% | 0% | 0% | 0% |
| Workspaces | 1 | 1 | 3 | 10 | 25 |
| Storage | 500 MB | 10 GB | 100 GB | 250 GB | 250 GB |
| Emails / month | 100 | 1,000 | 5,000 | 20,000 | 25,000 |
| API calls / month | 1,000 | 25,000 | 250,000 | 1,000,000 | 1,000,000 |
| Automation runs / month | 0 | 500 | 10,000 | 25,000 | 100,000 |
| Contacts | 100 | 2,000 | 25,000 | 100,000 | 250,000 |
| Projects | 3 | 15 | 500 | 2,000 | 5,000 |
| Open invoices | 5 | 200 | 2,000 | 10,000 | 25,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 key | Free | Studio | Team | Firm | Dedicated |
|---|---|---|---|---|---|
| crm:contacts | ✓ | ✓ | ✓ | ✓ | ✓ |
| crm:companies | ✓ | ✓ | ✓ | ✓ | ✓ |
| crm:leads | — | ✓ | ✓ | ✓ | ✓ |
| crm:deals | — | ✓ | ✓ | ✓ | ✓ |
| crm:quotes | — | ✓ | ✓ | ✓ | ✓ |
| crm:sequences | — | — | ✓ | ✓ | ✓ |
| crm:csv-import | ✓ | ✓ | ✓ | ✓ | ✓ |
| crm:export | ✓ | ✓ | ✓ | ✓ | ✓ |
Projects
| Feature key | Free | Studio | Team | Firm | Dedicated |
|---|---|---|---|---|---|
| projects:crud | ✓ | ✓ | ✓ | ✓ | ✓ |
| projects:tasks | ✓ | ✓ | ✓ | ✓ | ✓ |
| projects:kanban | ✓ | ✓ | ✓ | ✓ | ✓ |
| projects:milestones | — | — | ✓ | ✓ | ✓ |
| projects:gantt | — | — | ✓ | ✓ | ✓ |
| projects:time-tracking-log | — | — | ✓ | ✓ | ✓ |
| projects:budget | — | — | ✓ | ✓ | ✓ |
| resource-planning:page | — | — | ✓ | ✓ | ✓ |
Money & platform
| Feature key | Free | Studio | Team | Firm | Dedicated |
|---|---|---|---|---|---|
| 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.mjsdiffs the price book against the account — products by identity metadata, prices bylookup_key, EUR at parity ascurrency_options, coupons from the promotions table — and prints the env block. Dry run by default;--applyexecutes;scripts/check-stripe-prices.mjsproves the numbers match. - Webhook:
POST /api/webhooks/stripehandles the subscription lifecycle and writes the commercial terms (price_book_version,price_locked_until,included_full_seats); Connect events land onPOST /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 theplatemetadata 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
noneby default; gating is driven by the signed licence’splanCeiling(a plan id) instead of Stripe, and per-request subscription reads short-circuit
See Configuration for Stripe env vars and setup steps.