Skip to content

Meeting Liveness Bot — How It Works

How the bot joins meetings, processes video frames, and delivers liveness results—at a level useful for integrators and technical stakeholders.

In plain terms

Your backend only calls POST /api/sessions. Behind that, a meeting provider joins the call on your behalf, video frames flow into the bot, and each participant is checked through the Moveris API. You get verdicts on your webhook; an optional browser link shows live status.

System Architecture

flowchart TB
    subgraph Client["Your systems"]
        BE[Your backend]
        OBS[Interviewer browser]
    end

    subgraph Bot["Meeting Liveness Bot"]
        API[REST API]
        WS[WebSocket ingest per session]
        FH[FrameHandler per session]
        ACC[Frame accumulator]
        PIPE[Liveness pipeline]
    end

    subgraph External["External services"]
        REC[Meeting provider]
        MOV[Moveris API]
    end

    BE -->|POST /api/sessions| API
    API -->|Create bot| REC
    REC -->|Video frames ?sid=sessionId| WS
    WS --> FH
    FH --> ACC
    ACC -->|N frames after manual trigger| PIPE
    PIPE -->|POST /api/v1/fast-check| MOV
    MOV -->|verification.completed| BE
    PIPE -->|Verdict + SSE| OBS
    API -->|GET /api/interview/token/events| OBS

Processing pipeline

Once the bot is in the call, each participant on camera follows this path:

flowchart LR
    A[Participant on camera] --> B[Meeting provider streams H.264 video]
    B --> C[Ready to scan]
    C --> D[Interviewer triggers scan]
    D --> E[FFmpeg extracts consecutive PNG frames]
    E --> F[Quality gate]
    F -->|pass| G[Optional background segmentation]
    F -->|fail| R[Reset buffer + frame-quality-rejected SSE]
    G --> H[Buffer N frames at native resolution]
    H --> I[POST /api/v1/fast-check]
    I --> J[Verdict]
    J --> K[SSE to interviewer UI]
    J --> L[Webhook to your backend]
Stage What happens
Frame ingest The meeting provider sends an H.264 video stream per participant (routed by ?sid= to the correct session handler). FFmpeg extracts complete PNG frames
Manual trigger Interviewer starts scan per participant — frames do not auto-accumulate
Quality gate Blur, brightness, and optional face checks run before buffering. A rejected frame resets the batch and emits frame-quality-rejected on SSE
Background segmentation (optional) When enabled for the session, replace the background with neutral grey before encoding
Buffering After a frame passes the gate, frames accumulate until the model’s required count (e.g. 10 for mixed-10-v3_1_1)
Preparation Frames are kept at native capture resolution and consecutive (no downscale, no skipping), then encoded for the API
Analysis Moveris runs liveness inference and returns verdict, score, and confidence
Delivery Results are pushed via SSE (live UI), webhook (your server), and optionally Slack (operator alerts when configured)

Two Ways You Receive Updates

Integrators often confuse bot status with liveness verdicts. They use different channels:

flowchart TB
    subgraph Status["Bot status — where is the bot in the call?"]
        S1[bot_created → joining_call → in_call_recording → done]
        S2[SSE: /api/sessions/:id/events or /api/interview/:token/events]
    end

    subgraph Verdict["Liveness verdict — is this participant live?"]
        V1[Per participant after N frames analyzed]
        V2[Webhook: verification.completed from Moveris API]
    end

    Status --> S2
    Verdict --> V2
Update type Examples Typical channel Who consumes it
Bot status joining_call, in_call_recording, call_ended SSE (interviewer UI or authenticated session stream) Observers, optional polling via GET /api/sessions/:id
Liveness verdict verdict: live, score, participant_id HTTPS webhook to your registered URL Your backend (production integration)
Operator alert (optional) Same verdict fields formatted for chat Slack (when enabled by Moveris) Internal monitoring—not a substitute for your webhook

Build on webhooks for verdicts

For production, treat the Moveris webhook (verification.completed) as the source of truth for pass/fail decisions. Use SSE or GET /api/sessions/:id for monitoring and UX only.


What Happens When You Create a Session

When your backend calls POST /api/sessions:

sequenceDiagram
    participant BE as Your backend
    participant Bot as Meeting Liveness Bot
    participant Provider as Meeting provider
    participant Portal as Moveris API

    BE->>Bot: POST /api/sessions + Bearer sk-bot-*
    Bot->>Portal: Validate bot key
    Portal-->>Bot: Org context + branding
    Bot->>Provider: Create bot (meeting URL)
    Bot->>Bot: Configure analysis (model, sessionId)
    Bot->>Bot: Sign interviewToken (24h TTL)
    Bot-->>BE: sessionId, botId, interviewToken

After creation, the meeting provider connects to the bot’s WebSocket endpoint and begins streaming participant video.


Authentication Paths

Machine-to-machine

Your backend sends Authorization: Bearer sk-bot-<key> on session APIs. The bot validates the key with Moveris and loads org branding. No browser login.

Browser UI (optional)

Operators sign in with Google OAuth. Email domain must match configured allowlist. Used for the scheduling UI—not required for API integration.

Interviewer view

Read-only link /interview/<token>. Token is HMAC-signed, 24-hour TTL. No API key or Google login.

M2M validation flow

sequenceDiagram
    participant BE as Your backend
    participant Bot as Meeting Liveness Bot
    participant API as Moveris API

    BE->>Bot: Authorization: Bearer sk-bot-*
    Bot->>API: Validate bot key
    API-->>Bot: Org context + branding fields
    Bot-->>BE: Session created (or 401 if invalid)

Concurrent Sessions

Each active bot session gets its own isolated FrameHandler — separate frame buffers, participant maps, and result stores. The meeting provider WebSocket callback URL includes ?sid=<sessionId> so frames route to the correct handler when multiple meetings run in parallel.

Participant Events

The frame handler reacts to meeting provider participant events:

Event Effect
Participant joins Start tracking; show Ready to scan in interviewer UI
Participant leaves Stop tracking
Interviewer triggers scan Begin buffering frames for that participant
Camera on Participant eligible for scan again
Camera off Pause; may require re-scan after threshold

Camera toggle re-scan: If a camera stays off longer than a configurable threshold (about 10 seconds by default) and comes back on, the participant returns to Ready to scan. A new analysis uses a fresh Moveris session_id to avoid API cache hits.


Correlation ID (UUID v5)

Each participant analysis gets a stable session_id in the webhook:

flowchart LR
    A["sessionId from POST /api/sessions"] --> C["uuid_v5(namespace, sessionId + '-' + participantId)"]
    B["participant_id from webhook"] --> C
    C --> D["webhook session_id"]

Fixed namespace: 5e8cf7e0-e4c4-4b3a-8d3a-2f1c3b4a5e6f

See Session Lifecycle for code examples.


Interview Token

flowchart LR
    P["payload = base64url({ sessionId, exp })"] --> T["token = payload + '.' + HMAC-SHA256(payload, secret)"]
    T --> U["/interview/<token>"]

The interviewer SPA calls GET /api/interview/<token> then opens SSE at GET /api/interview/<token>/events.


Hosting & Persistence

Moveris hosts and operates the bot

The Meeting Liveness Bot is hosted and operated by Moveris. You integrate over HTTPS—there is no infrastructure to deploy or environment to configure on your side. Session data (meeting context, participants, and verdicts) is persisted by the service so results remain available for polling and audit.