DEV Community

Harpreet Singh Seehra
Harpreet Singh Seehra

Posted on

Monitor Call Quality in Real Time with Telnyx Webhooks and Python

Call quality is not a single number you check once. It is a stream of measurements — MOS, jitter, latency, packet loss — that arrive while a call is active and that you need to inspect afterward. This sample puts that stream into a Flask dashboard with live SSE updates, SQL-backed history, and threshold alerting, all verifiable without a Telnyx account.

I work with Telnyx. The code is available here: https://github.com/team-telnyx/telnyx-code-examples/tree/main/call-quality-monitor

What the sample does

The Call Quality Monitor receives Telnyx call quality webhooks, verifies each payload's Ed25519 signature, and stores the metrics in SQLite for historical analysis. An in-memory key-value store tracks per-call state so the dashboard can show what is happening right now. When MOS drops below a threshold or jitter exceeds a limit, an alert fires and is pushed to the browser over a Server-Sent Events stream.

The dashboard at / shows live metrics and alerts. The API exposes filtered queries by call ID, time range, and aggregate statistics. A demo server (demo/demo_server.py) generates an Ed25519 keypair on first run, signs webhook payloads, and posts them to the real /webhooks/call-quality endpoint — the full pipeline runs end-to-end on localhost:5555 without Telnyx credentials or ngrok.

Architecture

Telnyx Voice
    │
    │  Call quality webhooks (MOS, jitter, latency, packet loss)
    ▼
Flask App (app.py)
    │
    ├── Ed25519 signature verification (pynacl)
    │
    ├── Process quality metrics
    │   ├── SQLite INSERT (historical analytics)
    │   └── In-memory KV update (per-call state)
    │
    ├── Threshold checker
    │   ├── MOS < 3.5  → alert
    │   ├── Jitter > 30ms  → alert
    │   └── Latency > 150ms  → alert
    │
    └── SSE broadcast → browser dashboard
Enter fullscreen mode Exit fullscreen mode

Run the demo locally

No Telnyx account is needed. The demo server generates its own Ed25519 keypair, signs webhook payloads, and posts them to the real Flask endpoint.

git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/call-quality-monitor

pip install -r requirements.txt
pip install requests  # needed by the demo webhook sender

python demo/demo_server.py
Enter fullscreen mode Exit fullscreen mode

Open http://localhost:5555/ and use the demo controls:

  • Start 3 Calls — Simulates three concurrent calls with varying quality metrics. One call is healthy, one degrades over time, and one starts bad. The dashboard updates live via SSE.
  • Trigger Alert — Pushes a metric that crosses the MOS threshold and watch the alert appear in real time.
  • Reset Data — Clears the SQLite database and in-memory state.

The demo server runs on port 5555 and hosts both the Flask app and a webhook sender thread that posts signed payloads to /webhooks/call-quality. The Ed25519 keypair is generated on first run and stored in a local file. No external service is contacted.

How webhook verification works

Telnyx signs every webhook payload with an Ed25519 key. The Flask app verifies each signature using pynacl before processing the payload. This prevents forged webhook deliveries from injecting fake quality metrics into your dashboard.

import nacl.signing
import nacl.encoding

verify_key = nacl.signing.VerifyKey(
    base64.b64decode(public_key_b64),
    encoder=nacl.encoding.RawEncoder
)
verify_key.verify(signed_payload, signature)
Enter fullscreen mode Exit fullscreen mode

The demo server generates its own keypair on first run, signs payloads with the private key, and the Flask app verifies them with the corresponding public key. In production, Telnyx holds the private key and you configure the public key from the Portal.

Why SSE instead of WebSocket

Server-Sent Events are unidirectional (server to browser), which is all a monitoring dashboard needs. SSE runs over standard HTTP, works through proxies without extra configuration, and reconnects automatically. The browser's EventSource API is simpler than WebSocket and sufficient for a read-only live feed.

Connect to live Telnyx webhooks

For production use, configure your Telnyx webhook URL in the Portal:

https://<your-domain>/webhooks/call-quality
Enter fullscreen mode Exit fullscreen mode

Set these environment variables:

Variable Description Example
TELNYX_API_KEY Your Telnyx API key KEY019...
TELNYX_PUBLIC_KEY Your Telnyx public key (Ed25519) -----BEGIN PUBLIC KEY-----...
DB_PATH SQLite database path quality.db
MOS_THRESHOLD Minimum acceptable MOS score 3.5
JITTER_THRESHOLD Maximum jitter in ms 30
LATENCY_THRESHOLD Maximum latency in ms 150
PORT Flask server port 5000

API endpoints

Method Endpoint Description
POST /webhooks/call-quality Receive and verify Telnyx call quality webhooks
GET / Live SSE dashboard
GET /events SSE stream endpoint
GET /api/quality/<call_id> All metrics for a specific call
GET /api/quality All metrics with optional filters
GET /api/quality/stats Aggregate statistics
GET /api/quality/alerts All threshold alerts
GET /health Health check

Testing

python -m pytest smoke_test.py -v
Enter fullscreen mode Exit fullscreen mode

9 tests covering webhook verification, metric storage, threshold alerting, API endpoints, and SSE streaming. All pass.

Top comments (0)