reply.wav. By the end, you’ll have a one-file script in Python or Node.js you can port to any language with a WebSocket client.
Prism answers the phone for a made-up cafe, and a recorded caller asks when the cafe closes today. Take this path when your backend’s language has no Rime SDK, or you want to handle Prism’s JSON events yourself.
What you’ll need
- A Rime API key. If you don’t have one yet, create one.
- uv for Python, or Node.js 22 or later.
- Your computer or a server to run the script. A browser can’t connect directly, because Prism checks your API key in the
Authorizationheader, which the browserWebSocketAPI can’t set.
Build it step by step
1
Create the project and install a WebSocket client
Any WebSocket client works, as long as it can set headers on the connection request. These commands pin
websockets 17.2 for Python and ws 8.22.0 for Node.js.- Python
- Node.js
Create a project and add the
websockets package:2
Set your API key
The script reads your key from
RIME_API_KEY and sends it as Authorization: Bearer YOUR_API_KEY when it connects. Set it in the terminal where you’ll run the script:- macOS and Linux
- Windows PowerShell
3
Download the sample question
Your caller is a 3-second recording made with Rime text-to-speech, asking, “Hi there! I was just wondering, what time do you close today?” This command saves it to your project folder:You should now have Run the script with
- macOS and Linux
- Windows PowerShell
question.wav, and question.pcm, the same audio without its 44-byte WAV header. That’s the input format Prism accepts: raw 16-bit samples at 16 kHz, mono. To hear the question first, open question.wav in your browser.To ask your own question, record it in English without pausing partway through. Prism ends the turn at a long pause, so a mid-sentence pause splits your question into two turns. Convert it to 16 kHz mono PCM16 with FFmpeg. This command reads recording.mp3 and writes recording.pcm:recording.pcm in place of question.pcm.4
Save the script
The script prints when Prism hears the caller start and stop, prints the reply text, and saves the reply audio to The instructions give Prism one fact you can check by ear: Juniper Cafe closes at 4 p.m. Each message is one JSON object, and audio is base64 in both directions. The script stops when
reply.wav. Save it in your project folder:- Python
- Node.js
ask.py
response.done ends the reply, or after 60 seconds.5
Run it and listen
Run the script with the question file:After a few seconds, you should see output like this:The first two lines come from
- Python
- Node.js
input_audio_buffer.speech_started and input_audio_buffer.speech_stopped. Prism started its reply only after it found the end of the caller’s turn. The Prism: line is the reply text, printed one response.text.delta at a time. The words and audio length change from run to run.Now open reply.wav in an audio player. You should hear Prism say the cafe closes at 4 p.m. If the script doesn’t work, here’s where to look:- The script stops with an error: find its code in Errors and limits. In Python,
KeyError: 'RIME_API_KEY'means the variable isn’t set in your terminal. In Node.js, a missing variable givesUnexpected server response: 401, because the script sendsBearer undefined. - Prism refuses the connection: the HTTP status in the error tells you why. Upgrade refusals lists each status.
- Nothing prints until
No reply within 60 seconds: check that the audio is 16 kHz mono PCM16.
6
Try another voice
The script uses You should see output like this:The closing time is the same, but you’ll hear a higher voice in
aurora, one of four Prism voices, and Choose a voice describes the others: wawona, brando, and astra. Change the voice in the script’s session.update to wawona, and run it on the same question again:- Python
- Node.js
reply.wav. This script sets the voice and instructions once. To change them during a conversation, send another session.update, and the next reply uses the new settings.How it works
The script behaves like a live call, so two things matter:- Order in the first messages: it sends
session.updateas soon as the connection opens. It holds the audio untilsession.updatedarrives, because Prism refuses audio sent before it accepts the first update. - The pace of a microphone: it streams the recording in real time, a 40 ms chunk every 40 ms, and keeps sending silence after the question ends. The silence matters, because Prism finds the end of the caller’s turn by itself.
Prism sends a few other events too, such as
session.created, which comes first, and the caller’s transcript. The script ignores them, and so can a first port.
Next steps
Now that Prism answers a recording, you can build toward a live call:- Stop playback when the caller interrupts, and report how much the caller heard. The WebSocket reference lists the event for each step.
- Let Prism ask your backend for data it doesn’t have, such as Saturday’s opening hours.
- Run the complete example, a Python client that adds a tool call to the same cafe conversation.
- Write instructions for a phone call, starting with rules for speech.

