DEV Community

anusha
anusha

Posted on

I Built a Voice AI Assistant That Can Bring a Human Into the Same Call

Most voice AI demos stop at the assistant. A caller talks to an AI, the AI answers, maybe it creates a ticket, and the call ends.

But a lot of real workflows need a human in the loop. A support assistant might need an integrations engineer. A healthcare assistant might need a care team member. A financial services assistant might need a licensed advisor. A sales assistant might need the right account executive.

The interesting part is doing that without making the caller hang up, call a different number, or repeat the same context to another person.

The Telnyx code example is here:

https://github.com/team-telnyx/telnyx-code-examples/tree/main/ai-assistant-multiparticipant-calling-nodejs

It shows a Node.js + Express backend that lets a Telnyx AI Assistant add another participant to the same live AI conversation.

The Flow

The caller dials a Telnyx number. Telnyx sends a call.initiated webhook to the app. The app answers the call with an inline AI Assistant configuration.

The assistant has a few tools:

  • classify_issue
  • record_specialist_consent
  • dial_specialist

The first two are there so the assistant does not immediately dial someone. It should understand the issue and ask permission before adding another participant.

The third tool is the bridge to the backend. When the assistant calls dial_specialist, the backend creates a second call leg with POST /v2/calls.

Then, when the specialist answers, Telnyx sends a call.answered webhook for that second leg. At that point, the backend calls ai_assistant_join and passes two important values:

  • the existing AI conversation_id
  • the specialist's call_control_id

That is the moment the call becomes multiparticipant.

The Part I Care About

The AI is not just transferring the caller. It is coordinating the call.

The caller stays in the original conversation. The assistant already has the issue context. When the specialist joins, the assistant can summarize what is happening and then get out of the way unless it needs to clarify, create a ticket, or close the loop.

That makes the experience feel much closer to a real support handoff:

caller: I am seeing webhook delivery failures
ai: I can bring in an integrations specialist on this same call. Does that work?
caller: yes
backend: dials specialist
specialist: hi, this is Anusha from integrations
ai: Anusha, the caller is seeing intermittent 401 responses on webhook deliveries...
Enter fullscreen mode Exit fullscreen mode

Why Use a Backend Tool Instead of Only Portal Setup?

The Telnyx Portal supports assistant tools, and the docs cover multi-participant calls. This example focuses on the backend-driven version because developers often need control over:

  • which participant to dial
  • when consent was recorded
  • how the second call leg is tracked
  • what metadata is stored
  • what happens if the participant does not answer
  • how the handoff is audited

The backend tool makes that logic explicit.

Run It Locally

git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/ai-assistant-multiparticipant-calling-nodejs
cp .env.example .env
npm install
npm test
npm start
Enter fullscreen mode Exit fullscreen mode

Expose it:

ngrok http 8787
Enter fullscreen mode Exit fullscreen mode

Point your Programmable Voice / Voice API application webhook to:

https://<your-ngrok-domain>/webhooks/voice
Enter fullscreen mode Exit fullscreen mode

Then call your Telnyx number and ask for a support case that needs a specialist.

What To Watch In The Code

The main file is server.js.

The assistant instructions and tool definitions are inline, so someone can recreate the demo from the folder without copying a prebuilt Portal assistant. The dial_specialist endpoint handles the tool call. The call.answered handler joins the specialist into the conversation.

The key call is:

await fetch(`https://api.telnyx.com/v2/calls/${id}/actions/ai_assistant_join`, {
  method: "POST",
  headers: telnyxHeaders(),
  body: JSON.stringify({
    conversation_id: session.conversationId,
    participant: {
      id: specialistCallControlId,
      role: "user",
      name: "support specialist",
      on_hangup: "continue_conversation",
    },
  }),
});
Enter fullscreen mode Exit fullscreen mode

That is the handoff.

Production Notes

The code sample keeps state in memory because it is meant to be easy to run. For production, I would store session state in Redis or Postgres, verify webhook signatures, add retries around outbound dialing, log consent, and add assistant behavior for when humans are talking directly to each other.

The pattern is still the same:

AI handles the front door
AI calls a backend tool
backend dials the next participant
Telnyx joins them into the same AI conversation
Enter fullscreen mode Exit fullscreen mode

Resources

Top comments (0)