ThunderPhone 2.0 is live.Self-serve, from 2¢/min.Read the announcement

Connect tools & data

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/realtime

Authenticate the WebSocket upgrade from your server:

Authorization: Bearer sk_live_YOUR_API_KEY

Query-string authentication exists for clients that cannot set handshake headers, but URLs are easier to leak into logs.

Choose who owns the configuration

ModeConnect withConfiguration source
Saved agent?agent_id=12Deployed prompt, voice, product, languages, knowledge, and compatible server-run tools
InlineNo agent_idThe first accepted session.update from your client

Saved agent

Connect with the deployed agent ID:

wss://api.thunderphone.com/v1/realtime?agent_id=12

The 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:

  1. The server accepts the configuration and sends session.updated.
  2. Input produces transcript and response-audio events at the expected speed.
  3. Interruption and response cancellation stop remaining output audio.
  4. Inline function results or saved-agent tool results return to the model.
  5. Invalid input produces an error event your client handles.
  6. 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.