A caller says they phoned and it did not work. Your dashboard says the call ended with an error. Those two sentences are the whole problem: somewhere between the caller's handset and your agent, seven things could have gone wrong, and a single word in a dashboard cannot tell you which. The answer is almost always sitting in the SIP dialog, one layer below where most voice-AI platforms let you look.
Four layers, and only one of them is the AI
When people debug a voice agent they reach for the prompt first, because that is the part they wrote. But a phone call is a stack, and the model sits near the top of it. Below the agent there is an orchestration layer that decides which assistant answers and holds the session. Below that is SIP, the signalling protocol that sets the call up, negotiates what audio both sides can speak, and tears it down again. And below that, running on its own separate path, is the media: the actual RTP packets carrying voice.
That last split is the single most useful thing to internalise. Signalling and media travel separately. A call can be perfectly connected at the SIP layer while no audio flows at all, which is why "the call connected but the agent never spoke" is a media problem and not a prompting problem. Ninety percent of the confusion in voice AI debugging comes from treating one stack as if it were the other.
Fix the layer the symptom lives in
What a healthy call looks like in SIP
Before you can spot a broken dialog you need to know what an intact one reads like. An inbound call is a short, extremely predictable conversation between the carrier and your platform. The carrier offers the call with an INVITE that includes an SDP body listing the codecs it can speak. Your side acknowledges, rings, answers with 200 OK and its own SDP, and both ends start sending RTP. When someone hangs up, a BYE closes it with a reason.
>> INVITE sip:+31570238200@trunk SIP/2.0
From: <sip:+31612345678@carrier>;tag=8f2b
Call-ID: 4c1d9e77-3a2f-4b10-9c8e-11e2a6d0f5aa
SDP: audio 41002 RTP/AVP 8 0 101 (PCMA, PCMU, telephone-event)
<< SIP/2.0 100 Trying
<< SIP/2.0 180 Ringing
<< SIP/2.0 200 OK
SDP: audio 24518 RTP/AVP 8 101 (PCMA agreed)
[rtp] inbound stream established ssrc=0x6b21f0
[rtp] outbound stream established ssrc=0x11c4ae
>> BYE sip:+31570238200@trunk SIP/2.0
Reason: Q.850;cause=16;text="normal clearing"
call ended · 47.2s · two-way audio · caller hung upFour things in that trace are worth pointing at. The Call-ID is the handle that ties every packet, log line and recording of this one call together, and it is the first thing to ask for when a customer reports a problem. The SDP lines show a codec negotiation that succeeded: the carrier offered PCMA, PCMU and telephone-event, and your side agreed on PCMA. The two RTP lines confirm audio in both directions, each with its own synchronisation source. And the BYE carries a Q.850 cause, which is the telephony network telling you in a standard number why the call ended.
The response codes that explain a failure
When a call does not survive setup, the SIP response code is the diagnosis. Most voice platforms collapse all of these into "failed", which throws away the only information that would have told you whose problem it is. These are the ones that actually show up on live trunks.
| Code | Means | Who owns it |
|---|---|---|
| 403 Forbidden | The trunk rejected the request outright, usually an IP that is not on the allow-list, or credentials that do not match. | Configuration |
| 404 Not Found | The number does not exist on that trunk. Frequently a routing entry that was never created, or a number ported away. | Carrier / routing |
| 407 Proxy Authentication Required | Registration or digest auth is not completing. Normal as a first challenge; a problem when it repeats. | Configuration |
| 408 Request Timeout | Nobody answered the signalling at all. Often a firewall silently dropping SIP, not a busy line. | Network |
| 480 Temporarily Unavailable | Reached the right place, but no endpoint was available to take it. On an AI trunk this often means no worker picked up the session. | Platform |
| 486 Busy Here | A real busy signal, or a concurrency ceiling being hit. Worth checking your own limits before blaming the carrier. | Platform / capacity |
| 487 Request Terminated | The call was cancelled before it was answered: the caller hung up during ringing. Not a fault. | Nobody |
| 488 Not Acceptable Here | Codec or SDP mismatch. Both sides were willing to talk and could not agree on how. Silent, instant, and never the model's fault. | Configuration |
| 503 Service Unavailable | The far end is up but declining work: congestion, a carrier incident, or a downstream component refusing. | Carrier / platform |
The 488 deserves its own paragraph, because it is the failure that looks most like magic. Here the same call, offered with a codec the trunk was not configured to accept:
>> INVITE sip:+31570238200@trunk SIP/2.0
SDP: audio 41002 RTP/AVP 9 101 (G.722, telephone-event)
<< SIP/2.0 100 Trying
<< SIP/2.0 488 Not Acceptable Here
Warning: 304 "Media type not available"
call never connected · 0.4s · no RTP · no transcriptFour hundred milliseconds, no audio, no transcript, nothing for the model to have done wrong. From a dashboard that only reports end reasons this is indistinguishable from a dozen other failures. From the SIP dialog it is a five-minute fix in the trunk's codec list.
One code deserves a mention precisely because it never appears in a failure: OPTIONS. It is not an error at all, it is a keepalive: a small periodic request whose only job is to ask the other end whether it is still there. Enabling OPTIONS pings on a trunk, at an interval in the region of thirty seconds, turns "is the trunk alive" from a question you answer during an incident into a graph you can look at before one. It also keeps NAT bindings warm, which quietly prevents a class of one-way audio failures described below. It is the cheapest monitoring in telephony and it is switched off by default nearly everywhere.
Q.850 cause codes worth memorising
Where SIP describes the request, Q.850 describes what the telephone network thought of it. Cause codes arrive in the Reason header on a BYE or a CANCEL, and they survive across gateways, so they often carry the truth even when the SIP code has been rewritten somewhere along the path.
| Cause | Standard meaning | What it usually is in practice |
|---|---|---|
| 1 | Unallocated number | The dialled number is not assigned. On outbound campaigns this is a bad data problem, not a platform problem. |
| 16 | Normal clearing | A healthy hang-up. Seeing this on a call the customer says failed means the failure was in the audio, not the call. |
| 17 | User busy | Genuinely engaged, or a device rejecting a second call. |
| 19 | No answer from user | It rang out. Check your ring timeout before assuming the number is dead. |
| 21 | Call rejected | Something actively said no: a blocklist, a spam filter, or a carrier policy on the destination. |
| 34 | No circuit available | Capacity, upstream. Retry logic matters here; a hard fail does not. |
| 38 | Network out of order | A real upstream fault. This is the one where you check the carrier's status page first. |
| 41 | Temporary failure | The vaguest useful code. Almost always worth one retry. |
| 102 | Recovery on timer expiry | Something did not answer in time and a timer gave up. Points at latency or a stalled leg, not at a rejection. |
| 127 | Interworking, unspecified | A gateway translated something and lost the original reason. Treat as 'no information available'. |
One-way audio: the classic that is never the model
The most common serious complaint on any new voice deployment is that one side cannot hear the other. The caller hears the agent perfectly and the agent behaves as if the caller never spoke, or the reverse. It presents as an AI failure and it is almost never an AI failure.
Because media travels on its own path, audio only works if RTP can reach both endpoints in both directions. Signalling can complete over one route while the media route is blocked, and nothing in the SIP dialog will complain. The usual culprits are predictable: a firewall permitting the SIP port but not the RTP range, network address translation rewriting an address inside the SDP so the far end sends packets into nowhere, or symmetric-RTP expectations that one side does not honour.
The diagnosis is mechanical rather than clever. Look at the packet counters for each direction separately. If inbound RTP is arriving and outbound is zero, you have a routing or filtering problem on the way out, not a transcription problem. If both counters are healthy and the agent still behaves as if it heard silence, only then does it become a speech-recognition question.
This is the layer a reseller cannot reach
When the keypress does not register
A caller is asked to press 1 and presses 1, and nothing happens. DTMF, the tones a keypad makes, can travel in three different ways, and a mismatch between them is silent. The tone can be carried as its own RTP payload type as described in RFC 4733 and its predecessor RFC 2833, which is what the telephone-event entry in the SDP above is announcing. It can be sent as plain audio, mixed into the voice stream, which survives fewer hops than people expect. Or it can be sent out-of-band as a SIP INFO message, entirely outside the media path.
If your side is listening for RTP events and the carrier is sending inband tones, the keypress is physically present in the audio and completely invisible to your application. Confirm which of the three is actually in use before touching anything in the agent, and prefer negotiating telephone-event in the SDP so the tone arrives as a discrete event rather than as a sound you have to detect.
Why your end reason is not a diagnosis
Good platforms give you a deterministic end reason per call. On VoiceDock every call closes with one of a fixed set: the caller hung up, the assistant hung up, the call was transferred, it hit its maximum duration, voicemail was detected, nobody answered, credit ran out, or an error occurred. That set is genuinely useful for reporting, because it means you can count outcomes without parsing free text.
What an end reason cannot do is explain a failure. "Error" is a category, not a cause. The value of owning the layer underneath is that when the category is not enough, there is a next question to ask: which SIP code, which Q.850 cause, which direction was the RTP missing in, which Call-ID. A platform that stops at the end reason has made that next question unanswerable by design.
The end reason tells you what happened to the call. The SIP dialog tells you why. You need a vendor who can hand you both.
A triage order that actually converges
Debugging telephony badly means opening five things at once. Debugging it well means asking questions in an order that halves the search space each time. This is the order we use.
- 01
Get the Call-ID before anything else
Everything else is guesswork without it. A timestamp and a caller number will do if that is all the customer has, but the Call-ID turns a story into a record. - 02
Establish whether the call connected at all
A 200 OK splits the problem cleanly. No 200 OK means the failure is in setup and the response code names it. A 200 OK means setup worked and you move to media. - 03
Check RTP in each direction separately
Two counters, not one. Silence in one direction only is a network or NAT problem, and it is the single most likely cause of "the agent ignored me". - 04
Read the closing cause, not just the status
A Q.850 cause of 16 on a call the customer says failed is a strong signal: the call was fine and the experience was not. That points at audio quality, latency, or the agent, and away from telephony. - 05
Only now open the transcript and the prompt
If setup succeeded, audio flowed both ways and the call closed normally, then and only then is it a model or instruction problem, and you have ruled out four layers of noise before spending time there.
None of this is exotic knowledge. It is ordinary telephony engineering, and it is exactly what disappears when a voice platform treats the phone network as an implementation detail you are not allowed to see. We publish it because the question is not whether calls will occasionally fail on a real network. They will. The question is whether the people you build with can tell you why.
Sources Response codes and dialog behaviour as defined in RFC 3261 (SIP), DTMF carriage in RFC 4733, and cause values from ITU-T Q.850. The triage order and the practical readings reflect our own experience running voice traffic on production trunks.