{
  "openapi": "3.1.0",
  "info": {
    "title": "TIC Realtime Avatar API",
    "version": "1.0.0",
    "description": "Public developer API for the TIC Realtime Avatar platform. Create avatars from a portrait image or source video, prepare them for realtime rendering, and stream audio-clocked realtime avatar turns. Usage is billed in credit micros (1 realtime second = 1,000,000 credit micros); realtime turns reserve estimated credits up front and settle to actual usage. Errors return `{\"error\": \"<actionable message>\", \"code\": \"<machine_code>\", \"retryable\": false}` \u2014 401 invalid key, 403 missing scope or suspended tenant, 402 insufficient credits or spend limit (top up at /platform/billing), 409 reused idempotency key, 429 rate limited.",
    "contact": {
      "name": "TIC Realtime Avatar",
      "url": "https://realtimeavatar.ai/platform"
    }
  },
  "servers": [
    {
      "url": "https://realtimeavatar.ai",
      "description": "Production"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "name": "avatars",
      "description": "Tenant avatar records and lifecycle"
    },
    {
      "name": "assets",
      "description": "Source images/videos used to build avatars"
    },
    {
      "name": "realtime",
      "description": "Prepare avatars and stream realtime turns"
    },
    {
      "name": "credits",
      "description": "Credit wallet balance"
    },
    {
      "name": "api-keys",
      "description": "Programmatic API key management"
    }
  ],
  "paths": {
    "/api/v1/avatars": {
      "get": {
        "tags": [
          "avatars"
        ],
        "operationId": "listAvatars",
        "summary": "List avatars",
        "description": "List all avatars in your tenant. Requires scope `avatars:read`.",
        "responses": {
          "200": {
            "description": "Avatar list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Avatar"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "avatars"
        ],
        "operationId": "createAvatar",
        "summary": "Create an avatar",
        "description": "Create an avatar record from an uploaded asset (see `POST /api/v1/assets` or `POST /api/v1/assets/remote`). Requires scope `avatars:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAvatarRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Avatar created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Avatar"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/api/v1/avatars/{avatarId}": {
      "parameters": [
        {
          "name": "avatarId",
          "in": "path",
          "required": true,
          "description": "Platform avatar id (starts with `ava_`).",
          "schema": {
            "type": "string",
            "pattern": "^ava_"
          }
        }
      ],
      "get": {
        "tags": [
          "avatars"
        ],
        "operationId": "getAvatar",
        "summary": "Get avatar",
        "description": "Fetch one avatar including readiness status. Requires scope `avatars:read`.",
        "responses": {
          "200": {
            "description": "Avatar",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Avatar"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "avatars"
        ],
        "operationId": "updateAvatar",
        "summary": "Update avatar",
        "description": "Update display name, default voice, settings, or metadata. Requires scope `avatars:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateAvatarRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated avatar",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Avatar"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      },
      "delete": {
        "tags": [
          "avatars"
        ],
        "operationId": "deleteAvatar",
        "summary": "Delete avatar",
        "description": "Soft-delete an avatar. Requires scope `avatars:write`.",
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/assets": {
      "get": {
        "tags": [
          "assets"
        ],
        "operationId": "listAssets",
        "summary": "List assets",
        "description": "List uploaded assets in your tenant. Requires scope `avatars:read`.",
        "responses": {
          "200": {
            "description": "Asset list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Asset"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "assets"
        ],
        "operationId": "uploadAsset",
        "summary": "Upload an asset",
        "description": "Upload an image/video/audio file as multipart form data under the `file` field, with optional `kind` (`image` | `video` | `audio`). Requires scope `avatars:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "kind": {
                    "$ref": "#/components/schemas/AssetKind"
                  }
                },
                "required": [
                  "file"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Asset stored",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Asset"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/api/v1/assets/remote": {
      "post": {
        "tags": [
          "assets"
        ],
        "operationId": "createRemoteAsset",
        "summary": "Ingest an asset from a URL",
        "description": "Fetch a remote image/video/audio URL into tenant asset storage. Requires scope `avatars:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateRemoteAssetRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Asset stored",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Asset"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/api/v1/realtime/prepare": {
      "post": {
        "tags": [
          "realtime"
        ],
        "operationId": "prepareRealtime",
        "summary": "Prepare an avatar for realtime",
        "description": "Warm or register the avatar cache so the first realtime turn starts fast. Call this once before a session. Requires scope `realtime:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RealtimePrepareRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Avatar prepared",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RealtimePrepareResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          }
        }
      }
    },
    "/api/v1/realtime/turn": {
      "post": {
        "tags": [
          "realtime"
        ],
        "operationId": "realtimeTurn",
        "summary": "Stream a realtime avatar turn",
        "description": "Run one conversational turn and stream back an audio-clocked binary avatar mux (interleaved audio + video frames; decode with `readAvatarMux` from the `realtime-avatar` SDK). Reserves estimated credits up front and settles to actual usage after the stream ends; settlement never exceeds the reservation. Send an `Idempotency-Key` header to make retries safe \u2014 reusing a key returns 409. Requires scope `realtime:write`.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Unique key per turn attempt; reusing a key returns 409.",
            "schema": {
              "type": "string",
              "maxLength": 180
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RealtimeTurnRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Audio-clocked avatar mux stream",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          }
        }
      }
    },
    "/api/v1/realtime/video-cache/plan": {
      "post": {
        "tags": [
          "realtime"
        ],
        "operationId": "planVideoCache",
        "summary": "Plan source-video cache generation",
        "description": "For source-video avatars: compute (and persist) the video cache generation plan. Requires scope `avatars:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "avatar_id": {
                    "type": "string",
                    "description": "Platform avatar id (`ava_...`)."
                  },
                  "video_cache": {
                    "type": "object",
                    "description": "Optional cache settings overrides."
                  }
                },
                "required": [
                  "avatar_id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cache plan",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "queued",
                        "ready"
                      ]
                    },
                    "plan": {},
                    "manifest": {
                      "type": "object",
                      "description": "Avatar cache manifest record."
                    }
                  },
                  "required": [
                    "status",
                    "manifest"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/credits/balance": {
      "get": {
        "tags": [
          "credits"
        ],
        "operationId": "getCreditBalance",
        "summary": "Get credit balance",
        "description": "Current credit wallet: available, reserved, and lifetime totals. 1 realtime second = 1,000,000 credit micros. Requires scope `credits:read`.",
        "responses": {
          "200": {
            "description": "Credit balance",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditBalance"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/v1/api-keys": {
      "post": {
        "tags": [
          "api-keys"
        ],
        "operationId": "createApiKey",
        "summary": "Create an API key",
        "description": "Mint a new scoped API key for your tenant. The full key (`tic_live_...` or `tic_test_...`) is returned once and never stored. Requires scope `api_keys:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateApiKeyRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "API key created \u2014 store `apiKey` now; it is not retrievable later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateApiKeyResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          }
        }
      }
    },
    "/api/v1/lipsync": {
      "post": {
        "tags": [
          "realtime"
        ],
        "operationId": "lipsync",
        "summary": "Render audio to a talking-head video (non-streaming)",
        "description": "Non-streaming lipsync: download `audioUrl`, render the full clip in one pass, store the MP4, and return its public URL. No LLM, no TTS, no streaming \u2014 ideal for backend and batch jobs. Provide either `avatarId` (an avatar you created) or `portraitUrl` (registered on the fly). Bills realtime seconds like a turn. Requires scope `realtime:write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LipsyncRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rendered lipsync video",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LipsyncResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamError"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "TIC API key as a bearer token: `Authorization: Bearer tic_live_...` or `tic_test_...`. Create keys in the dashboard at /platform/dashboard or via POST /api/v1/api-keys."
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid API key.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "API key lacks the required scope, or the tenant is suspended.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "Insufficient credits or key spend limit exceeded. Top up or upgrade at /platform/billing, or lower the requested duration.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found in your tenant.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "Idempotency key already used.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ValidationError": {
        "description": "Request body failed validation; the error message names the offending field.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "UpstreamError": {
        "description": "Renderer upstream failed; safe to retry.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "Human- and agent-readable description of what went wrong and what to do next."
          },
          "status": {
            "type": "integer",
            "description": "HTTP status code repeated for clients that only inspect JSON bodies."
          },
          "code": {
            "type": "string",
            "description": "Stable machine-readable error code when available, such as insufficient_credits or spend_limit_exceeded."
          },
          "retryable": {
            "type": "boolean",
            "description": "Whether retrying the same request without user action is expected to help."
          },
          "billingUrl": {
            "type": "string",
            "description": "Relative billing URL for 402 errors that require a top-up, card, or plan upgrade."
          }
        },
        "required": [
          "error"
        ]
      },
      "ApiKeyScope": {
        "type": "string",
        "enum": [
          "*",
          "api_keys:write",
          "credits:read",
          "avatars:read",
          "avatars:write",
          "realtime:write",
          "usage:read",
          "usage:write"
        ]
      },
      "AssetKind": {
        "type": "string",
        "enum": [
          "image",
          "video",
          "audio"
        ]
      },
      "CreateApiKeyRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "environment": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ],
            "default": "test"
          },
          "scopes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiKeyScope"
            },
            "minItems": 1,
            "maxItems": 16,
            "default": [
              "realtime:write",
              "credits:read",
              "avatars:read"
            ]
          },
          "spendLimitCreditMicros": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Optional hard spend cap for this key, in credit micros."
          },
          "expiresAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "name"
        ],
        "additionalProperties": false
      },
      "ApiKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "keyId": {
            "type": "string"
          },
          "tenantId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "environment": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "redactedKey": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiKeyScope"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "revoked",
              "expired"
            ]
          },
          "spendLimitCreditMicros": {
            "type": [
              "integer",
              "null"
            ]
          },
          "createdAt": {
            "type": "string"
          },
          "expiresAt": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "keyId",
          "tenantId",
          "name",
          "environment",
          "redactedKey",
          "scopes",
          "status",
          "createdAt"
        ]
      },
      "CreateApiKeyResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ApiKey"
          },
          {
            "type": "object",
            "properties": {
              "apiKey": {
                "type": "string",
                "description": "Full secret key (`tic_live_...` / `tic_test_...`). Shown once; store it now."
              }
            },
            "required": [
              "apiKey"
            ]
          }
        ]
      },
      "CreditBalance": {
        "type": "object",
        "properties": {
          "tenantId": {
            "type": "string"
          },
          "balanceCreditMicros": {
            "type": "integer",
            "minimum": 0
          },
          "reservedCreditMicros": {
            "type": "integer",
            "minimum": 0
          },
          "availableCreditMicros": {
            "type": "integer",
            "minimum": 0,
            "description": "balance minus reserved; what new turns can spend. 1,000,000 micros = 1 realtime second."
          },
          "lifetimeGrantedCreditMicros": {
            "type": "integer",
            "minimum": 0
          },
          "lifetimeUsedCreditMicros": {
            "type": "integer",
            "minimum": 0
          },
          "updatedAt": {
            "type": "string"
          }
        },
        "required": [
          "tenantId",
          "balanceCreditMicros",
          "reservedCreditMicros",
          "availableCreditMicros",
          "updatedAt"
        ]
      },
      "CreateAvatarRequest": {
        "type": "object",
        "properties": {
          "displayName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 160
          },
          "sourceKind": {
            "type": "string",
            "enum": [
              "image",
              "video"
            ]
          },
          "modelId": {
            "type": "string",
            "description": "Avatar model id; defaults to the platform default model."
          },
          "sourceAssetId": {
            "type": "string",
            "description": "Asset id from POST /api/v1/assets or /api/v1/assets/remote."
          },
          "defaultVoiceId": {
            "type": "string",
            "minLength": 8,
            "maxLength": 160
          },
          "settings": {
            "type": "object"
          },
          "metadata": {
            "type": "object"
          }
        },
        "required": [
          "displayName",
          "sourceKind"
        ],
        "additionalProperties": false
      },
      "UpdateAvatarRequest": {
        "type": "object",
        "description": "Provide at least one field.",
        "properties": {
          "displayName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 160
          },
          "defaultVoiceId": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 8,
            "maxLength": 160
          },
          "settings": {
            "type": "object"
          },
          "metadata": {
            "type": "object"
          }
        },
        "additionalProperties": false
      },
      "Avatar": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Platform avatar id (`ava_...`). Use this in realtime prepare/turn calls."
          },
          "tenantId": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          },
          "sourceKind": {
            "type": "string",
            "enum": [
              "image",
              "video"
            ]
          },
          "modelId": {
            "type": "string"
          },
          "sourceAssetId": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "preprocessing",
              "ready",
              "failed",
              "disabled",
              "deleted"
            ]
          },
          "defaultVoiceId": {
            "type": [
              "string",
              "null"
            ]
          },
          "cacheManifestId": {
            "type": [
              "string",
              "null"
            ]
          },
          "cacheVersion": {
            "type": "integer",
            "minimum": 0
          },
          "createdAt": {
            "type": "string"
          },
          "updatedAt": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "tenantId",
          "displayName",
          "sourceKind",
          "modelId",
          "status",
          "createdAt",
          "updatedAt"
        ]
      },
      "Asset": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "tenantId": {
            "type": "string"
          },
          "kind": {
            "$ref": "#/components/schemas/AssetKind"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending_upload",
              "uploaded",
              "processing",
              "ready",
              "failed",
              "deleted"
            ]
          },
          "contentType": {
            "type": "string"
          },
          "sizeBytes": {
            "type": "integer",
            "minimum": 0
          },
          "sha256": {
            "type": [
              "string",
              "null"
            ]
          },
          "publicUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "createdAt": {
            "type": "string"
          },
          "updatedAt": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "tenantId",
          "kind",
          "status",
          "contentType",
          "sizeBytes",
          "createdAt",
          "updatedAt"
        ]
      },
      "CreateRemoteAssetRequest": {
        "type": "object",
        "properties": {
          "kind": {
            "$ref": "#/components/schemas/AssetKind"
          },
          "remoteUrl": {
            "type": "string",
            "format": "uri"
          },
          "originalFilename": {
            "type": "string",
            "minLength": 1,
            "maxLength": 240
          },
          "metadata": {
            "type": "object"
          }
        },
        "required": [
          "kind",
          "remoteUrl"
        ],
        "additionalProperties": false
      },
      "RealtimePrepareRequest": {
        "type": "object",
        "properties": {
          "avatar_id": {
            "type": "string",
            "description": "Platform avatar id (`ava_...`)."
          },
          "source_kind": {
            "type": "string",
            "enum": [
              "portrait",
              "source_video"
            ],
            "default": "portrait"
          },
          "video_cache_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "video_cache": {
            "type": "object",
            "description": "Source-video cache settings overrides."
          }
        },
        "required": [
          "avatar_id"
        ]
      },
      "RealtimePrepareResponse": {
        "type": "object",
        "properties": {
          "avatar_id": {
            "type": "string",
            "description": "Platform avatar id you sent."
          },
          "renderer_avatar_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Internal renderer avatar id."
          },
          "model_id": {
            "type": "string"
          },
          "backend_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "const": "ready"
          },
          "registered": {
            "type": "boolean"
          },
          "source_kind": {
            "type": "string",
            "enum": [
              "portrait",
              "source_video"
            ]
          },
          "timings": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            }
          },
          "video_cache": {
            "type": [
              "object",
              "null"
            ],
            "description": "Video cache manifest when source_kind is source_video."
          }
        },
        "required": [
          "avatar_id"
        ]
      },
      "RealtimeChatMessage": {
        "type": "object",
        "properties": {
          "role": {
            "type": "string",
            "enum": [
              "user",
              "assistant",
              "system"
            ]
          },
          "content": {
            "type": "string",
            "minLength": 1,
            "maxLength": 8000
          }
        },
        "required": [
          "role",
          "content"
        ]
      },
      "RealtimeTurnRequest": {
        "type": "object",
        "properties": {
          "avatar_id": {
            "type": "string",
            "description": "Platform avatar id (`ava_...`)."
          },
          "mode": {
            "type": "string",
            "enum": [
              "chat",
              "speak_text"
            ],
            "default": "chat",
            "description": "`chat` generates a reply from `messages`; `speak_text` renders `text` verbatim."
          },
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RealtimeChatMessage"
            },
            "maxItems": 24
          },
          "text": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 4000,
            "description": "Text to speak when mode is `speak_text`."
          },
          "voice_id": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 8,
            "maxLength": 120
          },
          "emotion": {
            "type": "string",
            "default": "curious"
          },
          "language": {
            "type": "string",
            "default": "en"
          },
          "speed": {
            "type": "number",
            "default": 1
          },
          "background_id": {
            "type": "string",
            "default": "plain_white"
          },
          "source_kind": {
            "type": "string",
            "enum": [
              "portrait",
              "source_video"
            ],
            "default": "portrait"
          },
          "video_cache_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "low_latency_first_chunk": {
            "type": "boolean",
            "default": false,
            "description": "Render the first 200ms chunk as soon as current audio exists."
          },
          "playout_delay_ms": {
            "type": "integer",
            "minimum": 0,
            "maximum": 2000,
            "default": 520
          },
          "max_buffer_delay_ms": {
            "type": "integer",
            "minimum": 0,
            "maximum": 5000,
            "default": 120
          }
        },
        "required": [
          "avatar_id"
        ]
      },
      "LipsyncRequest": {
        "type": "object",
        "required": [
          "audioUrl"
        ],
        "description": "Provide audioUrl plus either avatarId or portraitUrl.",
        "properties": {
          "audioUrl": {
            "type": "string",
            "format": "uri",
            "description": "Public URL of the speech audio to lipsync."
          },
          "avatarId": {
            "type": "string",
            "description": "An avatar you created (ava_...)."
          },
          "portraitUrl": {
            "type": "string",
            "format": "uri",
            "description": "Portrait image to register as the source avatar."
          },
          "backgroundId": {
            "type": "string",
            "description": "Optional background id."
          }
        }
      },
      "LipsyncResponse": {
        "type": "object",
        "required": [
          "url",
          "avatarId",
          "frames",
          "fps",
          "durationSeconds"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Public URL of the rendered MP4."
          },
          "avatarId": {
            "type": "string"
          },
          "frames": {
            "type": "integer"
          },
          "fps": {
            "type": "integer"
          },
          "durationSeconds": {
            "type": "number"
          }
        }
      }
    }
  }
}
