i have telephony setup for my voice agent using tw...
# ask-ai
l
i have telephony setup for my voice agent using twilio. how can i capture incoming calls and store in DB . also how can i get call logs or transcripts for call session ?
t
Thanks for reaching out, Anirban. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I’m responding to your question about capturing incoming Twilio calls, storing them in a DB, and getting call logs/transcripts. High-level plan (what you can do) • Route incoming calls into LiveKit via a Twilio inbound trunk + dispatch rule so each call opens a unique LiveKit room. • Start an AgentSession for the call with STT enabled to get live transcripts and enable recording to capture full audio/metadata. • Persist transcripts, metadata, and/or recordings to your DB or object store as events arrive or when the session finishes. • Use webhooks and AgentSession events plus LiveKit CLI/observability tools for call logs, playback, and audit data. Concrete steps 1) Route incoming Twilio calls to LiveKit • Create an inbound trunk and associate your phone numbers. • Create a dispatch rule that maps an incoming call to a LiveKit room (e.g., prefix rooms with
call-
). 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 | DeepWiki
l
show code example in nodejs . prisma + postgres
t
Thanks — I’ll show a concise Node.js example using Prisma + PostgreSQL that captures transcripts from a LiveKit AgentSession event and stores call sessions/transcripts. This example assumes Twilio routes calls into LiveKit rooms and your server receives AgentSession events (e.g., via the agent code or webhook handler). 1) Prisma schema (save as prisma/schema.prisma)
Copy code
datasource 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:
Copy code
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?