Skip to content
DocsStart free

Multi-Tenant & White-Label

SIP.IO is multi-tenant from the ground up, with first-class support for resellers and white-label operators. This page explains the account hierarchy and how tenancy flows through the system.

An account is a tenant. Accounts form a tree via parent_id:

  • A platform-direct account has parent_id = NULL.
  • A reseller account (is_reseller = 1) can own child accounts, brand them, and bill them.
Platform
├── Account: Acme Corp (platform-direct)
└── Account: VoiceReseller Inc. (is_reseller = 1)
├── Account: Bistro Group (white-labeled under VoiceReseller)
└── Account: City Clinic

Every tenant row in the data model carries an explicit, indexed account_id, so tenancy is never implicit: it’s a column you can see, index, and enforce.

Each account carries its own branding and defaults, which children inherit unless overridden:

FieldPurpose
brand_name, brand_domainWhite-label brand and the portal/API host for a reseller.
timezoneDefault timezone for the account (used by schedules unless a schedule overrides it).
languageDefault prompt language (one of the 16 supported languages).
voice_genderDefault narrator gender for system prompts (male / female).
default_cid_numberDefault outbound caller-ID number.
did_default_dest_kind / did_default_dest_idWhere unrouted DIDs land.

A reseller serves its own customers under its own SIP domains (sip_domain) and brand domain, so end customers never see the platform.

An account can answer on several SIP domains: its own plus white-label vanity domains. The domain is the realm used for digest authentication, so 1002@acmeA and 1002@acmeB are distinct identities even with the same extension number. Registration lookups are domain-scoped (use_domain), keeping tenants cleanly separated at the registrar.

Tenancy isn’t just a data convention: it’s enforced at each decision point:

  • /auth resolves the device by (auth_username, realm) and returns the owning account_id; the SIP signaling layer binds that account context to the registration.
  • /route scopes every lookup by account_id: DIDs, extensions, outbound routes, transforms, and caller-ID rules are all matched within the tenant.
  • stateful edge objects are per-account. The PresenceDO and BalanceDO are addressed by account_id, so one tenant’s presence, ACD, CAC counters, and wallet are physically isolated in their own object.

Each account has its own concurrency ceilings (max_in, max_out, max_dialer) and its own CAC counters in its PresenceDO. Spend is governed by a separate retail wallet (BalanceDO) plus the wholesale carrier’s authoritative balance, so one tenant can’t consume another’s capacity or budget.

The account tree carries the billing relationship: a child account’s usage rolls up to its reseller parent. Wholesale termination and balance are handled by the carrier integration per subaccount, while the platform tracks the retail relationship.

  • Put each end customer in its own account; give resellers is_reseller = 1 and their own brand_domain.
  • Set the account’s timezone, language, and voice_gender once, and schedules and prompts inherit them.
  • Use per-account max_* ceilings as the first line of capacity isolation; layer rate rules on top for finer control.