SIP signaling reference
Use this page when you need which SIP message was logged, where it was captured, or what a response code means on a Delphi deployment. The primary operator surface is the conversation Debug tab SIP ladder in TelWeb; this reference explains what feeds it and how to query the same data in SigNoz.
For application and media lifecycle logs (call start/hangup, transfer, routing rejects, RTPEngine quality) — as opposed to SIP frames — see Application and call-flow logging.
For TelPro routing, Redis keys, and firewall rules, see TelPro operations. For SigNoz dashboards, PII, and general call investigation, see SigNoz monitoring.
Where to look first
| Surface | Best for |
|---|---|
| TelWeb → conversation → Debug → SIP ladder | Per-call timeline with Outside / TelPro / TelSys lanes, raw message inspector, DTMF chips, and text export. |
| SigNoz Logs | Ad-hoc search by call_id, trace_id, or recordKind=sip_message when you do not have a conversation row open. |
| Container stdout | Last resort when telemetry is missing — docker compose logs voiceai-telpro / voiceai-telsys. |
Correlate with call_id (SIP Call-ID, same value as TelPhi X-Call-ID) and trace_id across TelPro, Voice, and TelAPI.
Three-lane ladder
The Debug SIP ladder reconstructs signaling across three lanes. In v0.9.14-patch3, the Call Detail Debug tab separates Logs, Spans, and SIP Ladder panes; use Export on the SIP Ladder pane when support needs a plain-text capture bundle.
| Lane | Component | Typical peer |
|---|---|---|
| Outside | Carrier trunk or WebRTC (Janus User-Agent: VoiceAI-WebRTC/…) | Public SIP provider, browser voice client |
| TelPro | voiceai-telpro (Kamailio) | Terminates carrier/WebRTC hop, RTPEngine anchor, dispatches to Voice |
| TelSys | voiceai-telsys (Asterisk PJSIP) | Accepts relayed INVITE from Kamailio, bridges to TelPhi via ARI |
TelPro egress toward the carrier (telpro_egress_to_outside) is the source of truth for custom headers applied after TelSys emits a message. TelSys captures may show the pre-mutation copy; Kamailio egress captures show what actually left the platform.
What gets logged
Kamailio (TelPro) — structured [SIP_CAPTURE] JSON
Kamailio is the SIP proxy in the TelPro service. It writes one JSON object per line to voiceai-telpro stdout for every captured SIP request or response:
| JSON field | Meaning |
|---|---|
recordKind | Always sip_message for ladder captures. |
logType | sip_capture |
service / service.component | telpro / kamailio |
capturePoint | Where in the proxy the message was seen — see Capture points. |
sipCallId / callId | SIP Call-ID (use as call_id in SigNoz). |
trace_id | Distributed trace id (WebRTC calls may be updated from Redis after TelAPI validation). |
method | SIP method on requests (INVITE, CANCEL, PRACK, …). |
statusCode | Numeric SIP response code on replies (180, 200, 481, …). |
capturedAtMs | Kamailio clock at capture time — used for ladder ordering. |
sip100relReliable / sip100relSupported | RFC 3262 flags when Require: 100rel, RSeq, or PRACK is involved. |
rawMessage | Escaped first line (request or SIP/2.0 … status line). |
Operational routing logs use recordKind=log via the same JSON emitter (LOG_DELPHI) — search by component (for example routing, egress, dispatcher).
Not re-emitted by log-to-span: Kamailio already emits final JSON; the TelPro log-to-span sidecar ingests RTPEngine/Janus spans but skips duplicate Kamailio sip_message lines.
TelSys (Voice) — PJSIP packet traces
Asterisk with PJSIP handles the Voice-side SIP stack. When SIP_CAPTURE_ENABLE_PJSIP_LOGGER=1 (default), Asterisk logs multiline PJSIP frames prefixed with:
<--- Received SIP response/request from …→ capture pointtelsys_ingress_from_telpro<--- Transmitting SIP …→ capture pointtelsys_egress_to_telpro
PJSIP output is buffered until the full SIP frame (headers + SDP) is present, so TelSys rows can appear slightly after Kamailio's single-line captures for the same leg.
The Voice host log-to-span sidecar (sipcapture mode) assembles these multiline frames into the same recordKind=sip_message shape and exports OTLP logs with service.name=telsys.
Media and WebRTC (related, not SIP ladder rows)
| Source | Logged as | Use |
|---|---|---|
Kamailio (voiceai-telpro) | recordKind=trace events: rtpengine_offer, rtpengine_answer, rtpengine_delete, rtpengine_error | NG control plane — offer/answer/delete failures |
RTPEngine (voiceai-rtpengine) | recordKind=log rtpengine_call_stats + trace span rtpengine.call_stats; dtmf_media_event; rtpengine_media_error | QoS (MOS, jitter, loss), DTMF, media-layer errors |
Janus (voiceai-webrtc) | Janus log config via TelPro log-to-span | WebRTC WSS / SIP-plugin bridge failures |
Control vs data plane: Kamailio logs RTPEngine NG commands (offer/answer/delete). The RTPEngine daemon logs media outcomes — end-of-call stats appear as both a trace span and a searchable log entry on the same trace_id. See Structured logging schema.
SigNoz log query for call QoS:
service.name = "telpro" AND attributes.event = "rtpengine_call_stats" AND attributes.trace_id = "<trace-id>"
Pipeline to SigNoz and TelWeb
- OTel collector on TelPro and Voice parses JSON container logs, promotes Kamailio records to
service.name=telpro, and forwards to SigNoz. - TelAPI loads captures for a conversation trace, deduplicates Kamailio worker duplicates, merges TelPro ↔ TelSys legs, and builds ladder events.
- Tasker persists the ladder snapshot after call end (delay
SIGNOZ_SIP_LADDER_PERSIST_DELAY_SECONDS, default 90s) so support can reopen it after SigNoz retention expires.
SIP captures preserve routing detail, but the assembled frame has selected header and phone-number redactions. The collector also has conditional DTMF rules whose field coverage depends on the role and release. See Redaction in the capture path; do not treat the complete record as anonymized.
Capture points
| Capture point | Component | Meaning |
|---|---|---|
telpro_ingress_from_outside | Kamailio | Request or response from carrier / WebRTC side |
telpro_egress_to_telsys | Kamailio | Forwarded toward TelSys |
telpro_ingress_from_telsys | Kamailio | Reply received from TelSys |
telpro_egress_to_outside | Kamailio | Forwarded toward carrier / WebRTC (carrier header source of truth) |
telpro_local_response | Kamailio | Locally generated reply (sl_send_reply, OPTIONS keepalive, validation failures) |
telsys_ingress_from_telpro | TelSys PJSIP | Received from TelPro |
telsys_egress_to_telpro | TelSys PJSIP | Sent toward TelPro |
Matching TelPro egress and TelSys ingress (same method + CSeq) collapse to one ladder row with up to two raw captures in the inspector.
SigNoz query patterns
Starting filters (adjust time range to the call window):
service.name = "telpro" AND attributes.callId = "<call-id>"
attributes.recordKind = "sip_message" AND attributes.callId = "<call-id>"
service.name = "telsys" AND attributes.callId = "<call-id>"
attributes.trace_id = "<trace-id>" AND attributes.recordKind = "sip_message"
Older deployments may still index Kamailio under service.name = "kamailio" before collector promotion — try both if a query returns empty.
Hide OPTIONS health checks in TelWeb by default; enable Show health-check SIP (OPTIONS) on the ladder tab when debugging carrier keepalives.
SIP methods — scenarios
This is the deployed gateway's routing/handling scope. Successful use also depends on dialog state, trunk policy, TelSys configuration, and carrier support. Seeing a method in a capture is not proof that every use of it is supported.
| Method | Typical role on Delphi | Where it appears |
|---|---|---|
| OPTIONS | Out-of-dialog keepalive / reachability probe. Kamailio answers proxy OPTIONS with 200 OK (telpro_local_response). In-dialog OPTIONS relay like other mid-dialog methods. | Often hidden in TelWeb; visible in SigNoz or with health-check toggle. |
| INVITE | Call setup (inbound carrier, outbound transfer, WebRTC leg). | First row of a new dialog; pairs with 100 Trying → 180/183 → 200 OK. |
| PRACK | RFC 3262 acknowledgment of a reliable provisional (180/183 with Require: 100rel). Kamailio relays in-dialog PRACK; ladder label PRACK (100rel). | TelPro + TelSys when SIP_100REL_MODE is not off. |
| UPDATE | Mid-call SDP or session-timer refresh. | TelSys ↔ TelPro during hold, codec renegotiation, or session timers. |
| ACK | Confirms final response to INVITE. Ladder marks Call connected after first ACK following 200 OK. | Required for dialog establishment. |
| BYE | Normal hangup from either side. | Ladder Call disconnected divider prefers BYE, then CANCEL, then TelPhi hangup logs. |
| CANCEL | Cancels an unanswered or ringing INVITE (UAC side). Kamailio matches or rejects with 481 if no transaction exists. RTPEngine session may be deleted on matched CANCEL. | Common on caller abandon before answer. |
| REFER | Blind or attended transfer toward carrier/PBX. TelPhi publishes egress intents; Kamailio applies headers before telpro_egress_to_outside. | Transfer failures — correlate TelPhi action logs. |
| INFO | DTMF or feature payloads (carrier-dependent). | DTMF timeline merges INFO bodies with RFC4733 SDP and TelPhi dtmf_received. |
| NOTIFY / SUBSCRIBE | Existing-dialog requests can be relayed. A new out-of-dialog subscription is not a general presence service; unmatched non-INVITE routing is rejected. | Check carrier integration docs. |
Additional method handling:
| Method | Scope |
|---|---|
| REGISTER | Kamailio's registrar path saves a location binding or returns its registrar error. Carrier trunk provisioning is documented separately; this does not imply support for arbitrary subscriber-registration deployments. |
| MESSAGE / PUBLISH | Recognized by capture/relay code; no standalone messaging or presence service is defined by the default call-routing flow. Out-of-dialog non-INVITE requests reaching call routing are rejected with 481. |
For ACK, a matched transaction/dialog is required; an unmatched ACK may be dropped. Do not assume every rejected SIP request receives a response. For methods outside this table, establish an integration-specific supported flow before enabling them.
RFC 3262 (100rel / PRACK)
Controlled on TelSys via SIP_100REL_MODE (PJSIP endpoint 100rel):
| Value | Behavior |
|---|---|
off | Default — no PRACK / reliable provisionals. |
peer_supported | Reliable 180/183 only when peer sends Supported: 100rel. Start here for carriers that expect PRACK. |
yes | Always send reliable provisionals toward TelPro. |
required | Strict 100rel on the TelSys leg. |
Kamailio logs [SIP_100REL] debug lines and sets sip100relReliable / sip100relSupported on captures. TelWeb labels reliable 1xx and PRACK as (100rel).
SIP response codes — operator reference
Use response codes as evidence along the ladder, not as application error codes. The first failing response on the Outside or TelPro ingress lane usually explains why no conversation was created.
The tables cover common observed responses and explicit TelPro rejection paths, rather than every response an upstream carrier or Asterisk module can emit. Preserve the exact status, reason phrase, CSeq, SIP Call-ID, time, and capture point when a code is not listed. A SIP failure code and an HTTP API status with the same number have different meanings.
1xx — Provisional
| Code | Meaning | Delphi notes |
|---|---|---|
| 100 | Trying | Kamailio auto_inv_100 may appear as telpro_local_response while routing to TelSys. |
| 180 | Ringing | 180 Ringing (upstream) when first seen on telpro_ingress_from_outside. Append (100rel) when 100rel headers present. |
| 183 | Session progress | Early media possible; check RTPEngine spans if audio starts before answer. |
2xx — Success
| Code | Meaning | Delphi notes |
|---|---|---|
| 200 | OK | INVITE answered — look for following ACK to mark connected phase. |
4xx — Client / dialog errors
| Code | Phrase | Typical cause on Delphi |
|---|---|---|
| 403 | Forbidden | Policy/authentication rejection: unknown trunk, disallowed source IP, invalid call token, required TLS, failed certificate verification, or peer-name mismatch. Read the reason phrase and the emitting hop before changing configuration. |
| 404 | Not Found | Unknown base number / trunk, or egress target not in Redis. |
| 408 | Request Timeout | Carrier or egress member timeout; outbound pools may fail over to next member on 408 or 5xx. |
| 481 | Call/Transaction Does Not Exist | An unmatched non-INVITE request reaching call routing can receive this response. Late teardown requests can race with transaction expiry; compare CSeq and timing. Unmatched ACK can be dropped; do not expect a 481 response to every stray ACK. |
| 486 | Busy Here | Callee busy or policy rejection from TelSys/carrier. |
| 487 | Request Terminated | INVITE transaction cancelled — usually follows a matched CANCEL (caller hung up while ringing). |
| 488 | Not Acceptable Here | Codec / SDP mismatch — check trunk codec preferences and RTPEngine offer. |
| Additional code | Definition | Operator action |
|---|---|---|
| 483 | Too Many Hops | TelPro rejected the request's Max-Forwards budget. Check for a SIP routing loop or incorrect next hop. |
5xx — Server / proxy errors
| Code | Phrase | Typical cause on Delphi |
|---|---|---|
| 500 | Server Internal Error | Internal failure. TelPro can also generate this for inbound-auth or egress-TLS misconfiguration, or failed egress failover relay. Inspect the emitting component and reason phrase. |
| 503 | Service Unavailable | No healthy Voice dispatcher target, TelPro misconfig, or carrier overload. |
6xx — Global failure
| Code | Phrase | Typical cause on Delphi |
|---|---|---|
| 603 | Decline | Carrier or policy explicitly declined the call — TelPhi transfer mapping may classify as declined. |
If capturePoint is telpro_local_response, Kamailio generated the code (routing validation, OPTIONS, unmatched CANCEL). If the code appears on telpro_ingress_from_outside, it came from the carrier or WebRTC peer. If on telpro_ingress_from_telsys, TelSys/Asterisk generated it before Kamailio relayed outward.
Correlation SIP headers
Kamailio forwards selected Delphi correlation ids as SIP headers toward TelSys so the voice PBX can include them in structured logs and OTel spans.
| Header | Set by | Direction | Purpose |
|---|---|---|---|
X-Trace-ID | Kamailio | TelPro → TelSys | Distributed trace id (32-char hex). TelSys reads it into X_TRACE_ID for all structured logs. |
X-Span-ID | Kamailio | TelPro → TelSys | Parent span id for TelSys spans. TelSys reads it into X_SPAN_ID. |
These headers are stripped before egress toward the carrier. They are internal Delphi correlation headers, not user-facing SIP headers.
Configuration
| Variable | Service | Default | Purpose |
|---|---|---|---|
SIP_CAPTURE_ENABLE_PJSIP_LOGGER | Voice | 1 | Asterisk PJSIP packet logging for TelSys ladder lane |
SIP_100REL_MODE | Voice | off | PRACK / reliable provisional mode toward TelPro |
SIP_CAPTURE_MAX_MESSAGE_BYTES | log-to-span | 65536 | Max assembled SIP frame size |
SIP_CAPTURE_MAX_BUFFER_AGE_MS | log-to-span | 5000 | PJSIP fragment assembly timeout |
SIP_CAPTURE_REDACT_HEADERS | log-to-span | authorization,proxy-authorization,www-authenticate,proxy-authenticate,cookie,set-cookie | Comma-separated SIP headers replaced with [REDACTED] in the assembled frame |
SIGNOZ_SIP_LADDER_PERSIST_DELAY_SECONDS | Tasker | 90 | Delay before persisting ladder snapshot |
LOG_TO_SPAN_EXPORT_LOGS | TelPro | false | Export parsed SIP as OTLP logs in addition to spans — increases volume |
TELPRO_LOG_LEVEL | TelPro | LOG_LEVEL | Raises Kamailio routing verbosity — use briefly; SIP captures remain independent |
RTPENGINE_LOG_LEVEL | TelPro | LOG_LEVEL | RTPEngine media log verbosity, mapped from Pino labels |
WEBRTC_LOG_LEVEL | TelPro | LOG_LEVEL | Janus WebRTC log verbosity, mapped from Pino labels |
Redaction in the capture path
log-to-span redacts the assembled raw frame before emitting that frame to the capture output:
- Every header named in
SIP_CAPTURE_REDACT_HEADERSbecomes[REDACTED]. This applies whateverENABLE_PII_LOGGINGis set to, in the same way the in-process logger always removes credentials. - Phone-like
sip:/sips:/tel:URI users, quoted phone display names, and bare phone display names become***plus the lastMSISDN_UNMASKED_DIGITSdigits (default4). This step is skipped whenENABLE_PII_LOGGING=true.
Other content is retained for routing analysis. This protection covers the emitted assembled raw frame: metadata is parsed from the original frame, and the parser does not rewrite original container logs or guarantee that every other telemetry attribute is scrubbed. The collector adds conditional DTMF Signal= rules on configured fields; verify the host and release configuration before relying on a particular copy being masked.
Container log access
When SigNoz is unavailable:
# TelPro — JSON sip_message lines (grep Call-ID or capturePoint)
docker compose logs voiceai-telpro 2>&1 | grep '"recordKind": "sip_message"'
# Voice — raw PJSIP frames
docker compose logs voiceai-telsys 2>&1 | grep -E 'Received SIP|Transmitting SIP'
Raise TELPRO_LOG_LEVEL=debug on TelPro only for short windows — routing LOG_DELPHI lines add volume but do not replace [SIP_CAPTURE] rows.
See also
- SigNoz monitoring — dashboards, alerts, Debug tab workflow, DTMF timeline.
- SIP trunk log reference — catalog of TelPhi and TelAPI SIP trunk health logs.
- TelPro operations — SIP edge routing, Redis keys, transfers, firewall.
- Voice operations — TelSys, ARI,
SIP_100REL_MODEenv bundle. - SIP trunks (admin) — inbound trunk identification and TLS.
- Egress trunks — outbound transfer routing pools.