Call centre
Transcribe stereo calls one party per channel, then measure talk share, talk-overs and dead air.
Why separate channels
Most call recorders put the agent on one channel and the customer on the other. With channel_mode: "separate" each channel is transcribed on its own, so every segment is attributed exactly — no guessing who spoke — and nothing is lost when both talk at once.
python call_centre.py call-0192.wav ta-IN --agent-channel 0 > call-0192.json{
"duration_seconds": 312.4,
"agent_talk_share": 0.58,
"customer_talk_share": 0.42,
"talk_overs": 7,
"longest_silence_seconds": 11.2,
"transcript": [
{ "who": "agent", "start": 0.4, "end": 3.1, "text": "…" }
]
}At volume
- Send each call with its own
Idempotency-Key(the call ID works well), so a retried batch never transcribes a call twice. - Register a webhook and process calls as they finish instead of waiting on each one.
- Separate channels are billed per channel transcribed; see Limits and billing.
The script
call_centre.py
"""Call centre: transcribe a stereo call one channel at a time and compute simple call metrics.
python call_centre.py call.wav ta-IN --agent-channel 0 > call.json
Needs TWENTYSIX_API_KEY and TWENTYSIX_BASE_URL. Separate channels are billed per channel transcribed.
"""
import argparse
import json
from twentysix import Client
def analyse(result, agent_channel=0):
role = {agent_channel: "agent", 1 - agent_channel: "customer"}
segs = sorted((s for s in result["segments"] if "start" in s), key=lambda s: s["start"])
talk = {"agent": 0.0, "customer": 0.0}
for s in segs:
talk[role[s.get("channel", 0)]] += s["end"] - s["start"]
# Talk-over: a segment starting while the other party's segment is still running.
over, last_end = 0, {"agent": 0.0, "customer": 0.0}
for s in segs:
me = role[s.get("channel", 0)]
other = "customer" if me == "agent" else "agent"
if s["start"] < last_end[other]:
over += 1
last_end[me] = max(last_end[me], s["end"])
# Longest silence: gaps where neither party speaks.
silence, end = 0.0, 0.0
for s in segs:
silence = max(silence, s["start"] - end)
end = max(end, s["end"])
total = talk["agent"] + talk["customer"] or 1.0
return {
"duration_seconds": result["duration_seconds"],
"agent_talk_share": round(talk["agent"] / total, 3),
"customer_talk_share": round(talk["customer"] / total, 3),
"talk_overs": over,
"longest_silence_seconds": round(silence, 1),
"transcript": [{"who": role[s.get("channel", 0)], "start": s["start"], "end": s["end"], "text": s["text"]} for s in segs],
}
if __name__ == "__main__":
ap = argparse.ArgumentParser()
ap.add_argument("path")
ap.add_argument("language", nargs="?", default="hi-IN")
ap.add_argument("--agent-channel", type=int, choices=(0, 1), default=0)
a = ap.parse_args()
with Client() as client:
result = client.transcribe_file(a.path, a.language, mode="codemix", channel_mode="separate")
print(json.dumps(analyse(result, a.agent_channel), ensure_ascii=False, indent=2))