<!-- AvatarFactory Docs · https://avatarfactory.in/docs/sdk/react/types · Full map: https://avatarfactory.in/llms.txt -->

# Types

Every type the SDK exports, in one place — for autocomplete and type-safe config.

## Importing types

All public types are importable directly from the package. Use `import type` for
type-only imports to keep the bundle clean.

```tsx
import type {
  AvatarConfig,
  AvatarError,
  AvatarEventMap,
  TranscriptEntry,
  UseAvatarResult,
} from "@avatarfactory/react";
```

The SDK ships its own types — there is no `@types` package to install.

## Config

### AvatarConfig

A discriminated union: exactly one of `apiKey`, `getSessionToken`, or `deployId`.

```tsx
type AvatarConfig = AvatarConfigBase &
  (
    | { apiKey: string; getSessionToken?: never; deployId?: never }
    | { getSessionToken: () => Promise<string>; apiKey?: never; deployId?: never }
    | { deployId: string; apiKey?: never; getSessionToken?: never }
  );

type AvatarConfigBase = {
  // Session wiring
  mode?: AvatarMode;
  turnTaking?: TurnTakingConfig;
  controls?: { enabled: boolean; stopSpeaking?: boolean };
  perception?: PerceptionConfig;
  transcript?: TranscriptConfig;
  statusBanner?: StatusBannerConfig;
  thinkingIndicator?: ThinkingIndicatorConfig;
  connectTone?: ConnectToneConfig;
  debug?: boolean;

  // Agent definition
  avatar: {
    avatarId: string;
    layout?: Record<string, string>;
    voiceSettings?: AvatarVoiceSettings;
    brain?: AvatarBrain;
    languages?: AvatarLanguages;
    systemPrompt?: string;
    greeting?: GreetingConfig;
  };
};
```

### AvatarMode

```tsx
type AvatarMode = "player" | "tts" | "audio" | "call";
```

### TurnTakingConfig

```tsx
type TurnTakingMode = "auto" | "push-to-talk";

type TurnTakingConfig = {
  mode?: TurnTakingMode;  // default "auto"
  spacebar?: boolean;     // web only, default true
};
```

### AvatarBrain

```tsx
// Open-ended: the server validates, so newer providers work without an SDK release.
type BrainProvider = "openai" | "anthropic" | "groq";

type AvatarBrain = {
  provider: BrainProvider | (string & {});
  model?: string;
  useOwnBrain?: boolean;  // bill the model to your own vendor key
};
```

### AvatarVoiceSettings

```tsx
type TtsProvider = "elevenlabs" | "inworld";

type AvatarVoiceSettings = {
  provider?: TtsProvider;
  voiceId?: string;
  useOwnVoice?: boolean;
  visimeMs?: number;        // viseme grouping window (ms); default 40
  voiceSpeed?: number;
  voiceStability?: number;
  similarityBoost?: number;
};
```

### AvatarLanguages

```tsx
type AvatarLanguage =
  | "en" | "es" | "fr" | "de" | "hi"
  | "ru" | "pt" | "ja" | "it" | "nl";

// "auto" lets the server detect and switch mid-conversation.
type AvatarLanguages = "auto" | AvatarLanguage[];
```

### GreetingConfig

```tsx
// Note: no Dutch — nine languages, against ten for the conversation.
type GreetingLanguage =
  "en" | "es" | "fr" | "de" | "hi" | "ru" | "pt" | "ja" | "it";

type GreetingConfig = {
  enabled: boolean;
  message?: string;           // omit for the built-in line
  language?: GreetingLanguage;
};
```

### ConnectToneConfig

```tsx
type ConnectToneConfig = {
  enabled: boolean;
  src?: string;        // URL or data URI; omit for the built-in tone. Web only.
  loop?: boolean;      // default true
  volume?: number;     // 0–1, default 0.4
  fadeOutMs?: number;  // default 350
};
```

> The tone is capped at 15 seconds regardless of `loop`, so a stalled connect
>   cannot leave it playing forever.

### PerceptionConfig

```tsx
// Camera is OFF unless enabled, and even then it opens only just-in-time.
type PerceptionConfig = { camera?: boolean };

type CameraPreviewCorner =
  "top-left" | "top-right" | "bottom-left" | "bottom-right";
```

### TranscriptConfig

```tsx
type CaptionPosition =
  | "top-left" | "top-center" | "top-right"
  | "bottom-left" | "bottom-center" | "bottom-right";

type TranscriptConfig = {
  enabled: boolean;
  captions?: boolean;         // default true
  position?: CaptionPosition; // default "bottom-center"
  maxEntries?: number;        // default 50
};
```

### StatusBannerConfig

```tsx
// Corners only — a banner does not centre.
type StatusBannerPosition =
  "top-left" | "top-right" | "bottom-left" | "bottom-right";

type StatusBannerConfig = {
  enabled?: boolean;                // default true
  position?: StatusBannerPosition;  // default "bottom-left"
};
```

### ThinkingIndicatorConfig

```tsx
type ThinkingIndicatorPosition = CaptionPosition;

type ThinkingIndicatorConfig = {
  enabled?: boolean;                     // default true
  position?: ThinkingIndicatorPosition;  // default "top-left"
};
```

## Conversation data

### TranscriptEntry

```tsx
type TranscriptSpeaker = "user" | "avatar";

type TranscriptWord = {
  text: string;
  start: number;  // seconds on the utterance's audio clock
  end: number;
};

type TranscriptEntry = {
  id: string;
  speaker: TranscriptSpeaker;
  text: string;
  words: TranscriptWord[];
  isFinal: boolean;
  turnId: number;
  createdAt: number;
};
```

> Avatar words carry real timings from the speech alignment. User words do not —
>   their speech was already said by the time recognition landed.

### SessionNotice

A graceful, server-initiated message — a limit reached, a plan stop, a test key
expiring. **Not an error**: the session is ending deliberately, and the avatar
speaks the message first.

```tsx
type NoticeKind = "demo" | "plan" | "test" | (string & {});
type NoticeAction = "add_key" | "upgrade" | (string & {});

type SessionNotice = {
  kind: NoticeKind;
  action?: NoticeAction;
  message: string;
};
```

Drive your upgrade CTA off `notice`, not off `error`.

### ConnectionHealth

```tsx
type HealthService = "ingest" | "stt" | "llm" | "tts" | "downlink";
type HealthState = "ok" | "degraded" | "failed";

type HealthConcern = {
  scope: "network" | "device" | "service";
  service: HealthService;
  state: Exclude<HealthState, "ok">;
  code?: string;  // e.g. "INGEST_SLOW" — for logs, not logic
} | null;

type ConnectionHealth = {
  services: Record<HealthService, HealthState>;
  concern: HealthConcern;
};
```

`concern` is the blame-resolved conclusion to render — `scope` is the
actionability axis: `network` is the user's to fix, `device` is their machine
failing to keep up, `service` is ours. Neither `degraded` nor `failed` is fatal;
the session stays open and the server drives recovery.

### AvatarSpeech

A pre-rendered speech job, used by `play()` in player mode.

```tsx
type AvatarSpeech = {
  audioChunks: string[];              // base64-encoded audio segments
  alignments: NormalizedAlignment[];  // character-level lip-sync timing
  isGreeting?: boolean;
};

type NormalizedAlignment = {
  characters: string[];                    // ["H","e","l","l","o"]
  character_start_times_seconds: number[];
  character_end_times_seconds: number[];
};
```

> The three arrays are parallel — `characters[i]` starts at
>   `character_start_times_seconds[i]` and ends at
>   `character_end_times_seconds[i]`.

### LiveContextArgs

```tsx
type LiveContextArgs = {
  context: string;   // non-empty, max 2000 characters
  update?: boolean;  // true replaces the previous context instead of appending
};
```

## Errors

### AvatarError

```tsx
type AvatarError = {
  // --- Branch on these ---
  code: AvatarErrorCode;
  fatal: boolean;      // is the session over?
  retryable: boolean;  // would start() plausibly succeed? Only meaningful when fatal.

  // --- Log these; don't render them ---
  message: string;     // written for you, not your users
  source: AvatarErrorSource;
  cause?: unknown;
};

type AvatarErrorSource = "sdk" | "server" | "network";
```

See [Error Handling](/docs/sdk/react/error-handling) for how to use `fatal` and
`retryable`.

### AvatarErrorCode

Grouped by **who fixes it** — the only distinction you can act on.

```tsx
type AvatarErrorCode =
  // Your integration — you fix these in your own code.
  | "SESSION_TOKEN_FETCH_FAILED"
  | "INVALID_MODE"
  | "INVALID_INPUT"
  | "NOT_CONNECTED"
  // Your user — they must act; retrying alone won't help.
  | "MIC_PERMISSION_DENIED"
  // The service — retry, or report to us.
  | "CONNECTION_FAILED"
  | "SERVER_ERROR"
  | "SERVER_CLOSE_REQUESTED"
  | "AVATAR_LOAD_FAILED"
  | "BRAIN_ERROR"
  // Expected lifecycle.
  | "SESSION_IDLE_TIMEOUT"
  // Nothing else fit.
  | "UNKNOWN";
```

> These are deliberately coarse: they split where the remedy splits, and nowhere
>   else. Every transport failure is one `CONNECTION_FAILED` — which socket call
>   failed lives in `message` and `cause`, so it never costs you a branch.

## Events

### AvatarEventMap

```tsx
interface AvatarEventMap {
  start:      () => void;
  stop:       () => void;
  interrupt:  () => void;
  speaking:   () => void;
  ready:      () => void;
  error:      (error: AvatarError) => void;
  notice:     (notice: SessionNotice) => void;
  transcript: (entries: TranscriptEntry[]) => void;
  health:     (health: ConnectionHealth) => void;
}
```

`transcript` only fires when `transcript.enabled` is set.

## Hook and component types

### UseAvatarResult

```tsx
interface UseAvatarResult {
  // --- Paint axis: is there an avatar on screen? ---
  isReady: boolean;
  isLoading: boolean;
  isFailed: boolean;

  // --- Session axis: is there a live call? ---
  isIdle: boolean;
  isConnecting: boolean;
  isConnected: boolean;

  // --- Turn state (only meaningful while connected) ---
  isListening: boolean;
  isThinking: boolean;
  isSpeaking: boolean;
  isResponseSlow: boolean;

  // --- Mute ---
  isMicMuted: boolean;
  isSpeakerMuted: boolean;
  setMicMuted: (muted: boolean) => void;
  setSpeakerMuted: (muted: boolean) => void;

  // --- Push to talk ---
  isPushToTalk: boolean;
  isTalking: boolean;
  startTalking: () => void;
  stopTalking: () => void;

  // --- Session control ---
  start(): void;
  stop(): void;
  interrupt(): void;
  stopSpeaking(): void;

  // --- Mode-specific (undefined in the wrong mode) ---
  play?(job: AvatarSpeech): void;
  speakText?: (sentence: string) => void;
  speakAudio?: (audio: ArrayBuffer | Float32Array | Blob) => void;
  addLiveContext?: (args: LiveContextArgs) => void;

  // --- Gestures ---
  triggerAction?: (name: string) => void;
  availableActions?: string[];

  // --- Live data ---
  error: AvatarError | null;
  notice?: SessionNotice | null;
  health?: ConnectionHealth;
  transcript?: TranscriptEntry[];
  avatarId?: string;
  controls: { enabled: boolean; stopSpeaking?: boolean };

  // --- Event subscription ---
  on<E extends keyof AvatarEventMap>(event: E, handler: AvatarEventMap[E]): void;
  off<E extends keyof AvatarEventMap>(event: E, handler: AvatarEventMap[E]): void;
}
```

> `play`, `speakText`, `speakAudio`, and `addLiveContext` are optional because
>   they are `undefined` in unsupported modes. Always use optional chaining:
>   `speakText?.("hello")`.

### AvatarProps

The props `` accepts.

```tsx
type AvatarProps = {
  className?: string;
  style?: React.CSSProperties;
  loader?: React.ReactNode;
  errorFallback?: React.ReactNode;

  // Camera
  cameraPreviewCorner?: CameraPreviewCorner;
  hideCameraPreview?: boolean;
  usePictureInPicture?: boolean;
  pictureInPictureCorner?: CameraPreviewCorner;

  // Overlay styling hooks
  captionClassName?: string;
  captionStyle?: React.CSSProperties;
  statusBannerClassName?: string;
  statusBannerStyle?: React.CSSProperties;
  thinkingIndicatorClassName?: string;
  thinkingIndicatorStyle?: React.CSSProperties;

  // Overrides the server's rive fit ("contain", "cover", …).
  fit?: string;
};
```

### AvatarConnectingLoaderProps

The SDK's own animated loader, exported so you can use it as a `loader`.

```tsx
type AvatarConnectingLoaderProps = {
  label?: string;  // spelled out under the orb, one letter at a time
  className?: string;
  style?: React.CSSProperties;
};
```

It sizes itself from a container query, so one node works in a 64px bubble and a
full-bleed stage.

## Lifecycle enums

Exported as values, not just types — useful for logging and debugging.

```tsx
enum SessionPhase {
  Idle = "idle",              // no session; start() is available
  Connecting = "connecting",  // token → config → socket → init ack
  Active = "active",
}

enum PaintPhase {
  None = "none",        // nothing resolved yet
  Loading = "loading",  // resolving avatar config + riv asset
  Painted = "painted",
  Failed = "failed",
}

enum TurnPhase {
  Idle = "idle",
  Greeting = "greeting",
  Listening = "listening",
  Thinking = "thinking",
  Speaking = "speaking",
}
```
