fierce-zoo-69908
07/22/2025, 10:30 AMimport { FastifyPluginAsync } from 'fastify';
import { createDatabaseClient } from '@pf-workflo/core';
import { inboundMappings } from '@pf-workflo/core/src/services/db/schema/telephony';
import { eq } from 'drizzle-orm';
import { WorkflowType } from '@pf-workflo/core/dist/types/index';
import { createWorkflowEngine } from '../engine';
const webhookSecret = process.env.LIVEKIT_WEBHOOK_SECRET; // Set in env
export const livekitWebhookRoutes: FastifyPluginAsync = async (fastify) => {
const workflowEngine = createWorkflowEngine({ signingKey: process.env.INNGEST_SIGNING_KEY || '', eventKey: process.env.INNGEST_EVENT_KEY || '' });
<http://fastify.post|fastify.post>('/api/livekit-webhook', async (request, reply) => {
const sig = request.headers['lk-signature'] as string;
// Verify signature logic here
const event = request.body as any;
if (event.event === 'participant_joined' && event.participant.attributes?.sip) {
const dialedNumber = event.participant.attributes.sip.dialedNumber;
const caller = event.participant.attributes.sip.phoneNumber;
const sipIdentity = event.participant.identity;
const { db } = await createDatabaseClient();
const mapping = await db.select().from(inboundMappings).where(eq(inboundMappings.phoneNumber, dialedNumber)).limit(1);
if (mapping.length > 0) {
const { tenantId, workflowId } = mapping[0];
await workflowEngine.startWorkflow(tenantId, workflowId, {
isInbound: true,
roomName: event.room,
sipIdentity,
callerPhoneNumber: caller,
dialedNumber
}, WorkflowType.SERVER);
}
}
reply.status(200).send({});
});
};
and then inside startWorkflow
this.config.roomName = inputs.roomName as string;
agentMetadata.participantIdentity = inputs.sipIdentity as string;
agentMetadata.isInbound = true;
await this.createExplicitDispatch(agentMetadata);
return { status: ExecutionStatus.PENDING, output: {
roomName: this.config.roomName,
agentName: agentName ?? this.config.agentName,
participantToken: await this.createTokenWithAgentDispatch(participantIdentity, agentMetadata),
participantIdentity,
phoneNumber,
executionId: this.context.executionId
}, resumeExecutionId: resumeExecutionId, triggeredOutputPorts: [], creditsUsed: 0 };
workers in my case don't have context and nuances of each specific workflow.
Alternative solution I am considering is having an livekit worker, who will call my server to get the context before creating an agent, but this is not ideal , as I am losing control from server side.tall-airline-16756
07/22/2025, 10:30 AMtall-airline-16756
07/22/2025, 10:31 AMAgentDispatchClient (Node.js) or AgentDispatchService (Python) to create a dispatch for an agent, including custom metadata like phone number and workflow context. This can be triggered directly from your webhook handler.
• Server-Side Token Generation: You can generate participant tokens server-side and include agent dispatch instructions and metadata in the token payload.
• Custom Metadata: The metadata field accepts JSON, so you can pass structured data (e.g., phone number, workflow ID, tenant ID) to the agent.
• Webhook Integration: Your webhook can analyze the incoming event, determine the correct agent and context, and then use the dispatch API to add the agent to the room with all necessary metadata.
• Room Creation: If the room does not exist, it will be created automatically during dispatch.
• Agent Requirements: The agent must be registered with an agentName in your worker configuration to be eligible for dispatch.
This approach gives you full server-side control over agent selection and context, without requiring the worker to fetch context from your server. The agent can access the custom metadata via the JobContext when handling the dispatch.
Example (Node.js):
const agentDispatchClient = new AgentDispatchClient(process.env.LIVEKIT_URL);
await agentDispatchClient.createDispatch(roomName, agentName, {
metadata: JSON.stringify({
phoneNumber: dialedNumber,
context: 'support',
tenantId,
workflowId
})
});
This pattern matches your described workflow and is fully supported by LiveKit's APIs.
Sources: packages/livekit-server-sdk/src/AgentDispatchClient.ts | examples/agent_dispatch.py | client/web/src/app/api/connection-details/route.ts | ivr-nagivator/flask_livekit/extension.py | Agent dispatch | LiveKit Docs