Docs · API · Calls

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#

FieldTypeDescription
uploadedstatusAudio stored, analysis job queued. This is what the upload response returns.
processingstatusTranscription, redaction, analysis, and QA scoring in flight — typically minutes.
analyzedstatusDone. transcript, analysis, and qa are populated on the call detail.
failedstatusThe pipeline failed; error explains. The credit was refunded automatically.

Upload calls#

POST/v1/calls

multipart/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.

FieldTypeDescription
filesfile[]required1–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_hintstringoptional3-letter ISO 639-3 code (e.g. ara, eng) applied to every file in the request. Skip it and the language is auto-detected.
queuestringoptionalFree-form tag (≤120 chars) for the queue or campaign the calls came from — filterable on GET /v1/calls.
agent_external_idstringoptionalThe 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.
Request
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"
Response · 201 Created
{
  "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
    }
  ]
}
FieldTypeDescription
calls[].idstringThe call ID — poll GET /v1/calls/{id} with it.
calls[].job_idstring | nullThe analysis job. null only when job creation failed (then status is "failed").
calls[].file_namestringThe uploaded file name, echoed back.
calls[].statusstring"uploaded" on success. Replayed items carry the original call's current status.
calls[].replayedbooleantrue 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#

GET/v1/calls
QueryTypeDescription
statusstringoptionalOne of uploaded, processing, analyzed, failed.
languagestringoptionalExact detected language code (ISO 639-3), e.g. ara, eng, spa. Arabic dialects all report ara.
queuestringoptionalExact match on the queue tag set at upload.
created_afterISO 8601optionalOnly calls created strictly after this timestamp.
created_beforeISO 8601optionalOnly calls created strictly before this timestamp.
limitintegerdefault: 25Page size, 1–100.
starting_afterstringoptionalCursor: a call ID from a previous page. Returns calls older than it.
Request
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"
Response · 200 OK
{
  "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:

Pagination
# 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#

GET/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.

Request
curl https://pulse.whizztech.ai/v1/calls/9f2c51b8-4a07-4e63-b1d8-72e0a5c93f14 \
  -H "Authorization: Bearer $PULSE_KEY"
Response · 200 OK
{
  "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#

FieldTypeDescription
statusstringuploaded · processing · analyzed · failed.
sourcestring"api" for API uploads, "upload" for dashboard uploads.
duration_secnumberRecording length in seconds, measured during transcription (0 until then).
language / language_probstring / numberDetected language as an ISO 639-3 code (e.g. ara, eng, spa — Arabic dialects all report ara) and the detector's confidence.
sentimentstring | nullOverall call sentiment rollup: positive · neutral · negative.
qa_scorenumber | nullWeighted QA scorecard total, 0–100. Mirrors qa.overall_score.
csat_predictedinteger | nullPredicted customer satisfaction, 1–5.
escalation_riskstring | nulllow · medium · high.
queue / language_hintstring | nullEchoes of what you sent at upload.
agent_idstring | nullThe linked agent (set via agent_external_id at upload).
errorstring | nullFailure reason when status is failed.

transcript#

FieldTypeDescription
textstringThe full transcript as plain text, PII already redacted.
segmentsobject[]Diarized turns: { speaker, start, end, text } with speaker "agent" / "customer" and start/end in seconds.
language_code / language_probstring / numberWhat the ASR engine detected, and how confidently.
word_countintegerWords in the transcript.
redactionsobject[]PII/PCI spans removed before analysis: { type, start, end } as character offsets into text.

analysis#

FieldTypeDescription
summary / call_reasonstringWhat happened, and why the customer called — written in the language of the call.
sentiment_overall / sentiment_by_speakerstring / objectOverall plus per-speaker: { customer, agent } → positive · neutral · negative.
topics / keywords / intentsstring[]What the call was about, at three levels of granularity.
entitiesobject[]Extracted entities: { type, value }.
pain_pointsobject[]{ issue, quote } — each customer pain point with the supporting quote.
service_gapsobject[]{ gap, detail } — process failures the call exposed.
agent_suggestionsobject[]Coaching rewrites: { original, improved, why } — what the agent said, a better version, and the reason.
action_itemsstring[]Follow-ups the conversation committed to.
csat_predicted / escalation_risk / churn_riskinteger / string / stringPredicted CSAT 1–5; risks low · medium · high.
compliance_flagsobject[]{ rule, passed, detail } — per compliance rule checked.

qa#

FieldTypeDescription
scorecard_idstringThe scorecard the call was graded against (default: 6 weighted criteria; customizable in the dashboard).
overall_scorenumberWeighted total, 0–100.
verdictstringexcellent · good · partial · poor.
needs_reviewbooleantrue when the model's confidence was low — the call is queued for human review instead of being presented as certain.
criterion_scoresobject[]{ criterionId, name, score, weight, passed, reasoning, confidence } per criterion.
citationsobject[]Explainability: { criterionId, quote, start, end, speaker } — the transcript quote each criterion was judged on.