Diagnose SIP routing and termination
Use this procedure for an unexpected SIP routing response, when a call ends with an unexpected cause, an expected diagnostic header is missing, or optional termination headers are dropped.
Before you start
Identify the deployment release, affected conversation or channel, and SIP call leg. Obtain access to the relevant application logs and SIP capture. Record the time of the event and whether the call had been answered. Avoid combining captures from different call attempts or transfer legs.
The termination reference defines cause values, generated headers, and default optional-header limits. A SIP cause inside Reason is not the same thing as the response status of the message carrying it.
Identify a routing response first
Confirm which component sent the response. A carrier response with the same numeric code is not automatically the Delphi branch described below. These are selected configured responses, not an exhaustive error catalogue.
| Response from the platform | Configuration or runtime condition | Next step |
|---|---|---|
403 Forbidden - Unknown Trunk | The request reached the unknown-trunk branch | Check inbound identification and the request's actual source/From/User-Agent |
403 Forbidden - TLS Required | The selected trunk requires TLS and the request used a different transport | Compare incoming transport with the selected trunk policy |
500 Inbound Auth Misconfiguration | Required digest credentials are missing | Ask the authorized administrator to correct the trunk credentials |
403 Forbidden - Source IP Not Allowed | The ip_and_digest source-IP check failed | Compare the source address with the allowed IP/CIDR configuration |
404 No Outbound Route | Required route context or the corresponding egress payload is missing | Check outbound selection and its applied runtime state |
404 No Outbound Identity | The resolved route has no outbound base-number identity | Check the route's base identity and endpoint configuration |
503 Routing Error | An egress Redis lookup failed | Investigate the routing dependency before repeatedly changing the user's configuration |
503 Service Unavailable during maintenance drain | The platform rejects a new INVITE in its drain branch | Check the deployment's maintenance state; do not assume every 503 is caused by draining |
A 481 response concerns the relevant transaction/dialog context; it does not, on its own, prove that a SIP method is unsupported. Similarly, a successful OPTIONS response is a probe result, not proof that a real call passed routing and authorization.
For a configuration correction, follow Check SIP routing configuration, then return to the verification step. For termination/header questions, continue below.
1. Find the selected cause
Look for termination_signal_selected from termination_signaling_service for the affected channel. Its metadata includes method, causeId, category, sipCause, q850Cause, and reasonTextLength.
Compare the selected cause with the cause mapping. Use application events around the same time to determine why that cause was selected. Numeric causes alone cannot distinguish every failure; STT and TTS errors, for example, share the same SIP and Q.850 values.
If this event is absent, first confirm the channel, release, logging coverage, and whether the action used this termination path. Absence alone does not prove the call did not end.
2. Distinguish header preparation from publication
| Event | What it tells you | Next check |
|---|---|---|
optional_termination_headers_sanitized | Optional names or values were normalized, or entries removed | Compare intended headers with normalized output and the reserved-header rules |
optional_termination_headers_dropped | The optional set exceeded the platform budget | Reduce the set according to the header limits |
sip_call_id_unavailable | Header intent could not be indexed using a SIP Call-ID | Check that you are inspecting the correct dialog and whether the channel's Call-ID was available |
sip_egress_intent_published | The service published a header intent | Continue to the outgoing SIP capture; this event is not proof of peer delivery |
sip_egress_intent_failed | Header intent publication failed | Inspect the accompanying failure and dependencies; termination may still have continued |
Record the event's method and identifiers rather than assuming all events refer to the original inbound dialog. A transfer can create a new outbound leg.
3. Inspect the corresponding SIP message
Compare the observed message with the generated header format:
- For an answered termination, inspect the relevant BYE path.
- For a rejection before answer, inspect the final response; do not require a BYE as evidence.
- For an in-dialog REFER, inspect that request.
- For a new transfer INVITE, look for
X-Transfer-Reasonon the new leg rather than assuming the old dialog'sReasonwas copied.
A service log, a platform-side capture, and the peer's received message are different evidence. Record the capture point and direction when reporting a discrepancy. The published signaling guide linked from SIP and call handling describes the existing capture tools.
Follow an action from intent to observed outcome
For a disputed hangup or transfer, separate the requested action, header preparation, edge processing and observed SIP transaction. A call disappearing from the UI or a hangup_success event is not proof that the remote peer received the intended headers.
Selected termination. Record termination_signal_selected, method/cause, channel and time. The service selected a signal; this does not prove the peer saw it.
Published header intent. Record sip_egress_intent_published, method, intentId, headerCount and indexedBySipCallId. The publish helper returned; this does not prove that the edge found or materialized those headers.
Edge application. Read Applied SIP egress intent … for … call=… together with adjacent warnings. The edge reached the end of the intent application path. The same path logs Failed to materialize SIP egress intent … if msg_apply_changes() fails, then still emits the applied line. The applied line alone is insufficient.
Platform-side SIP capture. Record recordKind=sip_message, logType=sip_capture, sipCallId, capturePoint, method, statusCode, time and the raw message where authorized. A message was observed at that capture point. Pair the request and response on the same SIP transaction; a response on another leg or method is not an acknowledgement of this action.
Peer/application outcome. Collect relevant peer-side evidence and the expected disconnected/transferred state. This confirms the separate outcome you are investigating. A platform-local capture or API acknowledgement does not establish every downstream business/media result.
Investigate a BYE without assuming a universal three-event guarantee
- Start with an existing affected conversation/channel and a bounded time window. Record the relevant SIP Call-ID and dialog, not only the higher-level conversation ID. A transfer can add a separate SIP leg; keep its identifiers distinct.
- Check the selected cause and header-publication events. For an indexed BYE, expect the method to be
BYEand inspectindexedBySipCallId;sip_call_id_unavailablemeans the lookup index could not be formed. The intent may still have been stored under its generated ID, so do not equate the warning with no Redis write at all. - Match the intent ID and method to edge processing for that SIP Call-ID. Inspect lookup/method-mismatch and materialization warnings before relying on the applied line. The inspected edge path applies action headers only to its private
10.*source path; deployment placement and actual routing matter. - Find the corresponding outbound BYE capture and matching response. Inspect the raw SIP transaction identifiers where available, including CSeq method/number, rather than counting any nearby
200. A matched response from the relevant peer supports acknowledgement of that transaction; independently confirm the expected call state and headers. - If the chain stops, investigate that boundary. Missing capture can also mean missing instrumentation, ingestion, permissions or an incorrect query. Do not conclude “the carrier received nothing” solely because one stored log search returned no rows.
hangup_success is emitted after the platform's hangup call returns successfully. It does not itself verify the carrier-facing transaction. Intent publication is deliberately fail-open: sip_egress_intent_failed can coexist with a completed hangup. Redis intent/index entries expire after 15 minutes; inspect timely evidence rather than expecting them to remain as a durable audit trail. The publisher awaits its Redis pipeline but does not inspect each command's returned error slot, so its success log is not an independent Redis integrity check.
Follow the transfer method and the new leg
For an in-dialog REFER, the intent is indexed by the existing SIP Call-ID. For a new INVITE, the platform cannot know the new SIP Call-ID in advance: it supplies an X-Delphi-Egress-Intent-ID bootstrap value, which the edge uses to bind the new request and then removes. Do not expect that internal bootstrap header at the carrier or require indexedBySipCallId=true on the original INVITE publication.
Use transfer routing to establish the selected method, destination and egress path, then inspect that method's request/response and the intended transfer result. The generated new-leg header is X-Transfer-Reason; the original dialog's Reason is not automatically its substitute. A successful REFER request response is not by itself evidence of a completed destination conversation. Keep result-aware notification/outcome evidence, when applicable to the configured transfer path, distinct from request acceptance.
Transfer header-budget failure can return transfer_blocked with sip_header_budget_exceeded before dispatch. That is different from fail-open intent publication failure. Use the termination/header reference and observed method before deciding whether missing optional headers or an entirely blocked action is the problem. For a rejection before answer, follow the final-response path rather than demanding a BYE.
Use evidence gaps as diagnostic signals, not assumed deployed alerts
An intent without a matching observed action, publication/index warnings, or a rise in dropped headers can inform an operator's alert design. They are not a claim that this deployment has those alerts installed. Counting intents minus captures is not a reliable delivery proof without accounting for retries, duplicate captures, multiple legs, time windows and telemetry loss. Establish the actual alert definition, data source and tested behavior separately; preserve the bounded event chain for investigation through logs.
4. Send configuration questions to the right owner
If the destination, outbound identity, or selected route is unexpected, inspect the transfer-routing configuration before changing diagnostic headers. Inbound identification and outbound egress selection are separate configuration questions.
If the intended action came from a flow, identify that control's configuration and permissions. Do not assume a runtime command always overrides saved configuration.
5. Verify after a correction
Repeat the affected scenario with a test destination approved for your environment. Compare the selected cause, header publication result, and observed SIP message on the same relevant call leg. Confirm the application outcome independently—for example, that the channel disconnected—not merely that a header-publication log exists.
For a remaining discrepancy, retain the release, scenario, time, channel/conversation identifier, SIP Call-ID where available, capture point, selected cause, publication event, and observed message. Share only the diagnostic data appropriate for the recipient; recordings, transcripts, credentials, and unrelated call data are not needed for this comparison.
For transport, reliable provisional response, codec or network assumptions, consult the SIP signaling and media reference before interpreting a successful probe as proof of a working call.
Investigate a 100rel configuration mismatch
Use this check when the peer's reliable provisional response requirements differ from the behavior seen during call setup.
- Identify the TelSys instance handling the call and record its deployed version. Keep evidence for both sides of the TelPro-to-TelSys boundary; one peer-facing capture does not establish the internal endpoint's configuration.
- Ask the deployment operator to check that instance's effective
SIP_100REL_MODE. Compare it with the canonical values and fallback behavior. An unrecognized value selectspeer_supported, so a typo is not equivalent to leaving the setting at its default. Check the startup warning/configuration log. - Confirm the generated and loaded
100relsetting for the TelPro-facing TelSys endpoint. An environment value or a generated file alone does not show which configuration the running process is using. Resolve an environment/load mismatch through the deployment's configuration procedure before retesting. - If the setting matches the intended peer requirements but setup still fails, collect the complete provisional-response/PRACK exchange and final result for each relevant call leg. Include timestamps, deployed versions, intended mode, observed endpoint setting and sanitized signaling when escalating the mismatch.
Verify the result with a controlled call using the same peer and call path. A successful OPTIONS probe does not exercise this negotiation. These checks establish configuration and diagnostic evidence; they do not certify peer interoperability.
Diagnose missing or unexpected keypad input
- Identify the collecting stage: pre-flow extension selection, the SIP flow's
dtmf_collectnode, or a separate provider/browser input path. The collection reference explains why their retry rules differ. - For a SIP call, inspect telephone-event negotiation and the relevant call leg using the SIP media reference. A keypad tone heard locally is not proof that a digit event reached the collector.
- Correlate the channel and time with
dtmf_receivedand the active collection node. Input before the collector registers its listener is not evidence of input accepted by that node. Verify prompt timing rather than changing retry settings to compensate for missing transport events. - Compare the result and transition with collection outcomes.
digits_collectedcan represent partial input. A no-inputtimeoutlog can accompanyaborted; check the actual signal. After an abort, a previous context value can remain and must not be treated as a newly collected answer. - Reproduce with non-sensitive test digits, exercising silence, partial input and the termination key. Check downstream validation separately from collection. Retain the release, node identifier, configured limit/deadline/attempt count, relevant call leg, observed signal and result length; omit real keypad values.
If transport events are missing, escalate with the negotiated DTMF method and sanitized call-leg evidence. If events arrive but the flow takes an unexpected branch, investigate collection outcome and transition configuration first.