how can I have visibility of which calls got rejec...
# ask-ai
r
how can I have visibility of which calls got rejected or the ones that ring forever? what are the best practices?
✅ 1
t
Thanks for reaching out, luisa. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I’m responding to your question about how to see which calls were rejected or that ring forever, and best practices to monitor those outcomes. Summary — what to track • Use participant lifecycle events (webhooks) like
participant_joined
and
participant_left
and read the
disconnected_reason
on left events (e.g.,
USER_REJECTED
,
CONNECTION_TIMEOUT
,
CLIENT_INITIATED
) to classify rejections and failures. • Detect “ring forever” / unanswered calls by combining join-timeouts and silence/interaction monitoring (no join or no audio/activity within a threshold). Practical best practices to implement 1. Enable webhooks and record these events ◦ Subscribe to
participant_joined
,
participant_left
,
room_started
,
room_finished
,
ingress_started
,
ingress_ended
. ◦ Persist event ID, timestamps, participant id, and
disconnected_reason
so you can audit outcomes and deduplicate events. 1. Classify call outcomes ◦ Rejected:
participant_left
with
USER_REJECTED
or similar explicit reason. ◦ Failed/Timed out:
CONNECTION_TIMEOUT
or no
participant_joined
within setup timeout. ◦ Unanswered/ringing-forever: no agent/client join and no activity within configured ring timeout. 1. Implement timeouts & silence detection ◦ Set a sensible setup/ring timeout (e.g., N seconds). If no
participant_joined
by then, mark as unanswered. ◦ Run a short silence/activity detector after join (e.g., track last audio or interaction). If silence exceeds threshold, treat as voicemail/abandoned and close/flag the call. 1. Use fast, reliable webhook handling ◦ Acknowledge webhooks quickly (return 2xx) and process asynchronously to avoid missed or retried events. ◦ Store events durably so you can reconstruct call flows for later analysis. 1. Correlate logs and agent telemetry ◦ Combine webhook events with server logs, SIP lifecycle events, and any agent observability/traces to diagnose why calls were rejected or never connected. ◦ Keep timestamps and unique IDs to correlate across systems. 1. For outbound flows, wait-for-join pattern ◦ For controlled outbound calls, wait for the remote participant to actually join (or hit your join timeout) before marking the call as delivered; otherwise mark as undelivered/rejected. If you want, I can: • Suggest specific timeout values and an event schema for storing call outcomes. • Provide a sample webhook-processing flow or example code for silence detection. Which would you like next? Sources: Server-side | livekit/livekit_composite | DeepWiki | How to detect and handle silence during calls | Best practices for managing webhook event streams | LiveKit | Job lifecycle | LiveKit Documentation | Streamline troubleshooting with Agent Observability | LiveKit, with interactive elements
r
how can I enable webhooks if we use sip trunk?
t
Thanks — I’m responding to your question about enabling webhooks when you use a SIP trunk. Short answer • Webhooks work with SIP trunks. Enable project-level webhooks (dashboard or API) to receive room/participant events for SIP calls (e.g.,
participant_joined
,
participant_left
,
participant_connection_aborted
,
room_started
,
room_finished
). How to enable 1. Dashboard: ◦ Go to Settings → Webhooks in your LiveKit project dashboard and add your endpoint URL. 1. Programmatically: ◦ Use your server SDK / LiveKit API to configure the webhook endpoint at the project level (same endpoint receives SIP-related room/participant events). What you’ll receive and how to use it • Events:
participant_joined
,
participant_left
(with
disconnected_reason
),
participant_connection_aborted
,
room_started
,
room_finished
, plus egress events if recording is enabled. • SIP-specific signaling (INVITE/RING) is not sent; infer call outcomes from room/participant events and timing patterns (e.g., no
participant_joined
within your ring timeout → unanswered). • Webhook requests include an
Authorization
header with a signed JWT and
Content-Type: application/webhook+json
— verify the signature. Best practices • Acknowledge webhooks quickly (return 2xx) and process asynchronously. • Make handlers idempotent (events may be retried). • Correlate event IDs, timestamps, and participant IDs with your SIP call identifiers to classify rejected vs unanswered calls. • Enable auto-egress if you want `egress_started`/`egress_ended` events for recordings. If you’d like, I can: • Provide a sample webhook handler (signature verification + durable storage schema). • Suggest ring/setup timeout values and an event-to-outcome mapping you can store. Which would be most helpful? Sources: Best practices for managing webhook event streams | LiveKit | sip | livekit/livekit_composite | DeepWiki, with interactive elements
r
suggest the ring setup timeout
t
Thanks — I’m responding to your request for a recommended ring/setup timeout. Recommendation • Default: 30 seconds — good balance for most PSTN/SIP flows (gives voicemail and human pickup time without long waits). • Shorter: 10–15 seconds — for high-volume outbound where speed/throughput matters and you prefer quick retries or fallbacks. • Longer: 45–60 seconds — for situations where recipients often take longer to answer (e.g., international calls, complex IVR flows). Best-practice rules • Start with 30s, run for 1–2 weeks, and measure: percent answered, rejected, and timed-out. • Use adaptive timeouts: longer for known slow destinations or numbers with high voicemail rates. • Correlate with `participant_joined`/`participant_left` and mark unanswered if no join by timeout. • When timeout hits, trigger a clear outcome (mark undelivered), cleanup resources, and optionally retry with backoff. If you want, I can suggest: • A simple monitoring/timeout implementation snippet, or • A metric set to track during the trial period (answer rate, average time-to-join, timeout rate). Which do you prefer?, with interactive elements
r
it seems quite a lot of work, can’t I just used participant joined and participant left?
t
Yes — you can keep it simple using just
participant_joined
and
participant_left
. That will cover most cases if you apply a couple of rules. How to use them simply • If you see
participant_joined
→ treat the call as connected. • When
participant_left
arrives, read
disconnected_reason
to classify outcome (e.g.,
USER_REJECTED
,
CONNECTION_TIMEOUT
,
CLIENT_INITIATED
). • If no
participant_joined
occurs within your ring/setup timeout (e.g., 30s), mark the call as unanswered/undelivered. Minimal implementation checklist • Subscribe to
participant_joined
and
participant_left
webhooks. • Persist event IDs, timestamps, and participant IDs. • Implement a single ring timer per outbound call that fires if
participant_joined
hasn’t been received by timeout. • On
participant_left
, store
disconnected_reason
and correlate with the timer (cancel timer if joined). Caveats / quick tips •
participant_left
+
disconnected_reason
is the primary source for rejections, but SIP-specific signals like INVITE/RING aren’t provided. • Make webhook handling idempotent and acknowledge quickly (2xx). • 30s is a safe default timeout; shorter (10–15s) for high-volume flows, longer for slower destinations. If you want, I can give a one-file example webhook handler (verify signature, start/clear timer, store outcome). Which language do you prefer? , with interactive elements
r
how about using twilio?
t
Responding to your question about using Twilio with LiveKit. Short answer • Yes — Twilio can simplify call status visibility. Twilio provides granular call events (ringing, answered, busy, no-answer, failed) via its call status callbacks, and you can route calls into LiveKit (via TwiML or a Twilio SIP trunk). Use Twilio callbacks plus LiveKit webhooks together to get the clearest picture. How to combine them (recommended) 1. Route calls into LiveKit: ◦ Option A: Use TwiML/Programmable Voice to dial into a LiveKit room. ◦ Option B: Use Twilio SIP Trunking to forward SIP calls to LiveKit. 1. Subscribe to both sides of events: ◦ Configure Twilio Call Status Callbacks (your endpoint) to receive Twilio states:
ringing
,
in-progress
(answered),
no-answer
,
busy
,
failed
. ◦ Enable LiveKit webhooks to receive
participant_joined
,
participant_left
, and
participant_connection_aborted
. 1. Correlate events: ◦ Use a shared call identifier (e.g., Twilio CallSid stored in room metadata or participant identity) to join Twilio callbacks with LiveKit events. ◦ Use Twilio
ringing
→ if LiveKit
participant_joined
follows, call answered; if Twilio reports
no-answer
or you never see
participant_joined
by your timeout, mark unanswered. ◦ Use Twilio
busy
or
failed
plus LiveKit
participant_left
/
participant_connection_aborted
to classify rejections or failures. Why this is useful • Twilio gives SIP/PSTN-level signaling you don’t get from LiveKit alone (INVITE/RING/BUSY), while LiveKit gives room/participant lifecycle and media state. Combining both gives reliable outcomes with minimal custom timers. If you want, I can: • Provide a small example showing how to attach Twilio CallSid to LiveKit room/participant metadata and a webhook correlation flow. • Or give a sample Twilio callback handler and LiveKit webhook handler that maps outcomes to a single call record. Which example would you like? , with interactive elements
r
but how about from the dashboard how can I see it?
t
Responding to your question about how to see rejected or forever‑ringing calls from the dashboard. Where to look in the LiveKit Cloud dashboard • Telephony / Call Logs: shows SIP call records and telephony observability. Filter by time range and SIP trunk/dispatch rule to find specific calls. • Sessions (or Sessions/Participant pages): open a session to see participants, connection minutes, join/leave timestamps, and connection quality. • Participant details: check
participant_left
and its
disconnected_reason
(e.g.,
USER_REJECTED
,
CONNECTION_TIMEOUT
) to classify rejects. • Agent Insights (if enabled): replay synchronized audio, transcripts, and traces to diagnose long ringing or rejections. How to identify rejected vs ringing-forever • Rejected: find
participant_left
with
disconnected_reason = USER_REJECTED
or a short room lifecycle (room_started → room_finished quickly). • Ringing-forever / unanswered: look for rooms with
room_started
but no
participant_joined
within your ring timeout, or very long time-to-join in the session list. Tips for using the dashboard effectively • Apply filters (time range, trunk, dispatch rule, participant id) to narrow results. • Sort or filter by call duration — very short lifetimes often indicate rejections; very long pre-join durations indicate ringing. • Correlate dashboard entries with Twilio CallSid (if using Twilio) by storing CallSid in room/participant metadata so you can join Twilio callbacks with dashboard records. If you want, I can: • Provide exact steps/screens to filter the Call Logs for
disconnected_reason
and long pre-join durations, or • Give a short example of metadata naming (e.g., store
twilio_callsid
in participant identity) so dashboard rows correlate with Twilio events. Which would you like? Sources: Say hello to improved Cloud analytics | LiveKit | Best practices for managing webhook event streams | LiveKit | Agents (Python) | livekit/livekit_composite | DeepWiki | Streamline troubleshooting with Agent Observability | LiveKit | Why is my Agent insights data missing? | LiveKit, with interactive elements
r
how can I have more complex filters. only see the status filter that’s it
t
I’m responding to your request about creating more complex filters in the dashboard beyond the single status filter. Short answer • The dashboard supports filtering by multiple attributes (time range, SIP trunk/dispatch rule, participant ID, call duration, disconnected_reason) and saving/exporting results — but to give exact steps I need to know which dashboard view you’re using (Call Logs vs Sessions vs Agent Insights). Which one are you looking at? If you want an immediate example, tell me: • Which view (Call Logs / Sessions / Agent Insights)? • Which fields you want to combine (e.g.,
disconnected_reason = USER_REJECTED
+ duration < 5s + trunk = X) I’ll then give step‑by‑step filter instructions or a saved-filter example you can paste into the dashboard. , with interactive elements
r
the telephony -> call
yes those fields
t
Thanks — I’m responding about adding more complex filters in Telephony → Call Logs. Step-by-step (Telephony → Call) 1. Open Telephony → Call Logs and set your time range at the top. 2. Use the filter bar / search field to add criteria: ◦ Click the filter dropdown and add each attribute you need (e.g., SIP trunk / dispatch rule, participant id, disconnected_reason, call duration). ◦ For durations, choose the duration filter and set operators (e.g.,
< 5s
,
> 30s
). ◦ For
disconnected_reason
, select one or multiple values (e.g.,
USER_REJECTED
,
CONNECTION_TIMEOUT
). 1. Combine filters: each added filter narrows results (AND logic). Use multiple values inside a single filter for OR behavior when available. 2. Sort and inspect results: sort by start time or duration to surface short/failed calls or long pre-join sessions. 3. Save or export: ◦ Save the current filter as a preset (if the dashboard offers Save Filter). ◦ Export the filtered rows to CSV if you need offline analysis. Concrete example filter to find short rejections on a trunk • Time range: Last 7 days • Trunk = MySIPTrunk • disconnected_reason IN (USER_REJECTED, CONNECTION_TIMEOUT) • Duration < 5s If your dashboard only shows a single “status” filter, tell me whether you see a filter icon or a column header menu in Call Logs — I can give exact clicks or a screenshot walkthrough for your UI. Which would you prefer? , with interactive elements
Thanks — I’m responding to your request for how to build more complex filters in Telephony → Call Logs. Step-by-step: create a multi-criteria filter 1. Open Telephony → Call Logs and set the Time range in the top-right. 2. Use the global search bar to add free-text matches (e.g., CallSid or participant id). 3. Use the column filter dropdowns for structured fields: ◦ Click the header for a column (e.g.,
trunk
,
dispatch_rule
,
disconnected_reason
,
duration
) and choose the filter operator (equals, contains, , ). 1. Combine multiple column filters — the dashboard applies them with AND logic so results match all criteria. 2. To filter by short duration: open the
duration
column filter and set
< 5
(seconds). 3. To show rejected calls: open
disconnected_reason
and select
USER_REJECTED
(or type it into the filter). 4. To scope to a trunk or dispatch rule: filter
trunk = your_trunk_name
and/or
dispatch_rule = your_rule
. Save and reuse • After composing filters, click Save (or Save view/preset) to persist the filter set for quick reuse. • Use Export (CSV) on the filtered result set if you want offline analysis. Example filter (rejected, short calls on trunk X) • Time range: Last 7 days •
trunk = trunk-X
•
disconnected_reason = USER_REJECTED
•
duration < 5
• (Optional) global search:
participant_id:agent-123
or
callSid:CAxxxx
If you don’t see specific columns (e.g.,
disconnected_reason
), enable them from the column selector (gear or Columns menu) in the Call Logs view. Would you like a screenshot-style walkthrough for each click, or the exact field names to use for creating a saved preset? Which do you prefer? , with interactive elements