Open in
Use ThunderPhone with OpenAI Realtime clients
Point an OpenAI Realtime-compatible server client at ThunderPhone, using either a saved agent or inline session configuration.
ThunderPhone implements a focused subset of the OpenAI Realtime event model. An existing server-side client can keep its WebSocket, audio, session, and response event flow while using a ThunderPhone voice agent.
Before you connect
You need a ThunderPhone organization secret API key and a server runtime that can open WebSockets. Do not connect from browser code or expose the key to a browser. To use a saved agent, deploy it first and copy its numeric agent ID.
Connect to:
wss://api.thunderphone.com/v1/realtimeAuthenticate the WebSocket upgrade from your server:
Authorization: Bearer sk_live_YOUR_API_KEYQuery-string authentication exists for clients that cannot set handshake headers, but URLs are easier to leak into logs.
Choose who owns the configuration
| Mode | Connect with | Configuration source |
|---|---|---|
| Saved agent | ?agent_id=12 | Deployed prompt, voice, product, languages, knowledge, and compatible server-run tools |
| Inline | No agent_id | The first accepted session.update from your client |
Saved agent
Connect with the deployed agent ID:
wss://api.thunderphone.com/v1/realtime?agent_id=12The agent starts while the socket connects. Its greeting remains available,
but Realtime disables its spoken silence check-ins and omits transfer_call
and send_keypad_input. Other compatible tools run on ThunderPhone. Do not
send inline instructions or client-executed tools for this mode.
Set wire audio before the agent starts with input_audio_format,
output_audio_format, input_rate, and output_rate query parameters. You
cannot change the saved configuration or audio formats after connection.
Inline session
Without agent_id, wait for session.created, then send session.update:
{
"type": "session.update",
"session": {
"type": "realtime",
"instructions": "Answer questions clearly and keep responses brief.",
"audio": {
"input": {
"format": { "type": "audio/pcm", "rate": 24000 }
},
"output": {
"format": { "type": "audio/pcm", "rate": 24000 },
"voice": "olivia"
}
},
"config": {
"product": "bolt"
}
}
}The first accepted update provisions the call. session.updated means the
session is live. Instructions, voice, product, tools, and audio formats cannot
change after that point.
Inline sessions have no automatic greeting or spoken silence check-ins. To
make the agent speak first, append a system or user message and send
response.create. A completely idle session still ends at the platform's
silent-call limit, 600 seconds by default.
Use the official OpenAI SDK
Pass a WebSocket base URL ending at /v1; the SDK appends /realtime. The
model value is a compatibility name and does not select the ThunderPhone
product. Choose the product in the session configuration or the saved agent.
This connection check creates an inline Bolt session, prints events until the
first session.updated, and closes. Use the minimal Python
client to stream audio.
import asyncio
import os
from openai import AsyncOpenAI
async def main():
client = AsyncOpenAI(
api_key=os.environ["THUNDERPHONE_API_KEY"],
websocket_base_url="wss://api.thunderphone.com/v1",
)
async with client.realtime.connect(
model="thunderphone-realtime"
) as connection:
await connection.session.update(session={
"type": "realtime",
"instructions": "Listen to the caller and help them complete the call.",
"config": {"product": "bolt"},
})
async for event in connection:
print(event.type)
if event.type == "session.updated":
break
if __name__ == "__main__":
asyncio.run(main())Keep your existing handling for input-audio append, response audio deltas, interruptions, function calls, errors, and clean socket closure. Inline custom functions execute in your client; return their results through the Realtime protocol. Saved-agent tools execute on ThunderPhone.
Session lifecycle and failures
One WebSocket represents one call. An invalid agent ID or rejected inline
configuration produces an error event. Wait for session.updated before
treating the session as live. Declare the actual input and output formats and
sample rates: a PCM rate mismatch plays audio too fast or too slowly instead of
producing a validation error.
After the session starts, close the socket cleanly when your application is done. Inline sessions can use client-executed functions. Saved-agent sessions use compatible ThunderPhone-run tools and do not offer transfer or keypad input.
Test the integration
Start with the minimal Python WAV client and a mono PCM16 WAV at its declared rate. Verify:
- The server accepts the configuration and sends
session.updated. - Input produces transcript and response-audio events at the expected speed.
- Interruption and response cancellation stop remaining output audio.
- Inline function results or saved-agent tool results return to the model.
- Invalid input produces an
errorevent your client handles. - Your client closes the socket and the call appears in Call history.
The Realtime WebSocket reference lists the accepted events, audio formats, session fields, and complete examples.
Cost
Realtime calls use the selected product's normal per-minute rate. Enabling live transcript deltas adds a per-minute surcharge for the whole session; see Live transcripts for the rate and Pricing for product rates.
POST /v1/realtime/sessions is a separate managed LiveKit path. It creates a
room and scoped participant token; it is not required for a direct WebSocket
connection.
For framework integrations, see Use ThunderPhone from Pipecat and Use ThunderPhone from LiveKit Agents.