This document describes the EFSwitch media-bug live transcription path that streams call audio to Azure Speech, receives final ASR text over ESL, and posts messages into Expertflow CCM for Agent Desk. It covers runtime components, call lifecycle, multi-tenant CCM routing, APISIX Bearer auth (Voice Connector parity), operational controls, and capacity notes.
1. Overview
1.1 Executive Summary
-
Transcription runs inside EFSwitch via
mod_azure_transcribe(media-bug + Microsoft Speech SDK). Recognition is performed by Azure Speech in the cloud. -
A separate systemd process,
tmb-relay-azure(tmb_app_azure.py), is intentionally thin: ESL events, start/stop TX, map call fields, POST finals to CCM. -
TX attaches only on
CHANNEL_BRIDGE(not early media). Default layout isdual_mono: two mono Azure streams per bridged call (caller + agent). -
The relay does not create the CX conversation; Voice Connector already reserved the agent / conversation. The relay only appends
PLAINmessages to/ccm/message/receive. -
Multi-tenant CCM URL:
https://{FS domain_name}.{CCM_ROOT_DOMAIN}/ccm, withCCM_BASE_URLas fallback. -
When EFCX APISIX auth is on:
AUTH_ENABLED=true+API_USERNAME/API_PASS→POST {tenantFqdn}/unified-admin/keycloakLogin→Authorization: Beareron CCM (shared helperrelay/ccm_auth.py). Default is off (no regression on non-APISIX labs).
2. Architecture Diagram
2.1 Component flow
|
Step |
From |
To |
What happens |
|---|---|---|---|
|
1 |
Customer / Agent |
EFSwitch |
Bridged voice call |
|
2 |
EFSwitch |
|
Media-bug attaches; audio streamed |
|
3 |
Module |
Azure Speech |
Cloud ASR |
|
4 |
EFSwitch ESL |
|
|
|
5 |
Relay (optional) |
|
|
|
6 |
Relay |
CCM |
|
|
7 |
CCM |
Agent Desk |
Transcript visible on conversation |
|
— |
Voice Connector |
CCM |
Creates/attaches conversation (before TX) |
2.2 Sequence Diagram
-
Voice Connector has already created the CX conversation.
-
EFSwitch emits
CHANNEL_BRIDGE(A+B UUIDs) to the relay over ESL. -
Relay applies tenant / call-type / allowlist gates.
-
Relay starts
uuid_azure_transcribeon caller and agent legs (dual_mono). -
Azure returns recognition events; EFSwitch forwards finals to the relay.
-
If
AUTH_ENABLED=trueand no cached token:POST {fqdn}/unified-admin/keycloakLogin→ cachedata.token. -
Relay
POSTs CCM/message/receivewith Bearer when auth is on. -
On HTTP 401: invalidate cache, refresh token once, retry the CCM request once.
-
On hangup / destroy: stop azure_transcribe once per call.
3. Component Responsibilities
|
Component |
Location |
Responsibility |
|---|---|---|
|
|
EFSwitch process; |
Attach media-bugs; Speech SDK encode/stream to Azure |
|
Microsoft Speech SDK libs |
|
Native ASR client libraries |
|
Azure Speech |
Microsoft cloud |
Recognition |
|
|
systemd; typically |
ESL, start/stop TX, CCM POST, auth |
|
|
Shared Azure + Deepgram + |
Optional Keycloak login, token cache, 401 refresh+retry |
|
Voice Connector |
Separate CX Voice service |
Agent reserve + conversation id |
|
CCM |
EFCX |
Persist messages; Agent Desk render |
3.1 What the relay does not do
-
Does not originate SIP or bridge calls
-
Does not call StartConversation / create CX conversations
-
Does not store long-term transcript archives (beyond operational logs)
-
Does not replace Voice Connector APISIX NodePort / gateway install work
4. Call Lifecycle and Gating
4.1 Trigger
-
ESL
CHANNEL_BRIDGEwith A and B channel UUIDs -
Resolve EFSwitch
domain_name→ tenant gate (TMB_DOMAIN_ALLOWLIST) -
Classify call type (inbound / manual outbound / campaign); apply
TMB_ENABLE_*flags -
Optional inbound DID filter (
TMB_ALLOWLIST;*= all DIDs on allowed tenants) -
If
TMB_MODE=observe, log only; ifactive, start TX (unless kill fileDISABLE.azureexists) -
Enforce
TMB_MAX_CONCURRENT
4.2 Layout
|
|
Behavior |
|---|---|
|
|
One mono Azure session per leg → 2 streams per call |
|
|
Single stereo path — use only when validated for the deployment |
4.3 Finals → CCM
-
Only final recognition results are posted as conversation messages
-
Payload shape matches prior Jambonz apps: conversation
id,header.channelData, sender CONNECTOR vs AGENT,body.type=PLAIN
4.4 Hangup and recovery
-
On hangup / channel destroy, stop media-bugs once per call
-
Mid-call relay start recovers already-bridged calls (
recover: CHANNEL_BRIDGE attach) -
Stopping the systemd unit stops live media-bugs; starting again re-attaches bridged calls when recovery is enabled
5. Multi-Tenant CCM Routing
CCM base = https://{fs_domain_name}.{CCM_ROOT_DOMAIN}/ccm
Example: FS domain_name=thirdparty, CCM_ROOT_DOMAIN=intelicx.ai → https://thirdparty.intelicx.ai/ccm.
-
Matches CX IVR / Voice Connector tenant FQDN pattern
-
CCM_BASE_URLis used when dynamic resolution is unavailable -
Tenant header
tenantidis set when required by the deployment
6. APISIX / Keycloak Auth (CCC-2331)
Auth is opt-in and shared across Azure, Deepgram, and the relay/ package via CxApiAuth.
|
Mode |
Behavior |
|---|---|
|
|
No |
|
|
Require |
6.1 Token flow
-
Derive tenant FQDN from CCM base (
…/ccmstripped) or fallback base -
POST {fqdn}/unified-admin/keycloakLoginwith JSON{username, password} -
Read
data.token; cache per FQDN -
Attach
Authorization: Bearer <token>on CCM HTTP calls -
On 401: invalidate cache, refresh token once, retry the request once
-
Log markers:
AUTH token acquired,AUTH token cache cleared,CX request unauthorized (401)
This mirrors Voice Connector (ecx_generic_connector) env names and login path so RMT can reuse the same Keycloak Expertflow-realm API user (admin + default roles).
6.2 Failure modes
|
Observation |
Typical cause |
|---|---|
|
CCM |
APISIX enabled on EFCX; enable auth env |
|
|
Bad credentials, wrong FQDN, unified-admin unreachable |
|
|
Neither dynamic nor fallback CCM base usable |
|
Token OK but wrong desk |
Conversation id / SI mapping — not auth |
7. Deployment Topology
|
Item |
Path / unit |
|---|---|
|
Relay app |
|
|
Config |
|
|
Module |
|
|
Speech libs |
|
|
Unit |
|
|
Autoload drop-in |
|
Hard rules: Keep Speech SDK only under the tmb lib path — never /usr/lib. Do not make install EFSwitch solely for this module. Do not run Deepgram and Azure relays on the same live traffic.
8. Operational Configuration (key knobs)
|
Key |
Role |
|---|---|
|
|
|
|
|
FS tenant domains allowed |
|
|
Call-type gates |
|
|
Optional inbound DID filter ( |
|
|
Cap concurrent transcribed calls |
|
|
|
|
|
Speech key, region/endpoint, language |
|
|
Local EFSwitch ESL |
|
|
CCM routing |
|
|
APISIX Bearer |
Full RMT steps: Set up Azure live call transcription on an EFSwitch server
9. Capacity and Sizing Notes
-
Each bridged call in
dual_mono= 2 Azure concurrent recognition streams -
Plan Azure Speech quota and EFSwitch CPU for
2 × concurrent bridged calls -
Start with a conservative
TMB_MAX_CONCURRENT(for example 3–10), raise after soak / load test -
Relay CPU/RAM is usually small vs EFSwitch + Speech SDK in-process cost
-
Prefer horizontal Media Server capacity and Azure quota over multi-worker tricks inside the relay
10. Security Considerations
-
Secrets live in
.env.azure(mode600recommended); never commit keys or API passwords -
With APISIX on, use a dedicated Keycloak API user — not personal admin logins in shared docs
-
Transcripts and ANI are PII; restrict log retention and Agent Desk access via CX roles
-
ESL is local-only by design; do not expose
8021publicly
11. Observability
|
Marker |
Meaning |
|---|---|
|
|
TX attach path |
|
|
ASR final received |
|
|
Resolved conv / svc / ani / tenant |
|
|
Keycloak login succeeded |
|
|
Message accepted |
|
|
Auth or CCM rejection |
12. Related Work and Alternatives
|
Path |
Role |
|---|---|
|
This architecture (media-bug + Azure) |
Current EFSwitch-native live TX |
|
Deepgram variant ( |
Same ESL→CCM pattern; different ASR module |
|
Jambonz Real-Time Transcription |
Legacy eavesdrop / |
|
CCC-2329 |
Media-bug transcription implementation |
|
CCC-2331 |
APISIX / CCM auth for transcription |
13. Related Documents
-
EFSwitch Media-Bug Live Transcription: Real-Time Call Text on Agent Desk Without Jambonz — Feature Guide
-
Set up Azure live call transcription on an EFSwitch server — RMT setup
-
Real-Time Transcription — Jambonz path
-
CX Voice with APISIX Auth: Required NodePort Exposure for EFCX Services
-
PII-Masker Architecture — Architecture style reference
-
GitLab: rtc/media-bug-for-transcription