{"intent":"voice-ai-agent","title":"Build a Voice AI Agent","description":"Create a real-time voice AI agent using Telnyx Call AI with WebSocket audio streaming. Handles inbound calls, streams audio to your AI model, and plays responses back in real time.","category":"ai","difficulty":"intermediate","time_to_run":"5 minutes","stack":["node.js","typescript","telnyx-sdk","websocket"],"commands":[{"step":"Clone and install","command":"mkdir voice-ai-agent && cd voice-ai-agent && cp -r ../starter/* . && npm install","expected_output":"added 15 packages in 3s"},{"step":"Configure environment","command":"cp .env.example .env && edit .env with your API key and phone number","expected_output":".env file created"},{"step":"Run the agent","command":"npx tsx index.ts","expected_output":"Voice AI Agent listening on ws://localhost:8080"}],"prerequisites":["Telnyx account with Call AI enabled","Node.js 24+","A Telnyx phone number configured for voice","An AI model endpoint (e.g., Telnyx Inference)"],"files":{"index.ts":"import { Telnyx } from \"telnyx\";\nimport { createServer } from \"http\";\nimport { WebSocketServer, WebSocket } from \"ws\";\n\n// Load environment variables\nconst TELNYX_API_KEY = process.env.TELNYX_API_KEY;\nconst TELNYX_NUMBER = process.env.TELNYX_NUMBER;\nconst AI_MODEL_URL = process.env.AI_MODEL_URL || \"https://api.telnyx.com/v2/ai/chat/completions\";\nconst PORT = parseInt(process.env.PORT || \"8080\", 10);\n\nif (!TELNYX_API_KEY || !TELNYX_NUMBER) {\n  console.error(\"Missing required environment variables. Check .env file.\");\n  process.exit(1);\n}\n\nconst telnyx = new Telnyx(TELNYX_API_KEY);\n\n// Track active calls\nconst activeCalls = new Map<string, { ws: WebSocket; callControlId: string }>();\n\n// Health check endpoint\nconst server = createServer((req, res) => {\n  if (req.url === \"/health\") {\n    res.writeHead(200, { \"Content-Type\": \"application/json\" });\n    res.end(JSON.stringify({ status: \"ok\", activeCalls: activeCalls.size }));\n  } else {\n    res.writeHead(404);\n    res.end();\n  }\n});\n\n// WebSocket for real-time audio streaming\nconst wss = new WebSocketServer({ server, path: \"/audio\" });\n\nwss.on(\"connection\", (ws) => {\n  console.log(\"WebSocket client connected\");\n\n  ws.on(\"message\", async (data) => {\n    try {\n      const message = JSON.parse(data.toString());\n\n      if (message.event === \"call_started\") {\n        console.log(`Call started: ${message.call_control_id}`);\n        activeCalls.set(message.call_control_id, { ws, callControlId: message.call_control_id });\n      }\n\n      if (message.event === \"audio\") {\n        // Stream audio to AI model for processing\n        const aiResponse = await processAudio(message.audio, message.call_control_id);\n        // Send AI response back as audio\n        ws.send(JSON.stringify({ event: \"audio_response\", audio: aiResponse, call_control_id: message.call_control_id }));\n      }\n    } catch (err) {\n      console.error(\"Error processing WebSocket message:\", err);\n    }\n  });\n\n  ws.on(\"close\", () => {\n    console.log(\"WebSocket client disconnected\");\n  });\n});\n\n// Process audio through AI model\nasync function processAudio(audioBase64: string, callControlId: string): Promise<string> {\n  try {\n    const response = await fetch(AI_MODEL_URL, {\n      method: \"POST\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n        Authorization: `Bearer ${TELNYX_API_KEY}`,\n      },\n      body: JSON.stringify({\n        model: \"moonshotai/kimi-k2-5\",\n        messages: [\n          { role: \"system\", content: \"You are a helpful voice assistant. Keep responses concise for real-time conversation.\" },\n          { role: \"user\", content: `[Audio input received from call ${callControlId}]` },\n        ],\n        stream: false,\n      }),\n    });\n\n    if (!response.ok) {\n      throw new Error(`AI model returned ${response.status}`);\n    }\n\n    const result = await response.json();\n    return result.choices?.[0]?.message?.content || \"I'm sorry, I couldn't process that.\";\n  } catch (error) {\n    console.error(\"AI processing error:\", error);\n    return \"I'm sorry, there was an error processing your request.\";\n  }\n}\n\n// Webhook handler for Telnyx call control events\nexport async function handleCallEvent(event: { data: { event_type: string; payload: { call_control_id: string; [key: string]: unknown } } }) {\n  const { event_type, payload } = event.data;\n\n  switch (event_type) {\n    case \"call.initiated\": {\n      console.log(`Incoming call to ${payload.to}`);\n      try {\n        await telnyx.calls.answer(payload.call_control_id);\n        console.log(`Call answered: ${payload.call_control_id}`);\n      } catch (err) {\n        console.error(\"Error answering call:\", err);\n      }\n      break;\n    }\n    case \"call.answered\": {\n      console.log(`Call answered: ${payload.call_control_id}`);\n      break;\n    }\n    case \"call.hangup\": {\n      activeCalls.delete(payload.call_control_id);\n      console.log(`Call ended: ${payload.call_control_id}`);\n      break;\n    }\n  }\n}\n\nserver.listen(PORT, () => {\n  console.log(`Voice AI Agent listening on ws://localhost:${PORT}`);\n  console.log(`Health check: http://localhost:${PORT}/health`);\n});\n","package.json":"{\n  \"name\": \"voice-ai-agent\",\n  \"version\": \"1.0.0\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"engines\": {\n    \"node\": \">=24\"\n  },\n  \"scripts\": {\n    \"start\": \"npx tsx index.ts\"\n  },\n  \"dependencies\": {\n    \"telnyx\": \"^2.0.0\",\n    \"ws\": \"^8.16.0\"\n  },\n  \"devDependencies\": {\n    \"@types/ws\": \"^8.5.10\",\n    \"tsx\": \"^4.7.0\"\n  }\n}\n",".env.example":"# Get your API key at https://portal.telnyx.com/\nTELNYX_API_KEY=your_api_key_here\n\n# Your Telnyx phone number (E.164 format)\nTELNYX_NUMBER=+15551234567\n\n# AI model endpoint (default: Telnyx Inference API)\nAI_MODEL_URL=https://api.telnyx.com/v2/ai/chat/completions\n\n# Server port\nPORT=8080\n"},"evaluate_command":"curl -s http://localhost:8080/health | jq .status","telnyx_products":["call-control","call-ai","phone-numbers"],"schema":"telnyx.starter-detail.v1","schema_version":"1.0","canonical_url":"https://telnyx.com/ai/starters/voice-ai-agent.json","bias_disclosure":{"maintainer":"Telnyx","data_sources":["telnyx-product-docs"],"permission":"product-evaluation"}}