Skip to main content
Point your coding agent at your OpenAI Realtime app and move it to Dialog-RSN-1 in three prompts. The app keeps its SDK, its tools and its spoken replies, so you review a short diff, not a rewrite. What you’ll build:
  1. Your app running on Dialog-RSN-1, on the same SDK or WebSocket code, speaking its replies and calling the same tools.
  2. A test that streams a caller’s spoken question into the app, saves the spoken answer, and shows the server kept every setting the app sends.
  3. A short line the caller hears while a slow tool runs, instead of silence.
Before you start:
  • Your coding agent, set up as in the quickstart’s Set up your agent: the skill installed, POLY_API_KEY saved, and the connection check passing.
  • Your OpenAI Realtime app, in a folder the agent can run. It can use the openai SDK or a raw WebSocket, in Python or JavaScript. Commit it to git first, so you can review the port as a diff.
  • A local text-to-speech command for the test clip in Step 2: say on macOS, or espeak on Linux.
You don’t need your OpenAI key. Nothing in these steps calls OpenAI. This page keeps the server speaking the replies, so your playback code keeps working. The guide’s example goes the other way, to text replies for your own text-to-speech. If you’d rather write the code yourself, follow the OpenAI Realtime migration guide instead.

Step 1: port the app

Prompt
“Keep my SDK” matters. The skill steers new code to raw WebSockets, and you want a port, not a rewrite. What it should build. Dialog-RSN-1 speaks the OpenAI Realtime protocol, so most of the diff is the connection and the session.
  • The connection. On the openai SDK, only the client changes: websocket_base_url="wss://api.studio.poly.ai/v1" (or base_url="https://api.studio.poly.ai/v1") and the key from POLY_API_KEY. The SDK adds /realtime and sends the key as Authorization: Bearer, which Dialog-RSN-1 accepts. On a raw WebSocket, the URL becomes wss://api.studio.poly.ai/v1/realtime, and the key goes in an X-API-KEY or Authorization: Bearer header. In Node, the skill also has the agent set a User-Agent header. Either way the model name does nothing: the server runs dialog-rsn-1 whatever you pass.
  • The session. output_modalities stays ["audio"], so replies still arrive as response.output_audio.delta (24 kHz PCM16) with the words in response.output_audio_transcript.delta. The OpenAI voice name becomes a Cartesia voice id, since names like alloy and marin are ignored. turn_detection ends up as {"type": "server_vad", "create_response": true}.
  • Settings it removes. The ones Dialog-RSN-1 accepts and ignores: model, the turn-detection timing (threshold, prefix_padding_ms, silence_duration_ms, eagerness, semantic_vad), interrupt_response: true, the transcription model, noise_reduction, speed and tracing. The ones it refuses, if your app sends any: see OpenAI features with no match.
  • Errors. A refused session.update applies nothing, so one leftover field also drops your instructions and tools. Expect your coding agent to make the app wait for session.updated, and to stop and print error.code and error.param on an error.
  • What stays. The tool loop: a function_call item, your function_call_output, then response.create. Some agents move those two sends to after response.done. Sending them as soon as the call arrives also works. Caller transcripts still arrive as conversation.item.input_audio_transcription.completed, with no transcription model to set.
Check it. Run the text turn your coding agent ran, and read git diff against its list of changes. For the front desk agent in the guide’s repo, it looks like this:
output
You should hear the reply in the new voice. Agents usually also save it to reply.wav, as the skill asks. If your app makes out-of-band requests, such as a call summary with conversation: "none", check their output too. Agents tend to move them into the call session, where they can come back as another in-character reply instead of the summary. If it goes wrong:

Step 2: test it like a call

Prompt
What it should build. A clip of a caller’s question, made with say or espeak and converted to 24 kHz mono PCM16. The app streams it in real time with input_audio_buffer.append, about 20 ms per frame, then keeps streaming silence until the final reply is done. The server decides when the caller has finished, so the app sends no commit. The reply audio goes to a WAV file. For the settings, your coding agent compares the session your app sends with the one session.updated returns. A refused field comes back as an error. An ignored field is missing from session.updated or has a different value, except audio.input.transcription and parallel_tool_calls: those come back unchanged and still do nothing. Check it. Your coding agent shows what the server heard, the tool call and the reply. Play the saved file: it’s the reply, spoken in the voice you set. The session in effect should show output_modalities: ["audio"], your Cartesia voice id, server_vad with create_response: true, and your instructions and tools as you sent them.
output
The instructions here were written for text, so the reply says “15:00”. The first row below fixes that. If it goes wrong:

Step 3: give slow tools a line to say

An OpenAI Realtime agent often says “let me check” and calls a tool in the same response. Dialog-RSN-1 returns text or one tool call, never both, so the caller hears nothing until the tool returns. On a slow tool, that’s a long silence.
Prompt
What it should build. A new required string parameter on each slow tool, marked "poly_spoken": true, with a description that gives an example line. The model fills it in as part of the call, and the server speaks it while your tool runs.
  • Your function is unchanged. The app takes the spoken argument out before it calls your function. Left in, your function gets an argument it doesn’t expect.
  • The tool starts at once. It runs when response.function_call_arguments.done arrives, not when the response ends. With a spoken line, response.done comes only after the line has been said, so a tool started there loses that time.
  • The result goes back the same way. A function_call_output, then response.create. Agents send them once the response with the line is done.
Quick tools get no line. There, the line delays the answer: it has to finish before the answer can start. The prompt guide has the details. Check it. Your coding agent prints a timeline, in seconds from when the question was sent. The line should start as the tool starts. With a two-second tool, it usually ends before the tool returns:
output
If it goes wrong:

OpenAI features with no match

Some OpenAI Realtime features have no match here. If your app uses one, the port needs more than a settings change. The full list is in the reference and its known limitations.

Troubleshooting

For 401, 402 and other connection errors, see the quickstart’s Troubleshooting.

Where to go next

OpenAI Realtime migration

Every change by hand, and the move to text replies.

Streaming to your TTS

Speak the replies with your own text-to-speech voice.

Prompt guide

Turn-taking, tool calls and replies written for speech.

Reference

Every event, the session object, function calling and error codes.
Last modified on October 21, 2026