Choisissez l’API utilisée par votre application. Chaque API possède ses propres mécanismes d’authentification et de création de sessions, ainsi que son propre contrat d’événements.
Connectez un navigateur à GPT-Live
Utilisez WebRTC pour les applications vocales dans le navigateur. L’audio du microphone et la parole générée transitent par des pistes multimédias négociées. Un canal de données transporte les événements JSON liés aux transcriptions, aux mises à jour de session et aux tâches déléguées.
Votre navigateur crée une offre Session Description Protocol (SDP). Votre serveur d’application l’échange contre une réponse via POST /v1/live/sessions, en utilisant la clé API du projet. Conservez la clé et la configuration de session sur votre serveur de confiance.
Avant de commencer
Vous avez besoin des éléments suivants :
- Une clé API de projet donnant accès à GPT-Live.
- Un environnement d’exécution serveur adapté à l’exemple de SDK choisi. L’exemple Node.js nécessite Node.js 22.6 ou une version ultérieure.
- Un navigateur autorisé à accéder au microphone, sur HTTPS ou localhost.
L’exemple utilise la délégation à Responses avec gpt-5.6-terra et la recherche web hébergée. Pour les instructions du backend et les outils de l’application, consultez Délégation et outils. Pour l’utilisation de la voix et du backend, consultez Optimisation des coûts.
Comprenez les étapes de connexion
- Demandez l’accès au microphone à la suite d’une action de l’utilisateur et ajoutez ses pistes à une connexion pair à pair.
- Créez le canal de données et enregistrez les écouteurs d’événements avant de créer l’offre SDP.
- Définissez la description locale, attendez la collecte des candidats ICE, puis envoyez l’offre à votre serveur.
- Faites envoyer par votre serveur une requête POST à OpenAI contenant du JSON avec
sessionettransport: { type: "webrtc", sdp: ... }. - Appliquez la réponse SDP reçue comme description distante. Attendez
session.startedsur le canal de données avant d’envoyer des commandes de l’application.
La requête HTTP démarre la session. N’envoyez pas session.start sur le canal de données. Dans l’exemple, la chaîne oai-events est le libellé du canal de données.
La création d’une session WebRTC avec POST /v1/live/sessions entraîne la facturation de 15 secondes de durée vocale lors de l’initialisation. Ce montant est déduit des frais liés à la durée une fois la session démarrée ; il ne s’agit pas de 15 secondes supplémentaires ajoutées à la session en cours. Consultez Frais d’initialisation WebRTC pour en savoir plus sur le calcul des coûts.
Créez le serveur d’application
Enregistrez l’exemple de serveur dans un nouveau répertoire et définissez OPENAI_API_KEY dans son environnement. Pour Node.js, utilisez server.mjs et installez openai et express avec npm install openai express. Pour Python, installez openai. Cet exemple écoute sur 127.0.0.1, accepte les requêtes de session provenant de http://localhost:3000 et sert index.html depuis le répertoire où vous l’exécutez.
Choisissez ci-dessous un langage pour le serveur ; chaque variante sert index.html et expose le même point de terminaison /api/session sur le port 3000. Utilisez une version du SDK prenant en charge Live. N’exécutez qu’une seule variante à la fois.
import express from "express";
import OpenAI from "openai";
import { readFile } from "node:fs/promises";
import { resolve } from "node:path";
const app = express();
const client = new OpenAI({ maxRetries: 0 });
const port = 3000;
const origin = `http://localhost:${port}`;
const indexPath = resolve("index.html");
app.use(express.json({ limit: "64kb" }));
app.get("/", async (_request, response) => {
response.type("html").send(await readFile(indexPath, "utf8"));
});
// Local-only demo. Add your application's authentication and authorization
// before exposing session creation to other users.
app.post("/api/session", async (request, response) => {
if (request.headers.origin !== origin) {
response.status(403).json({ error: "Unexpected request origin" });
return;
}
if (typeof request.body?.sdp !== "string" || !request.body.sdp.trim()) {
response.status(400).json({ error: "An SDP offer is required" });
return;
}
if (!process.env.OPENAI_API_KEY) {
response.status(503).json({ error: "Set OPENAI_API_KEY on the server" });
return;
}
try {
const result = await client.live.create({
session: {
model: "gpt-live-1",
instructions:
"Be concise. Delegate requests needing current information to the backend, which can search the web.",
delegation: {
type: "responses",
responses: {
model: "gpt-5.6-terra",
instructions:
"Use web search when current facts are needed. Return concise, grounded results for a spoken conversation.",
tools: [{ type: "web_search" }],
tool_choice: "auto",
},
},
},
transport: {
type: "webrtc",
sdp: request.body.sdp,
},
});
// Preserve the SDK's typed session ID and SDP answer.
response.status(201).json(result);
} catch (error) {
if (!(error instanceof OpenAI.APIError)) throw error;
console.error("Live session creation failed", error.status);
response
.status(error.status ?? 502)
.json({ error: "Live session creation failed" });
}
});
app.listen(port, "127.0.0.1", () => console.log(`Open ${origin}`));Avant de rendre le serveur accessible à d’autres utilisateurs, protégez /api/session avec les mécanismes d’authentification, d’autorisation et de limitation des requêtes de votre application, ainsi qu’avec HTTPS. La vérification de l’origine dans cet exemple local n’authentifie pas les utilisateurs.
Créez le client navigateur
Créez index.html dans le répertoire où vous exécutez le serveur :
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>GPT-Live connection</title>
</head>
<body>
<script type="module">
// Paste the browser code below here.
</script>
</body>
</html>Collez le code suivant dans le script de type module. Il ajoute les commandes de démarrage et de fin, connecte le microphone et la sortie audio, et gère les événements de session. /api/session est une route de votre serveur d’application.
const start = document.createElement("button");
start.textContent = "Start conversation";
const stop = document.createElement("button");
stop.textContent = "End conversation";
stop.disabled = true;
const status = document.createElement("p");
const audio = new Audio();
audio.autoplay = true;
audio.controls = true;
document.body.append(start, stop, status, audio);
let peer;
let events;
let microphone;
let closeTimeout;
let ready = false;
let finalized = false;
function cleanup() {
clearTimeout(closeTimeout);
microphone?.getTracks().forEach((track) => track.stop());
events?.close();
peer?.close();
audio.srcObject = null;
ready = false;
start.disabled = false;
stop.disabled = true;
}
start.addEventListener("click", async () => {
start.disabled = true;
finalized = false;
status.textContent = "Connecting…";
try {
const connection = new RTCPeerConnection();
peer = connection;
connection.addEventListener("track", (event) => {
audio.srcObject = new MediaStream([event.track]);
audio.play().catch(() => {
status.textContent =
"Select play on the audio controls to hear the assistant.";
});
});
microphone = await navigator.mediaDevices.getUserMedia({ audio: true });
for (const track of microphone.getAudioTracks()) {
connection.addTrack(track, microphone);
}
// Create the event channel before creating the SDP offer.
events = connection.createDataChannel("oai-events");
events.addEventListener("message", ({ data }) => {
const event = JSON.parse(data);
if (event.type === "session.started") {
ready = true;
stop.disabled = false;
status.textContent = "Connected: " + event.session.id;
} else if (event.type === "session.closed") {
finalized = true;
console.log("Final session usage", event.usage);
status.textContent = "Conversation ended.";
cleanup();
} else {
// Save transcript and nested Responses events as needed by your app.
console.log(event);
}
});
events.addEventListener("close", (event) => {
if (event.target !== events) return;
if (!finalized) {
status.textContent = "Disconnected without final session usage.";
cleanup();
}
});
const offer = await connection.createOffer();
await connection.setLocalDescription(offer);
if (connection.iceGatheringState !== "complete") {
await new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
connection.removeEventListener("icegatheringstatechange", onState);
reject(new Error("Timed out while gathering ICE candidates"));
}, 10_000);
function onState() {
if (connection.iceGatheringState !== "complete") return;
clearTimeout(timeout);
connection.removeEventListener("icegatheringstatechange", onState);
resolve(undefined);
}
connection.addEventListener("icegatheringstatechange", onState);
onState();
});
}
const sdp = connection.localDescription?.sdp;
if (!sdp) throw new Error("Missing local SDP offer");
const response = await fetch("/api/session", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ sdp }),
});
if (!response.ok) throw new Error(await response.text());
const result = await response.json();
console.log("Created session", result.session.id);
await connection.setRemoteDescription({
type: "answer",
sdp: result.transport.sdp,
});
// The HTTP request started this session. Do not send session.start here.
} catch (error) {
status.textContent =
error instanceof Error ? error.message : String(error);
cleanup();
}
});
stop.addEventListener("click", () => {
if (!ready || !events || events.readyState !== "open") return;
stop.disabled = true;
status.textContent = "Finishing the conversation…";
// The session.closed handler is already registered. Keep media and events
// alive while pending work drains; only clean up after the final event.
events.send(JSON.stringify({ type: "session.close" }));
closeTimeout = setTimeout(() => {
status.textContent = "Incomplete finalization: no session.closed event.";
cleanup();
}, 15_000);
});Exécutez le serveur choisi (node server.mjs ou python server.py), ouvrez http://localhost:3000, puis sélectionnez Démarrer la conversation. Lorsque l’état passe à Connecté, posez une question nécessitant des informations récentes pour tester la recherche hébergée. Utilisez les commandes audio si votre navigateur bloque la lecture automatique.
Lisez la réponse de session
Une requête réussie renvoie un statut HTTP 201 et du JSON contenant l’identifiant de session et la réponse SDP :
{
"session": { "id": "live_123" },
"transport": { "type": "webrtc", "sdp": "<SDP answer>" }
}Lisez result.session.id et transmettez result.transport.sdp à setRemoteDescription. Traitez l’identifiant de session comme une valeur opaque et conservez-le tel quel, y compris son préfixe.
Gérez les médias et les événements
Envoyez l’audio du microphone et recevez la parole générée via les pistes multimédias. WebRTC négocie le format audio via SDP : omettez donc audio.format de la configuration de session. N’envoyez pas session.input_audio.append et ne vous attendez pas à recevoir session.output_audio.delta sur le canal de données.
Utilisez le canal de données pour les mises à jour incrémentales des transcriptions, les commandes de session et les messages response.event imbriqués. Consultez Gestion des sessions pour le traitement des transcriptions et les événements du cycle de vie, et Contrôles côté serveur si votre serveur a besoin de sa propre connexion aux événements.
Pour terminer la conversation, envoyez session.close et continuez à recevoir les événements jusqu’à session.closed avant de fermer la connexion pair à pair et d’arrêter les pistes du microphone. L’exemple enregistre l’écouteur de l’événement final avant d’envoyer la commande. Si la connexion échoue ou expire avant cela, les données d’utilisation finales ne sont pas confirmées. Consultez Utilisation et fermeture propre pour savoir comment gérer ces données.
WebRTC est un ensemble puissant d’interfaces standard pour créer des applications en temps réel. La Realtime API d’OpenAI permet de se connecter aux modèles temps réel via une connexion WebRTC pair à pair.
Pour les applications vocales de parole à parole dans le navigateur, nous vous recommandons de commencer par Agents vocaux, qui présente les fonctions utilitaires et les API de plus haut niveau du SDK Agents pour gérer les sessions Realtime. L’interface WebRTC est puissante et flexible, mais se situe à un niveau d’abstraction inférieur à celui du SDK Agents.
Pour vous connecter à un modèle Realtime depuis un client (comme un navigateur web ou un appareil mobile), nous vous recommandons d’utiliser WebRTC plutôt que WebSockets pour des performances plus stables.
Pour en savoir plus sur la création d’interfaces utilisateur avec WebRTC, consultez la documentation sur MDN.
Vue d’ensemble
La Realtime API propose deux mécanismes de connexion depuis le navigateur : des clés API éphémères (générées via l’API REST d’OpenAI) ou la nouvelle interface unifiée. L’interface unifiée est généralement plus simple à utiliser, mais elle place votre serveur d’application sur le chemin critique de l’initialisation de la session.
Connexion via l’interface unifiée
L’initialisation d’une connexion WebRTC via l’interface unifiée se déroule comme suit (avec un navigateur web comme client) :
- Le navigateur envoie une requête à un serveur contrôlé par le développeur, en utilisant les données SDP de sa connexion WebRTC pair à pair.
- Le serveur regroupe ces données SDP et sa configuration de session dans un formulaire multipart, puis envoie le tout à la Realtime API d’OpenAI en authentifiant la requête avec sa clé API standard.
Création d’une session via l’interface unifiée
Pour créer une session Realtime API via l’interface unifiée, vous devez créer une petite application côté serveur (ou intégrer cette fonctionnalité à une application existante) afin d’envoyer une requête à /v1/realtime/calls. Vous utiliserez une clé API standard pour authentifier cette requête sur votre serveur backend.
Voici un exemple de serveur Node.js simple utilisant express pour créer une session Realtime API :
import express from "express";
const app = express();
// Parse raw SDP payloads posted from the browser
app.use(express.text({ type: ["application/sdp", "text/plain"] }));
const sessionConfig = JSON.stringify({
type: "realtime",
model: "gpt-realtime-2.1",
audio: { output: { voice: "marin" } },
});
// An endpoint which creates a Realtime API session.
app.post("/session", async (req, res) => {
const fd = new FormData();
fd.set("sdp", req.body);
fd.set("session", sessionConfig);
try {
const r = await fetch("https://api.openai.com/v1/realtime/calls", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
"OpenAI-Safety-Identifier": "hashed-user-id",
},
body: fd,
});
// Send back the SDP we received from the OpenAI REST API
const sdp = await r.text();
res.send(sdp);
} catch (error) {
console.error("Token generation error:", error);
res.status(500).json({ error: "Failed to generate token" });
}
});
app.listen(3000);Si votre application attribue un identifiant de sécurité
à chaque utilisateur final, incluez-le dans l’en-tête OpenAI-Safety-Identifier de cette
requête côté serveur. Utilisez une valeur stable qui préserve la confidentialité, par exemple
un identifiant utilisateur interne haché. Cet en-tête doit être défini par votre backend de confiance, et non par le
navigateur.
Connexion au serveur
Dans le navigateur, vous pouvez utiliser les API WebRTC standard pour vous connecter à la Realtime API via votre serveur d’application. Le client envoie directement ses données SDP à votre serveur par une requête POST.
// Create a peer connection
const pc = new RTCPeerConnection();
// Set up to play remote audio from the model
audioElement.current = document.createElement("audio");
audioElement.current.autoplay = true;
pc.ontrack = (e) => (audioElement.current.srcObject = e.streams[0]);
// Add local audio track for microphone input in the browser
const ms = await navigator.mediaDevices.getUserMedia({
audio: true,
});
pc.addTrack(ms.getTracks()[0]);
// Set up data channel for sending and receiving events
const dc = pc.createDataChannel("oai-events");
// Start the session using the Session Description Protocol (SDP)
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
const sdpResponse = await fetch("/session", {
method: "POST",
body: offer.sdp,
headers: {
"Content-Type": "application/sdp",
},
});
const answer = {
type: "answer",
sdp: await sdpResponse.text(),
};
await pc.setRemoteDescription(answer);Connexion avec un token éphémère
Voici les étapes pour initialiser une connexion WebRTC à l’aide d’une clé API éphémère, avec un navigateur web comme client :
- Le navigateur envoie une requête à un serveur contrôlé par le développeur pour générer une clé API éphémère.
- Le serveur du développeur utilise une clé API standard pour demander une clé éphémère à l’API REST d’OpenAI, puis renvoie cette nouvelle clé au navigateur.
- Le navigateur utilise la clé éphémère pour authentifier une session directement auprès de la Realtime API d’OpenAI sous la forme d’une connexion pair à pair WebRTC.
Création d’un token éphémère
Pour créer un token éphémère à utiliser côté client, vous devez développer une petite application côté serveur, ou intégrer cette fonctionnalité à une application existante, afin de demander une clé éphémère à l’API REST d’OpenAI. Vous utiliserez une clé API standard pour authentifier cette requête sur votre serveur backend.
Voici un exemple de serveur Node.js simple utilisant express pour générer une clé API éphémère via l’API REST :
import express from "express";
const app = express();
const sessionConfig = JSON.stringify({
session: {
type: "realtime",
model: "gpt-realtime-2.1",
audio: {
output: {
voice: "marin",
},
},
},
});
// An endpoint which would work with the client code above - it returns
// the contents of a REST API request to this protected endpoint
app.get("/token", async (req, res) => {
try {
const response = await fetch(
"https://api.openai.com/v1/realtime/client_secrets",
{
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
"OpenAI-Safety-Identifier": "hashed-user-id",
},
body: sessionConfig,
}
);
const data = await response.json();
res.json(data);
} catch (error) {
console.error("Token generation error:", error);
res.status(500).json({ error: "Failed to generate token" });
}
});
app.listen(3000);Vous pouvez créer un point de terminaison serveur comme celui-ci sur toute plateforme capable d’envoyer et de recevoir des requêtes HTTP. Veillez simplement à utiliser les clés API OpenAI standard uniquement sur le serveur, jamais dans le navigateur.
Lorsque vous utilisez des tokens éphémères, définissez OpenAI-Safety-Identifier dans la requête côté serveur
qui crée le secret client. La Realtime API associe cet identifiant
au token éphémère obtenu. Le navigateur n’a donc pas besoin d’envoyer l’identifiant de sécurité
lorsqu’il se connecte ensuite avec ce token.
Connexion au serveur
Dans le navigateur, vous pouvez utiliser les API WebRTC standard pour vous connecter à la Realtime API avec un token éphémère. Le client récupère d’abord un token auprès du point de terminaison de votre serveur, puis envoie ses données SDP, accompagnées du token éphémère, à la Realtime API dans une requête POST.
// Get a session token for OpenAI Realtime API
const tokenResponse = await fetch("/token");
const data = await tokenResponse.json();
const EPHEMERAL_KEY = data.value;
// Create a peer connection
const pc = new RTCPeerConnection();
// Set up to play remote audio from the model
audioElement.current = document.createElement("audio");
audioElement.current.autoplay = true;
pc.ontrack = (e) => (audioElement.current.srcObject = e.streams[0]);
// Add local audio track for microphone input in the browser
const ms = await navigator.mediaDevices.getUserMedia({
audio: true,
});
pc.addTrack(ms.getTracks()[0]);
// Set up data channel for sending and receiving events
const dc = pc.createDataChannel("oai-events");
// Start the session using the Session Description Protocol (SDP)
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
const sdpResponse = await fetch("https://api.openai.com/v1/realtime/calls", {
method: "POST",
body: offer.sdp,
headers: {
Authorization: `Bearer ${EPHEMERAL_KEY}`,
"Content-Type": "application/sdp",
},
});
const answer = {
type: "answer",
sdp: await sdpResponse.text(),
};
await pc.setRemoteDescription(answer);Envoi et réception d’événements
La gestion des sessions de la Realtime API repose sur une combinaison d’événements envoyés par le client, que vous émettez en tant que développeur, et d’événements envoyés par le serveur, que la Realtime API crée pour signaler les événements du cycle de vie de la session.
Lorsque vous vous connectez à un modèle Realtime via WebRTC, vous n’avez pas à gérer les événements audio du modèle avec le même niveau de détail qu’avec WebSockets. L’objet de connexion pair à pair WebRTC, s’il est configuré comme indiqué ci-dessus, s’en charge pour vous.
Pour envoyer et recevoir les autres événements client et serveur, vous pouvez utiliser le canal de données de la connexion pair à pair WebRTC.
// This is the data channel set up in the browser code above...
const dc = pc.createDataChannel("oai-events");
// Listen for server events
dc.addEventListener("message", (e) => {
const event = JSON.parse(e.data);
console.log(event);
});
// Send client events
const event = {
type: "conversation.item.create",
item: {
type: "message",
role: "user",
content: [
{
type: "input_text",
text: "hello there!",
},
],
},
};
dc.send(JSON.stringify(event));Pour en savoir plus sur la gestion des conversations Realtime, consultez le guide des conversations Realtime.
Découvrez la Realtime API avec WebRTC dans cette application d’exemple légère.