AI video avatar agents
An avatar persona is a tenant-managed binding that holds two references: one of your conversational agents (the dialog brain) and one rendered talking-head provider persona reference — the avatar id you hold at your chosen provider. The persona can then either join a live video room as a digital host, or render a personalized video clip asynchronously from a script template with{{merge_tag}} tokens and a per-recipient merge-field
map.
Reach for a persona when you want the same conversational brain to show up
visually — a concierge onboarding host, a policy-recap clip, a personalized
campaign send — rather than in a voice or messaging-only channel. This guide
walks the whole path from creating the persona to verifying a finished clip.
What you build
One persona registry entry gives you three things:- A persona binding — one registry entry (
vaa_-prefixed) tying one of your conversational agents to one provider persona (for example a Tavus replica), with an optional opening greeting and locale. - One live-room digital host — dispatch the persona into an existing video room; the avatar-runtime joins it as a bot participant that renders the talking head driven by the bound agent’s turns.
- One async personalized clip render — queue a script plus merge fields
and get back a tracked job (
vavr_-prefixed) that ends incompletedwith anoutput_urlyou can attach anywhere, plus a verifiable content credential.
Prerequisites
Have these in hand before you start:- A conversational agent id (
agt_-prefixed). The avatar is a rendered front-end for that agent’s dialog, so you need the agent first — build one in Agents → Studio in the dashboard, or follow Author an AI Agent from a Prompt. - A provider persona reference — the avatar id you hold at your chosen provider — Tavus, HeyGen, or D-ID (for example a Tavus replica id). Provision the provider account with that provider; Orbit stores only the opaque reference id, never provider credentials.
- An API key with the
videoscope — reads needvideo:read; writes (register a persona, script a render, report its status) needvideo:writeplus an owner, admin, or developer role. - Optionally, a scheduled video room — the room the persona joins as a digital host. Any existing room name works; scheduled rooms give the dispatch a stable target.
Step 1 — Register the persona
POST the binding under/api/v1/video/avatar-agents:
201 the response carries the persona with a vaa_-prefixed id:
provider_persona_ref is rejected, so keep the full CRUD surface in reach:
A persona that must not be dispatched can stay in the registry: set
"enabled": false to retain it without letting it join rooms or render
clips.Step 2 — Dispatch a join plan into an existing room
When a persona is dispatched into a video room, the avatar-runtime requests a join plan and joins the room as a bot participant that renders the talking-head video driven by the bound agent’s turns:Step 3 — Confirm the bot joined the roster
Do not treat a200 on the join plan as a successful join. Read the room’s
live participant roster and check the bot is on it:
participants array; look for the join plan’s
bot_identity with is_publishing: true, meaning the talking-head video is
live in the room:
Step 4 — Queue a render job
The async counterpart to the live room is a render job posted to/api/v1/video/avatar-video-renders:
{{merge_tag}}
tokens — bare token names with no braces on the merge-field keys. A
merge_fields map can supply up to 20 keys, each key up to 64 characters,
each value up to 500 characters. Every token the template references must
have a supplied value, or the request is rejected with a 400 naming the
missing tokens.
On 202 the request is persisted as a queued job with a vavr_-prefixed
id, which your provider integration or render runtime consumes (Orbit never
calls your provider’s render API directly — same “no provider credentials
stored” posture as the join plan):
Step 5 — Track the job to completed
Report progress back viaPATCH /video/avatar-video-renders/:id, and poll
with GET /video/avatar-video-renders/:id:
rendering— mark the job in-flight.completed— supply the finished clip’soutput_urland either the raw clip (video_base64, hashed server-side) or a pre-computed SHA-256 digest plus byte length, so Orbit mints the content credential without storing the clip itself.failed— with an error message preserved on the job.
queued → rendering → completed / failed transitions are allowed; a
job already in completed or failed is terminal and rejects further
updates with a 409. Once completed, the clip’s output_url is a plain
HTTPS link and the render record carries its content credential.
Step 6 — Attach the clip to a campaign
Attach thecompleted clip’s output_url to a campaign send or a 1:1
MMS/WhatsApp message anywhere the existing content pipeline accepts a
generic media URL. The content credential persists on the render record and
travels with the clip, so the synthetic-media disclosure survives the
attachment.
Verification
Three checks confirm the pipeline is healthy:- Roster check for a live join —
GET /video/rooms/:namemust list the bot identity from the join plan inparticipants(Step 3). - Job-state check for a render —
GET /video/avatar-video-renders/:idreturns the full render record. A healthycompletedrender hasstatus: "completed", a non-nulloutput_url, a non-nullcompleted_at, and a populatedcontent_credential(token + manifest). List jobs withGET /video/avatar-video-renders, optionally filtered by?status=or?persona_id=. - Credential check for a finished clip — confirm a render’s content-credential token via the verify endpoint:
video_mismatch, not
a false pass. The verify surface is read-scoped and stateless, so a campaign
composer or an external reviewer can re-check a clip without write access.
Failure handling
- Render reaches
failed— the job preserves theerrorstring the render runtime reported (up to 500 characters). Read it fromGET /video/avatar-video-renders/:id; the job is terminal, so re-queue a fresh render rather than re-using the failed id. completedwithout a clip payload is rejected — the API requires eithervideo_base64or bothvideo_sha256+video_byteson thecompletedtransition, because every completed clip must carry a minted content credential. A 400 on the transition names what was missing.- Join plan returns 409 on a disabled persona — re-enable with
PATCH /video/avatar-agents/:idand dispatch again. - Bot never reaches the roster — the dispatch queue retries must eventually mint a join through the same gate humans use; if the room detail never shows the bot identity, re-issue the join plan. Re-running it collapses onto one deterministic dispatch job rather than duplicate bots.
- Pre-minted integrations — if your render runtime re-scopes credentials, re-mint only the integration’s own token; the persona’s provider reference and Orbit’s join-plan/render bookkeeping never hold provider secrets, so nothing in Orbit needs re-issuing.
Worked example — concierge welcome clip
End to end, from registry to verified clip:verify reports the token valid and bound, attach the output_url to
your campaign or 1:1 send.
Where to go next
- Avatar bot dispatch queue — the dispatch runtime that carries a join plan’s descriptor to a join, verified against the room’s live participant roster.
- Video meetings and conferences — the room a live avatar persona is dispatched into.
- Video API reference — the full scheduled-room endpoint surface the persona and render surfaces bind to.
- Author an AI Agent from a Prompt — build the conversational agent the persona binds to.