early-butcher-66889
12/20/2025, 9:48 AMabc ?
For example, I have a workflow that allows a user to join a call to speak with an AI agent to resolve issues in relation to billing. The workflow should be:
• create a backend endpoint that upon request, creates a room with an agent dispatch + required metadata
• in the same call, use the token endpoints to create a user token for the participant
• return the token to the participant and then use it to join the call via react components?tall-airline-16756
12/20/2025, 9:48 AMtall-airline-16756
12/20/2025, 9:48 AMroomJoin, target room abc, publish/subscribe permissions).
• Return that token to the frontend, and have the React components connect using the token to join the room.
Why this is preferred:
• Secrets stay server-side (token signed by your API key/secret).
• You can set per-user permissions and metadata.
• Agent dispatch can be triggered either by pre-creating the room or by embedding dispatch config in the token.
• Frontend simply uses the token with LiveKit React components to connect.
If you'd like, I can:
• Show a short Node.js example for room creation + token generation tuned to your billing-agent flow, or
• Show the minimal React connect snippet using the returned token.
Which would you prefer?
Sources: Generating tokens | LiveKit docs | Connecting to LiveKit | LiveKit docs | Authentication | LiveKit docs | Agent dispatch | LiveKit docsearly-butcher-66889
12/20/2025, 9:49 AMtall-airline-16756
12/20/2025, 9:49 AM// server.js
import express from "express";
import fetch from "node-fetch"; // or native fetch in newer Node
import { AccessToken, VideoGrant } from "livekit-server-sdk";
const app = express();
app.use(express.json());
// Replace these with your project values
const LIVEKIT_API_KEY = process.env.LIVEKIT_API_KEY;
const LIVEKIT_API_SECRET = process.env.LIVEKIT_API_SECRET;
const LIVEKIT_API_URL = process.env.LIVEKIT_API_URL || "<https://your-project.livekit.cloud>";
// Example endpoint: creates room (optional) and returns a token for given identity & room
<http://app.post|app.post>("/join", async (req, res) => {
const { identity, room = "abc", purpose = "billing-assistance" } = req.body;
if (!identity) return res.status(400).json({ error: "identity required" });
// Optional: pre-create the room and attach metadata/agent dispatch via LiveKit REST API
// If your LiveKit deployment supports Room Service REST, call createRoom here.
// Example minimal create-room call:
try {
await fetch(`${LIVEKIT_API_URL}/v1/rooms`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${createRoomServiceJwt()}`,
},
body: JSON.stringify({
name: room,
metadata: JSON.stringify({ purpose }),
// add additional room configuration if supported by your deployment
}),
});
} catch (e) {
// room may already exist; ignore or handle specific errors as needed
}
// Create access token for the participant
const at = new AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET, {
identity,
ttl: "2h",
});
const grant = new VideoGrant({
roomJoin: true,
room,
// set publish/subscribe as needed
// canPublish: true, // Note: grant fields depend on SDK version
});
at.addGrant(grant);
const token = at.toJwt();
return res.json({ token, room });
});
// Helper: if your LiveKit setup requires a service JWT for room creation calls,
// implement createRoomServiceJwt() to return a signed JWT using your api key/secret.
// If your deployment exposes a server SDK room service, prefer that instead.
function createRoomServiceJwt() {
// Minimal placeholder: for many hosted LiveKit setups you can use API key/secret directly in the RoomServiceClient.
// If your deployment needs a REST JWT, generate it here similarly to AccessToken but with appropriate grants.
return ""; // implement as needed for your environment
}
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Listening on ${PORT}`));
Notes:
• Replace LIVEKIT_API_URL/KEY/SECRET with your values.
• Room pre-creation is optional: you can also embed agent dispatch in room config or rely on token-embedded configuration depending on your deployment. The snippet shows a simple REST attempt; adapt to your environment (RoomServiceClient from server SDK can be used instead of raw REST).
React — fetch token and connect with LiveKit React components
// App.jsx
import React, { useEffect, useState } from "react";
import { Room, connect } from "livekit-client"; // or use @livekit/components-react for prebuilt UI
import { LiveKitRoom } from "@livekit/components-react"; // optional prebuilt components
export default function App() {
const [token, setToken] = useState(null);
const [roomName, setRoomName] = useState("abc");
useEffect(() => {
async function getToken() {
const resp = await fetch("/join", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ identity: "user-123", room: roomName }),
});
const data = await resp.json();
setToken(data.token);
}
getToken();
}, [roomName]);
if (!token) return <div>Connecting...</div>;
// Minimal: use LiveKitRoom component (prebuilt UI) — pass server URL and token
return (
<LiveKitRoom
serverUrl="wss://your-project.livekit.cloud"
token={token}
connect={true}
audio={true}
video={true}
>
{/* Your room UI / participants list / controls */}
<div>Joined room: {roomName}</div>
</LiveKitRoom>
);
// Alternatively, use the low-level client API:
// useEffect(() => {
// let lkRoom = new Room();
// connect("<wss://your-project.livekit.cloud>", token, { autoSubscribe: true })
// .then((remoteRoom) => {
// lkRoom = remoteRoom;
// // attach tracks to DOM, subscribe handlers, etc.
// });
// return () => lkRoom.disconnect();
// }, [token]);
}
If you want, I can:
• Provide a version using the LiveKit server SDK RoomServiceClient for room creation,
• Include agent dispatch config embedded in the token example,
• Or give a full example using @livekit/components-react UI controls.
Which of those would help you next?