Voxa Documentation
On this page

Voxa Documentation

Everything you need to install, connect, and run Voxa — the read-only call-telemetry and phone-refresh-planning platform for Cisco Unified Communications Manager.

The one guarantee: Voxa never writes to CUCM. Every interaction with your cluster is a read — AXL SQL queries, RisPort registration lookups, and the phones' own web pages. The application log at INFO level is the evidence. If a change would ever make Voxa write to CUCM, that is a defect — report it.

What Voxa does

  • Live inventory — every phone in the cluster: model, lifecycle (current / end-of-sale / end-of-support), registration state, IP, serial, and the access-switch port it sits on.
  • Refresh planning — what each model maps to, quantity lines for a quote, and a per-closet PoE budget before and after the swap.
  • Call telemetry — CDR/CMR ingest with call search, cradle-to-grave traces with a SIP ladder, MOS voice-quality scoring, and trunk capacity.
  • Operations — certificate expiry across CUCM nodes and CUBEs, emergency locations (built-in E911 mapping, or CER-aware for shops running Cisco Emergency Responder), reports, a read-only JSON API, and Active Directory sign-in.

Architecture & data sources

Voxa is a server-rendered web application (FastAPI + Postgres) that runs on-prem in your network as a Docker stack behind nginx with TLS. Data flows one way: a collection reads three sources, merges them into one row per phone, and stores the result. Pages read only the database — no page ever calls CUCM directly, so the inventory stays available even during a cluster outage.

FieldSourceNotes
Model, description, device pool, configured firmware, DN AXL executeSQLQuery One SQL query, paged. Publisher only.
Registration status, IP, running firmware, CM node RisPort70 Live state; answers on any node.
Serial, hardware rev, switch name + port, VLAN Phone web server Needs “Web Access” enabled on the phone; blank when unreachable.
Call detail & voice quality CDR / CMR files Delivered by CUCM’s Billing Application Server over SFTP.
PoE class, lifecycle, replacement Model catalog Editable in the UI; verify entries against Cisco data sheets.

Requirements

Server

  • A Linux VM (vSphere or otherwise) with Docker Engine + Compose. 2 vCPU / 4 GB RAM / 40 GB disk is comfortable for several thousand phones.
  • Network reachability from the VM to: the CUCM publisher on TCP 8443, the phone subnets on TCP 80/443, your SFTP server on TCP 22, and (optionally) access switches on UDP 161 for SNMP PoE polling.
  • A DNS name and a TLS certificate from your internal CA (the installer can generate a temporary self-signed one).

CUCM

  • CUCM 12.5 through 15 (set the matching AXL version per cluster).
  • A read-only service account — see CUCM service account.
  • For telemetry: CDR enabled and a Billing Application Server pushing to SFTP.

Installation

Voxa ships as two files: a deploy bundle (compose, nginx, installer, docs — no application source) and a Docker image tarball. On the server:

# unpack the bundle next to the image tarball, then
tar xzf voxa-deploy-1.0.0.tar.gz
cd voxa-deploy-1.0.0
./install.sh

The guided installer checks Docker, loads the image and pins the exact version, writes .env.prod with generated secrets (48-character SECRET_KEY and database password, chmod 600), offers a self-signed TLS certificate for your hostname, and starts the stack. It is idempotent — re-running never overwrites secrets you already have.

Then open https://<your-host>/ from any computer on the network. The first-run wizard creates your administrator account in the browser — no shell required. That account is a local break-glass admin: it keeps working even if Active Directory is down.

Production safety rails. Voxa refuses to start in production if login is disabled (AUTH_DISABLED) or the SECRET_KEY is still the built-in default. Replace the temporary self-signed certificate with one from your internal CA (same filenames in deploy/certs/) as soon as practical.

CUCM service account

Create an Application User in CUCM (User Management → Application User → Add New), e.g. voxa-ro, and add these access control groups:

Access control groupWhat it grants
Standard AXL API AccessexecuteSQLQuery — the phone configuration
Standard CCM Admin UsersRisPort authorization
Standard CCM Server MonitoringRisPort device state
Standard RealtimeAndTraceCollectionRisPort on some releases

Prefer tighter permissions? Build a custom role limited to AXL read and the serviceability pages; the connection test will tell you if you cut too deep.

Point Voxa at the publisher. AXL runs only there — a subscriber returns an error that reads like a permissions problem and will cost you an hour. One publisher connection covers the whole cluster; subscribers are discovered automatically.

For the phone-level data (serial, switch port), enable Web Access on the phones or their Common Phone Profile. Phones with web access disabled still appear in the inventory — just without those two fields.

Connecting a cluster

  1. Go to Settings → CUCM clusters → Add a cluster.
  2. Enter a name, the publisher host/IP, the service account and password, and the AXL version matching your CUCM release (e.g. 15.0 for CUCM 15). Leave TLS off if the publisher presents a self-signed certificate.
  3. Click Save & retest. A green Connected badge with the discovered node list means you’re ready. A red badge? The Connection logs underneath name exactly which check failed — AXL, RisPort, or the phone scrape — and the missing role it usually implies.

Voxa supports multiple clusters; each phone is tagged with the cluster it came from.

Collections

A collection is the read pass that refreshes the inventory: AXL configuration, then RisPort live registration, then the phones’ own web pages (25 concurrent requests — deliberately polite so it never looks like a port scan to an IPS). Run one from the dashboard (Collect from CUCM) or schedule it on the server:

# systemd timer shipped in the bundle, or cron:
make collect

Each run records what changed — phones that appeared, moved switch ports, re-registered, or dropped — feeding the History page and the fleet trend.

Inventory & dashboard

  • Tiles — total phones, past-end-of-support EOL, end-of-sale EOS, registered %, and serial / switch-port coverage. Coverage reflects how many phones answered their web page: phones with Web Access disabled or unreachable subnets lower it without affecting the rest of the row.
  • Cluster nodes — live registrations per publisher/subscriber, with an imbalance call-out when one node carries an outsized share.
  • Phones — the full inventory: search by name, description, DN, IP, serial, or switch; filter by lifecycle, site, model, cluster; track swap status per phone (planned → ordered → installed → verified). The Device column stays pinned while you scroll.
  • Phone 360 — click any phone for everything known about it, including its change history and call activity.

Refresh plan & PoE budgeting

The Refresh plan turns the inventory into quantity lines for a quote under your chosen replacement family, per site. The PoE budget shows, per access switch, what today’s phones draw and what the replacements will — so you find the closet that runs out of power before the order ships.

PoE figures use IEEE class ceilings, not typical draw — the ceiling is what a switch actually reserves per port, which is the number that runs a closet out of power. With SNMP enabled, Voxa also shows the switch’s real measured draw next to the estimate.

The Model catalog drives lifecycle and replacement mapping. Entries ship as working defaults; the dashboard flags any model in your cluster that hasn’t been fact-checked. Confirm each against its Cisco data sheet or EoL bulletin on the catalog page and mark it verified before a power budget leaves the building.

Call telemetry (CDR/CMR)

Voxa reads the call-detail (CDR) and call-quality (CMR) files CUCM produces. It never polls CUCM for them — CUCM’s Billing Application Server delivers files to your SFTP server, and Voxa pulls from there.

On CUCM

  1. System → Enterprise Parameters: set CDR Enabled Flag to True (and, if you want unanswered calls, CDR Log Calls with Zero Duration Flag).
  2. Cisco Unified Serviceability → Tools → CDR Management: add a Billing Application Server pointing at your SFTP server. CUCM pushes files on a rolling schedule.

In Voxa

  1. Settings → CDR SFTP: enter the SFTP host, port, username, password, and remote directory. Test connection and Pull & ingest now use the values in the form — no save required.
  2. Tick Enabled and set an auto-pull interval (e.g. 60 minutes), then Save. Voxa pulls, ingests, archives, and prunes on that schedule — no cron.

Ingest is idempotent: files already pulled are skipped by name against the drop directory and its processed/ archive, so nothing is ever double-counted. Keep processed files (days) bounds local disk (90 days is a good default — roughly 2.5 GB of raw CSV per million calls). The SFTP server’s host key is pinned on first connect and verified on every pull; a changed key is flagged as a possible man-in-the-middle.

Once records flow, Telemetry → Calls gives you call search with quality filters, Analytics the volume/busy-hour/quality picture, and Capacity Erlang-based trunk utilization. Every timestamp displays in the timezone you set (Settings → Application → Display timezone); storage is always UTC.

MOS quality scoring

MOS (Mean Opinion Score, 1.0–5.0) estimates perceived voice quality from packet loss, jitter, latency, codec behaviour and packet-loss concealment. Voxa reads it from each leg’s CMR — including modern CUCM releases that carry it as MLQKav inside the varVQMetrics blob — and classifies every page with the same bands:

MOSRatingMeaning
4.30 – 5.00ExcellentToll quality; nothing to do
4.00 – 4.29GoodHealthy — the normal range for G.711/G.722
3.60 – 3.99FairAudibly imperfect; watch the trend
3.10 – 3.59PoorUsers notice; investigate the path
< 3.10BadSeriously degraded; trace the call

A call’s listed MOS is its worst measured leg — what the caller actually heard. Legs without a reported MOS are excluded from every average, never counted as zero, and the sample size is always shown. Opening a call shows the telemetry behind the score (packet loss, jitter, latency, codec, concealed seconds) with a deterministic “likely contributor” — high packet loss, high jitter, high latency, or multiple impairments — only when the numbers actually support it.

Certificates & CUBEs

The Certificates page reads the TLS certificate every CUCM node serves (Tomcat 8443, CallManager SIP-TLS 5061, CAPF 2443) with a plain handshake — no CUCM API, still read-only — and shows subject, issuer, self-signed vs CA, and days to expiry. CUCM nodes are discovered automatically; add your CUBEs / border elements by host with per-device ports (5061 SIP-TLS, 8443 or 443 management).

Failures are classified so you know what to fix: DNS (name didn’t resolve), no response (firewall or nothing listening), port closed, or TLS handshake failed — the last one on a CUBE usually means the secure trunk requires mutual TLS, in which case read the same identity certificate from the CUBE’s HTTPS management port instead.

Emergency locations (E911 / CER)

Voxa adapts to how your shop handles E911 — set it under Emergency (E911) → E911 is managed by:

Voxa locations (built-in)

For sites without dedicated E911 tooling: define dispatchable locations and map switch-name prefixes or IP subnets to them. Voxa resolves every phone to a location from its discovered switch port / IP, shows coverage, and exports the device · DN · IP · switch/port · location record as CSV.

Cisco Emergency Responder (CER)

If you run CER, CER is the source of truth — ERLs, switch-port phone tracking, and ELIN routing live there, and Voxa deliberately does not offer a second, manually-maintained list that would drift from it. In CER mode Voxa instead:

  • monitors your CER servers' Tomcat certificates (register the publisher and subscriber; they join the Certificates page — an expired CER cert breaks the CER admin pages and its tracking services);
  • keeps per-phone switch/port visibility in the inventory — the same CDP data CER's tracking depends on — and flags port moves in History;
  • backs the refresh plan with closet/port detail, so CER's port-based ERL assignments still land after a phone swap.

Reports & export

The Reports engine produces the documents a refresh project needs — inventory, refresh plan, PoE budget, E911 — as CSV, XLSX, or print-to-PDF. Every table in the app also exports CSV directly, and the read-only JSON API serves the same data to your own tooling.

Sign-in & Active Directory

Voxa supports local accounts and Active Directory (LDAP). To enable AD:

  1. Settings → LDAP / Active Directory: set the LDAP URL (ldaps://dc01.corp.example.com:636), a read-only bind service account, the user base DN, and optionally a required group.
  2. Use Test Active Directory — it verifies each step separately: config present, service-account bind, user lookup, group membership, and (with a test password) one real credential bind.
  3. Set auth backend to ldap and Save.

How it authenticates: Voxa finds the user’s DN with the service account, then re-binds as that user with the password they typed. The AD password is never stored — an AD user’s row in Voxa carries no password hash at all.

Break-glass, always. Local accounts are checked first, so your local admin signs in even when every domain controller is unreachable. An AD outage can never lock every administrator out — and a deactivated account stays locked out regardless of a successful directory bind.

Users & roles

  • Admin — everything, including Settings, Integrations, the model catalog, and certificate management.
  • Member — the read/analysis surfaces: inventory, telemetry, plans, reports.

AD users are created automatically on first sign-in (as members; promote in the database or via scripts/manage.py). Deactivate accounts rather than deleting them to preserve the audit trail.

Backups & upgrades

Back up

make backup   # writes backups/voxa_YYYYMMDD_HHMMSS.sql.gz

A single pg_dump — schedule it nightly and copy the dumps off-box. Keep .env.prod in your server backup rotation too: losing SECRET_KEY only signs everyone out; losing the database loses your inventory and call history.

Upgrade

make backup
make image-load FILE=voxa-image-1.5.0.tar.gz
# edit .env.prod: VOXA_IMAGE=voxa:1.5.0
make up && make logs

Database migrations run automatically on start, under a lock so multiple workers can’t collide. An upgrade never touches your data volume, .env.prod, TLS certs — or CUCM. To roll back, restore the previous image tag (and, if the release changed the schema, the pre-upgrade dump — which is why the backup comes first).

JSON API & webhooks

A read-only JSON API under /api/v1 serves phones, calls, and summaries with bearer-token auth. Create tokens on Integrations — the plaintext is shown once, only the hash is stored. Outbound webhooks (collection finished, ingest finished) are off by default, per-hook and globally, and sign their payloads.

curl -H "Authorization: Bearer $TOKEN" https://voxa.corp.example.com/api/v1/phones

Security model

  • Read-only against CUCM — no write API is ever called; the AXL account needs no write role. Log lines at INFO record every CUCM interaction.
  • On-prem, no phone-home — Voxa makes no outbound connections except the ones you configure (your CUCM, your phones, your SFTP server, your switches, your domain controllers).
  • Secrets — session cookies are signed (48-char generated key), HTTPS-only in production, SameSite=Lax. Passwords for local accounts are scrypt hashes; AD passwords are never stored. Connection secrets live in the database, masked in the UI, on a volume you control.
  • SFTP host-key pinning — trust-on-first-use; a changed key is surfaced as a possible man-in-the-middle.
  • Boot guards — production refuses to start with login disabled or a default secret key.
  • Polite scraping — phone web reads are capped at 25 concurrent so collections never resemble a scan.

Troubleshooting

First stop, always: make check on the server. It tests AXL, RisPort, and a phone scrape independently and names the likely missing role for whichever fails. In practice it is nearly always the service account, not the app.

SymptomLikely causeFix
AXL fails, looks like bad credentials Pointed at a subscriber Use the publisher — AXL only runs there.
RisPort returns nothing / SOAP fault Missing serviceability role Add Standard CCM Server Monitoring (and RealtimeAndTraceCollection on some releases).
Serial / switch-port coverage low Web Access disabled, or the collector can’t reach phone subnets Enable Web Access on the Common Phone Profile; check routes/firewalls from the Voxa VM; raise the scrape timeout in Settings.
Hostname works from your PC but not in Voxa The container lacks your DNS search domain Use the FQDN or IP, or add your domain to the stack’s dns_search.
SFTP test: “host key CHANGED” Server rebuilt — or an actual MITM If expected, clear the pinned key (.voxa_sftp_known_hosts in the CDR directory) and retest.
Calls ingest but no MOS Legs without K-factor metrics Normal for some devices/short calls. MOS appears where the phone reports MLQKav; averages exclude unmeasured legs.
Times look shifted by hours Display timezone unset Set Settings → Application → Display timezone (storage is UTC by design).
Locked out of the UI AD outage or misconfig Sign in with the local break-glass admin — it always works. Reset it via scripts/manage.py if needed.

FAQ

Can Voxa change anything on my cluster?

No. There is no code path that writes to CUCM, and the service account needs no write permission. This is the product’s founding constraint.

Does it need agents on the phones?

No. Phones are read over the web interface they already serve. Phones with web access disabled still appear in the inventory from AXL/RisPort data.

How current is the data?

Inventory is as fresh as your last collection (run on demand or scheduled). Registration state is live at collection time via RisPort. Call records arrive on the CDR auto-pull interval you set.

Where does my data live?

In a Postgres database on your VM, in your network. Nothing is sent anywhere else — there is no cloud component and no telemetry back to us.

What happens during a CUCM outage?

Voxa keeps serving the last-collected inventory — pages never call CUCM directly. Collections resume when the cluster is reachable again.

Multiple clusters?

Yes — add each publisher in Settings; every phone is tagged with its cluster and the dashboard breaks totals out per cluster.

How is it licensed?

A commercial on-prem license sized by fleet, with annual support and updates — see pricing. No per-seat metering, no license server, no phoning home.

Something not covered here? The deploy bundle ships with the full admin guide, CUCM setup, deployment and upgrade runbooks — or get in touch.