Calls.
The core of the API: upload recorded customer-service calls, list and filter the corpus, and fetch the full result — diarized transcript, analysis, and the cited QA scorecard — per call.
Call lifecycle#
| Field | Type | Description |
|---|---|---|
| uploaded | status | Audio stored, analysis job queued. This is what the upload response returns. |
| processing | status | Transcription, redaction, analysis, and QA scoring in flight — typically minutes. |
| analyzed | status | Done. transcript, analysis, and qa are populated on the call detail. |
| failed | status | The pipeline failed; error explains. The credit was refunded automatically. |
Upload calls#
/v1/callsmultipart/form-data— the one endpoint that isn't JSON-in. Each file becomes one call plus one analyze_calljob, charged 1 credit. If the org balance can't cover the whole batch, the request fails with 402 insufficient_credits before anything is stored.
| Field | Type | Description | |
|---|---|---|---|
| files | file[] | required | 1–25 audio files, up to 60MB each. Accepted: wav, mp3, m4a, mp4, ogg, webm, aac, flac, opus — matched by content type or file extension. |
| language_hint | string | optional | 3-letter ISO 639-3 code (e.g. ara, eng) applied to every file in the request. Skip it and the language is auto-detected. |
| queue | string | optional | Free-form tag (≤120 chars) for the queue or campaign the calls came from — filterable on GET /v1/calls. |
| agent_external_id | string | optional | The agent's ID in your CCaaS/CRM (≤200 chars). The matching agent is looked up per org — and created automatically on first sight — then linked to every call in the request as agent_id. |
curl -X POST https://pulse.whizztech.ai/v1/calls \
-H "Authorization: Bearer $PULSE_KEY" \
-H "Idempotency-Key: batch-2026-07-11-a" \
-F "files=@call-0932.mp3" \
-F "files=@call-0947.wav" \
-F "language_hint=ara" \
-F "queue=billing" \
-F "agent_external_id=agent-104"{
"calls": [
{
"id": "9f2c51b8-4a07-4e63-b1d8-72e0a5c93f14",
"job_id": "5b8f0d21-6a3e-4c97-b1d0-84e7f2a9c655",
"file_name": "call-0932.mp3",
"status": "uploaded",
"replayed": false
},
{
"id": "1d84c0e7-95b2-4f31-a6d9-08c3e5f72b40",
"job_id": "7e0a2c94-1f68-4b3d-85c1-d92f40e6a713",
"file_name": "call-0947.wav",
"status": "uploaded",
"replayed": false
}
]
}| Field | Type | Description |
|---|---|---|
| calls[].id | string | The call ID — poll GET /v1/calls/{id} with it. |
| calls[].job_id | string | null | The analysis job. null only when job creation failed (then status is "failed"). |
| calls[].file_name | string | The uploaded file name, echoed back. |
| calls[].status | string | "uploaded" on success. Replayed items carry the original call's current status. |
| calls[].replayed | boolean | true when the Idempotency-Key matched a previous upload and this item is the original call. |
Idempotency#
Send an Idempotency-Key header and retries become safe: a repeated request returns the original calls instead of storing and charging again. Multi-file requests derive one key per file ({key}:{index}), so a retry of a 10-file batch replays all 10. When every item is a replay the response status is 200 instead of 201.
List calls#
/v1/calls| Query | Type | Description | |
|---|---|---|---|
| status | string | optional | One of uploaded, processing, analyzed, failed. |
| language | string | optional | Exact detected language code (ISO 639-3), e.g. ara, eng, spa. Arabic dialects all report ara. |
| queue | string | optional | Exact match on the queue tag set at upload. |
| created_after | ISO 8601 | optional | Only calls created strictly after this timestamp. |
| created_before | ISO 8601 | optional | Only calls created strictly before this timestamp. |
| limit | integer | default: 25 | Page size, 1–100. |
| starting_after | string | optional | Cursor: a call ID from a previous page. Returns calls older than it. |
curl "https://pulse.whizztech.ai/v1/calls?status=analyzed&queue=billing&created_after=2026-07-01T00:00:00Z&limit=25" \
-H "Authorization: Bearer $PULSE_KEY"{
"object": "list",
"data": [
{
"id": "9f2c51b8-4a07-4e63-b1d8-72e0a5c93f14",
"object": "call",
"status": "analyzed",
"source": "api",
"file_name": "call-0932.mp3",
"language": "ara",
"sentiment": "negative",
"qa_score": 71.5,
"csat_predicted": 2,
"escalation_risk": "high",
"duration_sec": 312.4,
"queue": "billing",
"agent_id": "c3a91f70-2d84-4b16-95e3-8f01d6c42a79",
"job_id": "5b8f0d21-6a3e-4c97-b1d0-84e7f2a9c655",
"created_at": "2026-07-11T09:32:18.000Z"
}
],
"has_more": true
}Results come newest-first. When has_more is true, pass the last id of the page as starting_after to get the next one:
# next page: pass the last id of the previous page
curl "https://pulse.whizztech.ai/v1/calls?limit=25&starting_after=9f2c51b8-4a07-4e63-b1d8-72e0a5c93f14" \
-H "Authorization: Bearer $PULSE_KEY"Get a call#
/v1/calls/{id}The full result. transcript, analysis, and qa are null until the pipeline produces them — on an analyzed call all three are populated. IDs from other organizations return 404 not_found.
curl https://pulse.whizztech.ai/v1/calls/9f2c51b8-4a07-4e63-b1d8-72e0a5c93f14 \
-H "Authorization: Bearer $PULSE_KEY"{
"id": "9f2c51b8-4a07-4e63-b1d8-72e0a5c93f14",
"object": "call",
"status": "analyzed",
"source": "api",
"file_name": "call-0932.mp3",
"mime_type": "audio/mpeg",
"size_bytes": 4812390,
"duration_sec": 312.4,
"language": "ara",
"language_prob": 0.97,
"sentiment": "negative",
"qa_score": 71.5,
"csat_predicted": 2,
"escalation_risk": "high",
"queue": "billing",
"language_hint": "ara",
"agent_id": "c3a91f70-2d84-4b16-95e3-8f01d6c42a79",
"job_id": "5b8f0d21-6a3e-4c97-b1d0-84e7f2a9c655",
"error": null,
"created_at": "2026-07-11T09:32:18.000Z",
"updated_at": "2026-07-11T09:36:02.000Z",
"transcript": {
"text": "…full redacted transcript text…",
"segments": [
{ "speaker": "customer", "start": 0.4, "end": 6.1, "text": "…" },
{ "speaker": "agent", "start": 6.3, "end": 11.8, "text": "…" }
],
"language_code": "ara",
"language_prob": 0.97,
"word_count": 843,
"redactions": [{ "type": "phone", "start": 1042, "end": 1054 }]
},
"analysis": {
"summary": "Customer called about a double charge on this month's invoice…",
"call_reason": "billing dispute",
"sentiment_overall": "negative",
"sentiment_by_speaker": { "customer": "negative", "agent": "neutral" },
"topics": ["billing", "double charge", "refund"],
"keywords": ["invoice", "refund", "charge"],
"intents": ["dispute_charge", "request_refund"],
"entities": [{ "type": "invoice_id", "value": "INV-…" }],
"pain_points": [
{ "issue": "Charged twice for the same invoice", "quote": "…" }
],
"service_gaps": [
{ "gap": "No proactive notification of billing errors", "detail": "…" }
],
"agent_suggestions": [
{
"original": "That's just how the system works.",
"improved": "I can see why that's frustrating — let me check what happened and fix it now.",
"why": "Acknowledges the emotion and takes ownership instead of deflecting."
}
],
"action_items": ["Process refund for duplicate charge", "Confirm by email"],
"csat_predicted": 2,
"escalation_risk": "high",
"churn_risk": "medium",
"compliance_flags": [
{ "rule": "Identity verification before account changes", "passed": true, "detail": "…" }
]
},
"qa": {
"scorecard_id": "e5b20c81-7f43-4a96-b0d2-19c8e6f35a07",
"overall_score": 71.5,
"verdict": "partial",
"needs_review": false,
"criterion_scores": [
{
"criterionId": "empathy",
"name": "Empathy",
"score": 55,
"weight": 2,
"passed": false,
"reasoning": "The agent deflected instead of acknowledging the customer's frustration.",
"confidence": 0.88
}
],
"citations": [
{
"criterionId": "empathy",
"quote": "That's just how the system works.",
"start": 148.2,
"end": 151.0,
"speaker": "agent"
}
]
}
}Call fields#
| Field | Type | Description |
|---|---|---|
| status | string | uploaded · processing · analyzed · failed. |
| source | string | "api" for API uploads, "upload" for dashboard uploads. |
| duration_sec | number | Recording length in seconds, measured during transcription (0 until then). |
| language / language_prob | string / number | Detected language as an ISO 639-3 code (e.g. ara, eng, spa — Arabic dialects all report ara) and the detector's confidence. |
| sentiment | string | null | Overall call sentiment rollup: positive · neutral · negative. |
| qa_score | number | null | Weighted QA scorecard total, 0–100. Mirrors qa.overall_score. |
| csat_predicted | integer | null | Predicted customer satisfaction, 1–5. |
| escalation_risk | string | null | low · medium · high. |
| queue / language_hint | string | null | Echoes of what you sent at upload. |
| agent_id | string | null | The linked agent (set via agent_external_id at upload). |
| error | string | null | Failure reason when status is failed. |
transcript#
| Field | Type | Description |
|---|---|---|
| text | string | The full transcript as plain text, PII already redacted. |
| segments | object[] | Diarized turns: { speaker, start, end, text } with speaker "agent" / "customer" and start/end in seconds. |
| language_code / language_prob | string / number | What the ASR engine detected, and how confidently. |
| word_count | integer | Words in the transcript. |
| redactions | object[] | PII/PCI spans removed before analysis: { type, start, end } as character offsets into text. |
analysis#
| Field | Type | Description |
|---|---|---|
| summary / call_reason | string | What happened, and why the customer called — written in the language of the call. |
| sentiment_overall / sentiment_by_speaker | string / object | Overall plus per-speaker: { customer, agent } → positive · neutral · negative. |
| topics / keywords / intents | string[] | What the call was about, at three levels of granularity. |
| entities | object[] | Extracted entities: { type, value }. |
| pain_points | object[] | { issue, quote } — each customer pain point with the supporting quote. |
| service_gaps | object[] | { gap, detail } — process failures the call exposed. |
| agent_suggestions | object[] | Coaching rewrites: { original, improved, why } — what the agent said, a better version, and the reason. |
| action_items | string[] | Follow-ups the conversation committed to. |
| csat_predicted / escalation_risk / churn_risk | integer / string / string | Predicted CSAT 1–5; risks low · medium · high. |
| compliance_flags | object[] | { rule, passed, detail } — per compliance rule checked. |
qa#
| Field | Type | Description |
|---|---|---|
| scorecard_id | string | The scorecard the call was graded against (default: 6 weighted criteria; customizable in the dashboard). |
| overall_score | number | Weighted total, 0–100. |
| verdict | string | excellent · good · partial · poor. |
| needs_review | boolean | true when the model's confidence was low — the call is queued for human review instead of being presented as certain. |
| criterion_scores | object[] | { criterionId, name, score, weight, passed, reasoning, confidence } per criterion. |
| citations | object[] | Explainability: { criterionId, quote, start, end, speaker } — the transcript quote each criterion was judged on. |