What WebRTC observability actually requires
WebRTC observability gets used as a synonym for dashboards and for getStats() access. Here is the requirements list: the six things a system has to capture before it can tell you why a call went bad.
Posted by
Related reading
Voice AI evals stop at the network - just when it became more important
Voice AI evals score the STT-LLM-TTS pipeline. Two transport failures leave no mark in a transcript, and full duplex removed the slack that hid them.
Your session data shouldn't leave your VPC by default
rtcStats collects WebRTC session data inside your infrastructure with rtcstats-server. Forwarding to rtcstats.com is off by default. You choose what leaves your VPC.
You don't need another dashboard for WebRTC observability
You pull the webrtc-internals dump and scroll thousands of getStats() lines. rtcStats reads it for you, keeps its analysis engine portable, and lets your data stay yours.

A user tells you the call sounded bad around 2:14. You have the session ID, a dashboard that says packet loss averaged 0.4% for the call, and a support ticket that is now yours. The dashboard is telling the truth and it is no help at all.
For WebRTC applications, everyone in this market says observability. Almost nobody says what the word obliges them to do. So here is the requirements list: what a system has to capture before it can answer the question on your desk.
WebRTC observability is the answer, monitoring is the measurement
The distinction is simple enough to state in one line. Monitoring tells you what happened on the call. Observability tells you why it happened.
Both start from the same raw data and stop in different places. Monitoring is the aggregate, continuous view: how many sessions ran today, what share of them relayed through TURN, whether your p95 setup time got worse after Thursday's release. That is how you find out a problem exists. Observability is the per-session, explanatory view. It takes the same numbers and asks what they mean together, so that "concealed audio samples climbed steadily for eleven seconds" becomes "the jitter buffer was fighting a bad network and the user heard robotic audio". The difference between the two is complaining there's a problem versus pointing towards the root cause of it. This is also why the two get confused so easily: the input is identical and only the output differs.
The interesting bit though is this - once you make that observation and find the path towards the root cause, you can feed that back into monitoring - so that your aggregate, continuous view gets an actual brain with better queries you can run.
But back to observability if I may...
The word gets stretched a second way in WebRTC, to mean getStats() access. Handing an engineer the raw stats object is handing them the vantage point and calling it the view. getStats() is where the data comes from, in the same way that a log file is where an incident timeline comes from. Everything that makes the data useful sits above it: sampling it often enough to survive a burst, correlating it with the connection state machine, tracking what your encoder decided to do about the network it found, and turning the combination into a cause a human can act on. A vendor who ships you a metrics endpoint and a chart library has shipped you the raw material and left the work where it was. The only thing he really did is say "instead of me throwing the logs to /dev/null just let me know where to throw them to instead". The reason this matters commercially is that both things are sold under the same word. One is a starting point while the other is the destination itself.
Your APM stack is structurally blind to this
The instinct for most teams is to reach for what they already run. You have Datadog or New Relic, you have traces and spans across your services, and you have an on-call rotation that already knows those tools. Pointing them at WebRTC feels like a configuration problem.
It is not, and the reason is architectural. An APM stack observes requests moving through infrastructure you own, which is exactly the assumption WebRTC breaks. The media is encrypted end to end, so nothing sitting along the path can read what the user actually received, and the SIP-era answer of dropping a probe in the middle to inspect the stream does not work here. Worse, a peer-to-peer session may never reach your infrastructure. Two of your users can have a call that fails completely while every service you operate reports green, because the failure happened on a network path you have no presence on. Even the parts that do touch your servers arrive pre-digested: your SFU reports what it forwarded, which tells you nothing about what survived the last mile into the user's living room. Your tracing stack is not badly configured for WebRTC. It is looking at a different system.
The failure mode this produces is specific and it recurs. Averages hide the shape. A call can carry 0.5% packet loss over its duration and still land at a MOS of 2.5, because the loss arrived in two bursts that gutted a sentence each rather than as an even sprinkle the concealment algorithm could paper over. Take that same session and average it into a per-call metric, which is what a generic stack will do with a number it does not understand, and you get a green row. The user heard the call break twice. Your dashboard heard a rounding error. Every WebRTC-specific failure I care about has this property: it is legible in the time series and invisible in the summary. The corollary is uncomfortable for anyone who has built a quality dashboard on averages, because it means the dashboard gets quieter exactly as the failures get more interesting.
I remember a telehealth vendor proudly showing me his service. The high quality of it, praising its HD video surfaces. It was great. His machine was top of the line. Mine as well. Best network money can buy (we both work on video communications after all). That's what a monitoring service would tell you as well. The thing is, the videos were decoded on surfaces way smaller than their resolution - wasted CPU and bandwidth. You could see that from local captures - if you knew what to look for. The moment this hits real people and real networks is when it crumbles and fails. And then it would be a challenge to figure out why without a lot more information.
The requirements list
Six things. A system that cannot do all six will hand you numbers and leave the reading to you.
1. It captures from the client. The only vantage point that sees what the user experienced is the user's own browser or app, through getStats(). Your SFU knows what it sent and your TURN server knows what it relayed, and both are the server telling you its own side of the story. The client knows what arrived, what got decoded, what the jitter buffer had to conceal, and what the device was doing while all of it happened. This is also why the collection layer belongs in your infrastructure: getting client-side data means running something in your app, and you should be able to read the code that does it and decide which sessions leave your network. Server-side inference is a reconstruction of the call from one endpoint's memory of it, and it is systematically optimistic about the leg you cannot see.
2. It keeps the time series, at a resolution that survives bursts. One row per session is a summary, and summaries are where WebRTC failures go to hide. The capture has to be a series across the life of the call, sampled often enough that a four-second freeze and a two-burst loss event are separate visible objects rather than a shifted average. Per-second is the working floor. This is the requirement that everything downstream depends on, because you cannot correlate a freeze with an ICE handover if your data does not know when either of them happened. It is also the one most commonly traded away, since a series costs meaningfully more to store than a scalar, and the saving is invisible until the day you need to explain a specific call. Retention is the honest place to make that trade, keeping full resolution for a window and collapsing to summaries after it.
3. It records the full connection state machine. A WebRTC session is a negotiation that keeps happening. Candidates get gathered, a pair gets selected, the pair changes mid-call when the network moves, and the connection can go to disconnected and come back without anybody noticing except the person on the call. A system that stores media quality and skips the transport state has thrown away half the explanations, and it will confidently attribute a transport event to the media layer because that is the only layer it can see. Setup matters here too: the time between the offer and the first media packet is where failed calls live, and a session that never connected produces almost no quality metrics to inspect. When a user walks from Wi-Fi to cellular, the resulting gap is a candidate-pair change and reads as an ICE event rather than a codec problem, and the only way to tell those apart is to have both timelines side by side.
4. It captures adaptation decisions, and what forced them. WebRTC is constantly deciding things about your call. The encoder drops resolution, the bitrate ramps down, FEC engages, packet loss concealment starts filling gaps, simulcast layers get dropped. Each of those is your stack reacting to a condition, and each one degrades the experience in a way the user notices. The quality limitation reason is the single highest-value field most teams do not collect: it says whether the encoder backed off because of the network, the CPU, or the bandwidth estimate. Those are three different tickets with three different owners, and without the field you get one indistinguishable "video looked soft" complaint. The adaptation is also the thing the user actually saw, since a call that degrades gracefully under pressure and one that holds resolution until it freezes can show the same average bitrate.
5. It carries device and environment context. The same network conditions produce different calls on different machines, and a surprising share of quality complaints resolve to something that was never about the network at all. A thermally throttled laptop drops frames while packets arrive perfectly on schedule. A Bluetooth headset renegotiates in the middle of a sentence. A virtual camera injects itself between the device and the encoder. A second conferencing app holds the microphone. None of this is visible in transport metrics, all of it is available at the client, and skipping it means every device-side failure gets misfiled as a network incident and sent to the wrong engineer. It is also the category that grows fastest, because your users keep adding hardware and software between their voice and your encoder that neither of you controls.
6. It interprets, and correlates to the experience. This is the requirement the other five exist to make possible, and it is where most tooling stops. Raw metrics become calculations, calculations become per-session features you can compare across calls, and features have to become a statement about what the user lived through. Concealed samples climbing for eleven seconds is a fact. "The user heard robotic audio for eleven seconds starting at 2:14" is an answer. Getting from one to the other takes encoded WebRTC expertise, because no metric declares its own meaning. A chart of the fact and an answer about the user are two different deliverables, and only one of them closes the ticket. The practical test is whether the output can be pasted into a support thread and understood by the person who took the call.
How to check what you have
Take a bad call from last week, one where you know the outcome, and ask your current setup a single question: why did that call go bad? Not what the numbers were. Why.
If the honest path to an answer runs through an engineer opening webrtc-internals and scrolling, you have monitoring. That is worth having and most teams should keep it. What you do not have is the layer that closes the ticket, and it is the layer that costs you a senior engineer's afternoon every time a vague complaint arrives. The three shapes it takes in production are ad-hoc debugging when somebody hands you a dump, passive complaint handling when you collect every session in advance so the data is there when the complaint lands, and active observability when the client-side data feeds your wider monitoring stack continuously.
Where rtcStats sits
We built rtcStats against this list. The collection layer is open source and runs in your infrastructure, so requirement 1 does not cost you your data ownership, and you choose which sessions get forwarded for analysis. The analysis layer reads the series and returns Observations for the moments that mattered, Deductions for what those moments meant together, an Experience Score for how the call felt in one number, and an AI Summary in plain English. That is requirement 6, which is the one we think is actually hard.
You can check the list against us the same way you would check it against anyone: upload a dump and see whether the answer is there. The free plan needs no credit card, and every new account runs at full Developer-level analysis for the first month.
Start at https://rtcstats.com
Which of the six does your current stack miss, and what did it cost you the last time it mattered?