Expand description
JSON diagnostics contract.
Every constant in diagnostics::route is a GET endpoint returning
200 OK JSON on success.
§Routes and Parameters
| request | JSON response | parameter source |
|---|---|---|
GET /internal/diagnostics/summary | one diagnostics::DiagnosticsSummaryResponse | none |
GET /internal/diagnostics/rooms | array of diagnostics::DiagnosticsRoomSummary | none |
GET /internal/diagnostics/workers | array of diagnostics::DiagnosticsWorkerSummary | none |
GET /internal/diagnostics/rooms/{uuid} | one diagnostics::DiagnosticsRoomDetail | uuid from the rooms response |
GET /internal/diagnostics/rooms/{uuid}/users | array of diagnostics::DiagnosticsUserSummary | uuid from the rooms response |
GET /internal/diagnostics/rooms/{uuid}/users/{id} | one diagnostics::DiagnosticsUserDetail | uuid from rooms and userKey from room users |
GET /internal/diagnostics/node-graph/rooms/{uuid} | { "nodes": [], "edges": [] } | uuid from the rooms response |
GET /internal/diagnostics/node-graph/rooms/{uuid}/users/{id} | { "nodes": [], "edges": [] } | uuid from rooms and userKey from room users |
userId may be a JSON number or string.
userKey is always the string to put into {id}.
URL-encode both path values before substitution.
§Summary Request and Response over HTTPS
GET /internal/diagnostics/summary HTTP/1.1
Host: o-sfu-observability.internal
Authorization: Bearer <diagnostics-token>
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json
{
"roomsActive": 1,
"publicationsActive": 1,
"recordingRoomsActive": 0,
"usersActive": 2,
"subscriptionsActive": 1,
"transport": {
"connectedUsers": 2,
"disconnectedUsers": 0,
"totalUsers": 2,
"unknownUsers": 0
}
}§JavaScript Fetch Example
const origin = "https://o-sfu-observability.internal";
const headers = {
Authorization: `Bearer ${process.env.DIAGNOSTICS_AUTH_TOKEN}`,
};
async function getJson(path) {
const response = await fetch(`${origin}${path}`, { headers });
if (!response.ok) {
throw new Error(`${response.status} ${await response.text()}`);
}
return response.json();
}
async function main() {
const rooms = await getJson("/internal/diagnostics/rooms");
const roomUuid = encodeURIComponent(rooms[0].uuid);
const room = await getJson(`/internal/diagnostics/rooms/${roomUuid}`);
const users = await getJson(`/internal/diagnostics/rooms/${roomUuid}/users`);
const userKey = encodeURIComponent(users[0].userKey);
const graph = await getJson(
`/internal/diagnostics/node-graph/rooms/${roomUuid}/users/${userKey}`,
);
console.log(room.summary, room.users, room.sources);
console.log(graph.nodes, graph.edges);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});The rooms response has this shape.
[
{
"createDate": "2026-07-15T10:20:30.000Z",
"mediaWorkerId": 0,
"publicationCount": 1,
"recordingState": {
"recording": false,
"audio": false,
"transcription": false,
"video": false
},
"remoteAddress": "203.0.113.10",
"sourceCount": 1,
"userCount": 2,
"subscriptionCount": 1,
"transport": {
"connectedUsers": 2,
"disconnectedUsers": 0,
"totalUsers": 2,
"unknownUsers": 0
},
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"webRtcEnabled": true
}
]The room users response has this shape.
[
{
"audioIncomingBitrateBps": 32000,
"cameraIncomingBitrateBps": 600000,
"connectionId": 91,
"health": "connected",
"incomingBitrateBps": 632000,
"mediaWorkerId": 0,
"publicationCount": 2,
"roomId": "550e8400-e29b-41d4-a716-446655440000",
"screenIncomingBitrateBps": 0,
"subscriptionCount": 1,
"userId": 42,
"userKey": "42"
}
]The response structs below list every field in each payload.
Wire names are camelCase unless a field documents an exception.
User detail is room-scoped because the same user key can be active in several rooms.
Modules§
- route
- Diagnostics
GETroutes.