LevelChat is a video / voice / live-streaming platform you drop into any web, mobile, or native app. This guide gets you to a working room as fast as possible.
The flow is the same on every platform:
- Sign up + create a project → get an API key.
- Mint a short-lived room token on your server.
- Hand the token to the client SDK.
- Publish camera / mic, render incoming tiles.
Total: ~50 lines of code.
0. Sign up + grab an API key
The SDKs talk to a LevelChat server. You can either point them at the managed cloud (fastest) or run the server yourself. For the managed cloud:
- Open https://app.levelchat.io/console/signup and create your workspace.
- After sign-in you land in Studio. The signup flow auto-creates a
Defaultproject for you. - Open Projects → Default → API Keys.
- Click + New key. Pick the scopes (
publish,subscribe,record— or all three for an admin key). Copy the plaintext key — it's shown once, never again.
Your key looks like lc_pk_<keyId>.<secret>. Keep secret server-side; never bake it into
the browser bundle.
For self-host, the same flow works at https://<your-domain>/console/signup once your stack
is up — see On-prem deployment.
1. Install the SDK
Pick the SDK for your platform:
npm install @levelchat/webnpm install @levelchat/web @levelchat/web-reactnpm install @levelchat/react-native react-native-webrtcpod 'LevelChat'implementation 'io.levelchat:sdk:1.0.0'flutter pub add levelchat2. Mint a token on your server
Tokens are minted on your server — never in the browser. Issue a POST /v1/rooms/<id>/tokens
to LevelChat with your project API key (lc_pk_*); the response includes a short-lived JWT
the user joins one specific room with.
A standalone @levelchat/node SDK is on the roadmap; until it lands, use plain fetch —
the request shape is small and the SDK won't change it:
export async function POST(req: Request) {
const userId = await yourAuth(req); // your existing auth
const roomId = new URL(req.url).searchParams.get('room')!;
const res = await fetch(`${process.env.LEVELCHAT_API_URL}/v1/rooms/${roomId}/tokens`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.LEVELCHAT_API_KEY}`, // lc_pk_xxx.yyy
},
body: JSON.stringify({
identity: userId,
role: 'publisher',
capabilities: ['publish:camera', 'publish:mic', 'subscribe:all'],
ttlSeconds: 600, // 10 minutes
}),
});
if (!res.ok) return new Response('token mint failed', { status: 500 });
const { token, url } = await res.json();
return Response.json({ token, url });
}Or for prototyping, curl the same endpoint directly:
curl -X POST "$LC_API_URL/v1/rooms/r_demo/tokens" \
-H "Authorization: Bearer lc_pk_xxx.yyy" \
-d '{"identity":"alice","role":"publisher","capabilities":["publish:camera","subscribe:all"]}'3. Connect from the browser
The vanilla @levelchat/web SDK is one named import:
'use client';
import { LevelChat } from '@levelchat/web';
import { useEffect, useRef } from 'react';
export default function Page() {
const localVideo = useRef<HTMLVideoElement>(null);
useEffect(() => {
(async () => {
const { token, url } = await fetch('/api/lc-token?room=r_demo').then((r) => r.json());
const lc = new LevelChat({ logLevel: 'info' });
const live = await lc.joinLive({ token, signalingUrl: url, roomType: 'meeting' });
const cam = await live.publishCamera({ resolution: '720p' });
await live.publishMic();
// Bind the local preview.
if (cam.mediaStreamTrack && localVideo.current) {
localVideo.current.srcObject = new MediaStream([cam.mediaStreamTrack]);
await localVideo.current.play();
}
// Render every remote track that arrives.
live.room.on('track-subscribed', (t) => {
if (!t.mediaStreamTrack) return;
const el = document.createElement('video');
el.srcObject = new MediaStream([t.mediaStreamTrack]);
el.autoplay = true;
el.playsInline = true;
document.getElementById('remote')!.appendChild(el);
});
})();
}, []);
return (
<div>
<video ref={localVideo} autoPlay playsInline muted style={{ width: 320 }} />
<div id="remote" style={{ display: 'flex', gap: 12 }} />
</div>
);
}That's it. Open the page in two tabs — they join the same r_demo room and see each other.
4. (Optional) Use the React bindings
If you want hooks + ready-made components, @levelchat/web-react wraps the vanilla SDK:
'use client';
import {
LevelChatProvider,
useRoom,
useLocalParticipant,
ParticipantGrid,
} from '@levelchat/web-react';
export default function Page() {
return (
<LevelChatProvider tokenEndpoint="/api/lc-token" room="r_demo" roomType="meeting">
<ParticipantGrid />
<Controls />
</LevelChatProvider>
);
}
function Controls() {
const { toggleCamera, toggleMic, camOn, micOn } = useLocalParticipant();
return (
<div>
<button onClick={toggleCamera}>{camOn ? 'Camera off' : 'Camera on'}</button>
<button onClick={toggleMic}>{micOn ? 'Mic off' : 'Mic on'}</button>
</div>
);
}What's next
- Add captions →
room.captions.start({ lang: 'en' }) - Record the room →
room.startRecording({ layout: 'tracks' }) - Promote a meeting → broadcast →
room.setRoomType('live') - Self-host → see On-prem deployment
- Per-platform deep dives → Web SDK, iOS, Android, React Native, Flutter
- Licensing model → open source vs commercial — what's MIT, what's BUSL, where the production threshold is.
Where to find your API key: sign in to the LevelChat Studio and open Projects → <your project> → API Keys.