REST API (/api/v1/)
ChannelWatch ships a versioned REST API at /api/v1/. New integrations should target the versioned endpoints documented here.
Authentication
Section titled “Authentication”When auth is enabled, requests can be authorized with the shared X-API-Key header. When RBAC is enabled, the backend also ships session-based auth endpoints under /api/v1/auth/*.
X-API-Key: <your-api-key>If auth is disabled (CW_DISABLE_AUTH=true), the API is accessible without credentials.
DVR endpoints
Section titled “DVR endpoints”These endpoints return data scoped to configured DVRs. Use GET /api/v1/dvrs to discover valid DVR IDs.
List all DVRs
Section titled “List all DVRs”GET /api/v1/dvrsReturns configured, non-deleted DVR records. The current response model is a small list item shape:
[ { "id": "dvr_abc12345", "name": "Living Room", "host": "192.168.1.100", "port": 8089, "enabled": true }]The list response sticks to the small id/name/host/port/enabled shape shown above.
DVR details
Section titled “DVR details”GET /api/v1/dvrs/{dvr_id}Returns a richer DVRStatus payload for one DVR. The current source-backed fields include:
id,name,host,portconnectedversion,version_compatible,version_warningdisk_usage_percent,disk_total_gb,disk_free_gblibrary_shows,library_movies,library_episodesmonitoring_status,monitoring_ready,monitoring_reasonfreshness_status,last_freshness_at,last_event_at,freshness_age_seconds,stale_threshold_seconds
Example response:
{ "id": "dvr_abc12345", "name": "Living Room", "host": "192.168.1.100", "port": 8089, "connected": true, "version": "2025.06.15", "version_compatible": true, "version_warning": null, "disk_usage_percent": 42, "disk_total_gb": 2000.0, "disk_free_gb": 1160.0, "library_shows": 85, "library_movies": 120, "library_episodes": 940, "monitoring_status": "healthy", "monitoring_ready": true, "monitoring_reason": null, "freshness_status": "fresh", "last_freshness_at": "2026-04-21T07:00:00+00:00", "last_event_at": "2026-04-21T06:59:45+00:00", "freshness_age_seconds": 15.0, "stale_threshold_seconds": 300}Per-DVR streams
Section titled “Per-DVR streams”GET /api/v1/dvrs/{dvr_id}/streamsReturns an aggregate stream snapshot for one DVR. The current payload shape is:
dvr_id,dvr_nametotalwatching— array of objects withdevice,channel, andimagerecording— array of objects withtitleanduntilsubtitleimage
Example response:
{ "dvr_id": "dvr_abc12345", "dvr_name": "Living Room", "total": 2, "watching": [ { "device": "Apple TV", "channel": "ESPN", "image": "https://example.invalid/image.jpg" } ], "recording": [ { "title": "Evening News", "until": "7:30 PM" } ], "subtitle": "1 watching, 1 recording", "image": "https://example.invalid/image.jpg"}This endpoint is not a session-ID-oriented stream list.
Per-DVR system info
Section titled “Per-DVR system info”GET /api/v1/dvrs/{dvr_id}/system-infoReturns current DVR status plus storage and library summary fields:
dvr_id,dvr_name,host,portconnectedversion,version_compatible,version_warningdisk_usage_percent,disk_usage_gb,disk_total_gb,disk_free_gb,disk_severitylibrary_shows,library_movies,library_episodes
It does not expose raw storage-path details.
Per-DVR activity history
Section titled “Per-DVR activity history”GET /api/v1/dvrs/{dvr_id}/activity-historyReturns activity history for one DVR. The route also supports pagination and filtering query parameters such as offset, limit, type, search, and sort.
Per-DVR upcoming recordings
Section titled “Per-DVR upcoming recordings”GET /api/v1/dvrs/{dvr_id}/recordings/upcomingReturns upcoming recordings for one DVR.
Per-DVR health
Section titled “Per-DVR health”GET /api/v1/dvrs/{dvr_id}/healthReturns the current DvrHealthResponse snapshot for one DVR. The current response fields are:
dvr_id,dvr_name,host,portconnectedversion,version_compatible,version_warningdisk_status,disk_free_gb,disk_total_gblast_checked,last_event_atlast_freshness_at,last_freshness_source,freshness_age_seconds,freshness_statusmonitoring_status,monitoring_ready,monitoring_reasonsession_state_size,recent_alert_rate
Example response:
{ "dvr_id": "dvr_abc12345", "dvr_name": "Living Room", "host": "192.168.1.100", "port": 8089, "connected": true, "version": "2025.06.15", "version_compatible": true, "version_warning": null, "disk_status": "normal", "disk_free_gb": 1160.0, "disk_total_gb": 2000.0, "last_checked": "2026-04-21T07:05:00+00:00", "last_event_at": "2026-04-21T06:59:45+00:00", "last_freshness_at": "2026-04-21T07:05:00+00:00", "last_freshness_source": "watchdog", "freshness_age_seconds": 15.0, "freshness_status": "fresh", "monitoring_status": "healthy", "monitoring_ready": true, "monitoring_reason": null, "session_state_size": 3, "recent_alert_rate": 4.0}Discovery endpoint
Section titled “Discovery endpoint”POST /api/v1/discovery/scanTriggers a source-backed mDNS scan and returns the discovery helper response:
{ "servers": [ { "host": "192.168.1.120", "port": 8089, "display_name_suggestion": "Basement DVR" } ], "manual_add_available": true, "message": null}When nothing new is found, servers is empty and message contains the reason or operator guidance.
This route is not a generic anonymous scan endpoint. It uses the backend auth path and, when RBAC is enabled, requires at least the operator role.
Auth and security endpoints
Section titled “Auth and security endpoints”The current backend exposes these auth and security routes:
POST /api/v1/auth/loginPOST /api/v1/auth/logoutGET /api/v1/auth/whoamiGET /api/v1/auth/setup-statusPOST /api/v1/auth/setupGET /api/v1/security/statusKey behaviors:
POST /api/v1/auth/loginreturns the authenticated username, role, and a CSRF token, and sets thechannelwatch_sessioncookie.POST /api/v1/auth/logoutinvalidates the current session and clears the cookie.GET /api/v1/auth/whoamireturns the current session identity when RBAC is enabled and the cookie is valid.GET /api/v1/auth/setup-statusreports whether RBAC still needs the first admin account.POST /api/v1/auth/setupcreates the first admin account when RBAC is enabled and no users exist yet.GET /api/v1/security/statusreports the current security mode, whether RBAC is enabled, whether the shared API key is configured, whether API-key fallback is active, whether session setup is still required, and whether stored per-DVR API keys are encrypted at rest.
The auth routes are auth-exempt so a first login or first-admin setup can happen without an existing session. They are still RBAC-aware: for example, POST /api/v1/auth/login returns 501 when RBAC is disabled.
Related pages
Section titled “Related pages”- Security modes — current auth and access behavior
- API Keys — create and manage API-key access
- Per-DVR Health — endpoint details for DVR health snapshots
- Health probes —
/api/healthand/healthz/*response semantics - Prometheus metrics — the current
/metricssurface