Outbound SIP calling is now available to all organizations OpenAI has made outbound SIP calling available to all organizations on GPT-Live, letting a backend create a session to dial a phone number through a SIP trunking provider such as Twilio without an incoming-call webhook or accept request. Inbound calls arrive as a `live.transport.incoming` webhook carrying `data.type: "sip"` and a `data.session_id`, which the backend accepts or rejects via `POST /v1/live/sessions/{session_id}/accept`; SIP signaling uses TLS and GPT-Live requires SRTP for call audio. OpenAI said existing integrations may still receive the deprecated `live.call.incoming` event, which has no `data.type`, and advised handling both names and keeping the old subscription until legacy deliveries and retries drain. Choose your API to see its connection steps and session events. Connect incoming and outgoing phone calls to GPT-Live through a SIP trunk. Your provider exchanges call audio with OpenAI, while your backend handles session configuration, call controls, and business logic. SIP signaling uses TLS, and GPT-Live requires SRTP for call audio. - Handle an inbound call handle-an-inbound-call : receive an incoming-call webhook, then accept or reject the call. - Place an outbound call place-an-outbound-call : create a session to dial a phone number through your provider. No incoming-call webhook or accept request is needed. Use a sideband connection https://developers.openai.com/api/docs/guides/voice-server-controls?api=live when your backend needs to receive session events or send commands. It attaches to the existing conversation while SIP carries the audio. Assign one handler to each action so that duplicate webhook deliveries or events observed on multiple connections don’t execute tools twice. Handle an inbound call Route incoming phone calls from your SIP provider to your OpenAI project. Your backend receives a webhook and decides whether to accept each call. Configure your phone number and trunk Use a SIP trunking provider, such as Twilio, to connect a phone number to GPT-Live. After purchasing a number from your provider: 1. Open your project in OpenAI settings https://platform.openai.com/settings . Under Webhooks , create a webhook endpoint https://developers.openai.com/api/docs/guides/webhooks subscribed to live.transport.incoming . 2. Find your project ID under General . It starts with proj ; use the same project that owns the webhook. 3. Point your provider’s SIP trunk at sip:$PROJECT ID@sip.api.openai.com;transport=tls . For European data residency, use sip:$PROJECT ID@sip-eu.api.openai.com;transport=tls instead. Confirm that GPT-Live SIP support is enabled for your project and that your provider’s SIP trunk is routed to that project before using this flow. Receive the incoming call When OpenAI receives a call for your project, it sends a live.transport.incoming event to your webhook endpoint. Verify the webhook signature and deduplicate deliveries, then accept or reject the call. The webhook identifies a SIP call with data.type: "sip" and provides data.session id . Use that session ID unchanged for every Live call action. Treat data.sip headers as untrusted caller metadata, not authorization. For example, the webhook body contains: { "object": "event", "id": "evt abc123", "type": "live.transport.incoming", "created at": 1750287018, "data": { "type": "sip", "session id": "live 123", "sip headers": { "name": "From", "value": "sip:+14155550123@sip.example.com" }, { "name": "To", "value": "sip:+18005550100@sip.example.com" }, { "name": "Call-ID", "value": "call-123@example.com" } } } The created at field is a Unix timestamp in seconds. Delivery headers include webhook-id for deduplication, webhook-timestamp for the delivery attempt, and webhook-signature for signature verification. See webhook verification examples https://developers.openai.com/api/docs/guides/webhooks verifying-webhook-signatures for server-side handlers. Existing integrations may still receive the deprecated live.call.incoming event, which has no data.type . During migration, handle both names and retain the old subscription until legacy deliveries and retries have drained. The same pending call can also emit a Realtime webhook; assign one handler to the accept/reject decision rather than accepting through both APIs. Accept or reject the call Apply your application’s authorization and routing rules. To accept the call https://developers.openai.com/api/reference/resources/live/subresources/sessions/methods/accept , send an authenticated POST /v1/live/sessions/{session id}/accept request with a top-level session object: { "session": { "type": "live", "model": "gpt-live-1", "instructions": "You are answering an inbound support call.", "audio": { "output": { "voice": "marin" } }, "delegation": { "type": "client" } } } Use Authorization: Bearer $OPENAI API KEY from your trusted backend for call-control requests. Choose the voice and delegation mode at acceptance. SIP negotiates the audio format, so omit audio.format . The example selects client delegation; your backend must handle delegated work. See Delegation and tools https://developers.openai.com/api/docs/guides/live-delegation for client and Responses configurations. A successful acceptance returns 200 OK with an empty body after session initialization. Handle HTTP errors before treating the call as accepted. To reject the call https://developers.openai.com/api/reference/resources/live/subresources/sessions/methods/reject , send POST /v1/live/sessions/{session id}/reject with a SIP status, such as { "status code": 486 } for busy. The status must be an integer from 300 through 699. The first accept or reject decision wins; a later competing decision returns decision already made . Attach your backend After acceptance, connect a sideband WebSocket https://developers.openai.com/api/docs/guides/voice-server-controls?api=live at wss://api.openai.com/v1/live/sessions/{session id}/attach . Use the accepted session ID and the same project authentication and connection headers. Do not send session.start again. SIP carries the call audio. Use the sideband for transcripts, delegation, tools, commands, and reflected audio. Choose one owner for each side effect, even if multiple connections observe an event. Once connected, use the active call controls manage-an-active-call to send keypad tones, transfer the call, or hang up. Place an outbound call Call a phone number through your SIP provider with Create session https://developers.openai.com/api/reference/resources/live/methods/create . Your provider handles the phone network connection while GPT-Live carries the conversation. Outbound SIP calling is available to all organizations through the Live API, not the Realtime API call-creation endpoint. Configure your trunk Use a trunk that supports TLS signaling and SDES-SRTP media. Enable SRTP in your provider’s settings before placing a call. Supply the trunk configuration with each request: | Field | Value | |---|---| | transport.destination | The phone number to call, in E.164 format, such as +14155550123 . SIP URI destinations aren’t supported. | | transport.trunk.provider url | A provider endpoint such as sips:sip.example.com:5061 . The default port is 5061 ; ;transport=tcp is optional. | | transport.trunk.auth.type | digest for SIP Digest authentication. | | transport.trunk.auth.username | Your provider’s SIP username. | | transport.trunk.auth.password | Your provider’s SIP password. | | transport.trunk.caller number | The caller phone number to send to your provider, in E.164 format. | The provider endpoint must use sips: for TLS signaling. Don’t include credentials, paths, URI headers, or other URI parameters in the URL. Local hostnames and literal private or local IP addresses are rejected. Keep your OpenAI API key and SIP credentials on your server. Create the session Send POST /v1/live/sessions with your session configuration and transport.type: "sip" . Choose the voice and delegation mode when creating the session. Omit audio.format because SIP negotiates the audio format. This example uses curl and jq . Set OPENAI API KEY , SIP USERNAME , and SIP PASSWORD in your server environment, and replace the example provider endpoint and phone numbers with your own values. The example selects client delegation; your backend must handle delegated work https://developers.openai.com/api/docs/guides/live-delegation . jq -n \ --arg username "$SIP USERNAME" \ --arg password "$SIP PASSWORD" \ '{ "session": { "model": "gpt-live-1", "instructions": "Help the user schedule an appointment.", "audio": { "output": { "voice": "marin" } }, "delegation": { "type": "client" } }, "transport": { "type": "sip", "destination": "+14155550123", "trunk": { "provider url": "sips:sip.example.com:5061", "auth": { "type": "digest", "username": $username, "password": $password }, "caller number": "+14155550100" } } }' | curl https://api.openai.com/v1/live/sessions \ -H "Authorization: Bearer $OPENAI API KEY" \ -H "Content-Type: application/json" \ --data-binary @- The request returns 200 OK after the session initializes: { "session": { "id": "live 123" }, "transport": { "type": "sip" } } This response doesn’t mean the call has been answered. It contains no SDP or trunk credentials. Preserve session.id unchanged for the sideband connection and call controls. You don’t need an incoming-call webhook or an accept request for an outbound call. Monitor and end the call Attach your backend to wss://api.openai.com/v1/live/sessions/{session id}/attach using your OpenAI API key. Don’t send session.start again. The sideband connection https://developers.openai.com/api/docs/guides/voice-server-controls?api=live carries conversation events, delegated work, and call progress while SIP carries the audio. | Event | Meaning | |---|---| | transport.ringing | The provider reports ringing or early media. | | transport.answered | The call has been answered and media is established. | | transport.failed | Call setup failed after session initialization. Inspect error.code and error.message . | Each call-progress event includes event id and session id . Attach immediately after creation: the sideband only replays events from the preceding 3 seconds, so a later attachment can miss earlier call progress. Replayed events retain their original event IDs. Deduplicate events by event id . Once connected, use the active call controls manage-an-active-call to send keypad tones, transfer the call, or hang up. Keep the sideband open for session.closed and final usage as described in Usage and graceful close https://developers.openai.com/api/docs/guides/live-conversations usage-and-graceful-close . Handle limits and errors Ringing is limited to 3 minutes, and a connected call is limited to 2 hours. These limits aren’t configurable in the creation request. Invalid session configuration is returned on the creation request. Transport setup failures can return 502 , and initialization timeouts return 504 . After creation succeeds, monitor transport.failed for asynchronous setup failures. Manage an active call These controls apply to both inbound and outbound SIP calls. Send and receive keypad events The sideband receives transport.dtmf.received https://developers.openai.com/api/reference/resources/live/sideband-websocket transport.dtmf.received when the caller presses a key. To send a DTMF tone to the SIP trunk, send a transport.dtmf.send client event https://developers.openai.com/api/reference/resources/live/sideband-websocket transport.dtmf.send over the sideband WebSocket. After the tone is successfully sent, the sideband receives transport.dtmf.sent https://developers.openai.com/api/reference/resources/live/sideband-websocket transport.dtmf.sent . If you supply an event id , the notification includes it as client event id ; failures return an error event with error.client event id . Transfer or end the call To transfer the call https://developers.openai.com/api/reference/resources/live/subresources/sessions/methods/refer , send POST /v1/live/sessions/{session id}/refer with { "target uri": "sip:agent@example.com" } for your destination. To hang up https://developers.openai.com/api/reference/resources/live/subresources/sessions/methods/hangup , send POST /v1/live/sessions/{session id}/hangup with no request body. Both return 200 OK with an empty body on success. The transfer target becomes the SIP Refer-To header and can be a telephone URI such as tel:+14155550123 or a SIP URI such as sip:agent@example.com . Your provider handles the remaining transfer flow. Keep the sideband open until session.closed supplies final usage. See Usage and graceful close https://developers.openai.com/api/docs/guides/live-conversations usage-and-graceful-close for usage reporting and close reasons. SIP network requirements GPT-Live SIP calls use separate network paths for signaling and media. Configure your network to allow both: - SIP signaling: sip.api.openai.com and sip-eu.api.openai.com are GeoIP-routed endpoints. Allow outbound TCP/TLS traffic to the addresses returned by DNS on port 5061 . - SRTP media: The API specifies a separate media IP address and UDP port in the negotiated SDP. Allow bidirectional SRTP traffic over UDP to and from the following CIDRs: - 13.79.45.80/28 - 23.98.140.64/28 - 40.67.149.176/28 - 40.83.204.240/28 Next steps with GPT-Live - Prompting guide https://developers.openai.com/api/docs/guides/voice-prompting?api=live : write instructions for your voice agent. - Webhooks and server-side controls https://developers.openai.com/api/docs/guides/voice-server-controls?api=live : manage a session from your backend. - Delegation and tools https://developers.openai.com/api/docs/guides/live-delegation : connect speech to your reasoning and tool backend. - Managing sessions https://developers.openai.com/api/docs/guides/live-conversations : handle transcripts, session state, and close. - Latency and cost https://developers.openai.com/api/docs/guides/voice-latency-cost?api=live : understand latency and usage. - GPT-Live partner integrations https://developers.openai.com/api/docs/guides/live-partner-integrations : find provider-specific setup guides and examples. SIP https://en.wikipedia.org/wiki/Session Initiation Protocol is a protocol used to make phone calls over the internet. With SIP and the Realtime API you can direct incoming phone calls to the API. Overview If you want to connect a phone number to the Realtime API, use a SIP trunking provider e.g., Twilio . This is a service that converts your phone call to IP traffic. After you purchase a phone number from your SIP trunking provider, follow the instructions below. Start by creating a webhook https://developers.openai.com/api/docs/guides/webhooks for incoming calls, through your platform.openai.com settings https://platform.openai.com/settings Project Webhooks . Then, point your SIP trunk at the OpenAI SIP endpoint, using the project ID for which you configured the webhook, e.g., sip:$PROJECT ID@sip.api.openai.com;transport=tls . For European data residency, use sip:$PROJECT ID@sip-eu.api.openai.com;transport=tls instead. To find your $PROJECT ID , visit settings https://platform.openai.com/settings Project General . That page will display the project ID, which will have a proj prefix. When OpenAI receives SIP traffic associated with your project, your webhook will be fired. The event fired will be a realtime.call.incoming https://developers.openai.com/api/docs/api-reference/webhook-events/realtime/call/incoming event, like the example below: POST https://my website.com/webhook endpoint user-agent: OpenAI/1.0 +https://platform.openai.com/docs/webhooks content-type: application/json webhook-id: wh 685342e6c53c8190a1be43f081506c52 unique id for idempotency webhook-timestamp: 1750287078 timestamp of delivery attempt webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4= signature to verify authenticity from OpenAI { "object": "event", "id": "evt 685343a1381c819085d44c354e1b330e", "type": "realtime.call.incoming", "created at": 1750287018, // Unix timestamp "data": { "call id": "some unique id", "sip headers": { "name": "From", "value": "sip:+142555512112@sip.example.com" }, { "name": "To", "value": "sip:+18005551212@sip.example.com" }, { "name": "Call-ID", "value": "03782086-4ce9-44bf-8b0d-4e303d2cc590"} } } From this webhook, you can accept or reject the call, using the call id value from the webhook. When accepting the call, you’ll provide the needed configuration instructions, voice, etc for the Realtime API session. Once established, you can set up a WebSocket and monitor the session as usual. The APIs to accept, reject, monitor, refer, and hangup the call are documented below. Accept the call Use the Accept call endpoint https://developers.openai.com/api/docs/api-reference/realtime-calls/accept-call to approve the inbound call and configure the realtime session that will answer it. Send the same parameters you would send in a create client secret https://developers.openai.com/api/docs/api-reference/realtime-sessions/create-realtime-client-secret request, i.e., ensure the realtime model, voice, tools, or instructions are set before bridging the call to the model. 1 2 3 4 5 6 7 8curl -X POST "https://api.openai.com/v1/realtime/calls/$CALL ID/accept" \ -H "Authorization: Bearer $OPENAI API KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "realtime", "model": "gpt-realtime-2.1", "instructions": "You are Alex, a friendly concierge for Example Corp." }' The request path must include the call id from the realtime.call.incoming https://developers.openai.com/api/docs/api-reference/webhook-events/realtime/call/incoming webhook, and every request requires the Authorization header shown above. The endpoint returns 200 OK once the SIP leg is ringing and the realtime session is being established. Reject the call Use the Reject call endpoint https://developers.openai.com/api/docs/api-reference/realtime-calls/reject-call to decline an invite when you do not want to handle the incoming call, e.g., from an unsupported country code. Supply the call id path parameter and an optional SIP status code e.g., 486 to indicate “busy” in the JSON body to control the response sent back to the carrier. 1 2 3 4curl -X POST "https://api.openai.com/v1/realtime/calls/$CALL ID/reject" \ -H "Authorization: Bearer $OPENAI API KEY" \ -H "Content-Type: application/json" \ -d '{"status code": 486}' If no status code is supplied the API uses 603 Decline by default. A successful request responds with 200 OK after OpenAI delivers the SIP response. Monitor call events After you accept a call, open a WebSocket connection to the same session to stream events and issue realtime commands. Note that when connecting to an existing call using the call id parameter, the model argument is not used as it has already been configured via the accept endpoint . WebSocket request GET wss://api.openai.com/v1/realtime?call id={call id} Query parameters | Parameter | Type | Description | |---|---|---| | call id | string | Identifier from the realtime.call.incoming webhook. | Headers - Authorization: Bearer YOUR API KEY The WebSocket behaves exactly like any other Realtime API connection. Send response.create https://developers.openai.com/api/reference/resources/realtime/client-events response.create , and other client events to control the call, and listen for server events to track progress. See Webhooks and server-side controls https://developers.openai.com/api/docs/guides/voice-server-controls?api=realtime for more information. js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16import WebSocket from "ws"; const callId = "rtc u1 9c6574da8b8a41a18da9308f4ad974ce"; const ws = new WebSocket wss://api.openai.com/v1/realtime?call id=${callId} , { headers: { Authorization: Bearer ${process.env.OPENAI API KEY} , }, } ; ws.on "open", = { ws.send JSON.stringify { type: "response.create", } ; } ; Redirect the call Transfer an active call using the Refer call endpoint https://developers.openai.com/api/docs/api-reference/realtime-calls/refer-call . Provide the call id as well as the target uri that should be placed in the SIP Refer-To header for example tel:+14155550123 or sip:agent@example.com . 1 2 3 4curl -X POST "https://api.openai.com/v1/realtime/calls/$CALL ID/refer" \ -H "Authorization: Bearer $OPENAI API KEY" \ -H "Content-Type: application/json" \ -d '{"target uri": "tel:+14155550123"}' OpenAI returns 200 OK once the REFER is relayed to your SIP provider. The downstream system handles the rest of the call flow for the caller. Hang up the call End the session with the Hang up endpoint https://developers.openai.com/api/docs/api-reference/realtime-calls/hangup-call when your application should disconnect the caller. This endpoint can be used to terminate both SIP and WebRTC realtime sessions. curl -X POST "https://api.openai.com/v1/realtime/calls/$CALL ID/hangup" \ -H "Authorization: Bearer $OPENAI API KEY" The API responds with 200 OK when it starts tearing down the call. SIP signaling and media IP ranges Realtime SIP calls use separate network paths for signaling and media. To ensure proper operation, configure your network to allow signaling and media traffic as described below. SIP signaling sip.api.openai.com and sip-eu.api.openai.com are GeoIP-routed endpoints. Your network must allow outbound TCP/TLS traffic to the addresses returned by DNS on port 5061 . SRTP media The API specifies a separate media IP address and UDP port in the negotiated SDP. Your network must allow bidirectional SRTP traffic over UDP to and from the following CIDRs: - 13.79.45.80/28 - 23.98.140.64/28 - 40.67.149.176/28 - 40.83.204.240/28 Server examples The following is an example of a realtime.call.incoming handler. It accepts the call and then logs all the events from the Realtime API. For the Ruby example, set the OPENAI API KEY and OPENAI WEBHOOK SECRET environment variables, then install the required dependencies with gem install openai webrick async-websocket . 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69from flask import Flask, request, Response, jsonify, make response from openai import OpenAI, InvalidWebhookSignatureError import asyncio import json import os import requests import time import threading import websockets app = Flask name client = OpenAI webhook secret=os.environ "OPENAI WEBHOOK SECRET" AUTH HEADER = {"Authorization": "Bearer " + os.environ "OPENAI API KEY" } call accept = { "type": "realtime", "instructions": "You are a support agent.", "model": "gpt-realtime-2.1", } response create = { "type": "response.create", "response": { "instructions": "Say to the user 'Thank you for calling, how can I help you'" }, } async def websocket task call id : try: async with websockets.connect "wss://api.openai.com/v1/realtime?call id=" + call id, additional headers=AUTH HEADER, as websocket: await websocket.send json.dumps response create while True: response = await websocket.recv print f"Received from WebSocket: {response}" except Exception as e: print f"WebSocket error: {e}" @app.route "/", methods= "POST" def webhook : try: event = client.webhooks.unwrap request.data, request.headers except InvalidWebhookSignatureError, ValueError as e: print "Invalid signature", e return Response "Invalid signature", status=400 if event.type == "realtime.call.incoming": requests.post "https://api.openai.com/v1/realtime/calls/" + event.data.call id + "/accept", headers={ AUTH HEADER, "Content-Type": "application/json"}, json=call accept, .raise for status threading.Thread target=lambda: asyncio.run websocket task event.data.call id , daemon=True, .start return Response status=200 if name == " main ": app.run port=8000 Next steps Now that you’ve connected over SIP, use the left navigation or click into these pages to start building your realtime application.