Post-call Analysis
Webhook receivers are usually where your “real” analytics starts: you want to transform events into durable facts (DB rows) and then aggregate them into dashboards. This guide shows a practical approach for the events Truedy emits for calls, batch campaigns, and voice training.What you can (and can’t) get from webhooks
You can reliably get from call events (call.ended, call.failed, call.billed)
Webhook payloads include call-level analytics fields such as:
- call duration (e.g.
duration/endedtimestamps when present) - cost and billing fields (e.g.
costUsd,billingStatus, billed duration) - summary fields (e.g.
summary,shortSummary) - outcome / termination reason fields (e.g.
endReason) - sentiment/context fields when present
You should not expect full transcript text in the webhook
Truedy stores full transcript content separately after the call ends (via backend fetching + persistence). Webhook payloads are intended for event-driven state changes and analytics, not for transporting every transcript token. If you need the full transcript, use the Truedy API endpoints:1) Persist the raw event (always)
Before transforming anything, store the incoming webhook payload so you can re-run analytics without re-contacting upstream services. A minimal table design:received_at(server timestamp)event_type(top-levelevent)dedupe_key(your idempotency key)payload(raw JSON body you received)
- replaying historical data
- debugging field differences (camelCase vs snake_case, missing keys)
- rebuilding aggregates
2) Extract a stable dedupe key
Use the rules from: In practice:- for call events, use
data.call.callId(or equivalent fallback) - for batch events, use
data.batch_id(or equivalent fallback) - for voice events, use
data.voice_id(or equivalent fallback)
3) Transform events into analytics facts
A. call.ended: create a “call_metrics” fact row
When event_type == "call.ended":
- Extract:
uv_call_id(fromdata.call.callIdor equivalent)duration_seconds(when present)cost_usd(fromcostUsd/cost_usd-like fields)summary/shortSummaryendReason,billingStatussentiment(if present)
- Determine outcome buckets:
- completed vs failed/no-answer based on
endReason
- completed vs failed/no-answer based on
- Write a single row into your analytics table keyed by
(event_type, dedupe_key)or by(uv_call_id, event_type).
B. batch.status.changed / batch.completed: update campaign aggregates
When receiving batch events:
- Extract
uv_batch_idfromdata.batch_id/data.batchId. - Extract the new status from either:
data.data.status- or
data.status
- Update your campaign aggregates:
- status timeline (draft/scheduled/active/completed)
- completion timestamp
C. voice.training.completed / voice.training.failed: update voice readiness
When voice.training.completed arrives:
- mark the voice as “ready/active” in your internal systems
voice.training.failed arrives:
- mark the voice as “failed”
- store
error_messagefor user display and troubleshooting
4) Build dashboards from facts
Once you have durable fact rows (calls metrics, campaign status timeline, voice readiness), your dashboards become simple aggregations:- calls per hour / per day
- completion rate and failure buckets
- avg duration and cost per call
- sentiment distribution (if present)
- campaign progress over time
5) Optional: enrich with transcripts/recordings
To enrich with full transcript and recordings:- You must have the Truedy
call_idthat matches the stored call. - Call:
GET /api/public/v1/calls/{call_id}/transcriptGET /api/public/v1/calls/{call_id}/recording
call_id is up to your integration:
- if you initiate calls from your system and persist
call_id, use that - if your workflow only has webhook payload identifiers, you may need an additional correlation step in your architecture

