From a4a891785da921bfcffdd5f92f664c92a98699ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82?= Date: Tue, 20 Oct 2026 10:51:38 +0200 Subject: [PATCH 1/3] Add the Room History API reference --- docusaurus.config.ts | 24 + static/api/room-history-openapi.json | 692 +++++++++++++++++++++++++++ 2 files changed, 716 insertions(+) create mode 100644 static/api/room-history-openapi.json diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 356746b6..0207c3f7 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -284,6 +284,7 @@ const config: Config = { items: [ { to: "/api/rest", label: "Server API" }, { to: "/api/compositions", label: "Composition API" }, + { to: "/api/room-history", label: "Room History API" }, ], }, { @@ -410,6 +411,11 @@ const config: Config = { slug: "composition", url: "/docs/api/composition-openapi.json", }, + { + title: "Room History API", + slug: "room-history", + url: "/docs/api/room-history-openapi.json", + }, ], hideSearch: true, persistAuth: true, @@ -438,6 +444,24 @@ const config: Config = { }, } as ScalarOptions, ], + [ + "@scalar/docusaurus", + { + id: "room-history-api", + label: "Room History API", + route: "/api/room-history", + showNavLink: false, + configuration: { + url: "/docs/api/room-history-openapi.json", + hideSearch: true, + persistAuth: true, + defaultOpenFirstTag: false, + authentication: { + preferredSecurityScheme: "ManagementTokenAuth", + }, + }, + } as ScalarOptions, + ], [ "docusaurus-plugin-typedoc", { diff --git a/static/api/room-history-openapi.json b/static/api/room-history-openapi.json new file mode 100644 index 00000000..c11878fc --- /dev/null +++ b/static/api/room-history-openapi.json @@ -0,0 +1,692 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Fishjam Room History API", + "version": "1.0.0", + "description": "Call quality history of the ended rooms of your Fishjam apps: which rooms had issues, which peers caused them, and each peer's bitrate, packet loss and round trip time over time. Authenticate with your management token. Data is kept for 60 days." + }, + "paths": { + "/room-history": { + "get": { + "operationId": "list_room_history", + "summary": "List rooms with their issue counts", + "parameters": [ + { + "in": "query", + "name": "start", + "schema": { + "format": "date-time", + "title": "Start", + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "end", + "schema": { + "format": "date-time", + "title": "End", + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "limit", + "schema": { + "default": 20, + "maximum": 1000, + "minimum": 1, + "title": "Limit", + "type": "integer" + }, + "required": false + }, + { + "in": "query", + "name": "offset", + "schema": { + "default": 0, + "maximum": 4294967295, + "minimum": 0, + "title": "Offset", + "type": "integer" + }, + "required": false + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "503": { + "description": "Service Unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoomsPageOut" + } + } + } + } + }, + "description": "Rooms that were both created and ended inside the window, newest first.", + "tags": ["Rooms"], + "security": [ + { + "ManagementTokenAuth": [] + } + ] + } + }, + "/room-history/{room_id}": { + "get": { + "operationId": "get_room_history", + "summary": "Get a room with its peers and their issues", + "parameters": [ + { + "in": "path", + "name": "room_id", + "schema": { + "title": "Room Id", + "type": "string" + }, + "required": true + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "503": { + "description": "Service Unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoomDetailOut" + } + } + } + } + }, + "description": "An ended room with its peers and their issues.", + "tags": ["Rooms"], + "security": [ + { + "ManagementTokenAuth": [] + } + ] + } + }, + "/room-history/{room_id}/peers/{peer_id}": { + "get": { + "operationId": "get_room_history_peer", + "summary": "Get a peer's bitrate, packet loss and RTT over time", + "parameters": [ + { + "in": "path", + "name": "room_id", + "schema": { + "title": "Room Id", + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "peer_id", + "schema": { + "title": "Peer Id", + "type": "string" + }, + "required": true + } + ], + "responses": { + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "503": { + "description": "Service Unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PeerDetailOut" + } + } + } + } + }, + "description": "A peer of an ended room with its bitrate, packet loss and RTT over time.", + "tags": ["Rooms"], + "security": [ + { + "ManagementTokenAuth": [] + } + ] + } + } + }, + "components": { + "schemas": { + "PublicRoomsQueryIn": { + "properties": { + "start": { + "format": "date-time", + "title": "Start", + "type": "string" + }, + "end": { + "format": "date-time", + "title": "End", + "type": "string" + }, + "limit": { + "default": 20, + "maximum": 1000, + "minimum": 1, + "title": "Limit", + "type": "integer" + }, + "offset": { + "default": 0, + "maximum": 4294967295, + "minimum": 0, + "title": "Offset", + "type": "integer" + } + }, + "required": ["start", "end"], + "title": "PublicRoomsQueryIn", + "type": "object" + }, + "Message": { + "properties": { + "detail": { + "title": "Detail", + "type": "string" + } + }, + "required": ["detail"], + "title": "Message", + "type": "object" + }, + "RoomOut": { + "properties": { + "room_id": { + "title": "Room Id", + "type": "string" + }, + "created_at": { + "format": "date-time", + "title": "Created At", + "type": "string" + }, + "ended_at": { + "format": "date-time", + "title": "Ended At", + "type": "string" + }, + "end_reason": { + "enum": ["deleted", "purged", "crashed"], + "title": "End Reason", + "type": "string" + }, + "peer_count": { + "title": "Peer Count", + "type": "integer" + }, + "issues_count": { + "title": "Issues Count", + "type": "integer" + } + }, + "required": [ + "room_id", + "created_at", + "ended_at", + "end_reason", + "peer_count", + "issues_count" + ], + "title": "RoomOut", + "type": "object" + }, + "RoomsPageOut": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/RoomOut" + }, + "title": "Items", + "type": "array" + }, + "count": { + "title": "Count", + "type": "integer" + } + }, + "required": ["items", "count"], + "title": "RoomsPageOut", + "type": "object" + }, + "IssueOut": { + "properties": { + "code": { + "enum": ["high_packet_loss", "high_rtt"], + "title": "Code", + "type": "string" + } + }, + "required": ["code"], + "title": "IssueOut", + "type": "object" + }, + "MetadataEntryOut": { + "properties": { + "key": { + "title": "Key", + "type": "string" + }, + "value": { + "title": "Value", + "type": "string" + } + }, + "required": ["key", "value"], + "title": "MetadataEntryOut", + "type": "object" + }, + "PeerOut": { + "properties": { + "peer_id": { + "title": "Peer Id", + "type": "string" + }, + "sdk": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Sdk" + }, + "joined_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Joined At" + }, + "left_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Left At" + }, + "end_reason": { + "anyOf": [ + { + "enum": ["left", "removed", "finished", "crashed"], + "type": "string" + }, + { + "type": "null" + } + ], + "title": "End Reason" + }, + "metadata": { + "items": { + "$ref": "#/components/schemas/MetadataEntryOut" + }, + "title": "Metadata", + "type": "array" + }, + "issues": { + "items": { + "$ref": "#/components/schemas/IssueOut" + }, + "title": "Issues", + "type": "array" + } + }, + "required": [ + "peer_id", + "sdk", + "joined_at", + "left_at", + "end_reason", + "metadata", + "issues" + ], + "title": "PeerOut", + "type": "object" + }, + "RoomDetailOut": { + "properties": { + "room": { + "$ref": "#/components/schemas/RoomOut" + }, + "peers": { + "items": { + "$ref": "#/components/schemas/PeerOut" + }, + "title": "Peers", + "type": "array" + }, + "thresholds": { + "$ref": "#/components/schemas/ThresholdsOut" + } + }, + "required": ["room", "peers", "thresholds"], + "title": "RoomDetailOut", + "type": "object" + }, + "ThresholdsOut": { + "properties": { + "packet_loss_pct": { + "title": "Packet Loss Pct", + "type": "number" + }, + "rtt_ms": { + "title": "Rtt Ms", + "type": "number" + } + }, + "required": ["packet_loss_pct", "rtt_ms"], + "title": "ThresholdsOut", + "type": "object" + }, + "PeerDetailOut": { + "properties": { + "peer": { + "$ref": "#/components/schemas/PeerOut" + }, + "thresholds": { + "$ref": "#/components/schemas/ThresholdsOut" + }, + "series": { + "items": { + "$ref": "#/components/schemas/PeerSeriesPointOut" + }, + "title": "Series", + "type": "array" + }, + "rtt": { + "items": { + "$ref": "#/components/schemas/PeerRttPointOut" + }, + "title": "Rtt", + "type": "array" + } + }, + "required": ["peer", "thresholds", "series", "rtt"], + "title": "PeerDetailOut", + "type": "object" + }, + "PeerRttPointOut": { + "properties": { + "timestamp": { + "format": "date-time", + "title": "Timestamp", + "type": "string" + }, + "rtt_ms": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Rtt Ms" + } + }, + "required": ["timestamp", "rtt_ms"], + "title": "PeerRttPointOut", + "type": "object" + }, + "PeerSeriesPointOut": { + "properties": { + "timestamp": { + "format": "date-time", + "title": "Timestamp", + "type": "string" + }, + "direction": { + "enum": ["inbound", "outbound"], + "title": "Direction", + "type": "string" + }, + "kind": { + "title": "Kind", + "type": "string" + }, + "bitrate_bps": { + "title": "Bitrate Bps", + "type": "number" + }, + "packet_loss_pct": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Packet Loss Pct" + } + }, + "required": [ + "timestamp", + "direction", + "kind", + "bitrate_bps", + "packet_loss_pct" + ], + "title": "PeerSeriesPointOut", + "type": "object" + } + }, + "securitySchemes": { + "ManagementTokenAuth": { + "type": "http", + "scheme": "bearer" + } + } + }, + "servers": [ + { + "url": "https://fishjam.io/api/v1/public" + } + ] +} From 4bda8a85b8e440aba7cf9f9cf1c4182c9e70ed3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82?= Date: Tue, 20 Oct 2026 12:38:12 +0200 Subject: [PATCH 2/3] Copy the Room History API spec from the cloud submodule --- .gitmodules | 3 +++ api/cloud | 1 + scripts/update_api.sh | 8 ++++++++ static/api/room-history-openapi.json | 2 +- 4 files changed, 13 insertions(+), 1 deletion(-) create mode 160000 api/cloud diff --git a/.gitmodules b/.gitmodules index d1480a8a..b1cfbc0d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,3 +25,6 @@ [submodule "packages/react-native-worklets"] path = packages/react-native-worklets url = https://github.com/fishjam-cloud/fishjam-react-native-worklets.git +[submodule "api/cloud"] + path = api/cloud + url = git@github.com:fishjam-cloud/cloud.git diff --git a/api/cloud b/api/cloud new file mode 160000 index 00000000..1f5ab02b --- /dev/null +++ b/api/cloud @@ -0,0 +1 @@ +Subproject commit 1f5ab02b8dd696fc555af18b72c7557253ecfaaa diff --git a/scripts/update_api.sh b/scripts/update_api.sh index f29ea75e..3afebd26 100755 --- a/scripts/update_api.sh +++ b/scripts/update_api.sh @@ -55,6 +55,11 @@ copy_composition_openapi() { echo "Copied openapi.json of composition to the assets directory." } +copy_room_history_openapi() { + cp backend/openapi/diagnostics.json "$ASSETS_DIRECTORY/room-history-openapi.json" + echo "Copied diagnostics.json of cloud to the assets directory." +} + PROTO_FILES="server_notifications agent_notifications notifications/shared" copy_protos() { for file in $PROTO_FILES; do @@ -74,4 +79,7 @@ copy_protos checkout_submodule_branch composition $COMPOSITION_BRANCH copy_composition_openapi +checkout_submodule cloud +copy_room_history_openapi + echo $'\nSubmodule update and copy complete.' diff --git a/static/api/room-history-openapi.json b/static/api/room-history-openapi.json index c11878fc..d781b1e1 100644 --- a/static/api/room-history-openapi.json +++ b/static/api/room-history-openapi.json @@ -3,7 +3,7 @@ "info": { "title": "Fishjam Room History API", "version": "1.0.0", - "description": "Call quality history of the ended rooms of your Fishjam apps: which rooms had issues, which peers caused them, and each peer's bitrate, packet loss and round trip time over time. Authenticate with your management token. Data is kept for 60 days." + "description": "Browse your Fishjam rooms that ended in the last 60 days. For each room, see which peers had issues such as high round trip time or packet loss, and how their bitrate changed over time. Authenticate with your management token." }, "paths": { "/room-history": { From 7eafe89e49c33a66f7a1a60f0e979b42a785cbe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82?= Date: Tue, 20 Oct 2026 20:00:24 +0200 Subject: [PATCH 3/3] Update the Room History API spec --- static/api/room-history-openapi.json | 39 +++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/static/api/room-history-openapi.json b/static/api/room-history-openapi.json index d781b1e1..008d71e0 100644 --- a/static/api/room-history-openapi.json +++ b/static/api/room-history-openapi.json @@ -402,7 +402,12 @@ "end_reason": { "enum": ["deleted", "purged", "crashed"], "title": "End Reason", - "type": "string" + "type": "string", + "x-enumDescriptions": { + "crashed": "Room crashed on our side.", + "deleted": "Room closed normally.", + "purged": "Room closed after everyone left." + } }, "peer_count": { "title": "Peer Count", @@ -496,6 +501,7 @@ "type": "null" } ], + "description": "When the peer joined. `null` if the peer never connected.", "title": "Joined At" }, "left_at": { @@ -514,12 +520,19 @@ "anyOf": [ { "enum": ["left", "removed", "finished", "crashed"], - "type": "string" + "type": "string", + "x-enumDescriptions": { + "crashed": "Connection crashed.", + "finished": "Finished its job, e.g. a VAPI peer whose call ended.", + "left": "Left on their own.", + "removed": "Removed by your app." + } }, { "type": "null" } ], + "description": "Leave reason. `null` if no reason was recorded.", "title": "End Reason" }, "metadata": { @@ -572,10 +585,12 @@ "ThresholdsOut": { "properties": { "packet_loss_pct": { + "description": "Packet loss limit, in %. At or above it a peer has the `high_packet_loss` issue.", "title": "Packet Loss Pct", "type": "number" }, "rtt_ms": { + "description": "Round trip time limit, in ms. At or above it a peer has the `high_rtt` issue.", "title": "Rtt Ms", "type": "number" } @@ -593,6 +608,7 @@ "$ref": "#/components/schemas/ThresholdsOut" }, "series": { + "description": "Bitrate and packet loss per report, track kind and direction.", "items": { "$ref": "#/components/schemas/PeerSeriesPointOut" }, @@ -627,6 +643,7 @@ "type": "null" } ], + "description": "Round trip time, in ms. `null` if the peer's app never reported back.", "title": "Rtt Ms" } }, @@ -644,13 +661,28 @@ "direction": { "enum": ["inbound", "outbound"], "title": "Direction", - "type": "string" + "type": "string", + "x-enumDescriptions": { + "inbound": "Sent. Media the peer sent to Fishjam.", + "outbound": "Received. Media the peer received from Fishjam." + } }, "kind": { + "enum": [ + "microphone", + "camera", + "screenShareVideo", + "screenShareAudio", + "customVideo", + "customAudio", + "audio", + "video" + ], "title": "Kind", "type": "string" }, "bitrate_bps": { + "description": "Bitrate, in bits per second, since the previous report.", "title": "Bitrate Bps", "type": "number" }, @@ -663,6 +695,7 @@ "type": "null" } ], + "description": "Packet loss, in %. `null` if no packets were expected.", "title": "Packet Loss Pct" } },