Guides
Whitelabel guide
Quattora can be fully white-labeled and resold as your own product — your logo, your colors, your domain, your pricing. This guide covers the complete configuration path.
What can be customized
| Element | Configurable | How |
|---|---|---|
| App name | Yes | quattora.config.ts branding.name |
| Logo | Yes | Drop logo.svg + logo-icon.svg in public/logos/, point branding.logo.{full,icon} at them |
| Favicon | Yes | Replace public/favicon.ico |
| Accent color / brand palette | Yes | branding.accentColor ({ light, dark } HSL triplets) in config |
| Email sender name | Yes | RESEND_FROM env var |
| Domain | Yes | Deploy to their domain |
| Pricing plates | Yes | packages/config/src/plates.ts |
| Enabled modules | Yes | modules.* in quattora.config.ts |
| Feature gating per plate | Yes | features.* (minPlate) in quattora.config.ts |
| Per-workspace branding | Yes (whitelabel only) | Admin → Branding, gated by platform:custom-branding (agency plate) |
Branding configuration
// quattora.config.ts
import { defineConfig } from '@quattora/config';
export default defineConfig({
distribution: 'whitelabel', // enables per-workspace branding overrides
branding: {
name: 'AgencyOS Pro',
logo: { full: '/logos/agencyos.svg', icon: '/logos/agencyos-icon.svg' }, // SVGs in /public/logos/
accentColor: { light: '221 83% 53%', dark: '217 91% 70%' }, // HSL triplets, no hsl() — drives all primary UI
},
});The app name appears in: sidebar header, browser tab title, invitation emails, notifications.
The primary color drives all highlighted UI elements: buttons, links, active nav items, badges, focus rings. The light value is used in light mode and the dark value in dark mode, so give the dark one a light shade; the text on top of the color (white or near-black) is worked out for you.
Per-workspace branding override
The config above sets one brand for the entire deployment. When distribution: 'whitelabel', each workspace can additionally override its own name, logo, and accent color from Admin → Branding. This is stored in organizations.branding_override (JSONB) and gated behind the platform:custom-branding feature (the agency plate in shipped defaults) and distribution: 'whitelabel' — SaaS and self-hosted deployments never see the settings row and the server action rejects writes.
Overrides apply only inside /org/{slug}; the landing page, login, and other non-workspace routes always use the deployment-wide branding. Empty override fields inherit from the deployment default. Every branding change is audit-logged (platform.branding.updated / platform.branding.cleared).
Module configuration
modules: {
crm: { enabled: true },
projects: { enabled: true },
automation: { enabled: true },
invoicing: { enabled: false }, // disable a module the client doesn't need
}Disabled modules are completely hidden — they do not appear in the sidebar, search, or command palette.
Your own plans and prices
Pricing is a domain of its own in packages/config/src/pricing/: the plan catalogue (catalog.ts — labels, seats, caps, what is sellable or public), the versioned price book (price-books.ts — every amount, per currency), promotions, the cost model and the invariants that refuse a book that hands over between plans at the wrong seat count or prices below the margin floor. The runtime platesCatalog is composed from these; nothing else holds a number.
To sell your own ladder: add a price book version with your amounts (never edit a live one), adjust labels and caps in the catalogue, run the config tests (the invariants), then node scripts/billing/sync-stripe-catalog.mjs --apply against your own Stripe account and paste the env block it prints. Your customers never see our prices or our name.
// packages/config/src/pricing/price-books.ts — one entry per version and currency
export const PRICE_BOOKS: PriceBook[] = [
{
version: 'v1',
currency: 'USD',
effectiveFrom: '2026-09-21',
plans: { studio: { monthly: …, annual: …, onlinePaymentFeeBps: 0 }, /* … */ },
seat: { monthly: …, annual: … },
// …addons, licence, partners
},
];To unlock a feature on an earlier plan, override its minPlate under features.* in quattora.config.ts. To raise an entity cap, edit that plan's entityCaps in the catalogue.
Emails
Set RESEND_FROM in .env:
RESEND_FROM="AgencyOS Pro <noreply@yourdomain.com>"This sets the sender name and address for all transactional emails (invitations, notifications, password resets).
Hiding the Quattora attribution
If your license includes removal of the “Powered by Quattora” attribution, remove or replace it in the footer component where it renders. No other components reference the product name directly — everywhere else it comes from branding.name in config, so setting that value white-labels the rest of the app automatically.
Deployment
Deploy the whitelabeled instance exactly as a private instance — see the Private instance guide. The only difference is the branding config and, if applicable, the reseller's own Stripe account.