livqenoDocs

BackendBuild your token endpoint

Build your token endpoint

The one small backend route every Livqeno app needs, in TypeScript, Python, Go, Java, PHP, Dart or plain HTTP.

Every Livqeno app, on any platform, needs one small route on a server you control. Your app (Flutter, iOS, Android, React Native, web) calls it, and it returns a short-lived token. The app then joins with that token.

Where this fits: project → API key → your token endpoint → your app joins. This page is the whole endpoint, start to finish.

Why this route exists

Making a token takes your API key (rvk_…). The key can create tokens for any room in your project and never expires, so it can't ship inside an app: anyone can pull a key out of an app binary or a web bundle.

So the key stays on your server, and your server hands out tokens:

Your app ──► your token endpoint ──► Livqeno API   (key stays here)
   ▲                  │
   └── token ◄────────┘
Your app ──► Livqeno (calls and chat, using the token only)

Call and chat traffic goes straight from the app to Livqeno. Your server only makes tokens. It does no media work and no message handling.

Pick your language

The server SDKs are thin wrappers over one HTTPS call, so use whichever language your backend is already in. There is no need to add Node.js to a Flutter, Swift or Kotlin project just for this.

Your backendUse
TypeScript / JavaScript@livqeno/server
Pythonlivqeno
Golivqeno
Java / Kotlin (JVM)com.livqeno:livqeno-server
PHPlivqeno/livqeno-php
Dart, Ruby, C#, Rust, anything elseThe plain HTTP call at the bottom

Each example below does the same thing: reads the room name from the request, asks Livqeno for a token, and returns token, endpoint and iceServers for the app. Keep the API key in an environment variable or your host's secret store.

server.ts (Express)
import express from 'express';
import { Livqeno } from '@livqeno/server';
 
const livqeno = new Livqeno({
  apiKey: process.env.LIVQENO_API_KEY!,
  baseUrl: process.env.LIVQENO_API_URL!, // https://api.livqeno.com
});
 
const app = express();
 
app.post('/livqeno/token', async (req, res) => {
  const grant = await livqeno.tokens.create({
    room: req.query.room as string,
    identity: currentUserId(req),
    permissions: { join: true, subscribe: true, publish: true, publishAudio: true, publishVideo: true },
    expiresIn: 3600,
  });
  res.json({ token: grant.token, endpoint: grant.endpoint, iceServers: grant.iceServers });
});
 
app.listen(3000);

Plain HTTP

Any language that can send an HTTPS request works the same way. This is the call every SDK above makes:

curl -X POST "$LIVQENO_API_URL/v1/rooms/room-1/rtc-tokens" \
  -H "Authorization: Bearer $LIVQENO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "participantIdentity": "user-42",
    "permissions": { "join": true, "subscribe": true, "publish": true,
                     "publishAudio": true, "publishVideo": true },
    "ttlSeconds": 3600
  }'

The response holds token, endpoint, iceServers, telemetryUrl and expiresAt. Forward token, endpoint and iceServers to the app unchanged.

Chat uses a separate token from POST /v1/chat/tokens; see Tokens.

Call it from your app

Your app calls your endpoint, never Livqeno's token API:

final res = await http.post(Uri.parse('https://your-backend.example.com/livqeno/token?room=room-1'));
final grant = jsonDecode(res.body);
 
final livqeno = Livqeno(
  token: grant['token'],
  endpoint: grant['endpoint'],
  iceServers: (grant['iceServers'] as List)
      .map((s) => LivqenoIceServer.fromJson(s as Map<String, dynamic>))
      .toList(),
);
final room = await livqeno.join('room-1');

Tokens are short-lived. Give the SDK a refreshToken callback that calls this same endpoint again, so long calls keep going.

Next