Multi-tenant cloud PBX admin and extension view

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:

  1. Shared engine, tenant in dialplan/XML — FusionPBX-style. Fast to start. Isolation depends on how strictly you generate XML, domains and ACLs.
  2. Shared media, dedicated routing service — the switch is dumb-ish; a control plane decides destinaton per INVITE using tenant policy.
  3. Pooled nodes with tenant affinity — large tenants or noisy neighbors get pinned capacity; small tenants share.
  4. 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

  1. One tenant, one DID, one extension, CDRs with tenant_id. Prove isolation with a second tenant that cannot see the first.
  2. Portal + API as source of truth; engine config is generated.
  3. Inbound IVR and queues as tenant data, not hardcoded XML.
  4. Outbound with CLI and country policy.
  5. Recordings and access control.
  6. WebRTC users on the same tenant model.
  7. Only then: resellers, white-label domains, AI agents, and dedicated capacity pools.

Related reading


Building a hosted PBX, not a one-off install?

Share how many tenants you expect, FusionPBX vs custom GUI, and whether WebRTC or AI sits on the same cluster. Isolation and provisioning are the first design conversation.

Discuss Your Project