Building your integration
This guide walks through building a complete Avatar integration using Next.js App Router. The same patterns apply to other React frameworks.
Architecture overview
Section titled “Architecture overview”Avatar Sessions require a server component to keep your API key secure. The client never sees your Runway API secret.
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐│ Client │ │ Your Server │ │ Runway API ││ (React App) │ │ (Next.js) │ │ │└────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │ │ │ │ 1. Request Session │ │ │ POST /api/avatar/session │ │ │ ─────────────────────────►│ │ │ │ │ │ │ 2. Create Session │ │ │ POST /v1/realtime_sessions │ │ ─────────────────────────►│ │ │ │ │ │ 3. Poll until ready │ │ │ GET /v1/realtime_sessions/:id │ │ ─────────────────────────►│ │ │ │ │ │ 4. Consume credentials │ │ │ POST /v1/realtime_sessions/:id/consume │ │ ─────────────────────────►│ │ │ │ │ 5. Return credentials │◄───────────────────────── │ │◄───────────────────────── │ │ │ │ │ │ 6. WebRTC connection │ │ │ ─────────────────────────────────────────────────────►│ │ │ │Installation
Section titled “Installation”Install both the server SDK and React components:
npm install @runwayml/sdk @runwayml/avatars-reactServer setup
Section titled “Server setup”Create an API route that handles Session creation. This endpoint receives an Avatar ID from the client, creates a Session with Runway, polls until it’s ready, consumes the credentials, and returns them to the client.
import RunwayML from '@runwayml/sdk';
const client = new RunwayML();
export async function POST(request: Request) { const { avatarId } = await request.json();
// 1. Create session const { id: sessionId } = await client.realtimeSessions.create({ model: 'gwm1_avatars', avatar: { type: 'custom', avatarId }, });
// 2. Poll until ready let sessionKey: string | undefined; for (let i = 0; i < 60; i++) { const session = await client.realtimeSessions.retrieve(sessionId);
if (session.status === 'READY') { sessionKey = session.sessionKey; break; } if (session.status === 'FAILED') { return Response.json({ error: session.failure }, { status: 500 }); } await new Promise(r => setTimeout(r, 1000)); }
if (!sessionKey) { return Response.json({ error: 'Session timed out' }, { status: 504 }); }
// 3. Consume session to get connection credentials const consumeResponse = await fetch( `${client.baseURL}/v1/realtime_sessions/${sessionId}/consume`, { method: 'POST', headers: { Authorization: `Bearer ${sessionKey}`, 'X-Runway-Version': '2024-11-06', }, } ); const credentials = await consumeResponse.json();
return Response.json({ sessionId, serverUrl: credentials.url, token: credentials.token, roomName: credentials.roomName, });}Set your API key as an environment variable:
RUNWAYML_API_SECRET=your_api_key_hereClient integration
Section titled “Client integration”Simple: AvatarCall
Section titled “Simple: AvatarCall”The simplest way to add an Avatar is with the AvatarCall component. It handles WebRTC connection and renders a default UI.
'use client';
import { AvatarCall } from '@runwayml/avatars-react';import '@runwayml/avatars-react/styles.css';
export default function Home() { return ( <AvatarCall avatarId="customer-service" connectUrl="/api/avatar/session" onEnd={() => console.log('Call ended')} onError={(error) => console.error('Error:', error)} /> );}To use a custom Avatar created in the Developer Portal, replace "customer-service" with your Avatar ID.
Fully custom: hooks
Section titled “Fully custom: hooks”For complete control over the UI, use AvatarSession with hooks. This example shows how to build a custom interface with useAvatarSession for Session state and useLocalMedia for camera controls:
'use client';
import { AvatarSession, AvatarVideo, UserVideo, useAvatarSession, useLocalMedia,} from '@runwayml/avatars-react';import type { SessionCredentials } from '@runwayml/avatars-react';
function CallUI() { const { state, end } = useAvatarSession(); const { isMicEnabled, toggleMic } = useLocalMedia();
return ( <div className="relative w-full h-screen"> <AvatarVideo className="w-full h-full object-cover" /> <UserVideo className="absolute bottom-4 right-4 w-48 rounded-lg" />
<div className="absolute bottom-4 left-4 flex gap-2"> <button onClick={toggleMic}> {isMicEnabled ? 'Mute' : 'Unmute'} </button> <button onClick={end}>End</button> </div>
{state === 'connecting' && <div>Connecting...</div>} </div> );}
export function CustomAvatar({ credentials }: { credentials: SessionCredentials }) { return ( <AvatarSession credentials={credentials} audio video> <CallUI /> </AvatarSession> );}For more components, hooks, and customization options, see the React SDK documentation.
Browser support
Section titled “Browser support”The Avatars SDK uses WebRTC for real-time communication:
| Browser | Minimum Version |
|---|---|
| Chrome | 74+ |
| Firefox | 78+ |
| Safari | 14.1+ |
| Edge | 79+ |
Users must grant microphone permissions when prompted. Camera permissions are needed if the user’s video is enabled.