Guides / Creating avatars
Creating avatars
Bring your own face: upload a custom image or video and get a streaming avatar you can drive from your app. Do it once in the portal, or entirely through the API.
From the portal
Sign in at /portal/avatars, paste an API key, and drop in a photo or a short video. A short video gives the most lifelike result: real head motion, captured from you. A still photo works too, and becomes a talking photo: the mouth moves in sync with the audio while the head stays still. When it finishes preparing you get an avatar id and a snippet to stream it. Manage your keys at /portal/keys.
From the API
Create the same avatar programmatically. A video source gives real head motion; a still photo gives a talking photo: moving mouth, still head.
from zeli import ZeliClient, AvatarConfig, ClientOptions
client = ZeliClient(
api_key="YOUR_API_KEY",
options=ClientOptions(server_url="https://zeli-avatar.zelibot.xyz"),
)
status = await client.create_avatar("founder.mp4", name="founder")
print(status.avatar_id, status.status) # e.g. "founder" "preparing"create_avatar returns an AvatarStatus (preparing or ready). Poll
list_avatars until it's ready:
from zeli._avatars import avatar_is_ready
while not avatar_is_ready(await client.list_avatars(), status.avatar_id):
await asyncio.sleep(2.5)Processing and consent
Avatar creation and live conversation processing are separate operations. Review the creation flow's provider and region information before uploading a face, and obtain the required consent. Some optional creation providers process images outside Australia. The selected speech and language-model providers determine conversation processing regions independently.
Natural motion from a photo
POST /v1/photo_avatar.create builds motion clips when that capability is available for your deployment and the submitted face passes consent checks. A deployment without the capability returns 501 feature_disabled; unverified consent is rejected. Use create_avatar above for the supported talking-photo upload flow.
The SDK method and its request/response shape are settled, and the call carries the server-side consent verification the pipeline requires:
created = await client.create_avatar_from_photo(
"headshot.jpg", avatar_id="founder", tones=["neutral", "warm"],
consent_token="<from your consent flow>",
)
print(created.avatar_id, [c.tone for c in created.clips])If consent isn't verified you get a ConsentError (with .decision); a rejected
key raises AuthenticationError. On a server without the feature enabled, the
501 surfaces as a ZeliError with code
feature_disabled.
Then stream it
Once an avatar is ready, drive it like any built-in avatar. Reference it by id:
client = ZeliClient(
avatar_config=AvatarConfig(avatar_id="founder"),
api_key="YOUR_API_KEY",
options=ClientOptions(server_url="https://zeli-avatar.zelibot.xyz"),
)
async with client.connect() as session:
await session.talk("Hi, welcome to the demo.")
async for frame in session.video_frames():
img = frame.to_ndarray(format="rgb24")See Connecting and Driving the avatar for the full streaming loop.