Features

Everything a private video visit needs, engineered deliberately.

Every capability below exists because a real consultation needs it — and nothing exists just because a platform usually has it. Grouped by where it sits in the lifecycle of a call.

Rooms & access

The core contract
🔒

Private rooms, no auto-create

LiveKit runs with auto_create: false. A room must be created through POST /rooms before anyone can join — a forged, LiveKit-valid token for a never-created room is refused, verified against a live server, not just mocks.

🎭

Role-based join tokens

doctor, patient, nurse, staff, and interpreter get full publish/subscribe/data grants. observer gets subscribe only. Grants are derived server-side from the role — a caller can never ask for more than it is entitled to.

⏱️

Real room lifecycle

CREATED → ACTIVE → EXPIRED → ENDED, derived from provider truth on every read — never a stale stored copy. EXPIRED outranks ACTIVE: a room past its deadline is expired even with people still connected.

🧹

Automatic cleanup sweep

An in-process interval sweep evicts expired rooms with no queue and no Redis dependency. Every instance sweeps; racing is expected and harmless — the room is gone either way.

👥

Live participant snapshots

GET /rooms/{roomName}/participants returns who is connected right now, with join state (joining / joined / active / disconnected) read straight from the provider.

Recording & compliance

Cloud recording without touching bytes
🎥

Cloud recording via LiveKit Egress

One call starts a room-composite recording, uploaded straight to S3-compatible storage. VonLinkage never proxies or stores the bytes — same invariant that keeps media out of this process entirely.

📥

Presigned downloads

A short-lived, presigned URL lets the caller fetch the file directly from storage once a recording is COMPLETED. No proxying, no bandwidth cost on this service.

🛡️

Integrity verification

A background sweep confirms every finished recording actually exists in storage at the size Egress reported. Mismatches retry up to a cap, then surface as a terminal, human-reviewable failure.

🗄️

Durable recording history

Egress ages finished jobs out of its own memory; a Postgres archive is the only way recording history survives that. GET keeps answering correctly whether or not Egress still remembers the job.

⚖️

Opt-in retention purge

Off by default. When RECORDING_RETENTION_DAYS is set, files past that window are permanently deleted and the deletion is recorded — erasing clinical audio is a policy decision, never a silent default.

Transcription & integrations

What happens after the call
📝

Batch medical transcription

Finished recordings are transcribed with AWS Transcribe Medical — full text, per-segment timing, and confidence scores, available from one authenticated endpoint.

🗣️

Speaker diarization

Segments carry vendor-assigned speaker labels (spk_0, spk_1). A caller that needs "who said this" correlates labels with the room’s participant list — no per-participant audio is fabricated to fake that mapping.

🔔

Signed outbound webhooks

recording.completed, recording.failed, transcript.completed, and transcript.failed are pushed to a single configured receiver, HMAC-signed over the exact request body.

🔁

Delivery retries & idempotency

A stable delivery id lets receivers dedupe retries. Failed deliveries retry on a fixed interval up to a configured attempt cap, then stop — no silent infinite retry storms.

🚫

No PHI over webhooks

Payloads carry identifiers and status only — never transcript text, never clinical content. Receivers call back into the authenticated API for anything more.

Architecture & operations

Why it stays easy to run
🧩

Provider-abstracted by design

Controllers and services depend only on ports (RoomProvider, TokenProvider, StorageProvider…). Only the adapters package knows LiveKit exists — an architecture rule enforced by an executable spec, not just a convention.

📡

Health & readiness probes

Liveness never touches a dependency; readiness round-trips LiveKit and returns 503 when degraded — so orchestrators can tell "up" from "actually working" apart.

🔗

Correlation ids everywhere

x-correlation-id is generated when absent, echoed on every response, and appears in every log line for that request — quote it in a bug report and the trace is right there.

📦

One docker compose up

The whole stack — API, LiveKit, Coturn — comes up with zero manual configuration. Committed defaults are wired together end to end.

Verified against a live server

Beyond unit and e2e suites, an integration suite boots real LiveKit, Postgres, and S3 adapters — nothing mocked — and has already caught real bugs a mocked suite couldn’t.

See it in the API, not just in prose.

Every feature above maps to a real endpoint — read the reference and try it.