A single-company PBX can hide a lot of mess in one dialplan and a spreadsheet of extensions. A cloud PBX cannot. The moment a second customer shares the same FreeSWITCH or Asterisk cluster, every INVITE, recording, CDR and admin click needs a tenant id — or you will leak calls, numbers and voicemail.
This is the architecture I use when the product is “phone system as software”: many companies, one platform, APIs, and an operations team that is not SSH’ing into a customer VM for every new extension.
Tenant model first
Define objects before you define Sofia profiles:
- Tenant — the billed customer: domains, limits, trunks they may use, feature flags
- Users — people who log into the portal, with roles (owner, admin, agent, receptionist)
- Extensions / devices — SIP credentials, WebRTC users, webrtc vs desk phone
- Numbers — DIDs owned by the tenant, not by “the server”
- Routing artifacts — IVRs, queues, time conditions, ring groups, outbound rules
- Integrations — CRM webhooks, recording buckets, AI agents, SMB/S3 for media
Every SIP request, HTTP API and background job must carry tenant context. Naming conventions in a shared context (“cust17-1001”) are not isolation. They are a future incident report.
Where tenancy lives in the telecom layer
Common patterns, from weaker to stronger:
- Shared engine, tenant in dialplan/XML — FusionPBX-style. Fast to start. Isolation depends on how strictly you generate XML, domains and ACLs.
- Shared media, dedicated routing service — the switch is dumb-ish; a control plane decides destinaton per INVITE using tenant policy.
- Pooled nodes with tenant affinity — large tenants or noisy neighbors get pinned capacity; small tenants share.
- Dedicated engine per tenant — simplest isolation, worst cost. Use for regulated or huge customers only.
Most SaaS PBXs should live in (1) or (2) with a hard rule: tenant A cannot dial tenant B’s extension by guessing, cannot fetch tenant B’s recording URL, and cannot register as tenant B’s user even if they know the extension number.
Practically that means: SIP domains or realms per tenant, unique credentials, TLS where you can, registration limits, and an outbound ACL so a compromised extension cannot become an open trunk.
Provisioning is the product
If humans provision numbers and phones, you do not have a cloud PBX — you have a consultancy. Automate:
- DID assignment from a number inventory (purchased, ported, held, released)
- extension create/disable and SIP password rotation
- device / WebRTC credential delivery (QR, email, MDM — never a shared default password)
- inbound map: number → tenant → IVR/queue/user
- outbound policy: which trunk, which CLI, which country class
The control plane (your SaaS API + database) is the source of truth. The PBX is a projection. When they drift, the database wins and the switch is regenerated. xml_curl, ARI, ESL or a config publisher should be how the engine learns about a new user — not a technician editing XML on disk.
Split two kinds of routing
Business routing is tenant-owned: “press 1 for sales,” lunch hours, overflow to a mobile, AI agent then human. That belongs in tenant config and should be changeable in the portal without a deploy.
Infrastructure routing is yours: which SBC, which carrier, LCR, emergency, fraud ceiling, codec, recording storage region. Tenants must not edit that. If you mix the two in one dialplan, every carrier change becomes a per-customer rewrite.
DID / INVITE → identify tenant (number inventory or SIP domain) → apply tenant business plan (IVR, queue, user, AI) → if outbound: tenant policy + your trunk selector → media / record / CDR tagged with tenant_id
Data, recordings and CDRs
Store CDRs, recordings and voicemail with tenant_id and object-level access control. Signed URLs expire. Support staff should impersonate with an audit log, not by sharing a super-admin SIP user.
Database tenancy is usually a single schema with tenant_id (simpler ops, row-level checks in every query) until a customer needs a data residency boundary — then you isolate storage, not just a column.
Operations: the SaaS half people forget
A PBX that “works” but cannot be operated is not a product. Plan:
- per-tenant and per-node dashboards: registrations, ACD, ASR/ACD, MOS if you have it
- alarms: trunk down, registration cliff, CPS spike, disk for recordings, certificate expiry
- call tracing by Call-ID and tenant, not by grepping a 20GB logfile
- usage and billing events from CDRs, not from “we will reconcile later”
- support tooling: listen-in with consent, retrieve a recording, reset a phone, disable an attacker extension
Capacity is not only “max calls on the VM.” It is media CPU, recording I/O, database write rate, and how many xml_curl hits you generate when 2,000 phones re-register after a blip.
Scale and failover
Keep application services stateless. Keep SIP registrations and dialogs on the telephony tier with a story for node loss: kamailio/opensips or an SBC in front, shared location table, or sticky load balancing you actually tested by killing a node.
Media should be able to live near the user or near the carrier; do not hairpin every RTP stream through one region “because the database is there.”
Before marketing “unlimited extensions,” write down: max registrations per tenant, max concurrent calls, burst CPS, and what you do when they exceed it (queue, reject, upsell) instead of melting a shared sofia profile.
Security baseline
- no SIP on the public internet without rate limits, geo policy and strong credentials
- separate signaling and media security groups in cloud VPCs
- outbound fraud: country classes, velocity, after-hours international lock
- admin SSO, 2FA, and no shared “fusionpbx / fusionpbx” leftovers
- tenant secrets (SIP passwords, API keys) encrypted, rotatable, never in git
A build order that does not collapse
- One tenant, one DID, one extension, CDRs with tenant_id. Prove isolation with a second tenant that cannot see the first.
- Portal + API as source of truth; engine config is generated.
- Inbound IVR and queues as tenant data, not hardcoded XML.
- Outbound with CLI and country policy.
- Recordings and access control.
- WebRTC users on the same tenant model.
- Only then: resellers, white-label domains, AI agents, and dedicated capacity pools.