Skip to main content

Migrate from HeyGen

HeyGen is a strong platform and we respect their team. Because HeyGen's interactive avatars already run over LiveKit — the same transport we use — most of your client-side code carries over, and the migration is mainly about a simpler object model and a shorter session handshake. The HeyGen snippets reflect its streaming.* API and are shown for orientation.

Two things are worth knowing up front:

  • One object instead of two. HeyGen binds an avatar_id (the look) and a separate voice_id (the sound) per session. Here they are a single identity: face, voice, and personality in one object you create once and reuse by identity_id.
  • One call instead of a handshake. HeyGen's streaming path is streaming.create_token → streaming.new → streaming.start, and you then drive speech with repeated streaming.task calls. Lookalike is one sessions/start, and the agent converses on its own — no per-utterance calls.

How the concepts map​

HeyGenLookalikeNotes
Avatar (avatar_id)part of an identityThe look
Voice (voice_id)part of an identityThe sound
avatar_id + voice_id (two refs)one identity_idBundled and reused
streaming.create_token + streaming.new + streaming.startPOST /v1/sessions/startOne call
streaming.task (repeat / talk)native conversationThe agent speaks on its own
streaming.stopdisconnect or POST /v1/sessions/{session_id}/endLeave the LiveKit room, or end it explicitly
X-Api-Key headerAuthorization: Bearer or X-API-KeyKeys start with lk_
Usage credits / concurrent sessionscreditsOne balance for onboarding and realtime

Authenticate​

HeyGen sends X-Api-Key against https://api.heygen.com. Lookalike sends a bearer token (or X-API-Key) against https://api.lookalike.com, and keys start with lk_. Get one in the console.

HeyGen

curl https://api.heygen.com/v1/streaming/avatar.list -H "X-Api-Key: $HEYGEN_API_KEY"

Lookalike

curl https://api.lookalike.com/v1/identities -H "Authorization: Bearer $LOOKALIKE_API_KEY"

Create the avatar​

In HeyGen you reference an avatar_id (a stock interactive avatar, or a Photo/Instant avatar you created) plus a separate voice_id, and wire them together at session time. In Lookalike you create one identity — face, optional voice clip, and personality prompt — in a single call that trains inline and returns ready.

Lookalike

curl -sS -X POST https://api.lookalike.com/v1/identities \
-H "Authorization: Bearer $LOOKALIKE_API_KEY" \
-F "name=Ada" \
-F "prompt=You are Ada, a warm and concise product guide." \
-F "image=@ada.jpg;type=image/jpeg" \
-F "audio=@ada.wav;type=audio/wav"
# -> { "identity_id": "id_...", "status": "ready" }

See Create an Identity for the full field list.

Start a session​

HeyGen mints a short-lived token, opens a session against an avatar_id + voice, then starts it. Lookalike takes just the identity_id and returns the LiveKit room, token, and URL directly.

HeyGen

# 1. mint a short-lived client token
curl -sS -X POST https://api.heygen.com/v1/streaming.create_token \
-H "X-Api-Key: $HEYGEN_API_KEY"
# -> { "data": { "token": "..." } }

# 2. open the streaming session (LiveKit-backed)
curl -sS -X POST https://api.heygen.com/v1/streaming.new \
-H "X-Api-Key: $HEYGEN_API_KEY" -H "Content-Type: application/json" \
-d '{ "version": "v2", "quality": "high",
"avatar_id": "Ada_public",
"voice": { "voice_id": "en-US-...", "rate": 1.0 } }'
# -> { "data": { "session_id": "...", "url": "wss://heygen-xxxx.livekit.cloud",
# "access_token": "..." } }

# 3. start it
curl -sS -X POST https://api.heygen.com/v1/streaming.start \
-H "X-Api-Key: $HEYGEN_API_KEY" -H "Content-Type: application/json" \
-d '{ "session_id": "..." }'

Lookalike

curl -sS -X POST https://api.lookalike.com/v1/sessions/start \
-H "Authorization: Bearer $LOOKALIKE_API_KEY" -H "Content-Type: application/json" \
-d '{ "identity_id": "id_3f9a2b7c4d1e8a05f6b3" }'
# -> { "room_name": "...", "token": "...", "livekit_url": "wss://..." }

The token is minted server-side and already encodes the room. See Realtime API for the full response.

Join the call​

Both HeyGen and Lookalike hand you a LiveKit URL and token, so your join code is the same — point your LiveKit client at ours (livekit_url + token) instead of HeyGen's url + access_token.

import { Room, RoomEvent, Track } from 'livekit-client';

// livekitUrl and token come from the sessions/start response
const room = new Room();

room.on(RoomEvent.TrackSubscribed, (track) => {
if (track.kind === Track.Kind.Video) {
document.getElementById('avatar').appendChild(track.attach());
} else if (track.kind === Track.Kind.Audio) {
document.body.appendChild(track.attach());
}
});

await room.connect(livekitUrl, token);
await room.localParticipant.setMicrophoneEnabled(true); // so the avatar can hear you
Avatar audio

When an avatar is enabled it publishes both audio and video. Attach both, but don't also play the agent's raw voice track, or you'll hear it twice.

Drive the conversation​

This is the biggest simplification. In HeyGen you push each utterance with streaming.task — repeat to speak your own LLM's output verbatim, or talk to use HeyGen's built-in LLM. In Lookalike the agent already runs the LLM and speech: the user just talks into the mic and the avatar replies, so there are no per-utterance calls.

HeyGen

curl -sS -X POST https://api.heygen.com/v1/streaming.task \
-H "X-Api-Key: $HEYGEN_API_KEY" -H "Content-Type: application/json" \
-d '{ "session_id": "...", "text": "Hello and welcome.", "task_type": "repeat" }'

Lookalike — nothing to send; the enabled microphone drives the conversation, and the agent handles turns and interruptions natively.

End the session​

HeyGen ends a session with streaming.stop. On Lookalike you can do either of two things: disconnect from the room, or call the End Session endpoint.

HeyGen

curl -sS -X POST https://api.heygen.com/v1/streaming.stop \
-H "X-Api-Key: $HEYGEN_API_KEY" -H "Content-Type: application/json" \
-d '{ "session_id": "..." }'

Lookalike

Disconnect the client:

await room.disconnect();

Or end it from your backend, passing the room_name from sessions/start as the session_id:

curl -sS -X POST https://api.lookalike.com/v1/sessions/ROOM_NAME/end \
-H "Authorization: Bearer $LOOKALIKE_API_KEY"

Billing​

Both platforms meter credits. On Lookalike, onboarding an identity is a one-time charge and a realtime session is charged per minute while the avatar is streaming, all from one balance. Buy credits and view usage in the Console; full rates are on the Billing & credits page. A request that would spend credits you don't have returns 402.

What's different​

  • One identity, not an avatar plus a voice. Face, voice, and personality live in one identity_id you create once.
  • One call to go live. No create_token → new → start sequence.
  • Conversational by default. No streaming.task loop — the agent listens and speaks on its own once the mic is on.
  • Same LiveKit transport. Your existing join and track-handling code carries over.

Next steps​