late-glass-71191
12/20/2025, 6:39 PMtall-airline-16756
12/20/2025, 6:39 PMtall-airline-16756
12/20/2025, 6:39 PMcall-). Use the lk sip dispatch create flow to register the rule.
2) Start the AgentSession and enable transcription + recording
• When the call connects, start an AgentSession with an STT model, LLM/TT S if needed, and set record: true on session start to capture conversation history.
• Example session config (conceptual):
◦ stt: model (e.g., Deepgram / configured STT)
◦ record: true
3) Capture transcripts and store to DB (real-time and final)
• Listen for session events like user_input_transcribed. When transcript.is_final is true, write the text plus metadata (room name, participant id, timestamps) to your DB.
• You can also write interim transcripts if you need near real-time UI updates.
• For structured outputs (surveys, fields), use a function tool or handler in the agent to write rows to CSV/DB.
4) Store full recordings and session metadata
• With record: true, export/store the recording blob and session timeline (timestamps, participant events) to object storage and save references in your DB.
• Save participant join/leave, start/end times, call SID, and room name for log correlation.
5) Retrieve call logs and transcripts
• Webhooks: enable LiveKit webhooks for events (room_started, room_finished, participant_joined/left). Persist these events to your DB for log history.
• AgentSession events: use emitted transcript events to build full transcripts in your DB.
• CLI/Observability: use lk agent logs and the Agent Observability playback to view session transcripts, recordings, and traces for debugging and audit.
6) Cleanup
• Delete rooms after processing if you want to free resources:
◦ call the room delete API: DeleteRoomRequest(room=<room_name>)
Questions to help me give exact code/config
• Do you want a Node.js or Python example for handling the user_input_transcribed event and writing to your DB?
• Which DB and object storage do you use (Postgres, MySQL, Mongo, S3, GCS)? That affects sample code and schema suggestions.
• Do you need realtime transcript streaming to a UI, or just final transcripts/recordings after the call?
If you tell me language and DB/storage choices, I’ll provide a short code snippet and example event-to-DB schema.
Sources: sip | livekit/livekit_composite | DeepWiki | Agents telephony integration | LiveKit docs | Repeater | LiveKit docs | Survey Calling Agent | LiveKit docs | Agents (Node.js) | livekit/livekit_composite | DeepWikilate-glass-71191
12/20/2025, 6:42 PMtall-airline-16756
12/20/2025, 6:42 PMdatasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model CallSession {
id String @id @default(cuid())
roomName String
callSid String? // optional Twilio Call SID if you map it
startedAt DateTime @default(now())
endedAt DateTime?
recordings String? // URL or storage reference
transcripts Transcript[]
}
model Transcript {
id String @id @default(cuid())
sessionId String
text String
isFinal Boolean
startedAt DateTime @default(now())
finishedAt DateTime?
speaker String?
session CallSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
@@index([sessionId])
}
Run:
1. Set DATABASE_URL env var.
2. npx prisma migrate dev --name init
3. npx prisma generate
2) Server code (Node.js + Express) — handle AgentSession transcript events and store them Install:
• npm install express @prisma/client prisma livekit (or your LiveKit SDK package) dotenv body-parser
Example app.js:
require('dotenv').config();
const express = require('express');
const bodyParser = require('body-parser');
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
const app = express();
app.use(bodyParser.json());
// Endpoint to be called when a new call/session starts (create CallSession)
<http://app.post|app.post>('/session/start', async (req, res) => {
// expected body: { roomName, callSid }
const { roomName, callSid } = req.body;
const session = await prisma.callSession.create({
data: { roomName, callSid },
});
res.json(session);
});
// Endpoint to receive transcript events from your AgentSession handler
<http://app.post|app.post>('/events/transcript', async (req, res) => {
/* expected body:
{
"roomName": "call-123",
"callSid": "CAxxxx", // optional
"transcript": "hello world",
"isFinal": true,
"speaker": "caller",
"startedAt": "2025-01-01T00:00:00Z",
"finishedAt": "2025-01-01T00:00:01Z"
}
*/
const { roomName, callSid, transcript, isFinal, speaker, startedAt, finishedAt } = req.body;
try {
// find session by roomName or callSid
let session = await prisma.callSession.findFirst({
where: { OR: [{ roomName }, { callSid }] },
});
if (!session) {
session = await prisma.callSession.create({
data: { roomName, callSid },
});
}
const t = await prisma.transcript.create({
data: {
sessionId: session.id,
text: transcript,
isFinal: !!isFinal,
speaker,
startedAt: startedAt ? new Date(startedAt) : undefined,
finishedAt: finishedAt ? new Date(finishedAt) : undefined,
},
});
res.json({ ok: true, transcriptId: t.id });
} catch (err) {
console.error(err);
res.status(500).json({ error: 'db_error' });
}
});
// Endpoint to mark session end and optionally store recording URL
<http://app.post|app.post>('/session/end', async (req, res) => {
const { roomName, callSid, endedAt, recordingUrl } = req.body;
const session = await prisma.callSession.findFirst({
where: { OR: [{ roomName }, { callSid }] },
});
if (!session) return res.status(404).json({ error: 'session_not_found' });
await prisma.callSession.update({
where: { id: session.id },
data: { endedAt: endedAt ? new Date(endedAt) : new Date(), recordings: recordingUrl },
});
res.json({ ok: true });
});
// Simple logs endpoint: get sessions with transcripts
app.get('/logs', async (req, res) => {
const sessions = await prisma.callSession.findMany({
include: { transcripts: true },
orderBy: { startedAt: 'desc' },
take: 50,
});
res.json(sessions);
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`listening ${PORT}`));
3) How to wire this with LiveKit agent events
• In your AgentSession code, listen for user_input_transcribed (or equivalent event) and POST to /events/transcript with roomName, transcript text, isFinal, timestamps, and speaker.
• On session start, POST /session/start with roomName and optional Twilio Call SID.
• On session end, POST /session/end with recordingUrl (if you exported recording) and endedAt.
4) Recording storage and transcripts
• Enable record: true on session start to capture full audio/metadata, then export recordings to object storage (S3/GCS) and save the URL into CallSession.recordings.
• Use webhook events (room_started/room_finished/participant_joined/left) to populate logs and timestamps.
5) Querying logs/transcripts
• Use the /logs endpoint above or query Prisma directly:
◦ Get transcripts for a session: prisma.transcript.findMany({ where: { sessionId } })
◦ Search text: prisma.transcript.findMany({ where: { text: { contains: "keyword" } } })
If you want, I can:
• Provide the small AgentSession event handler code snippet that posts transcripts to /events/transcript (Node.js).
• Show an example of exporting recordings and storing to S3 + saving the URL in DB.
Which one should I add next?