Wrap your app once. It connects to the avatar, holds the session, and shares it with every component inside.
AvatarProvider is where you connect and configure. You give it a config object — how to authenticate, which avatar to load, and which mode to run — and it makes the avatar available to <Avatar /> and the useAvatar hook anywhere below it.
For production on React Native, use getSessionToken — your app never embeds a live key; it calls your backend, which mints a short-lived, single-use session token. For local development you may use apiKey with a test key (af_test_*).
The apiKey method accepts test keys only (af_test_*), for local development. Live keys (af_live_*) are rejected — a shipped app bundle can be decompiled, so a live key inside one is effectively public. For production, always go through getSessionToken. (deployId is not used in mobile apps.)
A session token is a short-lived credential that works for a single connection. Because each token is used once, you give the SDK a function that fetches a fresh one — not a token string. The SDK calls this function every time it connects (the first time, and again on every reconnect), so each connection gets its own token and your secret key stays on your server.
Contract:
Promise<string> resolving to a non-empty token.SESSION_TOKEN_FETCH_FAILED error and aborts the connect attempt before any WebSocket is opened.The getSessionToken callback hits your backend, which exchanges your secret API key for a fresh token by calling the AvatarFactory token endpoint:
A minimal route handler (Next.js shown; any backend works) that keeps your key server-side:
Authenticate this route with your app's own session (the logged-in user) before minting a token — that's where you enforce who is allowed to start an avatar session and how often.
AvatarProvider accepts a single config prop of type AvatarConfig.
| Prop | Type | Default | Description |
|---|---|---|---|
getSessionTokenRequired | () => Promise<string> | No default | Callback returning a fresh, single-use session token. Invoked on every connect. Required for production; either getSessionToken or apiKey (dev) must be supplied. |
apiKeyOptional | string | No default | Test key (af_test_*) for local development only. Live keys (af_live_*) are rejected — use getSessionToken for production. |
avatar.avatarIdRequired | string | No default | The ID of the avatar character to load. |
avatar.layoutOptional | Record<string, string> | No default | Platform-agnostic layout key/value pairs interpreted by the native renderer (e.g. fit, alignment). |
avatar.systemPromptOptional | string | No default | Per-session context — who the end user is and why they are here. Call mode. Not the avatar's identity: set that on the platform. |
avatar.brainOptional | AvatarBrain | No default | Which model powers the conversation: { provider, model?, useOwnBrain? }. provider is server-validated, so newer vendors work without an SDK release. |
avatar.languagesOptional | "auto" | AvatarLanguage[] | No default | Languages the conversation may use. "auto" lets the server detect and switch. Omitted means English. |
avatar.voiceSettingsOptional | AvatarVoiceSettings | No default | Voice provider, voice, and tuning: provider, voiceId, useOwnVoice, visimeMs, voiceSpeed, voiceStability, similarityBoost. |
avatar.voiceSettings.providerOptional | "elevenlabs" | "inworld" | No default | Which speech engine voices the avatar. Choosing one needs no setup — it runs on your plan's included minutes with a default voice. |
avatar.voiceSettings.useOwnVoiceOptional | boolean | unset — the avatar's binding decides | Route speech through your own account with that provider. Requires a saved provider key, and a plan that includes BYOK; the key is resolved server-side and never travels through the app. Omit it to follow the avatar's binding; false vetoes that binding for this integration. |
avatar.greetingOptional | GreetingConfig | No default | An opening line spoken once the session goes live: { enabled, message?, language? }. Text, not pre-rendered audio. |
modeOptional | "call" | "tts" | "audio" | "player" | "call" | Operating mode. See Modes for details. |
turnTaking.modeOptional | "auto" | "push-to-talk" | "auto" | Who decides a turn is over. Call mode only. |
controls.enabledOptional | boolean | false | Show the built-in start / stop / mute bar. Hidden unless explicitly true. |
controls.stopSpeakingOptional | boolean | false | Add a "stop talking" button to the built-in bar, shown only while the avatar speaks. |
perception.cameraOptional | boolean | false | Allow the avatar to see. Opt-in, and even then the camera opens just-in-time — never on connect. |
transcript.enabledOptional | boolean | false | Assemble a live transcript and render captions. Off means none is built at all. |
statusBanner.enabledOptional | boolean | true | Built-in connectivity banner, driven by live stream health. |
connectTone.enabledOptional | boolean | false | Ringback tone while connecting. connectTone.src is ignored on mobile — the built-in tone is always used. |
debugOptional | boolean | false | Enable verbose console logging during development. |
Full types and defaults for every field are in Configuration, including the table of where mobile differs from web.
Customise how the avatar fits its view using platform-agnostic key/value pairs. The native renderer interprets these — common keys are fit and alignment.
Fine-tune the voice for a session. visimeMs sets the viseme grouping window — visemes within this many milliseconds are merged into one mouth shape; the rest are passed to the voice engine.
Refer to your TTS provider's documentation for valid value ranges of voiceSpeed, voiceStability, and similarityBoost.
Voice, brain, and lip-sync settings can come from two places: what you saved when you published the avatar, and what you pass in code. They resolve in this order:
So code overrides the published settings, which override the defaults.
Voice selection: an avatar's voice is normally chosen when you publish it. If no valid voice is selected at publish time (or supplied in code), AvatarFactory falls back to a default voice for the selected provider — for both "elevenlabs" and "inworld" — so the avatar always speaks. An invalid voice also falls back to the default.
An opening line the avatar speaks once the session goes live. You supply
text, not audio — omit message for the built-in line in that language.
Greeting languages: en, es, fr, de, hi, ru, pt, ja, it.
Migrating from an older integration that passed a pre-rendered greeting.job
at the top level? Move it to avatar.greeting and pass a message string
instead.
Context for this session — who your user is and what they came for — for call
mode, inside the avatar object. It applies to that conversation only.
Keep it short. Because it changes from session to session it cannot be cached the way the avatar's own configuration is, and it is re-sent on every turn.
This is not where an avatar's personality or reference material goes. Set those
on the avatar itself in the platform — they persist across every session, hold
far more text, and stay in effect when you leave systemPrompt unset.
The SDK ships with built-in start/stop buttons for call mode, but they are off by default. Opt in by setting controls.enabled to true; otherwise drive sessions yourself with the useAvatar hook.
Which text-to-speech engine voices the avatar — "elevenlabs" (the default) or
"inworld". It lives inside avatar.voiceSettings, not at the top level.
Choosing a provider needs no setup — the avatar speaks using your plan's
included minutes and a default voice for that provider. You only bring your own
provider key when you opt into useOwnVoice.
Route speech through your own account with the selected provider.
This is a switch, not a carrier: the key is resolved server-side from your account and never travels through the app. Calls on your own voice key do not draw platform minutes. See Bring your own keys.
useOwnVoice bills your own account with that vendor, outside your
AvatarFactory plan. Save the key under Profile → Developer first, and set
spend limits in the vendor's own dashboard.
Unlike voice BYOK, your own brain key covers the model cost but the session is still metered against your plan.
Don't nest multiple providers unless you deliberately want two independent avatar sessions running simultaneously. Each AvatarProvider opens its own WebSocket connection.
Changing getSessionToken or mode at runtime triggers a full session reset. Other fields like systemPrompt can be updated dynamically without resetting.