LevelChatDocs
Docs
Quickstart

Quickstart

From zero to 'I am in a room' in five minutes.

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:

  1. Sign up + create a project → get an API key.
  2. Mint a short-lived room token on your server.
  3. Hand the token to the client SDK.
  4. 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:

  1. Open https://app.levelchat.io/console/signup and create your workspace.
  2. After sign-in you land in Studio. The signup flow auto-creates a Default project for you.
  3. Open Projects → Default → API Keys.
  4. 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/web
~
npm install @levelchat/web @levelchat/web-react
~
npm install @levelchat/react-native react-native-webrtc
~
pod 'LevelChat'
~
implementation 'io.levelchat:sdk:1.0.0'
~
flutter pub add levelchat

2. 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:

TypeScript
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:

TypeScript
'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:

TypeScript (TSX)
'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 captionsroom.captions.start({ lang: 'en' })
  • Record the roomroom.startRecording({ layout: 'tracks' })
  • Promote a meeting → broadcastroom.setRoomType('live')
  • Self-host → see On-prem deployment
  • Per-platform deep divesWeb SDK, iOS, Android, React Native, Flutter
  • Licensing modelopen 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.