> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cekura.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent Dispatch Reliability

> Strategies for handling LiveKit agent dispatch latency and failures in production

export const CopyPageButton = () => {
  if (typeof window !== 'undefined') {
    setTimeout(function () {
      if (document.getElementById('ck-tools')) return;
      var anchor = document.getElementById('content-area') || document.querySelector('.mdx-content');
      if (!anchor) return;
      if (!document.getElementById('ck-style')) {
        var s = document.createElement('style');
        s.id = 'ck-style';
        s.textContent = '#ck-tools{position:absolute;top:6px;right:0;z-index:100;font-family:inherit;}' + '.ck-row{display:inline-flex;align-items:stretch;border:1px solid rgba(0,0,0,0.15);border-radius:8px;overflow:hidden;background:#fff;}' + ':root.dark .ck-row{background:rgba(255,255,255,0.06);border-color:rgba(255,255,255,0.12);}' + '.ck-btn{padding:5px 12px;border:none;background:none;cursor:pointer;font-size:13px;font-weight:500;font-family:inherit;color:#374151;}' + ':root.dark .ck-btn{color:#d1d5db;}' + '.ck-btn:hover{background:rgba(0,0,0,0.04);}' + ':root.dark .ck-btn:hover{background:rgba(255,255,255,0.06);}' + '.ck-chevron{padding:5px 8px;border:none;background:none;cursor:pointer;font-size:14px;font-family:inherit;color:#374151;}' + ':root.dark .ck-chevron{color:#d1d5db;}' + '.ck-chevron:hover{background:rgba(0,0,0,0.04);}' + ':root.dark .ck-chevron:hover{background:rgba(255,255,255,0.06);}' + '.ck-divider{width:1px;background:rgba(0,0,0,0.12);flex-shrink:0;}' + ':root.dark .ck-divider{background:rgba(255,255,255,0.12);}' + '.ck-dd{position:absolute;top:calc(100% + 4px);right:0;min-width:180px;background:#fff;border:1px solid rgba(0,0,0,0.12);border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,0.1);padding:4px;display:none;z-index:200;}' + ':root.dark .ck-dd{background:#1f2937;border-color:rgba(255,255,255,0.1);box-shadow:0 4px 16px rgba(0,0,0,0.35);}' + '.ck-item{display:block;width:100%;padding:7px 12px;border:none;background:none;border-radius:6px;cursor:pointer;font-size:13px;font-family:inherit;text-align:left;color:#374151;}' + ':root.dark .ck-item{color:#d1d5db;}' + '.ck-item:hover{background:rgba(0,0,0,0.05);}' + ':root.dark .ck-item:hover{background:rgba(255,255,255,0.07);}';
        document.head.appendChild(s);
      }
      var wrap = document.createElement('div');
      wrap.id = 'ck-tools';
      var row = document.createElement('div');
      row.className = 'ck-row';
      var mainBtn = document.createElement('button');
      mainBtn.className = 'ck-btn';
      mainBtn.textContent = 'Copy page';
      var divider = document.createElement('span');
      divider.className = 'ck-divider';
      var chevron = document.createElement('button');
      chevron.className = 'ck-chevron';
      chevron.textContent = '▾';
      var dd = document.createElement('div');
      dd.className = 'ck-dd';
      function closeDD() {
        dd.style.display = 'none';
      }
      function openDD() {
        dd.style.display = 'block';
      }
      chevron.onclick = function (e) {
        e.stopPropagation();
        if (dd.style.display === 'block') {
          closeDD();
        } else {
          openDD();
        }
      };
      document.addEventListener('click', function (e) {
        if (!e.target.closest('#ck-tools')) {
          closeDD();
        }
      });
      document.addEventListener('keydown', function (e) {
        if (e.key === 'Escape') {
          closeDD();
        }
      });
      function makeItem(label, fn) {
        var b = document.createElement('button');
        b.className = 'ck-item';
        b.textContent = label;
        b.onclick = function () {
          fn();
          closeDD();
        };
        return b;
      }
      function getMarkdown() {
        var walk = function (node) {
          if (!node) return '';
          if (node.nodeType === 3) return node.textContent || '';
          if (node.nodeType !== 1) return '';
          var tag = node.tagName.toLowerCase();
          var skip = ['script', 'style', 'svg', 'noscript', 'button', 'iframe'];
          if (skip.indexOf(tag) !== -1) return '';
          if (node.id === 'ck-tools') return '';
          var ch = Array.from(node.childNodes).map(walk).join('');
          if (tag === 'h1') return '\n# ' + ch.trim() + '\n\n';
          if (tag === 'h2') return '\n## ' + ch.trim() + '\n\n';
          if (tag === 'h3') return '\n### ' + ch.trim() + '\n\n';
          if (tag === 'p') return '\n' + ch.trim() + '\n\n';
          if (tag === 'pre') return '\n```\n' + node.textContent.trim() + '\n```\n\n';
          if (tag === 'li') return '- ' + ch.trim() + '\n';
          if (tag === 'code') return '`' + ch.trim() + '`';
          return ch;
        };
        var content = document.querySelector('.mdx-content') || document.getElementById('content-area') || document.body;
        return walk(content).replace(/\n\n\n+/g, '\n\n').trim();
      }
      function copyMd() {
        var md = getMarkdown();
        navigator.clipboard.writeText(md).then(function () {
          mainBtn.textContent = 'Copied!';
          setTimeout(function () {
            mainBtn.textContent = 'Copy page';
          }, 2000);
        });
      }
      function viewMd() {
        var md = getMarkdown();
        var safe = md.split('&').join('&amp;').split('<').join('&lt;').split('>').join('&gt;');
        var html = '<!DOCTYPE html><html><head><meta charset="utf-8"><style>body{font-family:monospace;max-width:860px;margin:40px auto;padding:0 24px;line-height:1.7;white-space:pre-wrap;word-wrap:break-word}</style></head><body>' + safe + '</body></html>';
        window.open(URL.createObjectURL(new Blob([html], {
          type: 'text/html'
        })), '_blank');
      }
      function openClaude() {
        var prompt = 'Can you read this Cekura docs page ' + window.location.href + ' so I can ask you questions?';
        window.open('https://claude.ai/new?q=' + encodeURIComponent(prompt), '_blank');
      }
      mainBtn.onclick = copyMd;
      dd.appendChild(makeItem('Copy page', copyMd));
      dd.appendChild(makeItem('View as Markdown', viewMd));
      dd.appendChild(makeItem('Open in Claude', openClaude));
      row.appendChild(mainBtn);
      row.appendChild(divider);
      row.appendChild(chevron);
      wrap.appendChild(row);
      wrap.appendChild(dd);
      anchor.style.position = 'relative';
      anchor.insertBefore(wrap, anchor.firstChild);
    }, 50);
  }
  return null;
};

<CopyPageButton />

## Overview

LiveKit agent dispatch is generally reliable, but transient platform delays can occasionally cause an agent to join a room with latency or fail to dispatch at all. In voice applications, this gap means a recipient answers a call and hears silence — a poor user experience.

This page covers patterns for making your outbound and inbound LiveKit flows resilient to these issues.

***

## Debugging First: Use the Cekura SDK

Before tuning your room lifecycle, determine whether you're dealing with a configuration issue or a transient dispatch delay. The [Cekura LiveKit SDK](/documentation/integrations/livekit/tracing) captures all session traces and logs automatically, giving you a timestamped record of:

* Agent join timing relative to room creation
* Full job dispatch metadata (scenario, run ID, test profile)
* Session logs with timestamps and log levels
* Raw STT, TTS, LLM, and EOU metrics per session

With this data you can quickly distinguish between a dispatch failure (agent never appeared) and join latency (agent appeared late). You can then point an AI assistant at the captured logs to triage whether the delay originated in the dispatch layer, the LLM initialization, or the infrastructure.

***

## Always Pass `agent_name` Explicitly

Passing `agent_name` explicitly in your `CreateAgentDispatchRequest` has been shown to fix cases where the dispatcher fails to route correctly — even when only one agent is running. Do not rely on implicit routing:

```python theme={null}
from livekit.api import LiveKitAPI, CreateAgentDispatchRequest
import json

livekit_api = LiveKitAPI(
    url="wss://<YOUR_LIVEKIT_URL>",
    api_key="<YOUR_API_KEY>",
    api_secret="<YOUR_API_SECRET>",
)

await livekit_api.agent_dispatch.create_dispatch(
    CreateAgentDispatchRequest(
        agent_name="your-agent-name",   # Always specify this explicitly
        room="your-room-name",
        metadata=json.dumps({}),
    )
)
```

***

## Outbound Calls: Wait-then-Dial

The most reliable pattern for outbound calling is to confirm the agent has joined the room **before** initiating the SIP dial. Dialing immediately after room creation means a recipient can answer and hear silence while the agent is still joining.

Recommended sequence:

<Steps>
  <Step title="Create the LiveKit room">
    Create and hold the room without placing any calls yet.
  </Step>

  <Step title="Dispatch the agent">
    Send a `CreateAgentDispatchRequest` targeting the new room, with `agent_name` set explicitly.
  </Step>

  <Step title="Confirm the agent has joined">
    Poll the room's participant list (or subscribe to participant-joined events) and wait until your agent appears as an active participant.
  </Step>

  <Step title="Initiate the SIP dial">
    Only once the agent is confirmed present, add the SIP participant to place the outbound call.
  </Step>
</Steps>

This sequence eliminates the silent-join window entirely by ensuring the agent is ready before the recipient's phone rings.

***

## Inbound Calls: Keep an Agent Pre-warmed

For inbound call flows you cannot apply a wait-then-dial delay — the caller connects immediately. The standard mitigation is to keep one agent instance **always running** in a standby state so it can handle an incoming room join without a cold-start dispatch round-trip.

<Note>
  A pre-warmed agent introduces a small ongoing cost (an idle process), but eliminates the latency gap between caller arrival and agent readiness. The trade-off is generally worthwhile for production inbound flows where call quality is critical.
</Note>

***

## Provider-Level Fallback

For flows where dispatch reliability is critical, consider configuring a secondary provider (such as Retell) as a fallback if the primary dispatch does not succeed within a timeout window. A two-provider setup adds operational complexity but provides a hard safety net during provider outages.

<Note>
  Provider-level outages are rare and transient. The strategies above (wait-then-dial, explicit `agent_name`, pre-warmed agents) address the more common case of per-dispatch latency spikes without requiring a full fallback stack. Reserve the provider-fallback approach for flows where any dispatch failure is unacceptable.
</Note>

***

## Related Resources

* [LiveKit Tracing](/documentation/integrations/livekit/tracing) — Set up the Cekura SDK to capture session logs and metrics
* [LiveKit Automated Testing](/documentation/integrations/livekit/testing) — Run evaluator scenarios with automated room and token management
