crooked-dawn-90821
09/24/2025, 12:09 PMtall-airline-16756
09/24/2025, 12:09 PMtall-airline-16756
09/24/2025, 12:10 PMlivekit-server-sdk in JavaScript! To do this smoothly, you can include the metadata field in the AccessTokenOptions object when creating your AccessToken. This metadata will be tied to the participant and will be accessible when they join the room.
Here’s a simple example to guide you:
const at = new AccessToken(apiKey, apiSecret, {
identity: "user123",
metadata: JSON.stringify({ displayName: "John Doe", role: "admin" }),
});
at.addGrant({
room: "my-room",
roomJoin: true,
canPublish: true,
canSubscribe: true,
});
const token = await at.toJwt();
If you prefer, you can also set the metadata after creating the AccessToken instance by using the .metadata setter like this:
const at = new AccessToken(apiKey, apiSecret, { identity: "user123" });
at.metadata = JSON.stringify({ customData: "value" }); // Set metadata separately
at.addGrant({ room: "my-room", roomJoin: true });
const token = await at.toJwt();
Just a quick tip: make sure the metadata field is a string, and using JSON is a great way to keep your data structured and easy to manage.
Sources: Dispatching agents | LiveKit Docs | src/pages/api/token.ts | Agent dispatch | LiveKit Docs | web/src/app/api/token/route.ts | web/src/app/api/token/route.tscrooked-dawn-90821
09/24/2025, 12:25 PMexport async function POST(req: Request) {
try {
if (LIVEKIT_URL === undefined) {
throw new Error('LIVEKIT_URL is not defined');
}
if (API_KEY === undefined) {
throw new Error('LIVEKIT_API_KEY is not defined');
}
if (API_SECRET === undefined) {
throw new Error('LIVEKIT_API_SECRET is not defined');
}
// Parse agent configuration from request body
const body = await req.json();
const student: Student = body?.student;
// Generate participant token
const participantName = 'user';
const participantIdentity = `voice_assistant_user_${Math.floor(Math.random() * 10_000)}`;
const roomName = `voice_assistant_room_${Math.floor(Math.random() * 10_000)}`;
const participantToken = await createParticipantToken(
{ identity: participantIdentity, name: participantName, metadata: JSON.stringify(student) },
roomName,
);
// Return connection details
const data: ConnectionDetails = {
serverUrl: LIVEKIT_URL,
roomName,
participantToken: participantToken,
participantName,
};
const headers = new Headers({
'Cache-Control': 'no-store',
});
return NextResponse.json(data, { headers });
} catch (error) {
if (error instanceof Error) {
console.error(error);
return new NextResponse(error.message, { status: 500 });
}
}
}
function createParticipantToken(
userInfo: AccessTokenOptions,
roomName: string,
): Promise<string> {
const at = new AccessToken(API_KEY, API_SECRET, {
...userInfo,
ttl: '15m',
});
const grant: VideoGrant = {
room: roomName,
roomJoin: true,
canPublish: true,
canPublishData: true,
canSubscribe: true,
};
at.addGrant(grant);
return at.toJwt();
despite that, I’m still getting an empty metadata field when receiving the request on the agent sidetall-airline-16756
09/24/2025, 12:26 PMmetadata field is clearly included in the AccessTokenOptions object. Here's a quick example:
const participantToken = await createParticipantToken(
{
identity: "user-identity",
name: "User Name",
metadata: JSON.stringify(student),
},
roomName,
);
1. Validate the Token Contents: Try decoding the JWT token you generated to see if the metadata field is actually there. You can use online JWT decoders or libraries like jose to peek inside the token.
2. Check How You Access Metadata on the Client Side: Make sure you're retrieving the metadata correctly. For instance:
console.log(room.localParticipant.metadata);
Or you can listen for updates like this:
room.localParticipant.on('metadataUpdated', () => {
console.log(room.localParticipant.metadata);
});
1. Verify Your API Credentials: It's important to confirm that LIVEKIT_API_KEY and LIVEKIT_API_SECRET are set correctly and passed properly to the AccessToken constructor.
2. Watch Out for Overwritten Metadata: Check that the metadata isn’t accidentally being overwritten with an empty string or undefined during token creation.
3. Consider Optional Permissions: If you want to update metadata after joining, remember to include canUpdateOwnMetadata: true in the VideoGrant.
If you’re still running into trouble, take a close look at the generated token to see whether the issue might be with how the token is created or how it’s handled on the client side. I’m here to help if you need more guidance!
Sources: src/pages/api/token.ts | src/room/Room.ts | packages/livekit-server-sdk/src/AccessToken.ts | app/api/connection-details/route.ts | Authentication | LiveKit Docscrooked-dawn-90821
09/24/2025, 12:32 PMtall-airline-16756
09/24/2025, 12:33 PMcrooked-dawn-90821
09/24/2025, 12:35 PMexport default defineAgent({
prewarm: async (proc: JobProcess) => {
proc.userData.vad = await silero.VAD.load();
},
entry: async (ctx: JobContext) => {
const vad = ctx.proc.userData.vad! as silero.VAD;
const metadata = ctx.job.metadata ? JSON.parse(ctx.job.metadata) : {};
console.log("metadata", metadata);
This is how I access the metadata on the agent sidecrooked-dawn-90821
09/24/2025, 12:36 PM[13:31:23.421] INFO (6367): received job request
version: "0.1.0"
job: {
"id": "AJ_HEqSTwkHTCCh",
"type": "JT_ROOM",
"room": {
"sid": "RM_fJfz7J2BPU3U",
"name": "voice_assistant_room_7502",
"emptyTimeout": 300,
"maxParticipants": 0,
"creationTime": "1758717083",
"turnPassword": "",
"enabledCodecs": [
{
"mime": "video/H264",
"fmtpLine": ""
},
{
"mime": "video/VP8",
"fmtpLine": ""
},
{
"mime": "video/VP9",
"fmtpLine": ""
},
{
"mime": "video/AV1",
"fmtpLine": ""
},
{
"mime": "audio/red",
"fmtpLine": ""
},
{
"mime": "audio/opus",
"fmtpLine": ""
}
],
"metadata": "",
"numParticipants": 0,
"activeRecording": false,
"numPublishers": 0,
"version": {
"unixMicro": "1758717083334102",
"ticks": 0
},
"departureTimeout": 20,
"creationTimeMs": "1758717083326"
},
"namespace": "",
"metadata": "",
"agentName": "",
"state": {
"status": "JS_RUNNING",
"error": "",
"startedAt": "1758717083459674915",
"endedAt": "0",
"updatedAt": "1758717083459674915",
"participantIdentity": "",
"workerId": "AW_DLBKHVR8YfBV",
"agentId": "A_ZJ2ewEkShrfu"
},
"dispatchId": ""
}
resuming: false
agentName: ""
This is the log of the request the agent receivestall-airline-16756
09/24/2025, 12:36 PMlk dispatch create \
--agent-name test-agent \
--room my-room \
--metadata '{"user_id": "12345"}'
1. Check the Metadata in the Job Context: The ctx.job.metadata field should have the metadata string. If it’s coming up empty, it’s worth double-checking that the metadata was indeed sent during dispatch.
2. Look Out for JSON Parsing Issues: The metadata string needs to be valid JSON. If there’s a formatting problem, JSON.parse will throw an error, so it’s good to verify this.
3. Use Logs to Inspect Metadata: Adding some logging can help confirm what’s inside ctx.job.metadata before parsing. For example:
console.log("Raw metadata:", ctx.job.metadata);
If you still find the metadata empty after these checks, it might be related to how the agent is dispatched or how the metadata is passed along. Please feel free to reach out if you’d like me to help you troubleshoot further—I’m here to support you!
Sources: Job lifecycle | LiveKit Docs | Agent dispatch | LiveKit Docs | External data and RAG | LiveKit Docs | Making calls using SIP | LiveKit Docs | Dispatching agents | LiveKit Docstall-airline-16756
09/24/2025, 12:37 PMjob.metadata field isn’t being populated during the agent dispatch process, and I’m here to help you work through this! Let’s go over some important points to get this sorted out:
1. Keep Participant Metadata and Job Metadata Separate: The metadata you set in the participant token is different from the job.metadata field. To make sure job.metadata is filled, it needs to be explicitly included during the agent dispatch.
2. Double-Check Your Dispatch Method: Make sure the agent is explicitly dispatched with the agent_name specified in the dispatch configuration. If dispatch isn’t explicit, job.metadata might not be passed along.
3. Include Metadata When Dispatching: When you dispatch the agent, be sure to add the metadata in the RoomAgentDispatch configuration or the API request. Here’s a quick example:
const at = new AccessToken(apiKey, apiSecret, { identity: "user123" });
at.roomConfig = new RoomConfiguration({
agents: [
new RoomAgentDispatch({
agentName: "test-agent",
metadata: JSON.stringify({ user_id: "12345" }),
}),
],
});
1. Check the Job Context: It’s helpful to log the full job context in the agent’s entrypoint so you can see exactly what data is coming through:
export default defineAgent({
entry: async (ctx: JobContext) => {
console.log("Full job context:", ctx.job);
},
});
If you’re still running into trouble, please make sure the dispatch request includes the metadata field and that the agent isn’t relying on automatic dispatch, which might skip job-specific metadata. I’m here if you want to walk through this together!
Sources: Dispatching agents | LiveKit Docs | Participant attributes and metadata | LiveKit Docs | Agent dispatch | LiveKit Docs | Authentication | LiveKit Docs | Job lifecycle | LiveKit Docscrooked-dawn-90821
09/24/2025, 12:38 PMtall-airline-16756
09/24/2025, 12:39 PM