WORLDSTREAMS

Tuning Into Reality

Connecting to the world

Skip to documentation
Sequencer
Live avatar documentation
Markdown

Build with Anima
live avatars.

Build real-time video avatars, voice chat, and text conversations with the API or your AI assistant.

From portrait to conversation

  1. Create your avatarAdd an image, voice, and personality.
  2. Start a sessionChoose video, voice, or text.
  3. Connect your appStream replies to your users.

Build with AI / MCP

Use Sequencer in ChatGPT or Claude for assistant-driven setup. For an app integration, give your coding assistant these docs as Markdown.

Assistant setup prompt
Use Sequencer to create a live avatar from my attached portrait. Choose a warm English voice, keep replies short, and prepare it for a conversation. Share the avatar link and quote the cost before starting a call.
MCP serverhttps://mcp.sequencer.media/
MCP tool reference
ToolUse
list_live_avatarsFind existing avatars before creating one.
list_live_avatar_voicesFind a real preset voice and its voiceId.
create_live_avatarSave an avatar draft. Supply a catalog voice ID or custom voice reference.
get_live_avatarRead settings, active version, and preparation progress.
update_live_avatarUpdate supplied fields. Read the avatar first.
prepare_live_avatarPrepare the saved avatar for live video.
get_live_avatar_readinessCheck preparation and runtime readiness.
quote_live_avatar_sessionRead the USD reservation for a bounded conversation.
start_live_avatar_sessionStart an approved, bounded voice and video conversation.
send_live_avatar_messageSend text into the active voice conversation.
stop_live_avatar_sessionEnd the call and settle connected-time billing.
link_live_avatar_to_worldstreamConnect an avatar to an existing Worldstream draft.

API quickstart

Create an API key, then list your workspaces. Keep the key on your server and send it in the x-api-key header.

Base URLhttps://api.sequencer.media/v1
Find your workspacejavascript
// Node.js 20+, on your server
const response = await fetch('https://api.sequencer.media/v1/workspaces', {
  headers: { 'x-api-key': process.env.SEQUENCER_API_KEY }
});
const data = await response.json();
if (!response.ok) throw new Error(data.error || 'Request failed');
console.log(data.workspaces); // Select a workspace's id
Set your environment variables
Server configurationbash
# Server environment, or your local shell for the cURL examples.
export SEQUENCER_API_KEY="YOUR_API_KEY"
export SEQUENCER_API_BASE="https://api.sequencer.media/v1"
export APP_ORIGIN="https://your-app.example"
# Set SEQUENCER_WORKSPACE_ID after listing workspaces.
# Set SEQUENCER_CHARACTER_ID after creating your avatar.

Use workspaces[].id as SEQUENCER_WORKSPACE_ID. Examples use server environment variables. The browser receives session credentials from your authenticated backend.

Create an avatar

Choose a voice from the catalog, then send your portrait and personality. Save liveCharacter.id as SEQUENCER_CHARACTER_ID.

POST /live-charactersbash
curl --fail-with-body -X POST \
  "$SEQUENCER_API_BASE/workspaces/$SEQUENCER_WORKSPACE_ID/live-characters" \
  -H "x-api-key: $SEQUENCER_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"name\": \"Maya\",
    \"avatarImageUrl\": \"https://your-cdn.example/maya.jpg\",
    \"voiceSource\": \"preset\",
    \"providerVoiceId\": \"$SEQUENCER_VOICE_ID\",
    \"systemPrompt\": \"You are Maya, a friendly, playful guide. Speak casually and keep replies to one or two sentences.\",
    \"runtimeProvider\": \"sequencer\",
    \"animaMode\": \"fast\",
    \"language\": \"en\",
    \"maxSessionMinutes\": 5
  }"

# Save liveCharacter.id from the response.
export SEQUENCER_CHARACTER_ID="your_character_id"
avatarImageUrl
Stable HTTPS portrait URL.
systemPrompt
Role, tone, and response instructions.
knowledgeText
Facts your avatar should know.
openingLines
Optional greetings to choose from.
orientation
square, vertical, or horizontal.
animaMode
fast or max (when enabled).
allowedOrigins
Web app origins permitted to start sessions.
maxSessionSeconds
Maximum call duration for this avatar.

Pass image and voice URLs when creating or updating. Direct portrait and voice uploads are available through the Sequencer interface.

Choose a voice

Use voices[].voiceId as providerVoiceId. Set voiceSource to preset or custom.

GET /live-character-voices/presetsbash
# Use an id from workspaces[].id, not a workspace name or URL slug.
export SEQUENCER_WORKSPACE_ID="your_workspace_id"

curl --fail-with-body "$SEQUENCER_API_BASE/workspaces/$SEQUENCER_WORKSPACE_ID/live-character-voices/presets" \
  -H "x-api-key: $SEQUENCER_API_KEY"

# Select voices[].voiceId from the returned catalog.
export SEQUENCER_VOICE_ID="voice_id_from_catalog"

Listen with GET /live-character-voices/presets/{voiceId}/preview. It returns audio bytes.

Prepare for video

Prepare the saved avatar, then check liveCharacter.status is active and runtimeStatus is ready. Voice-only and text chat can start without video preparation.

Prepare and check progressbash
curl --fail-with-body --max-time 900 -X POST \
  "$SEQUENCER_API_BASE/workspaces/$SEQUENCER_WORKSPACE_ID/live-characters/$SEQUENCER_CHARACTER_ID/prepare" \
  -H "x-api-key: $SEQUENCER_API_KEY"

# Get the latest configuration and preparation status.
curl --fail-with-body \
  "$SEQUENCER_API_BASE/workspaces/$SEQUENCER_WORKSPACE_ID/live-characters/$SEQUENCER_CHARACTER_ID" \
  -H "x-api-key: $SEQUENCER_API_KEY"

While preparation is queued or running, poll the avatar. If it fails, inspect runtimeError before retrying.

Your backend

Start sessions from your server. Choose the channels your app needs, then return the session credentials to that user.

ExperiencetextvoicevideoConnection
Video avatartruetruetrueLiveKit audio and video
Voice onlytruetruefalseWebSocket audio and transcripts
Text onlytruefalsefalseWebSocket transcripts
avatar-api.js · Node.js 20+javascript
// avatar-api.js, server only, Node.js 20+
const BASE = 'https://api.sequencer.media/v1';
const key = process.env.SEQUENCER_API_KEY;
const workspace = process.env.SEQUENCER_WORKSPACE_ID;
const character = process.env.SEQUENCER_CHARACTER_ID;
if (!key || !workspace || !character) throw new Error('Missing avatar configuration');
const wsPath = '/workspaces/' + encodeURIComponent(workspace);

export async function avatarApi(path, { method = 'GET', body, origin } = {}) {
  const response = await fetch(BASE + wsPath + path, {
    method,
    headers: {
      'x-api-key': key,
      'Content-Type': 'application/json',
      // For sessions, set this to your configured web app origin.
      ...(origin ? { Origin: origin } : {})
    },
    ...(body ? { body: JSON.stringify(body) } : {})
  });
  const data = await response.json();
  if (!response.ok) {
    const error = new Error(data.error || 'Avatar API request failed');
    error.status = response.status;
    error.retryAfter = response.headers.get('Retry-After');
    throw error;
  }
  return data;
}

export function startAvatar(userId, channels, conversationHistory = [], startToken) {
  return avatarApi('/live-characters/' + encodeURIComponent(character) + '/sessions', {
    method: 'POST',
    origin: process.env.APP_ORIGIN, // e.g. https://your-app.example
    body: {
      launchMode: 'webrtc', sessionMode: 'conversation',
      channels, endUserId: userId, maxSessionSeconds: 300,
      saveHistory: false,
      ...(conversationHistory.length ? { conversationHistory } : {}),
      ...(startToken ? { startToken } : {})
    }
  });
}

export function endAvatar(sessionId) {
  return avatarApi('/live-character-sessions/' + encodeURIComponent(sessionId) + '/end', {
    method: 'POST', body: { reason: 'USER_CLOSED' }
  });
}

Set SEQUENCER_API_KEY, SEQUENCER_WORKSPACE_ID, SEQUENCER_CHARACTER_ID, and APP_ORIGIN. Supply your app’s authentication and session/history stores. Store session ownership and authorize every end or message request.

Video avatar

Start a video conversation, then connect with livekitUrl and livekitClientToken. Attach the avatar’s audio/video tracks and publish the user’s microphone.

POST /live-characters/{characterId}/sessionsbash
curl --fail-with-body -X POST \
  "$SEQUENCER_API_BASE/workspaces/$SEQUENCER_WORKSPACE_ID/live-characters/$SEQUENCER_CHARACTER_ID/sessions" \
  -H "x-api-key: $SEQUENCER_API_KEY" \
  -H "Origin: https://your-app.example" \
  -H "Content-Type: application/json" \
  -d '{
  "launchMode": "webrtc",
  "sessionMode": "conversation",
  "channels": {
    "text": true,
    "voice": true,
    "video": true
  },
  "endUserId": "visitor_123",
  "saveHistory": false,
  "maxSessionSeconds": 300
}'

Browser examples use the backend routes above. Start from a user click. End the API session after disconnecting. For an explicit spending cap, use quote and set a cost cap.

Voice-only chat

Use the avatar’s personality and voice in an audio conversation. Set video: false and connect to conversationStream.

  1. Request microphone access from a user click, then start a voice session.
  2. Open the WebSocket, send the session token, and wait for ready.
  3. Send microphone_start, wait for microphone_ready, then stream mono PCM16 audio. Play returned audio events and display transcript events.
Voice channels · server requestbash
curl --fail-with-body -X POST \
  "$SEQUENCER_API_BASE/workspaces/$SEQUENCER_WORKSPACE_ID/live-characters/$SEQUENCER_CHARACTER_ID/sessions" \
  -H "x-api-key: $SEQUENCER_API_KEY" \
  -H "Origin: https://your-app.example" \
  -H "Content-Type: application/json" \
  -d '{
  "launchMode": "webrtc",
  "sessionMode": "conversation",
  "channels": {
    "text": true,
    "voice": true,
    "video": false
  },
  "endUserId": "visitor_123",
  "saveHistory": false,
  "maxSessionSeconds": 300
}'

Serve microphone access over HTTPS or localhost. Call the cleanup function returned by startVoiceChat() from your End button and on navigation. Outgoing audio is binary little-endian PCM16; incoming audio is base64 PCM16 at each event’s sampleRate. Clear queued playback on interrupt.

Text chat

Set voice: false and video: false. Use the same WebSocket helper to send text_turn and receive transcript events. Replace streamed messages by their id.

Text channels · server requestbash
curl --fail-with-body -X POST \
  "$SEQUENCER_API_BASE/workspaces/$SEQUENCER_WORKSPACE_ID/live-characters/$SEQUENCER_CHARACTER_ID/sessions" \
  -H "x-api-key: $SEQUENCER_API_KEY" \
  -H "Origin: https://your-app.example" \
  -H "Content-Type: application/json" \
  -d '{
  "launchMode": "webrtc",
  "sessionMode": "conversation",
  "channels": {
    "text": true,
    "voice": false,
    "video": false
  },
  "endUserId": "visitor_123",
  "saveHistory": false,
  "maxSessionSeconds": 300
}'

Your own voice agent

Already have a voice agent? Set sessionMode: audio_stream and send its speech through LiveKit to produce synchronized avatar video.

audio_stream · server requestbash
curl --fail-with-body -X POST \
  "$SEQUENCER_API_BASE/workspaces/$SEQUENCER_WORKSPACE_ID/live-characters/$SEQUENCER_CHARACTER_ID/sessions" \
  -H "x-api-key: $SEQUENCER_API_KEY" \
  -H "Origin: https://your-app.example" \
  -H "Content-Type: application/json" \
  -d '{
  "launchMode": "webrtc",
  "sessionMode": "audio_stream",
  "channels": {
    "text": false,
    "voice": true,
    "video": true
  },
  "endUserId": "visitor_123",
  "saveHistory": false,
  "maxSessionSeconds": 300
}'

Prepare the avatar for video first. Publish your agent’s speech as the microphone track. Keep it alive through pauses, then disconnect and end the session.

Manage sessions

Keep the returned sessionId to send messages, read metrics, and end the call. Bounded starts also return avatarSessionId.

Send a text turn from your serverbash
curl --fail-with-body -X POST \
  "$SEQUENCER_API_BASE/workspaces/$SEQUENCER_WORKSPACE_ID/live-character-sessions/{sessionId}/messages" \
  -H "x-api-key: $SEQUENCER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "message": "Hey Maya, what should we try first?" }'
Billing
Session start reserves funds. End reconciles usage and returns unused reservation. Text, voice, and video have different rates.
Video cost cap
Quote reservedUsd, then pass an approved maxCostUsd and maxSessionSeconds (30–3,600) when starting video. The quote is for video conversations.
History
Store transcripts in your backend. Supply up to 60 user/assistant messages in conversationHistory, with saveHistory: false and your authenticated endUserId.
Errors
Check the HTTP status and JSON error. Verify key and workspace access for 401/403. Honor Retry-After on 429. End sessions after connection failure.

All avatar API endpoints

Expand an endpoint for request and response details. Paths below are relative to /v1/workspaces/{workspaceId}, except /workspaces.

Workspaces & characters

GETList workspaces/workspaces
/v1/workspaces
Request
No body. Requires an API key.
Response
workspaces[].id and workspace metadata. Use the id in subsequent paths.
GETList avatars/live-characters
/v1/workspaces/{workspaceId}/live-characters
Request
No body.
Response
liveCharacters[] with saved configuration and preparation status.
POSTCreate an avatar/live-characters
/v1/workspaces/{workspaceId}/live-characters
Request
JSON: name, avatarImageUrl, systemPrompt, voiceSource, providerVoiceId (preset) or voiceReferenceUrl and voiceDescription (custom). Set runtimeProvider to sequencer. Optional knowledgeText, openingLines, language, orientation, animaMode, allowedOrigins, maxSessionSeconds, cerebro and avatarAnimationSettings.
Response
201. liveCharacter.id, configuration and status. Save the id as characterId.
GETGet an avatar/live-characters/{characterId}
/v1/workspaces/{workspaceId}/live-characters/{characterId}
Request
No body.
Response
liveCharacter, including runtimeStatus, prepStatus, runtimeError, activeVersionId and customAnimations. For video, wait for status: active and runtimeStatus: ready.
PATCHUpdate an avatar/live-characters/{characterId}
/v1/workspaces/{workspaceId}/live-characters/{characterId}
Request
JSON: only changed creation fields. status can be active, paused or archived. Image, voice and mode changes can require new video preparation.
Response
Updated liveCharacter.
POSTPrepare video assets/live-characters/{characterId}/prepare
/v1/workspaces/{workspaceId}/live-characters/{characterId}/prepare
Request
No body required. Poll GET avatar for progress.
Response
liveCharacter, prepared and prepJobId. Queued work can outlive the request. Skip video preparation for text and voice-only sessions.
DELETEArchive an avatar/live-characters/{characterId}
/v1/workspaces/{workspaceId}/live-characters/{characterId}
Request
No body. Requires write access.
Response
Archives the avatar. New sessions cannot start for an archived character.
POSTPreview a workspace move/live-characters/move/preview
/v1/workspaces/{workspaceId}/live-characters/move/preview
Request
JSON: characterIds (array). Requires write access to the source workspace.
Response
Affected characters, worlds, counts and canMoveCharactersOnly. Review dependencies before moving.
POSTMove avatars/live-characters/move
/v1/workspaces/{workspaceId}/live-characters/move
Request
JSON: characterIds, targetWorkspaceId, includeWorlds (boolean). Requires write access to both workspaces. Preview dependencies first.
Response
Moved characterIds, sourceWorkspaceId, targetWorkspaceId, movedCount, movedWorldCount and warnings.

Voices & preparation

GETList preset voices/live-character-voices/presets
/v1/workspaces/{workspaceId}/live-character-voices/presets
Request
No body. Filter the returned catalog in your application.
Response
voices[].voiceId and voice metadata. Pass voiceId as providerVoiceId when saving an avatar.
GETPreview a voice/live-character-voices/presets/{voiceId}/preview
/v1/workspaces/{workspaceId}/live-character-voices/presets/{voiceId}/preview
Request
voiceId from the catalog.
Response
Audio bytes with the appropriate Content-Type. This response is not JSON.
POSTProvision generated voice audio/live-character-voices/provision
/v1/workspaces/{workspaceId}/live-character-voices/provision
Request
JSON: sourceMediaId, voiceReferenceUrl, voiceDescription (at least 20 characters), language (optional). sourceMediaId must identify completed audio in this workspace and its URL must match. For a normal custom voice, set voiceReferenceUrl on the avatar instead.
Response
200 when ready, otherwise 202. Voice draft status and providerVoiceId when available.
POSTDescribe a portrait/live-character-visual-description
/v1/workspaces/{workspaceId}/live-character-visual-description
Request
JSON: avatarImageUrl (or sourceImageUri). Requires write access.
Response
characterVisualDescription generated from the image.
POSTCheck or warm the runtime/live-character-runtime/prepare
/v1/workspaces/{workspaceId}/live-character-runtime/prepare
Request
JSON: characterId, statusOnly, ttlSeconds and waitSeconds (optional). statusOnly: true inspects the cache. Warming can incur costs and requires write access.
Response
200 if ready, otherwise 202. ready, status and cache or setup estimates. This is separate from preparing the saved avatar.

Sessions & playback

POSTStart a session/live-characters/{characterId}/sessions
/v1/workspaces/{workspaceId}/live-characters/{characterId}/sessions
Request
JSON: launchMode: webrtc, sessionMode: conversation or audio_stream, channels: {text, voice, video}, endUserId and maxSessionSeconds. Optional startToken, initialText, dynamicVariables, conversationHistory and saveHistory: false. Forward your configured web app Origin header. For a cost cap, maxCostUsd is supported on video conversation sessions only.
Response
201. sessionId, channels, billing and connection credentials. Video returns livekitUrl and livekitClientToken. Text and voice-only return conversationStream.url and conversationStream.token.
POSTQuote a bounded video call/live-characters/{characterId}/sessions/quote
/v1/workspaces/{workspaceId}/live-characters/{characterId}/sessions/quote
Request
JSON: maxSessionSeconds (integer, 30 to 3600, within the avatar limit). This quote is for video conversation sessions.
Response
characterId, maxSessionSeconds, currency: usd, reservedUsd, ready and preparationRequired.
POSTStart a bounded video call/live-characters/{characterId}/bounded-sessions
/v1/workspaces/{workspaceId}/live-characters/{characterId}/bounded-sessions
Request
JSON: maxSessionSeconds and maxCostUsd covering the quote. Optional endUserId and legacy conversationId. Requires a prepared avatar. Uses text, voice and video channels.
Response
201. Video session connection fields plus avatarSessionId and maxCostUsd. Use avatarSessionId for message, metrics and end paths.
POSTCancel a pending start/live-character-session-starts/{startToken}/cancel
/v1/workspaces/{workspaceId}/live-character-session-starts/{startToken}/cancel
Request
Use the startToken you supplied when starting. No body required.
Response
Cancellation status. If a start completes with a sessionId, end that session too.
POSTSend a text turn/live-character-sessions/{sessionId}/messages
/v1/workspaces/{workspaceId}/live-character-sessions/{sessionId}/messages
Request
JSON: message. Up to 4,000 characters. Requires text channels and conversation mode. Receive replies through WebSocket transcripts or LiveKit playback.
Response
Video returns an acknowledgement. Lightweight sessions can return message and messageId. Use WebSocket transcript events for streamed replies.
GETRead session metrics/live-character-sessions/{sessionId}/metrics
/v1/workspaces/{workspaceId}/live-character-sessions/{sessionId}/metrics
Request
Optional query: live=true for current runtime diagnostics.
Response
Session usage, billing and available runtime diagnostics.
POSTEnd a session/live-character-sessions/{sessionId}/end
/v1/workspaces/{workspaceId}/live-character-sessions/{sessionId}/end
Request
JSON: reason (optional, defaults to USER_CLOSED). Close browser audio/video connections too.
Response
Ends the session and reconciles billing. Unused reservation is returned.
POSTStart a preset avatar/live-character-presets/{presetId}/sessions
/v1/workspaces/{workspaceId}/live-character-presets/{presetId}/sessions
Request
A supported presetId and the same body as Start a session. Forward the configured app Origin.
Response
201. A new session with the selected preset and the requested channels.
GETCheck a preset avatar/live-character-presets/{presetId}/status
/v1/workspaces/{workspaceId}/live-character-presets/{presetId}/status
Request
A supported presetId. No body.
Response
Preset availability and preparation status.
GETRead the legacy embed page/live-character-sessions/{sessionId}/embed
/v1/workspaces/{workspaceId}/live-character-sessions/{sessionId}/embed
Request
Query: token, the session embed capability. This is an HTML status page. Use LiveKit to implement working live video playback.
Response
HTML. A token is required. Video is only attached when a direct playback URL is enabled; this is not a ready-made LiveKit player.

Conversation history

GETRead legacy hosted history/avatar-conversations/{conversationId}
/v1/workspaces/{workspaceId}/avatar-conversations/{conversationId}
Request
Optional query: endUserId, limit and cursor. Scope access to the original user. New API integrations use their own history store and supply conversationHistory at session start.
Response
Authorized hosted conversation and paginated messages. Existing hosted IDs can resume; new API sessions use client-managed history.
DELETEDelete legacy hosted history/avatar-conversations/{conversationId}
/v1/workspaces/{workspaceId}/avatar-conversations/{conversationId}
Request
Optional query: endUserId. Requires authorized access to this conversation.
Response
202. Schedules history deletion.
GETRead Worldstream history/worldstream-avatar-conversations/{conversationId}
/v1/workspaces/{workspaceId}/worldstream-avatar-conversations/{conversationId}
Request
Signed-in viewer access to their own Worldstream conversation. Optional query: limit and cursor.
Response
Viewer-scoped hosted conversation history.
DELETEDelete Worldstream history/worldstream-avatar-conversations/{conversationId}
/v1/workspaces/{workspaceId}/worldstream-avatar-conversations/{conversationId}
Request
Signed-in viewer access to their own Worldstream conversation.
Response
202. Schedules viewer-scoped history deletion.

Animations & Worldstreams

POSTUpload an animation/live-characters/{characterId}/custom-animations/upload
/v1/workspaces/{workspaceId}/live-characters/{characterId}/custom-animations/upload
Request
multipart/form-data: file (video clip), name and trigger. Optional animationId to replace a clip.
Response
202. customAnimation.id and preparation status.
PATCHUpdate an animation/live-characters/{characterId}/custom-animations/{animationId}
/v1/workspaces/{workspaceId}/live-characters/{characterId}/custom-animations/{animationId}
Request
JSON: name and/or trigger guidance.
Response
Updated custom animation metadata.
POSTPrepare an animation/live-characters/{characterId}/custom-animations/{animationId}/prepare
/v1/workspaces/{workspaceId}/live-characters/{characterId}/custom-animations/{animationId}/prepare
Request
Optional JSON: force (boolean). Read the avatar for animation progress.
Response
200 when prepared, otherwise 202.
DELETEDelete an animation/live-characters/{characterId}/custom-animations/{animationId}
/v1/workspaces/{workspaceId}/live-characters/{characterId}/custom-animations/{animationId}
Request
No body.
Response
Removes the animation from the character.
POSTPlay an animation/live-character-sessions/{sessionId}/animations/trigger
/v1/workspaces/{workspaceId}/live-character-sessions/{sessionId}/animations/trigger
Request
JSON: animationId of a prepared motion. Requires a compatible active video conversation.
Response
Animation trigger acknowledgement.
POSTLink an avatar to a Worldstream/live-worlds/{liveWorldId}/avatar-links
/v1/workspaces/{workspaceId}/live-worlds/{liveWorldId}/avatar-links
Request
JSON: characterId and expectedVersionId from the current Worldstream draft.
Response
Updated draft with the avatar link. A stale expectedVersionId is rejected.