ADR-0207: Navin Enterprise Telephony & Voice AI Agent, enterprise telephony and AI voice above the Control Plane
- Status: proposed (2026-11-05)
- Deciders: Navin Enterprise AI platform team, brief 01 mission (
agent_uses_cases/tel_note_meeting/01_NAVIN_AGENT_TELEPHONIQUE_COMPLET.md) - References: ADR-0173 (telephony engine layer of the platform: Asterisk, Kamailio, LiveKit SIP, SeaweedFS,
multi-tenant and HA, read only), ADR-0101 (licence policy), ADR-0205 (Notes, model of a product with its own UI),
product conventions
navin-enterprise-telephony/CONVENTIONS.md, audit of the existing platformdocs/fr/guides/telephony/existing-audit.md, real scenariosdocs/fr/guides/telephony/scenarios.md, OPA packageeai.telephony_agent(deploy/policies/opa/telephony_agent.rego, teststelephony_agent_test.rego, keytelephony_agentofdeploy/policies/opa/data.json), OpenFGA fragmentdeploy/policies/openfga/products/telephony.fga(+telephony.tests.fga.yaml,telephony.tuples.yaml), Manager templatenavin-telephony-agent(navin-manager/agent-templates/navin-telephony-agent.yaml,published: false,navin-enterprise-telephony/navin-integration/template/manifest.yaml).
Context
Brief 01 asks for a complete enterprise telephony product: PBX, IVR, contact centre (ACD), WebRTC softphone, SIP trunks, supervision, legitimate campaigns, recording and transcription with consent, post call AI, multilingual AI voice agents, business tools, CRM integrations, FinOps, high availability, installation by the Navin Manager and publication of the template only after the explicit validation of the owner. It sets the topology rules (section 3): Asterisk is the PBX, Kamailio the SIP edge, LiveKit the realtime and AI voice framework (never a PBX), LiveKit SIP a distinct service never assumed present; Kamailio is optional for a small site.
The engine layer already exists. ADR-0173 (accepted on 2026-11-03) added the services asterisk, kamailio,
rtpengine, livekit-sip, livekit-egress, livekit-ingress and livekit-agents to the Manager catalog, and the
domain eai.domains.telephony to the Control Plane: tenant PBX resources, compilation and publication through AMI,
JSONRPC and Twirp with versions and rollback, calls (CDR and CEL), recordings to SeaweedFS, toll fraud protection,
ephemeral WebRTC credentials, the navin-voice worker. The audit of 3 November 2026 (existing-audit.md) fixed what is
reused, and the real bench (Asterisk 20.6.0, Kamailio 5.7.4 and 6.0.8, RTPengine, SIPp 3.7.2) ran 29 of the 31 end to
end scenarios, the other two (S30, S31: AI voice through LiveKit SIP and AI to human transfer) being blocked because
LiveKit SIP has no binary. What is missing is the business layer the brief asks for: governance, contact centre,
governed AI voice, campaigns, consents, quality, FinOps, UI and Manager integration.
Decision
- Two layers. The engine layer stays the one of the platform (ADR-0173) and is never rebuilt. The product
navin-enterprise-telephony/(product idtelephony, sectorcross_industry, servicenavin-telephony-agent) is built above the Control Plane: it drives the engines only through the/v1/telephonyAPI and theenterprise_aiSDK (client.telephony), never through AMI, ARI or JSONRPC directly, and never installs, compiles or configures an engine. Own Next.js 16 + React 19 + MUI 9 frontend (softphone, live wallboard, IVR editor), with no rootproduct.manifest.json, as in ADR-0205. - Never a second PBX configuration source. The engine configuration (trunks, numbers, extensions, routing, IVR,
queues, voice agents, pools, published versions) lives in the Control Plane. The product keeps governance metadata,
submitted versions, approvals and the
cp_*_idreferences, then asks the Control Plane for the preview, the publication and the rollback. LiveKit and Asterisk events are received by the Control Plane, not by the product. - Engine layer extensions (section 2.1 of the conventions, work package TEL-3).
POST /v1/telephony/originateinto the outbound context of the tenant (every outbound rule applies, the presented number is only a number of the tenant), recording control of an active call (MixMonitor), park and supervision (listen,whisper,bargethrough ChanSpy), queue member pause and queue status (QueuePause, QueueStatus),tuningobject of the voice agents (moderealtimerefused withtelephony_realtime_unavailable), voice tools from the tool registry of the platform with apending_approvalanswer, least privilege rolestelephony_supervisorandtelephony_agentand explicit service key scopestelephony:read,telephony:originate,telephony:record,telephony:supervise. - Identities (section 2.2). Request path: the BFF sends the user access token (audience
navin-telephony-api), the product resolves the context withGET /v1/meand forwards the same token to the Control Plane for the request only (administration bytenant_admin, self service of agents and supervisors). Background path: the worker uses the service API key of the instance (openbao://orenv://reference) after the product authorized the job when it was created; the job keeps the id of the requesting principal for audit. No user token is ever stored in Redis, a job, a log or the database. The tenant never comes from the browser nor from the SIP headers of an incoming call. - Data model. PostgreSQL, schema
telephony, tables of section 23 of the brief (call_sessions,campaigns, ...),tenant_idandproject_idon every table, RLS enabled and forced throughtelephony.current_tenant()/telephony.current_project(), rolesnavin_telephony_migrate(DDL),navin_telephony_app(NOBYPASSRLS) andnavin_telephony_worker; Alembic with named revisions (0001_foundation,0002_pbx_calls,0003_ai_campaigns_finops). Numbers in E.164 under RLS with a salted SHA-256 hash for deduplication, opt-outs and exclusions, masked everywhere else. Amounts as decimals with a currency, tariffs provided by the customer with their source, never invented. - Two layer authorization through the Control Plane. OpenFGA for relations:
telephony_instance(rolestenant_admin,telephony_admin,supervisor,cc_agent,quality_reviewer,campaign_manager,compliance_officer,auditor,viewer),telephony_queue(queue members and supervisors),telephony_call(instance, queue,handler),telephony_campaign,telephony_voice_agent; the backend only checks thecan_*relations and writes thehandlerandqueuetuples of a call itself. OPAeai.telephony_agentfor the decisions above the relations: PBX publication and trunk change (maker-checker, an emergency route is never removed nor intercepted), outbound call (allowed prefixes, international and premium only with the instance permission, presented number that is a DID of the tenant withcli_allowed, opted out or excluded contact, quiet hours in the time zone of the contact, open budget circuit), recording (consent required by the mode and the jurisdiction, pause during a payment), supervision (modes allowed by the governance, whisper and barge for supervisors only), external transfer (allowed prefixes), recording access (purpose), deletion (legal hold, minimum retention), transcript export, tools by risk and authentication level, campaigns, SMS, CRM writes, voice agent publication (models allowed for the classification, budget, AI disclosure), protected settings, retention and legal hold release. Fail closed, sorted reasons, obligations (audit, masking, expiring link, recording notice, minimal fields, verification before announcement), every decision audited. The configurable lists are under the keytelephony_agentofdata.json; the recording consent rules per jurisdiction (FR, BE, CH, GB, DE and an example list of US all party consent states) are a configurable example to be validated by the counsel of the customer, not legal advice; an unknown jurisdiction requires explicit consent. - Cascaded AI voice by default. The
navin-voiceworker (LiveKit Agents) chains STT, LLM and TTS through the model gateway of the platform (LiteLLM only), Silero VAD, interruption handling and turn taking tuning; realtime speech to speech is refused (realtime_unavailablein the product,telephony_realtime_unavailablein the Control Plane) as long as the gateway cannot carry a governed realtime session. Twelve presets (reception, AI switchboard, customer support, sales qualification, appointment booking, callback, order status, contract information after authentication, insurance claim assistance, confirmation, internal FAQ, after sales service) with AI disclosure, banking and insurance operations refused belowauth_level = strong, immediate handoff to a human on emergency or distress. - Tools through the platform gateway. The 21 typed tools (section 8 of the conventions) are registered as tool
manifests in the tool registry of the platform; the voice worker reaches them through the tool gateway (scopes,
OPA, DLP, approval, rate limit, audit) and the product executes them with its own checks (relation,
telephony.tool.execute, idempotency, timeout, circuit breaker, post execution validation). A tool waiting for an approval is spoken as "submitted", never as "done"; no CRM write, booking or message is announced before its execution is verified. - Consent, emergency and payment. Consent mode
explicit,announceornot_recordedper instance, disclosure of the AI nature of the agent when the jurisdiction or the settings require it, consents and opt-outs recorded with their evidence. Emergency numbers are routed by the dialplan compiled by the Control Plane, never by a model; the product refuses a node, a rule or an agent that would intercept them. The product never stores card data, pauses the recording during a payment step according topci_pause_on_paymentand never claims PCI-DSS compliance. - Licences. Asterisk (GPL-2.0 with the linking protocols exception), Kamailio (GPL-2.0 or later) and RTPengine
(GPL-3.0 or later) stay separate programs used through their network interfaces (SIP, AMI, ARI, JSONRPC); no
copyleft code is linked into the product, which is never announced as "100 % MIT/Apache". LiveKit and its services
are Apache-2.0. Every new dependency is checked and listed in
docs/LICENSES.md. - Observability. Grafana, Loki and Tempo, cited by the brief, are AGPL and refused by ADR-0101: they are replaced by Prometheus and Alertmanager (metrics and alerts), Alloy and OpenSearch (logs), OpenTelemetry and Jaeger (traces) and Opik (traces of model calls). No clear number, transcript text, recording link or secret in logs and metrics.
- Manager integration. Five components
runtime, migrate, backend, worker, web(strategydedicated, servicenavin-telephony-agent), development ports web 3491, API 8493, worker health 8494, Redis 6411; the engines are catalog services required by reference (asteriskrequired,kamailiooptional for a small site and required for HA,rtpengineoptional,livekit,livekit-sip,livekit-agents,livekit-egressoptional). The template staysprepared/published: false; the artifacts arependinguntil they are built.
Consequences
- The product depends on the routes added by TEL-3; until they are merged, the product packages call them through
ControlPlane.requestwith the exact paths of section 2.1 and test them against the fake Control Plane (tests/fakes/controlplane.py), and TEL-15 checks them against the real Control Plane and engines. - One source of truth per piece of data: the Control Plane for the engine configuration, calls (CDR, CEL), recordings and media; the PostgreSQL of the product for governance, contact centre state, AI results, campaigns, consents and FinOps; OpenFGA for relations.
- Every new sensitive action goes through a
can_*relation and aneai.telephony_agentdecision; an action missing from the policy is refused (unknown_action). - Consent rules, prefixes, campaign jurisdictions and quiet hours are defaults configurable per tenant; the customer validates them with its counsel before production.
- The template stays out of the catalog until an explicit validation is given.
Alternatives rejected
- Compiling the dialplan or the PJSIP configuration in the product: rejected, it would be a second PBX configuration source competing with the compiler of the Control Plane (versions, rollback, per tenant context isolation already tested).
- Talking directly to AMI, ARI, JSONRPC or the LiveKit Twirp API: rejected, the engine credentials stay in the Control Plane and isolation, quotas, toll fraud protection and audit would be bypassed.
- LiveKit as the PBX or instead of Asterisk: rejected (rule 2 of section 3 of the brief); Asterisk keeps voicemail, classic IVR, ACD, extensions, PBX recording and transfers.
- Mandatory Kamailio: rejected for a small site (rule 5); it is required for high availability or high volume.
- Realtime speech to speech by default: rejected until the gateway governs a realtime session (DLP, budget, audit).
- Keeping the user token for background jobs: rejected, the service key with explicit scopes is used after the job was authorized when it was created.
- Shared frontend shell of ADR-0180: rejected as for Notes, a softphone, a live wallboard and an IVR editor cannot be described by a product manifest.
- Grafana, Loki, Tempo: rejected (AGPL, ADR-0101).
- Proprietary cloud CPaaS for telephony or voice: rejected, the brief forbids imposing proprietary cloud services.
Known BLOCKED and NOT AVAILABLE items
| Capability | Status | Reason |
|---|---|---|
| AI voice over SIP through LiveKit SIP (S30) | BLOCKED | LiveKit SIP publishes a container image only, no binary, and no container engine is available here; proved against a fake Twirp server only |
| AI to human transfer over SIP REFER (S31) | BLOCKED | same reason; TransferSIPParticipant proved against a fake Twirp server |
| Realtime speech to speech | BLOCKED | until the gateway of the platform carries a governed realtime session |
| Real carrier trunk | NOT AVAILABLE | no carrier trunk is authorised here; tests use SIPp and loopback carriers, never a real call to a person |
| DTMF secure capture through a specialised provider | NOT AVAILABLE | absent from this delivery; the recording is paused during the payment |
Links
- Brief:
agent_uses_cases/tel_note_meeting/01_NAVIN_AGENT_TELEPHONIQUE_COMPLET.md(sections 1, 2, 3, 14, 18, 27, 30, 31) - Conventions:
navin-enterprise-telephony/CONVENTIONS.md - Engine layer:
docs/architecture/ADRs/en/ADR-0173-telephony-platform-asterisk-kamailio-livekit.md,docs/fr/guides/telephony/existing-audit.md,docs/fr/guides/telephony/scenarios.md - Policies:
deploy/policies/openfga/products/telephony.fga,deploy/policies/opa/telephony_agent.rego - Template:
navin-enterprise-telephony/navin-integration/template/manifest.yaml,navin-manager/agent-templates/navin-telephony-agent.yaml - French version:
docs/architecture/ADRs/ADR-0207-navin-enterprise-telephony-agent.md