Skip to content
Documentation
Esc
navigateopen⌘Jpreview
On this page

Quickstart

Get from zero to a live avatar session in five minutes — create the session, connect the stream, end it.

Create and start a live avatar session in one API call, connect your frontend to the media stream, and end the session from your backend. Everything below uses the six canonical /v1/ endpoints — you can copy-paste the whole flow.

Base URL

All examples use the placeholder below. Replace it with your assigned API host.

https://<orvyn-public-api-host>

The 60-second path

Create and start a session

One call creates the session, starts rendering, and returns a short-lived client token.

Connect the WebSocket

Open the ws_url from the session response — the token is already embedded.

Receive `session.ready` + media

The server sends session.ready, then fMP4 init and media segments.

End the session

Clean up from your backend to release GPU resources.

Step 1 — Create and start a session

The session must specify exactly one provider_type and a matching provider_config. Provider credentials are ephemeral — your backend mints a fresh token for every session and Orvyn holds it in memory only for the session lifetime. Never send provider credentials to the frontend.

curl -X POST "https://<orvyn-public-api-host>/v1/sessions" \
  -H "Authorization: Bearer $AVTR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "provider_type": "openai_realtime",
    "provider_config": {
      "openai_realtime": { "client_secret": "<ephemeral-client-secret>" }
    }
  }'
const response = await fetch("https://<orvyn-public-api-host>/v1/sessions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.AVTR_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    provider_type: "openai_realtime",
    provider_config: {
      openai_realtime: { client_secret: "<ephemeral-client-secret>" },
    },
  }),
});
const { data } = await response.json();
import os
import requests

response = requests.post(
    "https://<orvyn-public-api-host>/v1/sessions",
    headers={
        "Authorization": f"Bearer {os.environ['AVTR_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={
        "provider_type": "openai_realtime",
        "provider_config": {
            "openai_realtime": {"client_secret": "<ephemeral-client-secret>"}
        },
    },
)
data = response.json()["data"]

A successful response returns the session id, the stream metadata, and the short-lived client_token-backed WebSocket URL:

{
  "success": true,
  "data": {
    "session_id": "session:8f3c1a2b-...",
    "status": "created",
    "stream": {
      "transport": "websocket_fmp4",
      "ws_url": "wss://<orvyn-public-api-host>/v1/sessions/session:.../ws?token=eyJ...",
      "token_expires_at": 1718352300000
    }
  },
  "timings": { "latency_ms": 9 }
}

Save the session_id — you need it for cleanup. Treat the full ws_url as a bearer credential (see the security note below).

Step 2 — Connect the WebSocket

POST /v1/sessions already created AND started the session — there is no separate start step. Open the ws_url exactly as returned; the token is already embedded, so do not add auth headers.

const ws = new WebSocket(wsUrl);
ws.binaryType = "arraybuffer";

ws.onopen = () => {
  // No session.start needed — POST /v1/sessions already started the session.
};

ws.onmessage = (event) => {
  if (typeof event.data !== "string") return; // binary audio frames
  const msg = JSON.parse(event.data);

  switch (msg.type) {
    case "session.ready":
      console.log("Avatar ready:", msg.avatarId);
      break;
    case "avatar.fmp4.init":
      console.log("fMP4 init segment received");
      break;
    case "avatar.fmp4.segment":
      console.log("Media segment", msg.sequence);
      break;
    case "session.ended":
      console.log("Session ended");
      break;
    case "error":
      console.error("Server error:", msg.code, msg.message);
      break;
  }
};

The full message catalog — lifecycle, fMP4 media, and speech pipeline messages — is documented in the WebSocket guide.

Step 3 — End the session

Always end the session from your backend to release resources:

curl -X POST "https://<orvyn-public-api-host>/v1/sessions/$SESSION_ID/end" \
  -H "Authorization: Bearer $AVTR_API_KEY"
await fetch(
  `https://<orvyn-public-api-host>/v1/sessions/${sessionId}/end`,
  {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.AVTR_API_KEY}` },
  },
);
requests.post(
    f"https://<orvyn-public-api-host>/v1/sessions/{session_id}/end",
    headers={"Authorization": f"Bearer {os.environ['AVTR_API_KEY']}"},
)

Refreshing the client token

The token in ws_url expires after 5 minutes. To reconnect after expiry (network drop, browser sleep), mint a fresh one:

curl -X POST \
  "https://<orvyn-public-api-host>/v1/sessions/$SESSION_ID/client-token" \
  -H "Authorization: Bearer $AVTR_API_KEY"
{
  "success": true,
  "data": {
    "ws_url": "wss://<orvyn-public-api-host>/v1/sessions/session:.../ws?token=eyJ...",
    "token_expires_at": 1718352600000
  },
  "timings": { "latency_ms": 3 }
}

Use the newest ws_url for reconnects. Refreshing does not revoke previously issued tokens — they remain valid until their own expiry.

Handle the ws_url safely

The ?token= query parameter is a short-lived bearer credential (5-minute TTL). Anyone holding the URL can connect to the session’s WebSocket.

  • Do use the ws_url as-is to open the connection.
  • Do not log it, screenshot it, or store it in analytics or browser history.
  • Do call POST /v1/sessions/{session_id}/client-token for reconnects after expiry.

Next steps

Was this page helpful?