Skip to main content

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 system field

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.

FieldPurpose
CurrencyDetermines rate plan interpretation and export file splitting
Sell planWhat the customer is charged
Cost planWhat you pay the carrier. Optional; blank means blank margin
Tax profileApplied as a copy — see below
Billing recipientsInvoice email addresses, multiple allowed
TimezoneInvoice period boundaries, out-of-hours fraud windows
Invoice templateBranding, language, footer
Account referenceYour customer ID, matched against the PBX parm1 field
Payment terms overrideOverrides the global default due-date rule
Reminder overridesOverrides the global reminder schedule
Detection overridesPer-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.

ClassificationEffect
BillableCalls over this trunk are rated and charged
TransitCalls are recognised and stored, but not charged
Non-billableIgnored 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.

The two mistakes are not equally expensive

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

FieldExample
LabelPBX-01
PBX URLpbx01.northwind-example.com
Port443
Admin uservodia-billing
Admin password(encrypted at rest, never returned)
System IDa3f9c2d18b7e4506

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

TrunkPBX typeProposalDecision
carrier-primaryregisterbillablebillable
carrier-secondaryregisterbillablebillable
branch-linkgatewaytransittransit

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

FieldExample
Domainhq.northwind-example.com
CurrencyUSD
Sell planBusiness Standard USD
Cost planCarrier A Cost USD
Tax profileUS Telecom (applied 2026-08-01)
TimezoneAmerica/New_York
Billing recipientsaccounts@customer-example.com, ap@customer-example.com
Invoice templateDefault
Account referenceNW-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.