Events
Patter emits events at key moments during a call. Register callbacks in serve() to react to call starts, ends, transcripts, and messages.
Callback Overview
| Callback | Trigger | Available In |
|---|
onCallStart | When a call connects | serve() |
onCallEnd | When a call disconnects | serve() |
onTranscript | Each time a transcript segment arrives | serve() |
onMessage | User transcript ready for response (pipeline mode) | serve() |
onCallStart
Fires when a new call connects. Use it for logging, CRM lookups, or initializing per-call state.
await phone.serve({
agent,
onCallStart: async (data) => {
const callId = data.call_id as string;
const caller = data.caller as string;
const callee = data.callee as string;
const direction = data.direction as string;
console.log(`Call ${callId} from ${caller} to ${callee} (${direction})`);
},
});
Payload
{
call_id: string; // Unique call identifier
caller: string; // Caller phone number (E.164)
callee: string; // Callee phone number (E.164)
direction: string; // "inbound" or "outbound"
}
onCallEnd
Fires when a call disconnects. Includes the full conversation transcript.
await phone.serve({
agent,
onCallEnd: async (data) => {
const callId = data.call_id as string;
const transcript = data.transcript as Array<{
role: string;
text: string;
timestamp: number;
}>;
console.log(`Call ${callId} ended with ${transcript.length} messages`);
// Save transcript to database
await saveTranscript(callId, transcript);
},
});
Payload
{
call_id: string;
transcript: Array<{
role: string; // "user" or "assistant"
text: string; // Transcript text
timestamp: number; // Unix timestamp (ms)
}>;
}
onCallEnd is guaranteed to fire exactly once per call, even if the WebSocket disconnects unexpectedly.
onTranscript
Fires each time a transcript segment is generated during the call. Useful for real-time dashboards, live monitoring, or logging.
await phone.serve({
agent,
onTranscript: async (data) => {
const role = data.role as string;
const text = data.text as string;
const callId = data.call_id as string;
console.log(`[${callId}] ${role}: ${text}`);
},
});
Payload
{
role: string; // "user" or "assistant"
text: string; // Transcript segment
call_id: string; // Call identifier
}
onMessage (Pipeline Mode)
In pipeline mode, onMessage is the core callback. It receives the user’s transcript and conversation history, and must return the text to be spoken by the TTS engine.
await phone.serve({
agent,
onMessage: async (data) => {
const text = data.text as string;
const callId = data.call_id as string;
const caller = data.caller as string;
const history = data.history as Array<{
role: string;
text: string;
timestamp: number;
}>;
// Call your own LLM or business logic
const response = await generateResponse(text, history);
return response; // This text will be spoken via TTS
},
});
Payload
{
text: string; // User's transcript
call_id: string; // Call identifier
caller: string; // Caller phone number
history: Array<{ // Conversation history (max 200 entries)
role: string;
text: string;
timestamp: number;
}>;
}
Return Value
The function must return a string that will be converted to speech. If you return an empty string, nothing is spoken.
Type Signatures
type CallEventHandler = (data: Record<string, unknown>) => Promise<void>;
type PipelineMessageHandler = (data: Record<string, unknown>) => Promise<string>;
Combining Callbacks
You can use all callbacks together:
await phone.serve({
agent,
port: 8000,
onCallStart: async (data) => {
console.log("Call started:", data.call_id);
},
onCallEnd: async (data) => {
console.log("Call ended:", data.call_id);
await saveTranscript(data);
},
onTranscript: async (data) => {
await broadcastToWebSocket(data);
},
});