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 backend | Use |
|---|---|
| TypeScript / JavaScript | @livqeno/server |
| Python | livqeno |
| Go | livqeno |
| Java / Kotlin (JVM) | com.livqeno:livqeno-server |
| PHP | livqeno/livqeno-php |
| Dart, Ruby, C#, Rust, anything else | The 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.
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);import os
from fastapi import FastAPI, Request
from livqeno import AsyncLivqeno, CreateTokenParams, TokenPermissions
livqeno = AsyncLivqeno(
api_key=os.environ["LIVQENO_API_KEY"],
base_url=os.environ["LIVQENO_API_URL"], # https://api.livqeno.com
)
app = FastAPI()
@app.post("/livqeno/token")
async def livqeno_token(room: str, request: Request):
grant = await livqeno.tokens.create(
CreateTokenParams(
room=room,
identity=current_user_id(request),
permissions=TokenPermissions(
join=True, subscribe=True, publish=True, publish_audio=True, publish_video=True
),
expires_in=3600,
)
)
return {"token": grant["token"], "endpoint": grant["endpoint"], "iceServers": grant["iceServers"]}package main
import (
"encoding/json"
"net/http"
"os"
"github.com/atulsinghhhh/swalvo-sdks/sdks/go/livqeno"
)
func main() {
client := livqeno.New(
os.Getenv("LIVQENO_API_KEY"),
livqeno.WithBaseURL(os.Getenv("LIVQENO_API_URL")), // https://api.livqeno.com
)
http.HandleFunc("/livqeno/token", func(w http.ResponseWriter, r *http.Request) {
grant, err := client.Tokens.Create(r.Context(), livqeno.CreateTokenParams{
Room: r.URL.Query().Get("room"),
Identity: currentUserID(r),
Permissions: &livqeno.TokenPermissions{
Join: livqeno.Bool(true), Subscribe: livqeno.Bool(true), Publish: livqeno.Bool(true),
PublishAudio: livqeno.Bool(true), PublishVideo: livqeno.Bool(true),
},
ExpiresIn: 3600,
})
if err != nil {
http.Error(w, "could not create token", http.StatusBadGateway)
return
}
json.NewEncoder(w).Encode(map[string]any{
"token": grant.Token, "endpoint": grant.Endpoint, "iceServers": grant.IceServers,
})
})
http.ListenAndServe(":3000", nil)
}import com.livqeno.server.*;
import com.sun.net.httpserver.HttpServer;
import java.net.InetSocketAddress;
public class TokenServer {
public static void main(String[] args) throws Exception {
Livqeno livqeno = Livqeno.builder()
.apiKey(System.getenv("LIVQENO_API_KEY"))
.baseUrl(System.getenv("LIVQENO_API_URL")) // https://api.livqeno.com
.build();
HttpServer server = HttpServer.create(new InetSocketAddress(3000), 0);
server.createContext("/livqeno/token", exchange -> {
String room = queryParam(exchange, "room");
IssuedToken grant = livqeno.tokens().create(CreateTokenParams.builder()
.room(room)
.identity(currentUserId(exchange))
.permissions(TokenPermissions.builder()
.join(true).subscribe(true).publish(true)
.publishAudio(true).publishVideo(true).build())
.expiresIn(3600)
.build());
byte[] body = toJson(grant.token(), grant.endpoint(), grant.iceServers());
exchange.getResponseHeaders().add("Content-Type", "application/json");
exchange.sendResponseHeaders(200, body.length);
exchange.getResponseBody().write(body);
exchange.close();
});
server.start();
}
}In Spring Boot, the same livqeno.tokens().create(...) call goes inside a
@PostMapping method. queryParam, currentUserId and toJson are yours to
supply.
<?php
require 'vendor/autoload.php';
$client = new \Livqeno\Client(
apiKey: getenv('LIVQENO_API_KEY'),
baseUrl: getenv('LIVQENO_API_URL'), // https://api.livqeno.com
);
$grant = $client->tokens->create([
'room' => $_GET['room'],
'identity' => currentUserId(),
'permissions' => ['join' => true, 'subscribe' => true, 'publish' => true, 'publishAudio' => true, 'publishVideo' => true],
'expiresIn' => 3600,
]);
header('Content-Type: application/json');
echo json_encode([
'token' => $grant->token,
'endpoint' => $grant->endpoint,
'iceServers' => $grant->iceServers,
]);There is no Livqeno server SDK for Dart, so a Dart backend uses the
plain HTTP call. With package:shelf and package:http:
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
final apiUrl = Platform.environment['LIVQENO_API_URL']!; // https://api.livqeno.com
final apiKey = Platform.environment['LIVQENO_API_KEY']!;
Future<Response> token(Request request) async {
final room = request.url.queryParameters['room']!;
final res = await http.post(
Uri.parse('$apiUrl/v1/rooms/$room/rtc-tokens'),
headers: {'Authorization': 'Bearer $apiKey', 'Content-Type': 'application/json'},
body: jsonEncode({
'participantIdentity': currentUserId(request),
'permissions': {
'join': true, 'subscribe': true, 'publish': true,
'publishAudio': true, 'publishVideo': true,
},
'ttlSeconds': 3600,
}),
);
return Response(res.statusCode, body: res.body, headers: {'content-type': 'application/json'});
}
void main() => io.serve(token, '0.0.0.0', 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');let grant = try await myBackend.fetchRtcToken(room: "room-1") // GET/POST to your endpoint
try await room.connect(token: grant.token, endpoint: grant.endpoint)val grant = myBackend.fetchRtcToken(roomId) // call to your endpoint
val room = client.connect(grant.token, grant.endpoint, grant.iceServers)const grant = await fetch('/livqeno/token?room=room-1', { method: 'POST' }).then((r) => r.json());
const livqeno = new Livqeno({ token: grant.token, endpoint: grant.endpoint, iceServers: grant.iceServers });Tokens are short-lived. Give the SDK a refreshToken callback that calls this
same endpoint again, so long calls keep going.
Next
- Tokens: permissions, expiry, chat tokens.
- API keys: creating and revoking the key.
- Browser security & CORS: if your web app calls this endpoint from another origin.