Skip to main content

Available Webhooks

This page lists the webhook event types Truedy can send, what each event means, and the identifiers you should extract for safe processing. For end-to-end webhook delivery mechanics (signatures, envelope format, and receiver patterns), see:

Webhook request envelope (what you receive)

Truedy delivers an HTTP POST with this JSON shape:
{
  "event": "call.ended",
  "timestamp": "2026-03-18T14:30:00.000Z",
  "data": {
    "event": "call.ended",
    "call": {
      "callId": "uv_call_123",
      "agent": { "agentId": "uv_agent_123" }
    },
    "id": "uv_evt_123"
  }
}
Notes:
  • The top-level event is normalized (for example call.completed is normalized to call.ended).
  • The data object contains the original Ultravox event payload. Depending on the event, identifiers may live at data.call.*, data.batch_*, data.voice_*, etc.
  • There is no guarantee that any single optional field is present. Always validate defensively.
Because webhook delivery can be at-least-once and payloads may repeat, dedupe using one of:
  • A unique identifier inside the payload (often data.id / data.eventId)
  • A stable entity identifier for the event family:
    • Call events: prefer data.call.callId (or data.call_id / data.callId)
    • Batch events: prefer data.batch_id (or data.batchId)
    • Voice events: prefer data.voice_id (or data.voiceId)
If you cannot find one of these in an event, fall back to a composite key (for example: event + top_level_timestamp + url + phone_number), but that may be less reliable.

Event types

EventWhat it meansPrimary identifiers to extractTypical use
call.startedA call has starteddata.call.callId / data.call.agent.agentIdUpdate “in progress” UI/CRM
call.joinedThe participant joined the calldata.call.callIdStart “call connected” workflows
call.endedThe call has ended (final state)data.call.callId plus duration/cost/summary when presentMark call complete; store analytics
call.billedA billing event for a calldata.call.callIdReconcile billing / minutes usage
call.failedThe call faileddata.call.callIdMark failure; trigger retries or alerts
batch.status.changedBatch campaign status changeddata.batch_id / data.batchIdUpdate campaign progress
batch.completedBatch campaign completeddata.batch_id / data.batchIdFinal reconciliation
voice.training.completedVoice training finished successfullydata.voice_id / data.voiceIdEnable voice for use
voice.training.failedVoice training faileddata.voice_id / data.voiceIdSurface error to user; allow re-training

Call events (payload shapes)

call.started

Use this when you want near-real-time “started” updates. Example (minimal shape):
{
  "event": "call.started",
  "timestamp": "2026-03-18T14:30:00.000Z",
  "data": {
    "call": {
      "callId": "uv_call_123",
      "agent": { "agentId": "uv_agent_123" }
    }
  }
}
Recommended extract:
  • uv_call_id = data.call.callId
  • uv_agent_id = data.call.agent.agentId

call.joined

Example:
{
  "event": "call.joined",
  "timestamp": "2026-03-18T14:31:10.000Z",
  "data": {
    "call": {
      "callId": "uv_call_123",
      "agent": { "agentId": "uv_agent_123" }
    }
  }
}
Recommended extract:
  • uv_call_id = data.call.callId

call.ended

This is the final “call summary” event. Truedy normalizes call.completed into call.ended. Example (fields you may see):
{
  "event": "call.ended",
  "timestamp": "2026-03-18T14:32:00.000Z",
  "data": {
    "call": {
      "callId": "uv_call_123",
      "agent": { "agentId": "uv_agent_123" },
      "ended": "2026-03-18T14:32:00Z",
      "duration": 72,
      "costUsd": 0.13,
      "summary": "Short call summary",
      "shortSummary": "1-line summary",
      "endReason": "completed",
      "billingStatus": "billed",
      "billedDuration": "72s"
    },
    "id": "uv_evt_456"
  }
}
Recommended extract:
  • uv_call_id = data.call.callId (or data.call_id / data.callId)
  • duration_seconds from data.call.duration / data.call.ended (when present)
  • summary, shortSummary, costUsd, endReason, billingStatus (when present)

call.billed

Example:
{
  "event": "call.billed",
  "timestamp": "2026-03-18T14:33:10.000Z",
  "data": {
    "call": { "callId": "uv_call_123" },
    "billingStatus": "billed",
    "billedDuration": "72s"
  }
}
Recommended extract:
  • uv_call_id = data.call.callId

call.failed

Example:
{
  "event": "call.failed",
  "timestamp": "2026-03-18T14:30:45.000Z",
  "data": {
    "call": { "callId": "uv_call_123" },
    "endReason": "no_answer"
  }
}
Recommended extract:
  • uv_call_id = data.call.callId
  • failure reason fields you see (for example endReason)

Batch / campaign events (payload shapes)

batch.status.changed

This event is primarily for updating campaign progress. Truedy may reconcile state by fetching the latest batch status internally. Example (minimal shape):
{
  "event": "batch.status.changed",
  "timestamp": "2026-03-18T15:00:00.000Z",
  "data": {
    "batch_id": "uv_batch_abc",
    "data": { "status": "COMPLETED" }
  }
}
Recommended extract:
  • uv_batch_id = data.batch_id (or data.batchId)
  • new_status = data.data.status (or data.status)

batch.completed

Example:
{
  "event": "batch.completed",
  "timestamp": "2026-03-18T16:10:00.000Z",
  "data": {
    "batch_id": "uv_batch_abc",
    "status": "COMPLETED"
  }
}
Recommended extract:
  • uv_batch_id = data.batch_id (or data.batchId)

Voice training events (payload shapes)

voice.training.completed

Example:
{
  "event": "voice.training.completed",
  "timestamp": "2026-03-19T10:00:00.000Z",
  "data": {
    "voice_id": "uv_voice_789",
    "timestamp": "2026-03-19T10:00:00Z"
  }
}
Recommended extract:
  • uv_voice_id = data.voice_id (or data.voiceId)

voice.training.failed

Example:
{
  "event": "voice.training.failed",
  "timestamp": "2026-03-19T10:07:00.000Z",
  "data": {
    "voiceId": "uv_voice_789",
    "error_message": "Training failed: duration too short"
  }
}
Recommended extract:
  • uv_voice_id = data.voice_id (or data.voiceId)
  • error_message / data.error_message / data.data.error_message (wherever it appears)

Defensive parsing checklist

When writing your webhook receiver:
  • Treat every optional field as optional.
  • Support camelCase and snake_case where possible (callId vs call_id, etc.).
  • Always validate that the identifier you use for dedupe exists and is non-empty.
  • Store the dedupe key and the raw payload for debugging.

Managing webhook endpoints

Webhook endpoints are managed through the Truedy dashboard under Settings → Webhooks. You can add, edit, and delete endpoints there. When you create an endpoint, you are given a signing secret — store it immediately as it is only shown once.
Webhook endpoint management (create, list, delete) is not available via the public API. Use the dashboard at Settings → Webhooks to configure your endpoints.

Next steps

Securing Webhooks

Node.js, Python, and Flask signature verification

Error Handling & Retries

Idempotent receiver with full database example