Systems, Tenants and Trunks
The three Setup screens that describe your estate.
Systems
A system is one Vodia PBX. Vodia Billing can bill any number of them from one install.
Each system holds:
- Label — your name for it
- PBX URL, port — the PBX web interface
- Admin user, admin password — encrypted at rest under
ENCRYPTION_KEY, never returned to the browser. Saving with a blank password keeps the stored one. - System ID — the hash present in every CDR's
systemfield
Test connection verifies the credentials against the live PBX.
Discover pulls domains, owned trunks and DID lists in a single pass and reports what it found per domain. Re-run it whenever you add a domain, a trunk or a block of numbers on the PBX.
Why the system ID matters
CDRs arrive at /cdr from whatever the PBX is configured to send. Vodia
Billing matches each record to a configured system by its system hash, and
records from an unknown system are counted and dropped, never guessed at
and never attributed to the nearest match.
That is why an unconfigured system ID shows up as ingestion working
(accepted climbing on the health endpoint) with nothing appearing in Spend.
Check unknown_system in the ingest counters.
Tenants
A tenant is one PBX domain you bill. Discovery proposes them; you decide which are billable by configuring them.
| Field | Purpose |
|---|---|
| Currency | Determines rate plan interpretation and export file splitting |
| Sell plan | What the customer is charged |
| Cost plan | What you pay the carrier. Optional; blank means blank margin |
| Tax profile | Applied as a copy — see below |
| Billing recipients | Invoice email addresses, multiple allowed |
| Timezone | Invoice period boundaries, out-of-hours fraud windows |
| Invoice template | Branding, language, footer |
| Account reference | Your customer ID, matched against the PBX parm1 field |
| Payment terms override | Overrides the global default due-date rule |
| Reminder overrides | Overrides the global reminder schedule |
| Detection overrides | Per-tenant fraud thresholds |
Numbers
Tenants → Numbers shows the DID list captured for that domain, and can re-fetch live from the PBX for comparison. Number-based recurring charges price DIDs by longest matching prefix against this list, so a tenant with an empty list generates no number charges.
Tax profiles are copied, not linked
When you apply a tax profile to a tenant, the components are copied onto the tenant. Editing the profile afterwards does not change any tenant that already has it applied.
This is deliberate. A tax profile edited in June must never silently re-cut the tax on invoices issued in May, and a linked profile would do exactly that. To roll a change out, re-apply the profile to the tenants you want it on.
Trunks
Every trunk discovered from the PBX starts non-billable, and stays that way until you classify it.
| Classification | Effect |
|---|---|
| Billable | Calls over this trunk are rated and charged |
| Transit | Calls are recognised and stored, but not charged |
| Non-billable | Ignored entirely |
Vodia Billing proposes a classification from the trunk's PBX type: register
suggests billable, gateway and options suggest transit. Proposals are
never applied for you.
A trunk wrongly set billable fabricates charge rows. Those rows are read by the fraud detectors as real traffic, and once an invoice has been issued against them they cannot be un-rated.
A trunk wrongly set transit leaves a gap you can identify and re-rate.
Confirm each proposal deliberately rather than accepting them in bulk.
Trunk groups
Trunks can be grouped, which lets the same destination rate differently depending on which carrier carried the call. Assign a cost plan per group to get accurate margin on a multi-carrier estate.
Unreviewed trunks block invoicing
A tenant with traffic over an unreviewed trunk cannot be finalized. The draft reports it as a blocker. This is the guard that stops an invoice going out with a silently omitted carrier on it.
Worked examples
A system record
| Field | Example |
|---|---|
| Label | PBX-01 |
| PBX URL | pbx01.northwind-example.com |
| Port | 443 |
| Admin user | vodia-billing |
| Admin password | (encrypted at rest, never returned) |
| System ID | a3f9c2d18b7e4506 |
Finding the system ID
Make one test call after pointing the PBX at Vodia Billing, then read Spend → ingest counters:
accepted: 0
unknown_system: 3 (a3f9c2d18b7e4506)
unknown_domain: 0
unknown_trunk: 0
Paste the hash into the system record. The next CDR increments accepted
instead.
A discovery result
Discovery complete — PBX-01
Domains: 2
hq.northwind-example.com 18 numbers
branch.northwind-example.com 0 numbers
Trunks: 3 (own:true only, deduplicated)
carrier-primary register
carrier-secondary register
branch-link gateway
Two things to notice. branch has zero numbers, so no number-based
recurring charge will match it — check the PBX. And there are three trunks,
all currently non-billable.
Classifying those trunks
| Trunk | PBX type | Proposal | Decision |
|---|---|---|---|
carrier-primary | register | billable | billable |
carrier-secondary | register | billable | billable |
branch-link | gateway | transit | transit |
Both carrier trunks must be set billable or their traffic is never charged. This is the step most commonly skipped, and its symptom is an invoice with recurring charges and no usage.
A tenant configuration
| Field | Example |
|---|---|
| Domain | hq.northwind-example.com |
| Currency | USD |
| Sell plan | Business Standard USD |
| Cost plan | Carrier A Cost USD |
| Tax profile | US Telecom (applied 2026-08-01) |
| Timezone | America/New_York |
| Billing recipients | accounts@customer-example.com, ap@customer-example.com |
| Invoice template | Default |
| Account reference | NW-1041 |
What "applied" means for tax
2026-08-01 Tax profile "US Telecom" applied to hq.northwind-example.com
Sales Tax 8.25% usage, recurring, goods
USF 1% usage
PUC 2% usage
911 Fee 0.35 recurring, counts entity
2026-09-14 Tax profile "US Telecom" edited — Sales Tax raised to 8.50%
hq.northwind-example.com still carries 8.25%
The components were copied onto the tenant when applied. Editing the profile changes nothing until you re-apply it. That is what stops a June edit from re-cutting the tax on invoices issued in May.