polyai/dialog-rsn-1-skill/dialog-rsn-1
Claude Code and agent skill by polyai/dialog-rsn-1-skill
- community
Checking that the SKILL.md file is still in the repository.
Build with Dialog-RSN-1, PolyAI's realtime voice model, over its WebSocket API (wss://api.studio.poly.ai/v1/realtime). Use when writing or fixing code that connects to Dialog-RSN-1 or the PolyAI realtime API, or when asked to build a voice agent, speech-to-speech app, phone agent or transcriber with PolyAI. Covers the session, turns, audio formats, the server voice, tool calling, barge-in, errors and how to test.
Install
npx vskill@latest install polyai/dialog-rsn-1-skill/dialog-rsn-1SKILL.md
Building with Dialog-RSN-1
Dialog-RSN-1 is a realtime voice model. One WebSocket is one conversation. You stream the caller's audio in, the server decides when they've finished speaking, and the reply comes back as speech (the server voice) or as text for your own TTS.
The wire format is the OpenAI Realtime API (GA schema). You probably know that protocol well. Don't assume all of it carries over. The rules below are where Dialog-RSN-1 differs, and they are the mistakes agents make. Follow them over anything you remember about OpenAI Realtime.
Before you write code
- Check for a key, without printing it. The code reads
POLY_API_KEYfrom the environment or a.envfile, with the environment winning, and readsDIALOG_REALTIME_URLthe same way so the key and host always come from one place. Never ask the user to paste the key into the chat, and never put its value in your context: noecho $POLY_API_KEY,env,printenvorcat .env. To see whether it's set, run exactly this, which prints where the key is and never the key:
``bash if [ -n "$POLY_API_KEY" ]; then echo "set in environment"; elif grep -qs '^ *POLY_API_KEY=.' .env; then echo "set in .env"; else echo "missing"; fi ` If it's missing, send them to the agentic setup prompt at https://docs.poly.ai/adk/get-started/agentic-setup.md (it runs poly apikey --region studio`), or to studio.poly.ai, API Keys tab.
- Prove the key and host work before building anything. Do this in every new project, even when the key is set and the task looks simple, and tell the user the result:
``bash uv run <this skill's directory>/scripts/check.py ` It runs a spoken turn and a tool turn, and exits non-zero with the reason if anything fails. It says where it found the key, never the key itself. It needs uv: if uv is missing, install it (curl -LsSf https://astral.sh/uv/install.sh | sh, or brew install uv) instead of falling back to pip. If the user doesn't want uv, use a virtual environment: python3 -m venv .venv && .venv/bin/pip install 'websockets>=14' && .venv/bin/python <this skill's directory>/scripts/check.py. Never pip install` into the system Python. If it fails on a 401, read the Troubleshooting table below before you change any code.
Rules
Connect
- For new code, use raw WebSockets:
websockets>=14in Python (additional_headers=, not the pre-14extra_headers=),wsin Node. Don't reach for theopenaiSDK or a framework unless the user asks for one. - Python dependencies go in the project, never the system Python:
uv initthenuv add websockets, or a project.venvif the user doesn't useuv. - Porting an existing OpenAI Realtime app: keep its SDK and change only what Dialog-RSN-1 needs. Read
references/porting.mdfirst. - LiveKit Agents: use LiveKit's OpenAI realtime plugin with Dialog-RSN-1's URL and key, and
modalities=["text"]when you pass a TTS. Readreferences/livekit.md. - URL:
wss://api.studio.poly.ai/v1/realtime. Read an override fromDIALOG_REALTIME_URL(the fullwss://.../v1/realtimeURL). Enterprise keys fromstudio.us.poly.aionly work onwss://api.us.poly.ai/v1/realtime. - Auth:
X-API-KEY: <key>orAuthorization: Bearer <key>. No query-string key. - In Node, also set a
User-Agentheader.wssends none by default, and the gateway has refused handshakes without one with403in the past. - Browsers can't set WebSocket headers. A browser app always gets a small server-side relay that holds the key. Never put the key in browser code.
Session
- The first frame is
session.created. Then sendsession.update. The answer issession.updatedor anerror. A refused update applies nothing, so check which one came back. - Output is audio by default: the server speaks the reply. Audio arrives as
response.output_audio.delta(base64 PCM16 mono, 24 kHz) and the words asresponse.output_audio_transcript.delta. - For text, set
output_modalities: ["text"]and readresponse.output_text.delta. Do this only when the user wants their own TTS or a text UI.["text", "audio"]is refused. - Voice:
audio.output.voicetakes a Cartesia voice id. Usef786b574-daa5-4673-aa0c-cbe3e8534c02(Katie) unless the user picks another. OpenAI voice names (alloy,marin) are ignored. A made-up id makes every response fail withaudio_synthesis_failed. - Refused with an
error(and the whole update dropped):tool_choiceother than"auto", tools whose type isn't"function"(no MCP tools),idle_timeout_ms,interrupt_response: false,prompt,reasoning, and any unknown key not prefixedpoly_. - Accepted and ignored, so don't rely on them: VAD timing (
threshold,silence_duration_ms,prefix_padding_ms,eagerness),semantic_vad,noise_reduction,audio.output.speed,parallel_tool_calls.
Turns
- A text turn is two events:
conversation.item.create(the message), thenresponse.create. Read deltas untilresponse.done, then checkresponse.statusis"completed". - One response at a time. Wait for
response.donebefore the nextresponse.create, or sendresponse.cancelfirst. - Handle every
errorevent: logerror.code,error.paramanderror.message. Most errors leave the socket open, so a program that ignores them hangs or exits silently. Branch oncode, never onmessage.
Audio in
- Stream caller audio with
input_audio_buffer.append(base64, JSON text frames, about 20 ms per frame). Binary frames are refused. Audio insideconversation.item.createis refused. - Input defaults to PCM16 at 24 kHz. Always set the rate you actually send in
audio.input.format:{"type": "audio/pcm", "rate": 16000}. A wrong rate gets no error, just bad recognition or no reply. Telephony:{"type": "audio/pcmu"}or{"type": "audio/pcma"}(8 kHz G.711). - Keep streaming during silence. The server finds the end of the turn from the audio. Don't send
input_audio_buffer.commitand don't run your own VAD; with the defaultserver_vadthe reply starts on its own. - Push-to-talk is
audio.input.turn_detection: null: append while the button is held, theninput_audio_buffer.commitandresponse.create. Only use it if the user asks for push-to-talk. - What the caller said arrives as
conversation.item.input_audio_transcription.completed, during the reply. There are no live partial transcripts while they speak.
Barge-in
- When
input_audio_buffer.speech_startedarrives, stop local playback at once and drop any queued audio. The server cancels the reply (response.donewith status"cancelled"). - With the server voice, the server tracks what was played. Play audio as it arrives; don't buffer seconds ahead.
- With your own TTS (
["text"]), send playback reports (poly.output_audio.started,.delta,.stopped) so history holds only the words the caller heard. Seereferences/protocol.md.
Tools
- Function tools only, in
session.tools. A call arrives asresponse.function_call_arguments.done(name,arguments,call_id) and that response ends. - Answer with a
conversation.item.createholding{"type": "function_call_output", "call_id": ..., "output": "<JSON string>"}, then send `response.create`. Without the secondresponse.createthe model goes silent. - A response is text or one tool call, never both, so the caller hears silence while a tool runs. For a slow tool, add a required string parameter marked
"poly_spoken": true. The model fills in a short line that goes out before the tool runs: spoken by the server on an audio session, or asresponse.output_text.deltafor your TTS on a text session. Details inreferences/porting.md.
Prompting
- The
instructionsare for speech: short replies, no markdown or lists, numbers and times written the way they're said. See https://docs.poly.ai/dialog-rsn-1/prompt-guide.md. - The model says it doesn't know rather than invent facts. Put the facts the agent needs (hours, prices, policies) in
instructionsorpoly_knowledge, or it answers most questions with "I don't have information about that". If the user gave no facts, write a few plausible ones and tell them where to change them.
Make your work testable
- Give every program a headless mode, such as
--say "Is Friday free?", that sends one text turn, prints the reply, saves any audio toreply.wav, and exits non-zero on anerrorevent or aresponse.donethat isn'tcompleted. - For audio input without a mic, add
--wav caller.wavto stream a file in real time and then keep streaming silence until the reply arrives, so the server can find the end of the turn. Make a clip withcheck.py --speak "Is Friday free?" --out caller.wav, which says exactly those words in the server voice. - For a browser page, test the page in a headless browser with the clip as its microphone, not only the relay. See
references/audio.md. - After every change, run
check.pyand the headless mode. Don't tell the user it works until both pass. Then tell them what to try by voice.
Troubleshooting
| Symptom | Cause | Fix | |
|---|---|---|---|
401 on connect | Wrong key, or a key from a different Studio than the host | Key from studio.poly.ai on api.studio.poly.ai; key from studio.us.poly.ai on api.us.poly.ai | |
401 from the code but check.py passes | Key and host read from different places (shell profile vs .env) | Resolve both the same way as check.py, which prints where each came from | |
402 on connect | No balance on the account | The user adds funds at studio.poly.ai | |
429 with session_limit_reached | Too many open sessions | Close a session; there's no Retry-After | |
TypeError on additional_headers | websockets older than 14 | websockets>=14 | |
| Empty reply in a text UI | Output is audio by default | Set ["text"], or read the audio transcript deltas | |
| Reply audio sounds fast or slow | WAV or player set to the wrong rate | Output is 24 kHz mono PCM16 | |
| Audio streamed, no reply | Wrong or missing input rate, or audio stopped after the caller finished | Set audio.input.format.rate; keep streaming silence | |
| Silent after a tool call | No response.create after the function_call_output | Send it | |
unsupported_parameter or unknown_parameter | A field this server refuses | Remove the field named in error.param | |
audio_synthesis_failed | Voice id isn't a real Cartesia voice | Use a real id, or leave the voice unset | |
| Model talks over the caller | Playback not stopped on speech_started | Stop and flush playback on that event |
Where to read more
Fetch Markdown pages directly (append .md). The index of every page is https://docs.poly.ai/llms.txt. The docs are also an MCP server at https://docs.poly.ai/mcp.
references/protocol.md: event sequences, playback reports, knowledge and language extensions, error and close codes.references/audio.md: mic capture and playback in Python, Node and the browser, the relay, telephony, and testing without a mic.references/porting.md: porting an OpenAI Realtime app, features with no match, and spoken lines for slow tools.references/livekit.md: LiveKit Agents on LiveKit's OpenAI realtime plugin.- Full reference: https://docs.poly.ai/api-reference/dialog-rsn-1/reference.md
- Limits and pricing: https://docs.poly.ai/api-reference/dialog-rsn-1/limits.md
- Working examples (all raw WebSockets): https://github.com/polyai/dialog-rsn-1-python-from-scratch and https://github.com/polyai/dialog-rsn-1-javascript-browser
Security
- Security scanPassed, 90 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/dialog-rsn-1/SKILL.md
Popularity
- 7-day trend score
- 0
Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/polyai/dialog-rsn-1-skill/dialog-rsn-1)