From 197c69ef346c9d617cdd627fcc413ffdd153e1c7 Mon Sep 17 00:00:00 2001 From: suyash-lyzr Date: Fri, 3 Jul 2026 05:37:21 +0530 Subject: [PATCH] Fix OpenAI Realtime: use /v1/realtime/client_secrets (old /sessions returns 404) OpenAI retired POST /v1/realtime/sessions; it now returns HTTP 404 'Invalid URL', so voice mode fails to obtain an ephemeral token and the UI shows a misleading 'no API key set' error for every user. Migrate both the initial-connect and refresh paths to the current endpoint POST /v1/realtime/client_secrets: - body: { model } -> { session: { type: 'realtime', model } } - response: session.client_secret.value -> session.value (top-level) Verified live: old endpoint -> 404, new endpoint with this exact request -> 200 with a top-level ephemeral 'value'. tsc build passes. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/openai-realtime.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/openai-realtime.ts b/src/openai-realtime.ts index 464cc08..9a13288 100644 --- a/src/openai-realtime.ts +++ b/src/openai-realtime.ts @@ -61,22 +61,22 @@ export class OpenAIRealtimeAdapter implements MultimodalAdapter { ? `${this.config.apiKey.slice(0, 7)}...${this.config.apiKey.slice(-4)} (${this.config.apiKey.length} chars)` : "(empty)"; console.log(dim(`[voice] API key: ${keyPreview}`)); - const sessionResp = await fetch("https://api.openai.com/v1/realtime/sessions", { + const sessionResp = await fetch("https://api.openai.com/v1/realtime/client_secrets", { method: "POST", headers: { "Authorization": `Bearer ${this.config.apiKey}`, "Content-Type": "application/json", }, - body: JSON.stringify({ model }), + body: JSON.stringify({ session: { type: "realtime", model } }), }); if (!sessionResp.ok) { const body = await sessionResp.text(); throw new Error(`Failed to create realtime session: ${sessionResp.status} ${body}`); } - const session = await sessionResp.json() as { client_secret?: { value?: string } }; - const ephemeralKey = session.client_secret?.value; + const session = await sessionResp.json() as { value?: string }; + const ephemeralKey = session.value; if (!ephemeralKey) { - throw new Error("No ephemeral key returned from realtime sessions endpoint"); + throw new Error("No ephemeral key returned from realtime client_secrets endpoint"); } await this.connectWs(url, { @@ -298,14 +298,14 @@ export class OpenAIRealtimeAdapter implements MultimodalAdapter { const msg = err?.message || ""; if (!msg.includes("authentication") && !msg.includes("401")) throw err; // Ephemeral token fallback (matches connect() path) - const sessionResp = await fetch("https://api.openai.com/v1/realtime/sessions", { + const sessionResp = await fetch("https://api.openai.com/v1/realtime/client_secrets", { method: "POST", headers: { Authorization: `Bearer ${this.config.apiKey}`, "Content-Type": "application/json" }, - body: JSON.stringify({ model }), + body: JSON.stringify({ session: { type: "realtime", model } }), }); if (!sessionResp.ok) throw new Error(`refresh ephemeral token: ${sessionResp.status}`); - const session = (await sessionResp.json()) as { client_secret?: { value?: string } }; - const ephemeralKey = session.client_secret?.value; + const session = (await sessionResp.json()) as { value?: string }; + const ephemeralKey = session.value; if (!ephemeralKey) throw new Error("No ephemeral key on refresh"); await this.connectWs(url, { });