Skip to main content
Set your coding agent up once, then build a voice agent in three prompts. At each step you’ll know what the agent should build, how to check it works, and what to say when it goes wrong. What you’ll build:
  1. A coding agent that knows the Dialog-RSN-1 protocol and can test its own work.
  2. A script that sends one message and saves the spoken reply to reply.wav.
  3. A live voice conversation: your microphone in, the model’s voice out, and you can interrupt it.
  4. A tool the model calls mid-conversation.
Before you start:
  • A coding agent that can run shell commands: Claude Code, Codex, Cursor or Gemini CLI.
  • An empty folder to build in.
  • Python 3.10 or later with uv. The skill’s connection check runs on it, even if you build in JavaScript.
  • For Codex, Cursor or Gemini CLI: Node 22.20 or later, which npx skills needs.
The first prompt says “in Python”. Change it to “in JavaScript” if you prefer Node. If you’d rather write the code yourself, follow the quickstart instead.

Set up your agent

Do this once. Every step after it relies on it.

Install the Dialog-RSN-1 skill

Dialog-RSN-1 speaks the OpenAI Realtime protocol. Coding agents know that protocol well, so they assume all of it carries over, and some of it doesn’t. The Dialog-RSN-1 skill gives your agent the differences, the audio patterns, and a script that checks your key against the live API.
In Claude Code, run:
The plugin also connects the PolyAI docs as an MCP server, so Claude Code can search them while it works.This installs it for your user account. To share it with everyone on a project, run the same two commands in a terminal with --scope project:

Get an API key

Your code reads the key from the POLY_API_KEY environment variable. Don’t paste the key into the chat: the agent never needs to see it.
  • From your agent: paste the agentic setup prompt into it. It installs the PolyAI CLI, signs you in with GitHub, and saves POLY_API_KEY to your shell profile. Open a new terminal and restart the agent afterwards.
  • By hand: create a key at studio.poly.ai in the API Keys tab, then put it in a .env file in your folder as POLY_API_KEY=.... The quickstart has the steps.

Check it works

Ask your agent:
Prompt
It runs a spoken turn and a tool turn against the live API. To run it yourself after npx skills add, use uv run .agents/skills/dialog-rsn-1/scripts/check.py. You should see something like this, though the wording and audio length vary:
output
If it fails, the last line says why. A 401 means a wrong key, or a key from a different Studio than the host: see Troubleshooting.

Step 1: hear your first reply

Prompt
The facts in the prompt matter. Dialog-RSN-1 says it doesn’t know rather than make something up, so a concierge with no facts answers most questions with “I don’t have information about that”. What it should build. One WebSocket to wss://api.studio.poly.ai/v1/realtime, with the key in a header. After session.created, a session.update with the instructions. Then the turn, which is always two events: conversation.item.create adds your message, and response.create asks for a reply. The reply streams back as response.output_audio.delta (the speech, 24 kHz PCM16) and response.output_audio_transcript.delta (the words), until response.done. The server speaks the reply itself, so there’s no text-to-speech to set up. The quickstart walks through the same script line by line. Check it. Play reply.wav (afplay reply.wav on macOS, aplay reply.wav on Linux, or open the file). You should hear the concierge offer help with checkout, breakfast or the spa. If it goes wrong:

Step 2: talk to it

Prompt
What it should build. Microphone audio streamed in about 20 ms frames with input_audio_buffer.append, with the sample rate set in audio.input.format. The server decides when you’ve finished speaking, so your code sends no commit and runs no voice detection. Reply audio plays as it arrives. When input_audio_buffer.speech_started arrives while the reply is playing, playback stops at once and queued audio is dropped. The server cancels the rest of the reply. For the test without a microphone, the skill has the agent generate a clip of caller speech and stream it in as if you’d said it. Check it. Run voice_agent.py with headphones on. Ask the concierge something, then interrupt it mid-sentence. It should stop talking and answer what you just said. The mic and speaker need an audio library: in Python, sounddevice (which uses PortAudio); in Node, ffmpeg and ffplay. On macOS, allow your terminal to use the microphone when asked. If it goes wrong:

Step 3: give it a tool

Prompt
What it should build. A function tool in session.tools. When the model calls it, response.function_call_arguments.done carries the name, the arguments and a call_id, and that response ends. The code runs the function, sends the result back as a function_call_output item, then sends response.create again, so the model can speak the answer. Check it. Ask “Is the spa free on Friday?” You should hear the slots read back. If it goes wrong:

Build your own

You now have a voice agent that listens, talks, takes interruptions and calls tools. Some prompts to take it further. Each has a page of prompts in the same shape as this one, and a guide that walks through the code: Voice agents read differently from chat. Ask for short replies, no lists or markdown, and numbers written the way they’re said. Then test by talking, not by reading the transcript. The prompt guide covers what instructions can and can’t do.

Without the skill

Point your agent at the docs instead. Every page is available as Markdown: add .md to its URL. The index of every page is docs.poly.ai/llms.txt. Start the agent with:
Prompt
The docs are also an MCP server at https://docs.poly.ai/mcp, which lets your agent search them:

Troubleshooting

Every error code is in the reference.

Where to go next

Dialog-RSN-1 skill

The skill’s rules, references and connection check.

Reference

Every event, the session object, function calling and error codes.

Guides

Browser, your own TTS, speech to text, LiveKit and more.

Limits and pricing

Concurrency, session length and the connected-minute rate.
Last modified on October 21, 2026