Loading...
Loading...
Help users create Runway Characters (GWM-1 avatars) and integrate real-time conversational sessions into their apps
npx skill4agent add runwayml/skills integrate-charactersPREREQUISITES:
— Project must have a server-side component (API key must NEVER be exposed to the client)+check-compatibility — Load the latest API reference from https://docs.dev.runwayml.com/api/ before integrating+fetch-api-reference — API credentials must be configured+setup-api-keyOPTIONAL DEPENDENCIES:
— Add a knowledge base to your character+integrate-documents — Use the React SDK to embed the avatar call UI+integrate-character-embed
| Concept | Description |
|---|---|
| Avatar | A persistent persona with a defined appearance, voice, and personality. Created once, used many times. |
| Session | A live WebRTC connection for real-time conversation. Connects one user to one avatar. Max duration: 5 minutes. |
┌───────────┐
┌──────────┤ NOT_READY ├──────────┐
│ └─────┬─────┘ │
│ │ │
▼ ▼ ▼
CANCELLED READY FAILED
┌──┴──┐
│ │
▼ ▼
RUNNING FAILED
┌──┴──┐
│ │
▼ ▼
COMPLETED CANCELLED| Status | Description |
|---|---|
| Session is being provisioned. Poll until ready. |
| Session is ready. The |
| WebRTC connection is active. Conversation in progress. |
| Session ended normally. |
| Error occurred. Check the |
| Explicitly cancelled before completion. |
Important: Session credentials can only be consumed once. If the WebRTC connection fails after credentials are consumed, you must create a new Session.
Client (React) → Your Server → Runway API
↓
Client (React) ←─── WebRTC ───← Runway (realtime)POST /v1/realtime_sessionsREADYGET /v1/realtime_sessions/:idPOST /v1/realtime_sessions/:id/consumenpm install @runwayml/sdk @runwayml/avatars-react@runwayml/sdk@runwayml/avatars-react.txt8be4df61-93ca-11d2-aa0d-00e098032b8c// Node.js
import RunwayML from '@runwayml/sdk';
const client = new RunwayML();
const avatar = await client.avatars.create({
name: 'Support Agent',
referenceImage: 'https://example.com/avatar.png',
voice: {
type: 'runway-live-preset',
presetId: 'clara',
},
personality: 'You are a helpful customer support agent for Acme Corp. You help users with billing questions and technical issues.',
});
console.log('Avatar ID:', avatar.id);# Python
from runwayml import RunwayML
client = RunwayML()
avatar = client.avatars.create(
name='Support Agent',
reference_image='https://example.com/avatar.png',
voice={
'type': 'runway-live-preset',
'preset_id': 'clara',
},
personality='You are a helpful customer support agent for Acme Corp.',
)
print('Avatar ID:', avatar.id)+integrate-uploadsimport fs from 'fs';
const upload = await client.uploads.createEphemeral(
fs.createReadStream('/path/to/avatar-image.png')
);
const avatar = await client.avatars.create({
name: 'Support Agent',
referenceImage: upload.runwayUri,
voice: { type: 'runway-live-preset', presetId: 'clara' },
personality: 'You are a helpful customer support agent...',
});| Preset ID | Name | Style |
|---|---|---|
| Clara | Soft, approachable |
| Victoria | Firm, professional |
| Vincent | Knowledgeable, authoritative |
// 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 WebRTC 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,
});
}import RunwayML from '@runwayml/sdk';
import express from 'express';
const client = new RunwayML();
const app = express();
app.use(express.json());
app.post('/api/avatar/session', async (req, res) => {
const { avatarId } = req.body;
try {
// 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 res.status(500).json({ error: session.failure });
}
await new Promise(r => setTimeout(r, 1000));
}
if (!sessionKey) {
return res.status(504).json({ error: 'Session timed out' });
}
// 3. Consume 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();
res.json({
sessionId,
serverUrl: credentials.url,
token: credentials.token,
roomName: credentials.roomName,
});
} catch (error) {
console.error('Session creation failed:', error);
res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' });
}
});import time
import httpx
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from runwayml import RunwayML
app = FastAPI()
client = RunwayML()
class SessionRequest(BaseModel):
avatar_id: str
@app.post("/api/avatar/session")
async def create_session(req: SessionRequest):
# 1. Create session
result = client.realtime_sessions.create(
model='gwm1_avatars',
avatar={'type': 'custom', 'avatar_id': req.avatar_id},
)
session_id = result.id
# 2. Poll until ready
session_key = None
for _ in range(60):
session = client.realtime_sessions.retrieve(session_id)
if session.status == 'READY':
session_key = session.session_key
break
if session.status == 'FAILED':
raise HTTPException(status_code=500, detail=str(session.failure))
time.sleep(1)
if not session_key:
raise HTTPException(status_code=504, detail='Session timed out')
# 3. Consume credentials
async with httpx.AsyncClient() as http:
resp = await http.post(
f"{client.base_url}/v1/realtime_sessions/{session_id}/consume",
headers={
"Authorization": f"Bearer {session_key}",
"X-Runway-Version": "2024-11-06",
},
)
credentials = resp.json()
return {
"session_id": session_id,
"server_url": credentials["url"],
"token": credentials["token"],
"room_name": credentials["roomName"],
}+integrate-character-embed'use client';
import { AvatarCall } from '@runwayml/avatars-react';
import '@runwayml/avatars-react/styles.css';
export default function CharacterPage() {
return (
<AvatarCall
avatarId="your-avatar-id"
connectUrl="/api/avatar/session"
onEnd={() => console.log('Call ended')}
onError={(error) => console.error('Error:', error)}
/>
);
}key_<AvatarCall
avatarId="your-avatar-id"
connectUrl="/api/avatar/session"
onError={(error) => {
console.error('Avatar error:', error);
console.error('Error name:', error.name);
console.error('Error message:', error.message);
if (error.cause) console.error('Cause:', error.cause);
}}
/>import { useAvatarSession } from '@runwayml/avatars-react';
function DebugInfo() {
const { state, sessionId, error } = useAvatarSession();
return (
<pre>
{JSON.stringify({ state, sessionId, error: error?.message }, null, 2)}
</pre>
);
}npx degit runwayml/avatars-sdk-react/examples/nextjs-simple test-app
cd test-app
npm install
# Add your API key to .env.local
npm run dev| Browser | Minimum Version |
|---|---|
| Chrome | 74+ |
| Firefox | 78+ |
| Safari | 14.1+ |
| Edge | 79+ |
| Resource | Description |
|---|---|
| Developer Portal | Manage avatars, view logs, access dashboard |
| SDK Repository | Report bugs, view examples, check releases |
npm list @runwayml/avatars-react