Twelve stages, from a file landing to an agent disputing the score. Nothing here is a mock-up of a feature we are planning. It is what happens, in the order it happens.
One recording, dragged in. Or one row of a CSV in a batch of five hundred: the pipeline from here is identical either way.
Nothing has been charged yet.
MP3, WAV, WEBM, M4A, OGG
The audio becomes text, with each turn attributed to the agent or the customer.
Speakers are inferred from context rather than by acoustic diarisation. On a two-party call that reads correctly; on a heavily overlapping one it is less certain, and we would rather say so here than have you discover it.
context-based speaker labels · not acoustic diarisation
Agent: नमस्ते, मैं प्रिया बोल रही हूँ।
Customer: हाँ, बताइए।
Agent: क्या मैं आपके खाते के बारे में बात कर सकती हूँ?
Sixteen classes of identifier are masked inline while the transcript is produced, so the unmasked values are never written into an audit record.
Names stay readable on purpose: a coaching note about an unnamed agent is not much use, and the agent’s name is not the sensitive part of a call about somebody’s bank account.
This is best-effort, not a deterministic redaction pass.
card · CVV · bank · IFSC · UPI · Aadhaar · PAN · DOB · OTP · +8 more
Agent: Thanks Priya, the number ending [PHONE MASKED]?
Customer: My card is [CARD NUMBER MASKED] and the OTP is [OTP MASKED].
The call comes back in its original language alongside an English translation, and optionally a third language of your choosing.
That last one is why a single QA team can cover a floor running Hindi, Tamil, Telugu and Bengali queues without staffing a reviewer for each.
22 languages · English + one more
क्या मैं आपके खाते के बारे में बात कर सकती हूँ?
May I speak to you about your account?
உங்கள் கணக்கு பற்றி பேசலாமா?
Your parameters, your weights, your severities, and your SOP, if the campaign has one attached, so the call is graded against your documented process rather than generic best practice.
Scorecards above five parameters are split into batches, scored separately and merged with the weights recomputed. That keeps a thirty-parameter set from running into a response-length ceiling.
sopId → prompt · batches of 5
Every parameter returns the transcript spans it was judged on, not a paraphrase, the lines themselves.
This is the stage that makes a disputed score into a conversation about the call rather than an argument about the tool.
auditResults[].evidence[]
“Good morning, this is Priya from [MASKED]. May I confirm the last four digits?”
Mandatory disclosure was never read out before verification began.
A disclosure either was or was not read out, and the model reports near-certainty. Whether an agent showed genuine empathy is a judgement call, and a lower figure there is the honest answer rather than a defect.
Where the model reports nothing, the interface shows a dash. Substituting a plausible default would be trivial and nobody would notice, which is exactly why we do not.
0–100 per parameter · mean for the call
The weighted average came to 84. The disclosure parameter is marked zero-tolerance and it failed, so the call scores zero.
That override runs on our side after the model returns, rather than being an instruction the model is asked to remember. A rule this consequential should not depend on a prompt being followed.
ai.service.ts · applied server-side
Customer and agent are tracked separately, with the emotional turning points timestamped, so you can jump to the moment rather than re-listening to six minutes to find it.
A customer who starts angry and ends calm is a call that went well. One overall figure would hide that.
sentiment.customerScore / .agentScore
Strengths, areas to improve, and specific suggested actions, plus an analysis of what actually drove the failure.
Here the root cause is not the agent. The same omission appears on every failing call in the batch, which points at the opener script rather than at any one person.
coaching{} · rootCauseAnalysis
Verified identity before discussing the account.
Disclosure skipped entirely on this call.
Consistent across the batch: the script places the disclosure after verification.
Not on upload, not when the model was called, after the audit was saved.
A run that fails costs nothing. And because credits meter tokens rather than minutes, this six-minute call scored against six parameters costs what it actually took, not what a per-minute rate card says it should.
max(1, ceil(totalTokens ÷ rate)) · after persist
Which is the point of all of it. The score is zero and the agent thinks that is harsh, so the supervisor opens the audit and they both read the same quoted line.
Either the disclosure is in the transcript or it is not. The conversation is about the call, and it takes two minutes instead of a fortnight.
the whole argument, in one screen
Mandatory disclosure was never read out before verification began.
01 / 12