Docs/Ephemeral Sessions
POST /v1/ephemeral

Ephemeral Sessions

Use the OpenAI Realtime API directly from a browser — without shipping your API key to the client. Your server calls POST /v1/ephemeral, gets a 60-second client_secret, and forwards only that to the browser.

How it works

BrowserCalls your server route to request a session
Your serverPOSTs to /v1/ephemeral with your kvt_ token (stays server-side)
KVE proxyValidates token, decrypts real API key, calls OpenAI /v1/realtime/sessions
OpenAIReturns a client_secret valid for 60 seconds
KVE proxyLogs the session audit record, returns client_secret to your server
Your serverForwards only the client_secret to the browser
BrowserConnects to OpenAI Realtime API directly using the ephemeral key

Your real OpenAI API key is never sent to the browser — not even for a millisecond. The ephemeral key expires after 60 seconds and is scoped to a single Realtime session.

Setup

1

Create a kvt_ token for the realtime use-case

Create a token from the dashboard or API. Set allowedHosts to your backend domain — the browser never uses the kvt_ token directly.

bash
# Create a token with the openai provider and realtime scopecurl -X POST https://keyvaultedge.com/api/tokens \  -H "Authorization: Bearer <your_kve_api_key>" \  -H "Content-Type: application/json" \  -d '{    "name": "realtime-backend",    "providerSlug": "openai",    "allowedHosts": ["yourdomain.com"],    "rateLimit": { "requests": 60, "window": "1m" }  }'# → { "token": "kvt_abc123..." }
2

Add a server route that calls /v1/ephemeral

This route lives on your server. It holds the kvt_ token and calls the KVE proxy, then returns only the ephemeral client_secret to the browser.

// Server route: POST /api/realtime-session// Called by your front-end to get a short-lived client_secret export async function POST(request: Request) {  const session = await fetch("https://proxy.keyvaultedge.com/v1/ephemeral", {    method: "POST",    headers: {      "Authorization": "Bearer kvt_abc123...", // your kvt_ token      "Content-Type": "application/json",    },    body: JSON.stringify({      model: "gpt-4o-realtime-preview-2024-12-17",      voice: "alloy",    }),  }).then(r => r.json());   // session.session.client_secret.value — valid for 60 seconds  return Response.json({ client_secret: session.session.client_secret.value });}
3

Use the ephemeral key in the browser

Fetch the session from your server route, then connect to the OpenAI Realtime API using the returned client_secret.

browser.ts
// Browser — connect directly to OpenAI Realtime API// using the ephemeral key (never touches your real API key) const { client_secret } = await fetch("/api/realtime-session", {  method: "POST",}).then(r => r.json()); const pc = new RTCPeerConnection();const dc = pc.createDataChannel("oai-events"); const ms = await navigator.mediaDevices.getUserMedia({ audio: true });pc.addTrack(ms.getTracks()[0]); const offer = await pc.createOffer();await pc.setLocalDescription(offer); // Connect to OpenAI Realtime with the ephemeral keyconst answer = await fetch(  "https://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview-2024-12-17",  {    method: "POST",    headers: {      "Authorization": `Bearer ${client_secret}`,      "Content-Type": "application/sdp",    },    body: offer.sdp,  }).then(r => r.text()); await pc.setRemoteDescription({ type: "answer", sdp: answer }); dc.addEventListener("message", e => {  const event = JSON.parse(e.data);  console.log("Realtime event:", event);});

Response shape

POST /v1/ephemeral → 200 OK
{  "provider": "openai",  "session": {    "id": "sess_...",    "object": "realtime.session",    "model": "gpt-4o-realtime-preview-2024-12-17",    "client_secret": {      "value": "ek_...",        // send this to the browser      "expires_at": 1751500000  // Unix timestamp — ~60 s from now    }  },  "expires_at": "2025-07-02T18:06:40.000Z"}

The expires_at field is also returned as a top-level ISO-8601 string for convenience. The session object is passed through verbatim from OpenAI.

Rate limiting

The /v1/ephemeral endpoint counts against the same rate limit as normal proxy requests on the token. Set a conservative limit (e.g. 60 req/min) since each call provisions a full Realtime session on OpenAI's side.

Billing note

Realtime sessions are billed by OpenAI based on audio and token usage — not by KeyVault Edge. Each /v1/ephemeral call itself counts as one request against your token's rate limit.

Audit log

Every ephemeral session is logged in the ephemeral_sessions table in your Supabase database with: the token ID, organisation ID, provider, OpenAI session ID, expiry time, requesting IP, and Cloudflare PoP. These records are visible in the Security section of the dashboard and are retained for 90 days.

Related docs