openapi: 3.0.3 info: title: Browser Emulator API version: 5.0.1 description: | REST and WebSocket API for the Browser Emulator load testing worker. Used by the Loadtest Controller and client websites. ## Authentication No authentication required for internal APIs. ## Base URLs - HTTP: http://localhost:5000 - HTTPS: https://localhost:5000 ## WebSocket Events The WebSocket endpoint at ws://localhost:5001/events streams browser events. Connect with a WebSocket client and listen for JSON messages in the BrowserEvent format. servers: - url: http://localhost:5000 description: REST API (HTTP) - url: https://localhost:5000 description: REST API (HTTPS) - url: ws://localhost:5001 description: WebSocket server for events tags: - name: Events description: Browser event and stats endpoints - name: Stream Manager description: Browser stream management - name: Instance description: Instance lifecycle management - name: QoE description: Quality of Experience analysis - name: Webhooks description: LiveKit server webhook receiver paths: /webrtcStats: post: tags: - Events summary: Submit WebRTC statistics operationId: postWebRtcStats requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/WebRtcStatsRequest' responses: '200': description: Stats saved successfully '500': description: Internal server error content: application/json: schema: type: object /events: post: tags: - Events summary: Submit browser event operationId: postEvent requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BrowserEvent' responses: '200': description: Event received successfully '500': description: Internal server error /events/errors: post: tags: - Events summary: Submit browser error event operationId: postEventError requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BrowserEvent' responses: '200': description: Error event received successfully '500': description: Internal server error /webhooks/livekit: post: tags: - Webhooks summary: Receive a LiveKit server webhook event description: | Receives webhook events from the LiveKit server (e.g. `participant_left`, `participant_connection_aborted`, `track_unpublished`), used as a last-resort signal for participant disconnections not caught by healthchecks or client-side events. LiveKit broadcasts each event to every configured worker URL, so this worker verifies the request signature and silently ignores events that don't belong to a participant/loadtest run it created. Always responds `200` once the request body has been read, regardless of whether the event was recognized, verified, or acted upon. operationId: postLiveKitWebhook requestBody: required: true content: application/webhook+json: schema: type: string format: binary description: Raw LiveKit WebhookEvent protobuf-JSON payload, signed via the Authorization header. responses: '200': description: Event received (processing outcome is not reflected in the response) /openvidu-browser/streamManager: post: tags: - Stream Manager summary: Create a new browser participant operationId: createStreamManager requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateUserBrowser' responses: '200': description: Stream manager created successfully content: application/json: schema: $ref: '#/components/schemas/CreateUserBrowserResponse' '400': description: Invalid request parameters content: application/json: schema: type: object properties: message: type: string '500': description: Internal server error delete: tags: - Stream Manager summary: Delete all participants operationId: deleteAllStreamManagers responses: '200': description: All participants deleted successfully content: text/plain: schema: type: string '500': description: Internal server error /openvidu-browser/load-test: post: tags: - Stream Manager summary: Launch an lk load-test run description: > Starts a single `lk load-test` process that simulates many publishers/subscribers in one room. Used by the controller when a test case's `browser` is set to `emulated`. Simulcast is enabled by default. Unlike the streamManager path, this does not publish custom/reference media (lk load-test uses its own synthetic clip). operationId: runLoadTest requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/LoadTestRunRequest' responses: '200': description: Load-test run started successfully content: application/json: schema: $ref: '#/components/schemas/LoadTestRunResponse' '400': description: Invalid request parameters content: application/json: schema: type: object properties: message: type: string '500': description: Internal server error /openvidu-browser/streamManager/connection/{connectionId}: delete: tags: - Stream Manager summary: Delete participant by connection ID operationId: deleteStreamManagerByConnection parameters: - name: connectionId in: path required: true schema: type: string responses: '200': description: Connection deleted successfully '400': description: Invalid connection ID '500': description: Internal server error /openvidu-browser/streamManager/session/{sessionId}/user/{userId}: delete: tags: - Stream Manager summary: Delete participant by session and user ID operationId: deleteStreamManagerBySessionUser parameters: - name: sessionId in: path required: true schema: type: string - name: userId in: path required: true schema: type: string responses: '200': description: Session user deleted successfully '400': description: Invalid session ID or user ID '500': description: Internal server error /instance/ping: get: tags: - Instance summary: Check if instance is ready operationId: pingInstance responses: '200': description: Instance is ready content: text/plain: schema: type: string example: Pong '500': description: Instance is not ready /instance/initialize: post: tags: - Instance summary: Initialize the browser emulator instance operationId: initializeInstance requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/InitializePost' responses: '200': description: Instance initialized successfully content: text/plain: schema: type: string '500': description: Internal server error /instance/shutdown: delete: tags: - Instance summary: Shutdown the browser emulator instance operationId: shutdownInstance responses: '200': description: Shutdown initiated content: text/plain: schema: type: string '500': description: Internal server error /qoe/qoeRecordings: post: tags: - QoE summary: Upload QoE recording file operationId: uploadQoeRecording requestBody: required: true content: multipart/form-data: schema: type: object properties: file: type: string format: binary responses: '200': description: File uploaded successfully '400': description: No file uploaded '500': description: Internal server error /qoe/analysis: post: tags: - QoE summary: Start QoE analysis operationId: startQoeAnalysis requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/QoeAnalysisRequest' responses: '200': description: Analysis started successfully content: application/json: schema: type: object '500': description: Internal server error /qoe/analysis/status: get: tags: - QoE summary: Get QoE analysis status operationId: getQoeAnalysisStatus responses: '200': description: Status retrieved successfully content: application/json: schema: $ref: '#/components/schemas/QoeAnalysisStatus' components: schemas: WebRtcStatsRequest: type: object required: - timestamp - user - session - webrtcStats properties: timestamp: type: string format: date-time user: type: string session: type: string webrtcStats: type: object additionalProperties: true BrowserEvent: type: object required: - participant - session - timestamp properties: participant: type: string session: type: string timestamp: type: string format: date-time event: type: string connectionId: type: string connection: type: string enum: - local - remote reason: type: string name: type: string # Additional dynamic properties allowed additionalProperties: type: object CreateUserBrowser: type: object required: - openviduUrl - properties properties: openviduUrl: type: string format: uri token: type: string properties: $ref: '#/components/schemas/UserJoinProperties' UserJoinProperties: type: object required: - userId - sessionName - role - audio - video - resolution - frameRate - browser properties: userId: type: string sessionName: type: string role: type: string enum: - PUBLISHER - SUBSCRIBER audio: type: boolean video: type: boolean resolution: type: string enum: - 640x480 - 1280x720 - 1920x1080 frameRate: type: integer minimum: 1 maximum: 60 browser: type: string enum: - chrome - firefox - custom-emulated recording: type: boolean showVideoElements: type: boolean headless: type: boolean recordingMetadata: type: string mediaRecorders: type: boolean disableAutoSubscribeForPublishers: type: boolean description: >- Debug-only. For PUBLISHER participants, disables auto-subscription so the publisher doesn't also subscribe to other participants' tracks. For custom-emulated publishers it omits --auto-subscribe from the underlying `lk room join` command; for real-browser publishers it sets autoSubscribe=false in the LiveKit room connect options. Has no effect on SUBSCRIBER participants. CreateUserBrowserResponse: type: object properties: connectionId: type: string streams: type: integer participants: type: integer workerCpuUsage: type: number sessionId: type: string userId: type: string LoadTestRunRequest: type: object required: - openviduUrl - livekitApiKey - livekitApiSecret - room properties: openviduUrl: type: string livekitApiKey: type: string livekitApiSecret: type: string room: type: string videoPublishers: type: integer description: >- Number of participants publishing video tracks. Each one also publishes an audio track and subscribes to other participants' tracks (mirroring a NORMAL-mode publisher), adding to audioPublishers/subscribers below rather than replacing them. audioPublishers: type: integer description: >- Number of participants publishing only an audio track, in addition to the audio publishers implied by videoPublishers subscribers: type: integer description: >- Number of participants that only subscribe, in addition to the subscribers implied by videoPublishers numPerSecond: type: integer description: Number of testers to start every second (ramp) videoResolution: type: string enum: [low, medium, high] videoCodec: type: string enum: [h264, vp8] simulcast: type: boolean description: Simulcast is disabled by default; set true to opt in layout: type: string enum: [speaker, 3x3, 4x4, 5x5] description: Defaults to "5x5" when omitted (lk load-test itself defaults to "speaker") participantIds: type: array items: type: string description: >- Synthetic participant ids (e.g. "User1", "User2") assigned to this chunk's publishers/subscribers, in order. When Elasticsearch monitoring is enabled, one webrtc-stats document per id is indexed with node_role: browseremulator, matching NORMAL mode's per-participant reporting. LoadTestRunResponse: type: object properties: runId: type: string handleId: type: string room: type: string workerCpuUsage: type: number InitializePost: type: object properties: browserVideo: type: object description: Browser video configuration oneOf: - $ref: '#/components/schemas/BrowserVideoCustom' - $ref: '#/components/schemas/BrowserVideoPreset' vnc: type: boolean awsAccessKey: type: string awsSecretAccessKey: type: string s3BucketName: type: string s3Region: type: string s3Host: type: string s3HostAccessKey: type: string s3HostSecretAccessKey: type: string legacyMode: type: boolean elasticSearchHost: type: string elasticSearchUserName: type: string elasticSearchPassword: type: string elasticSearchIndex: type: string BrowserVideoCustom: type: object required: - videoType - customVideo properties: videoType: type: string enum: - custom customVideo: type: object properties: videoUrl: type: string format: uri audioUrl: type: string format: uri BrowserVideoPreset: type: object required: - videoType - videoInfo properties: videoType: type: string enum: - bunny - interview - game videoInfo: type: object properties: width: type: integer height: type: integer fps: type: integer QoeAnalysisRequest: type: object required: - fragmentDuration - paddingDuration properties: fragmentDuration: type: integer description: Fragment duration in seconds paddingDuration: type: integer description: Padding duration in seconds presenterVideoProperties: type: object properties: width: type: integer height: type: integer frameRate: type: integer qoeConfig: type: object additionalProperties: true QoeAnalysisStatus: type: object properties: remainingFiles: type: integer