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.
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.
Related¶
- Overview — Capabilities and models
- Integration Guide — What your backend implements
- Session Lifecycle — Bot state machine
- API Reference — REST endpoints