Request Payloads
These are the JSON payloads your webhook endpoint will receive for different events.Call Started Event
Triggered when a call is registered and queued for dialing — before it connects. At this point the call has not been placed yet, socall_sid, called_on, and all latency/cost fields
are still null, and the status is registered / QUEUED (not ongoing / ACCEPTED).
The overwhelming majority of
call_started events carry status: "registered", sub_status: "QUEUED".
An ongoing / ACCEPTED variant is emitted only for the subset of calls that connect quickly enough
to publish a second call_started. Design your handler around registered / QUEUED.Call Completed Event
Triggered when a call attempt finishes — this fires for every terminal outcome, not just successful conversations:completed, failed, error, cancelled, forwarded, and interim
retry. On non-completed outcomes the transcript is usually empty, call_duration /
latency / cost are null, and sub_status carries the reason (see the
Status & Sub-Status Reference section below).
attempts is the per-dial history for this call (one entry per telephony attempt, oldest first).
Each entry’s status is the raw, original-cased outcome of that attempt (e.g. "Busy Line",
"ACCEPTED") — the top-level sub_status is the lowercased form of the final attempt’s reason.
calling_source (the telephony provider, e.g. "plivo") is also included on a growing share of calls.Recording Completed Event
Triggered when call recording is processed and ready for download.The
recording_url is valid for 24 hours after the call. Download and store the recording within that window if you need to retain it.Unlike the other events,
recording_completed does not include status, agent_id,
workspace_id, version_slug, or version_description. It does carry sub_status.Platform Analysis Event
Triggered when Ringg AI’s built-in analysis completes.Analysis events are emitted for the call regardless of outcome, so the top-level
status /
sub_status reflect the call result (e.g. failed / busy line, or error /
not_able_to_call), not the analysis. For calls that never connected there is no conversation to
analyse, so analysis_data is empty or minimal. Use analysis_data.status (analyzed) to tell
whether analysis actually ran.Client Analysis Event
Triggered when your custom analysis completes based on your configured prompts.All Processing Completed Event
Triggered once all post-call processing is finished — recording uploaded, transcript generated, and both platform and client analysis complete. This single consolidated event carries the full call dataset in one payload.Linking Retries and Callbacks to the Original Call
Some calls are placed automatically on behalf of an earlier one — a scheduled callback, a retry after a failed attempt, or a redial after voicemail was detected. These are separate calls with their owncall_id, and they emit their own full set of webhooks.
To let you stitch them back together, such a call carries parent_call_id: the call_id of
the call it was created from.
The key is always present. For a call that was dialled directly it is sent as
null, the
same as every other optional field — you never have to distinguish “no parent” from “an older
payload that didn’t carry the field”.-
It appears on every event type —
call_started,call_completed,recording_completed, both analysis events, andall_processing_completed— so you can link a child call as soon as it starts, without waiting for post-call processing. -
It is
null, never absent, when there is no parent — so a plainpayload["parent_call_id"]read is safe, andparent_call_id != nullis a complete test for “this call came from another”. -
It is never equal to the event’s own
call_id. If you were previously deriving parentage some other way, note that a call is never its own parent in this field. -
The chain shape depends on why the call was placed, so do not assume either one:
- Scheduled callbacks are flat — every callback attempt for a call carries the same
parent_call_id, pointing at the original call. Two callbacks in a row are siblings, not parent and child. - Voicemail redials are nested — each attempt points at the attempt immediately before it, so a chain can be several levels deep.
parent_call_idupward until the field is absent, and bound the walk so a long redial chain cannot loop. - Scheduled callbacks are flat — every callback attempt for a call carries the same
-
A
parent_call_iddoes not by itself mean “callback”. Retries and voicemail redials carry one too. There is no separate flag on the webhook to distinguish them; use the call-history API if you need the specific category.
Status & Sub-Status Reference
Every call event carries astatus and a sub_status (except recording_completed, which carries
only sub_status). status is the call’s lifecycle state; sub_status refines it. The tables below
reflect the values actually observed on delivered webhooks across a recent production window.
status
sub_status
The sub_status you receive depends on the status.
On registered / retry (i.e. call_started): QUEUED.
On ongoing: ACCEPTED.
On completed — delivered uppercase, as-is:
On
cancelled:
On
failed / error — sub_status is the telephony carrier’s hangup-cause name, lowercased
(a few Ringg-internal reasons, also lowercased, appear here too). It is not the short enum you
might expect (busy, no-answer, …) — carriers such as Plivo return human-readable phrases. Values
observed in production:
The
failed / error list is provider-specific and open-ended — the exact strings depend on the
telephony provider (Plivo, Twilio, Exotel, Vobiz, …), so treat this sub_status as a free-form
lowercased string, not a fixed enum. The original-cased reason for each dial attempt is also available
under call_completed’s attempts[].status.Implementation Examples
Node.js/Express
Field Descriptions
Common Fields
Call Started Fields
Fired when the call is registered and queued, before it is placed. Because the call hasn’t connected yet,call_sid, called_on, created_at, call_cost, and the latency fields are
typically null at this point.
Call Completed Fields
Recording Fields
Analysis Fields
The analysis events (platform_analysis_completed, client_analysis_completed) fire for the call
regardless of outcome, so their top-level status / sub_status describe the call result, not the
analysis. Whether analysis actually ran is inside analysis_data.status.