CallEvent and you need the raw wire exchange.
The screen renders in two places — the agent suite (SIP capture under
admin) and the customer portal — from the same shared body component, so
both show the same data for the same workspace.
Where captures come from
Captures are produced by Heplify and stored in Homer (HEP). The console does not query Homer’s own UI or deep-link to it; it reads Homer’s storage through thesip.calls and sip.transaction procedures and
renders the ladder inline.
Two consequences follow:
- If Heplify was not running when the call was placed, there is nothing to show for that call, even if the call itself succeeded.
- Everything on this screen is signalling. The screen shows SIP messages, source/destination hosts, ports, and the raw message bodies. It does not show RTP statistics, codec negotiation results as media metrics, or a media-mode indicator. For those, use the CDR / metrics surfaces described in Telemetry.
How a capture is matched to your workspace
Homer stores captures for every call that traverses the capture point. The console never shows you the raw Homer set — it first resolves which Call-IDs belong to your workspace, then fetches only those. Ownership is resolved as the union of two paths:- CDR-based. ClickHouse
cdrs.tenant_idis the authoritative owner for outbound calls and answered inbound calls. - DID-based fallback. For inbound calls whose CDR row has not been
written yet, or was written with an empty
tenant_id, the console falls back to the called number. Homer’sdata_header.to_usercarries the dialled DID, and each DID belongs to exactly one workspace viaphone_number.org_id. The match is a digit-suffix comparison against your workspace’s DIDs.
BYE
before the CDR pipeline writes a row. Without path 2 those calls would be
invisible here — which is precisely the case you most often want to debug.
Expanding a row applies the same ownership check again before returning
message bodies. sip.transaction first counts matching cdrs rows for
your tenant and the requested Call-ID; if there are none, it repeats the
DID-suffix check. If neither path claims the Call-ID, the procedure returns
an empty message list rather than the bodies. A guessed Call-ID from
another tenant on a shared Homer therefore returns nothing.
Call-IDs are normalised before lookup: any @host suffix on the input is
stripped, so the wire-form value shown in the calls list still resolves
back to the CDR row.
The time window and refresh behaviour
The date-range picker at the top of the screen sets the capture window. The default window is the last hour (now-1h → now).
Both bounds are quantised down to 30-second buckets before they are sent
to the server. This is deliberate: the query key stays stable across
parent re-renders and tooltip hovers, so incidental renders do not trigger
a new fetch. The list also refetches on a 30-second interval, which is in
phase with the bucket size.
Practical effects:
- The window edges “snap” — a range you pick mid-bucket is rounded to the bucket boundary, so a call placed in the last few seconds may not appear until the next bucket.
- The header shows a
refreshed <time>stamp that updates whenever the list query returns new data. - Refresh forces an immediate refetch. Expanded ladder bodies are cached separately and are not refetched on window focus.
limit that the screen sets to 50 calls. The
procedure accepts a limit between 1 and 200.
Finding a call
Three controls narrow the listing, all applied client-side to the rows already fetched for the window:
Because filtering happens after the fetch, narrowing the filters does not
reach further back in time. If a call is missing, widen the time range
rather than adjusting the filters.
The counter above the list reads
N of M captured when a filter is active,
and M calls captured when it is not.
Reading a call row
Each collapsed row is one captured call:
Any of these may render as
—. The values come from the captured SIP
headers, and a call that failed early may not have populated them.
Clicking a row expands it and issues the sip.transaction query for that
Call-ID over the same quantised window.
Reading the ladder view
The expanded panel has two parts. Call summary. Four fields:Call-ID, Method (defaults to INVITE
when the capture does not carry one), Src Host, and Dst Host.
SIP ladder. A scrollable list of the captured messages for that
Call-ID, with a count in the header (SIP Ladder (N messages)). Each row
is collapsible and carries three columns:
Expanding a message row reveals the raw SIP message exactly as
captured, including all headers and any body. This is where you read the
response code, the
Reason header, and the SDP.
Messages are listed in the order Homer returns them for the transaction,
so you read the ladder top-to-bottom as the exchange occurred. For how to
interpret the response codes and Q.850 causes you find here, see the
debugging reference.
When a Call-ID returns no messages
A row can appear in the list and still expand toNo captured messages. The list and the ladder are resolved by two
different queries, so this is expected in a few situations:
- Heplify was not running at the point in time the leg was signalled, so no HEP frames were stored for it.
- The leg ran outside the time window. The ladder query uses the same
quantised
fromMs/toMsas the list. A call whoseINVITElanded just before the window start has its messages outside the bounds even though the call itself is listed. - Ownership could not be confirmed for the ladder. If neither the CDR
tenant_idmatch nor the DID-suffix fallback claims the Call-ID at the moment you expand it,sip.transactionreturns an empty list.
The empty state
When the window contains no captures at all, the screen showsNo SIP captures in this window. Captures are matched to your workspace’s
calls, so the two remedies are to widen the time range or to place a call
and let it appear on the next refresh.
Exporting the list
Export CSV downloads the currently filtered list — exactly the rows visible on screen, not the whole window. The button is disabled while a fetch is in flight and when the filtered list is empty. The export runs entirely in the browser from rows already delivered over tRPC; there is no extra server round-trip. The filename issip-capture-<ISO timestamp>.csv.
Columns, in order:
The CSV contains the call-level summary only. It does not contain the
SIP message bodies from the ladder, and the screen does not offer a pcap
or HEPv3 export. To inspect raw messages, expand the ladder rows in the
console.
Where this differs from call traces
Both screens are diagnostic, but they answer different questions.
Use SIP capture when a call fails before any
CallEvent reaches the
SDK, or when you need to prove what the carrier actually sent on the wire.
Use call traces when the call reached the gateway and you want the
platform’s view of it.
Related
- Telemetry — the HEPv3 capture stream, CDR schema, and metrics
- Telephony Metrics — definitions for the numbers that captures do not carry