Skip to content

Catalog hydrate — resolve an avatar by handle

Instead of hardcoding avatar_id values like avt_441ad4d95906438490a85b79 in your app, resolve avatars by their human handle — (owner, avatarname) — via GET /v1/avatars/{owner}/{name}. The gateway returns the record (resolved avatar_id, display name, idle video, optional greeting), and your code stays free of opaque ids.

Shipped 2026-10-06 on the gateway + Flutter SDK. iOS native in preview. Web SDK integration in flight.

  • Your UI shows multiple avatars and you want human-readable ids in your config.
  • You want to let users (or your product’s own logic) pick an avatar by name.
  • You’re building a reseller / embed where customers pick which avatar runs in their deployment.

If you only ever use one hardcoded avatar, this is an optional convenience — nothing breaks without it.

GET /v1/avatars/{owner}/{name}
Authorization: Bearer sk_...

owner + name are the human handles — e.g. alman/dr-alman for Dr. Alman. The gateway resolves either:

  • Public avatars — anyone with a valid sk_ can see them. Owned by a studio creator who’s listed the avatar publicly.
  • Private avatars — only visible to the developer whose sk_ was issued through the Developer Portal, and only when the avatar’s creator has flipped the Studio-side api_exposed_at toggle on. See Avatar Studio API exposure for the opt-in.

Not found: 404 no_such_avatar. (Uniform-404 regardless of whether the avatar doesn’t exist or exists but isn’t visible to your tenant — the response can’t be used to probe for private avatar existence.)

{
"avatar": {
"owner": "alman",
"name": "dr-alman",
"display_name": "Dr. Alman",
"avatar_id": "avt_4586c4366d51bfa70c0df7cd",
"listed": true,
"idle_url": "https://cdn.example.com/alman/idle.mp4",
"poster_url": "https://cdn.example.com/alman/poster.jpg",
"greeting": {
"id": "grt_123",
"video_url": "https://cdn.example.com/alman/intro.mp4",
"text": "Hi, I'm Dr. Alman.",
"kind": "intro",
"consumption_key": null
}
},
"view_token": null
}

Fields:

  • avatar_id — the resolved avt_... id. Pass this to POST /v1/sessions.
  • display_name — human name for your UI.
  • idle_url — background idle video to show before/during warm-up.
  • poster_url — static image before the idle video loads.
  • greeting (optional) — intro video + text the avatar should say on first connect. kind: "intro" plays once per user; kind: "return" plays on repeat visits with consumption_key for server-side idempotency.
  • view_token — reserved for future auth modes; null on T0.
final result = await restClient.getAvatar('alman', 'dr-alman');
print(result.avatar.displayName); // "Dr. Alman"
print(result.avatar.avatarId); // "avt_4586c4366d51bfa70c0df7cd"
// Use the resolved id to mint a session.
final session = await restClient.createLiveKitSession(
TwaiApiLiveKitSessionRequest(avatarId: result.avatar.avatarId),
);

Full SDK docs: Flutter SDK.

StatuscodeWhat it means
200—Avatar resolved.
401invalid_keysk_ wrong or revoked.
404no_such_avatarAvatar doesn’t exist publicly, OR exists privately but isn’t visible to your sk_’s tenant. Uniform 404 — can’t distinguish.
503catalog_hydrate_not_configuredGateway not fully configured for catalog hydrate. Shouldn’t appear in prod post 2026-10-08.

See Errors for the full ladder.