Create an Identity
An identity is a talking-head avatar — a face and a voice the renderer can drive from text. You build one once by uploading reference media, and from then on reuse the same identity_id in the Realtime API.
Creating an identity uploads its reference media and trains it in a single call — when you include a visual reference, the response comes back with the identity already ready to use.
Authentication
Every /v1/* request needs an API key. Keys start with lk_. Send it either way:
| Header | Value |
|---|---|
Authorization | Bearer lk_live_8f3c... |
X-API-Key | lk_live_8f3c... |
A missing, invalid, or revoked key returns 401. The base URL is https://api.lookalike.com; every path below hangs off it. The examples read the key from $LOOKALIKE_API_KEY.
Create an identity
POST /v1/identities — multipart/form-data.
| Field | Type | Required | Notes |
|---|---|---|---|
name | text | yes | Display name, min length 1. |
prompt | text | yes | The avatar's system prompt, min length 1. |
image | file | yes | image/jpeg, image/png, image/webp. |
audio | file | no | audio/wav, audio/mpeg, audio/mp4. A short clip (10–30 seconds) is plenty; 2 minutes is the maximum. |
language | text | no | Conversation language. One of auto, en, es, pl, uk; defaults to auto. |
pipeline_id | text | no | Pipeline the identity is built on. It decides which renderer serves the identity for the rest of its life, so it cannot be changed later - create a new identity instead. Omit it to use the default. |
- cURL
- Python
- Node.js
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" \
-F "language=en"
import httpx
with open("ada.jpg", "rb") as img, open("ada.wav", "rb") as aud:
resp = httpx.post(
"https://api.lookalike.com/v1/identities",
headers={"Authorization": f"Bearer {API_KEY}"},
data={
"name": "Ada",
"prompt": "You are Ada, a warm and concise product guide.",
"language": "en",
},
files=[
("image", ("ada.jpg", img, "image/jpeg")),
("audio", ("ada.wav", aud, "audio/wav")),
],
)
resp.raise_for_status()
identity = resp.json()
import { openAsBlob } from 'node:fs';
const form = new FormData();
form.set('name', 'Ada');
form.set('prompt', 'You are Ada, a warm and concise product guide.');
form.set('image', await openAsBlob('ada.jpg', { type: 'image/jpeg' }), 'ada.jpg');
form.set('audio', await openAsBlob('ada.wav', { type: 'audio/wav' }), 'ada.wav');
form.set('language', 'en');
const resp = await fetch('https://api.lookalike.com/v1/identities', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.LOOKALIKE_API_KEY}` },
body: form,
});
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const identity = await resp.json();
Because this request uploads an image, the identity trains inline and 200 OK returns an IdentityResponse with status: "ready". The uploaded files come back as HTTPS URLs under image and audio.
{
"identity_id": "id_3f9a2b7c4d1e8a05f6b3",
"name": "Ada",
"language": "en",
"prompt": "You are Ada, a warm and concise product guide.",
"image": "https://assets.lookalike.com/uploads/ada.jpg",
"audio": "https://assets.lookalike.com/uploads/ada.wav",
"status": "ready",
"detail": null,
"created_at": "2026-06-26T09:10:27.994Z",
"updated_at": "2026-06-26T09:14:48.501Z"
}
Choose a language
| Value | Behavior |
|---|---|
auto | English greeting, then follow the visitor's detected language |
en | Always speak English |
es | Always speak Spanish |
pl | Always speak Polish |
uk | Always speak Ukrainian |
language is optional and defaults to auto. It is stored on the identity and returned by create, get, and list endpoints.
Give the identity a voice: every value except en requires an ElevenLabs voice, so upload reference audio or pick a preset voice. Sessions on a voiceless identity are rejected with 422. Prefer a voice recorded in the language you intend to pin - the model will pronounce the words correctly either way, but the speaker's accent carries over.
Swap the value for any other supported language:
-F "language=pl" # Polish
-F "language=uk" # Ukrainian
Anything outside the five values is rejected with 422 before the identity is created, so a typo never leaves a half-built identity behind. A single session can override the stored value without changing it - see Start a Session.
Check the status
Since create trains the identity inline, the call above already returns it ready. To re-check state later, GET /v1/identities/{identity_id}/status returns an IdentityStatusResponse — only a ready identity can open a session.
{
"identity_id": "id_3f9a2b7c4d1e8a05f6b3",
"status": "ready",
"updated_at": "2026-06-26T09:14:48.501Z"
}
An identity moves through created → training → ready, and with a visual reference that happens inline during create. Once it's ready, stream it live with the Realtime API.
Notes
- Create once, reuse forever. Reuse the same
identity_idacross as many sessions and renders as you like. readybefore sessions. A session won't open until the identity isready(409); create returns itreadywhen you include a visual reference.- One renderer per identity. The pipeline you create on decides which avatar worker renders the identity, and sessions follow that choice automatically — you never select a renderer when starting one.
Additional resources
- Realtime API — stream the identity live in a LiveKit room.
- API concepts — how identities and sessions fit together.