Skip to content

Plans and Quotas

What this is

Every ComStack project has a set of capabilities — which features are active, how many pages it can hold, how many locales it supports, whether it can make phone calls. This page describes the system that controls all of it: modules, plans, tiers, and templates.

How it works

The entitlement system has four layers:

TermWhat it means
ModuleA feature capability — voice_web, voice_phone, whatsapp, knowledge_base, custom_pages, website, github_sync, chrome_ingest, api_keys. Each can be enabled or disabled independently.
PlanThe materialised entitlement document (settings/plan) that defines what a project is allowed to do right now. Contains per-module limits and project-wide constraints.
TierThe commercial level — trial or standard. Determines which plan limits apply.
TemplateA project template that defines the structural blueprint — which modules are available, what limits apply per tier, and trial duration.
QuotaA numeric limit within a plan — max_pages, max_locales, max_members, max_numbers, max_keys, max_sessions_per_day.

Key principle: Enforcement code reads ONE document — settings/plan. It never needs to know about templates, coupons, or subscriptions. This makes the enforcement layer dead simple and the commercial layer infinitely flexible.

When to use it

Consult this page when you need to understand why a project operation is returning a quota error, what limits apply to a given tier, or how plan limits are materialised from project templates.

Parameters / fields / inputs

Modules

Module configuration lives on the project root document at modules.*.

ModulePurposeKey config fields
voice_webBrowser-based live voice agentenabled, voice, greeting, instructions
voice_phonePSTN phone calls via Twilioenabled, numbers[]
whatsappWhatsApp Business voice via Twilioenabled, numbers[]
knowledge_baseAgent-optimised documentation pagesenabled, access, locales[]
custom_pagesHuman-facing rich content pagesenabled, access, locales[]
websitePublished Starlight documentation siteenabled
github_syncSync content from a GitHub repositoryenabled
chrome_ingestIngest web pages via Chrome Extensionenabled
api_keysMCP/HTTP API key authenticationenabled

access is now only a default-seed. Per-page gating moved to the doc-level audience field (public · agents · members · internal). The module access value seeds the audience of new pages of that type and is otherwise inert.

Plan schema

{
"tier": "trial",
"status": "active",
"modules": {
"website": { "enabled": true, "max_pages": 10 },
"voice_web": { "enabled": true, "max_sessions_per_day": 5 },
"voice_phone": { "enabled": false },
"whatsapp": { "enabled": false },
"knowledge_base": { "enabled": true, "max_pages": 20, "max_locales": 2 },
"custom_pages": { "enabled": true, "max_pages": 10, "max_locales": 2 },
"github_sync": { "enabled": false },
"chrome_ingest": { "enabled": true },
"api_keys": { "enabled": true, "max_keys": 1 }
},
"max_members": 2,
"expires_at": "2026-05-24T14:00:00Z",
"source": "trial",
"coupon_code": null
}
FieldTypeDescription
tierstring"trial" or "standard". Informational — enforcement uses the limits, not the tier name.
statusstring"active" or "suspended". A suspended plan blocks all project operations.
modulesmapPer-module entitlement. Each key matches a module name.
max_membersnumberMaximum project members across all roles.
expires_atTimestamp | nullWhen the plan expires. null = no expiry (paid plans).
sourcestringHow this plan was created: "trial", "coupon", "migration", "admin", or "stripe".
coupon_codestring | nullThe coupon code redeemed to activate this plan. null for trials.

Tiers

Trial

  • Not all templates allow trials — check trial.allowed on the template.
  • Duration is configurable per template (default: 14 days), stored in expires_at.
  • Limits: restrictive — fewer pages, fewer locales, no phone/WhatsApp, limited members.
  • When expires_at passes, all authenticated endpoints return 403 Trial expired. Project data is preserved — upgrading restores access.

Standard

  • Activated via coupon, admin override, or payment.
  • No expiry (expires_at: null).
  • Limits: generous — 200+ pages, 20 locales, phone/WhatsApp enabled, 50 members.

Enforcement

All enforcement reads settings/plan. Enforcement failures return 403.

CheckPlan field
Module enabled?modules.{type}.enabled
Page limitmodules.knowledge_base.max_pages or modules.custom_pages.max_pages
Locale limitmodules.knowledge_base.max_locales
Member limitmax_members
API key limitmodules.api_keys.max_keys
Phone number limitmodules.voice_phone.max_numbers
Live sessions/daymodules.voice_web.max_sessions_per_day
Trial expired?expires_at

The live-session quota is enforced atomically before a Gemini Live token is minted — a Firestore transaction reads the per-(project, user, UTC-day) counter and refuses with 429 if the limit is reached, preventing concurrent burst past the limit.

Template → Plan materialisation

When a project is created:

  1. Read the project template.
  2. Determine tier: trial if no coupon and trial.allowed; standard if coupon provided.
  3. For each module in the template: if available: false, skip; otherwise read the tier-specific config and write to settings/plan.
  4. Set max_members from the tier root.
  5. Set expires_at from trial.duration_days (or null for standard).

Upgrading patches settings/plan directly — no enforcement code changes needed.

Project templates

Two templates are available today:

TemplateBest for
real-estate-euEU/Spain real estate agencies — voice, multilingual, property listings
generic-businessAny business without a dedicated vertical — FAQ/knowledge-heavy, multi-locale, voice

Onboarding picks the template based on the extracted industry (real-estate industries → real-estate-eu; everything else → generic-business). The selection is always shown to the user and is overridable.

Project status lifecycle

Separate from the plan, each project has a status field:

StatusSite accessible?Can publish?Can configure?
setupHosting exists but nothing deployedYes (preview)Yes
livePublic at site_urlYesYes
pausedServes maintenance pageNoYes
archivedSite deletedNoNo

Going live requires: at least one page, a site title set, and an explicit request to set status: "live".

Example

Checking why a page creation is failing with a quota error:

  1. Call get-project-state — the response includes plan details and module status.
  2. Check modules.knowledge_base.max_pages against the current live page count.
  3. If at the limit, you need a plan upgrade (contact support) or delete archived pages first.

A 403 with "error": "Quota exceeded: knowledge_base.max_pages (20)" means the project is at its plan limit. A 403 with "error": "Trial expired" means expires_at has passed.

Common errors

ErrorCauseFix
403 Quota exceeded: knowledge_base.max_pagesHit the page limit for the current planUpgrade the plan or remove archived pages
403 Trial expiredexpires_at passedContact support to upgrade
403 Module not enabledThe module is disabled in settings/planEnable the module via the module settings API
429 on live session creationmax_sessions_per_day reachedWait until UTC midnight reset or upgrade for a higher limit

Last updated: