Skip to content

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.

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 │ │
│ ─────────────────────────────────────────────────────►│
│ │ │

Install both the server SDK and React components:

Terminal window
npm install @runwayml/sdk @runwayml/avatars-react

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.

app/api/avatar/session/route.ts
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:

.env.local
RUNWAYML_API_SECRET=your_api_key_here

The simplest way to add an Avatar is with the AvatarCall component. It handles WebRTC connection and renders a default UI.

app/page.tsx
'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.

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:

components/CustomAvatarUI.tsx
'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.

The Avatars SDK uses WebRTC for real-time communication:

BrowserMinimum Version
Chrome74+
Firefox78+
Safari14.1+
Edge79+

Users must grant microphone permissions when prompted. Camera permissions are needed if the user’s video is enabled.