# Integrating Discourse Voice with LiveKit

**URL:** https://meta.discourse.org/t/integrating-discourse-voice-with-livekit/411585
**Category:** Integrations
**Tags:** voice
**Created:** [September 3, 2026, 3:19pm UTC](https://meta.discourse.org/t/integrating-discourse-voice-with-livekit/411585 "2026-09-03T15:19:01Z")
**Posts on this page:** 1
**Page:** 1

<div class="post-metadata">

### Author: ![pmusaraj](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pmusaraj/32/119489_2.png) [@pmusaraj](https://meta.discourse.org/u/pmusaraj)
#### Post date: [September 3, 2026, 3:19pm UTC](https://meta.discourse.org/t/integrating-discourse-voice-with-livekit/411585/1 "2026-09-03T15:19:01Z")

</div>

This guide walks through connecting Discourse’s **Voice** plugin to a [LiveKit](https://livekit.io) media server. By default, voice calls run peer-to-peer: every participant sends their audio directly to every other participant. That works great for small rooms, but bandwidth grows with room size. Routing calls through LiveKit keeps each participant’s upload constant no matter how many people join.

## Prerequisites

Before you start, make sure you have:

- A Discourse site running the Voice plugin enabled
- A LiveKit server you can reach, with its API credentials. You can self-host LiveKit or use a managed offering such as LiveKit Cloud.
- Admin access to your Discourse site.

## Step 1 — Enable the Voice plugin

Voice ships bundled with Discourse. Go to **Admin → Settings → Plugins** and  
turn on the **Voice** (voice\_enabled) setting.

## Step 2 — Provision a LiveKit server and note the credentials

You need three things from your LiveKit deployment: the **WebSocket URL** , the  
**API key** , and the **API secret**.

These steps are relatively straightforward with LiveKit Cloud:

1. Create a project in the LiveKit Cloud dashboard.
2. From the project’s **Settings → Keys** page, reveal the **API key** and  
**API secret**.
3. Note the **WebSocket URL** (for example `wss://my-project.livekit.cloud`).

For Self-hosted LiveKit installations, please refer to the respective documentation.

## Step 3 — Configure the LiveKit connection in Discourse

Go to **Admin → Plugins → Voice** , open the settings and fill in the LiveKit section:

| Setting | What to enter |
| --- | --- |
| `voice_livekit_url` | The WebSocket URL, e.g. `wss://livekit.example.com` (or `ws://` for plain-HTTP labs). |
| `voice_livekit_api_key` | The API key from Step 2. |
| `voice_livekit_api_secret` | The API secret from Step 2. |
| `voice_livekit_room_policy` | Which rooms use LiveKit. |

The `voice_livekit_room_policy` setting decides how rooms pick a transport:

| Policy | Behaviour |
| --- | --- |
| `disabled` | All calls run peer-to-peer (the default). |
| `per_room` | Room creators/managers opt rooms in individually with a **Use media server (SFU)** checkbox in the room form. |
| `all_rooms` | Every room is routed through LiveKit. |

## Step 5 — Optional extra settings

These settings fine-tune the integration:

| Setting | Purpose |
| --- | --- |
| `voice_livekit_room_prefix` | Namespace prefix for room names on LiveKit. With multiple sites sharing one LiveKit server, set a unique prefix per site. Empty defaults to the site’s database name. |
| `voice_livekit_mesh_fallback` | When a LiveKit token cannot be issued and the room is empty, start on the peer-to-peer mesh instead of failing the join. Off by default — silent degradation can hide a LiveKit outage. |
| `voice_livekit_recording_enabled` | Allow room moderators to record calls that run on LiveKit. Recordings are produced by LiveKit Egress and stored on your LiveKit deployment’s storage (S3, GCS, local disk) — not uploaded to Discourse. |
| `voice_livekit_recording_filepath` | The filepath passed to LiveKit Egress, e.g. `voice/{room_name}-{utc}`. Supports placeholders such as `{room_name}`, `{room_id}`, `{time}`, `{utc}`; the file extension is added automatically. |

## Step 6 — Optional: enable LiveKit webhooks

Webhooks are a reconcile-only backstop. When a participant’s connection dies  
abruptly or a room ends, webhooks let the plugin clean up presence and room  
state within seconds instead of waiting out the heartbeat TTL. They are  
optional — if they can’t be delivered, calls still work, cleanup just takes  
a little longer.

On the LiveKit server, add the webhook endpoint (the plugin shows the exact  
snippet on its admin dashboard until the first webhook arrives):

```yaml
# livekit.yaml
webhook:
  api_key: <your-api-key>
  urls:
    - https://forum.example.com/voice/livekit/webhook

```

Restart LiveKit. Deliveries are authenticated with the API secret —  
no additional shared secret to configure.

## Step 7 — Verify the integration

### Check the Discourse Voice admin dashboard

Go to **Admin → Plugins → Voice → Dashboard**. Once any LiveKit setting is  
present, the dashboard shows a **LiveKit media server** status card with  
live checks:

- configuration status (which settings are present and the active policy),
- whether access tokens can be signed with the key pair,
- whether the server is reachable and how many rooms are active,
- the last automatic connectivity check,
- whether webhooks are being received.

Use **Refresh** to run an on-demand probe.

Here is a screenshot of the Voice plugin dashboard with LiveKit enabled:

 ![image](https://global.discourse-cdn.com/meta/original/4X/8/6/8/868586a2c22848b77b36bd859bcfc569731da99a.png)

### Check the LiveKit dashboard

There are details about the calls on the LiveKit dashboard, too. Here is an example:

 ![image](https://global.discourse-cdn.com/meta/original/4X/3/c/d/3cd7eac164569d2cac25af9f0d7beb686b812322.png)
