---
title: "Partial update for an avatar's editable identity fields."
method: PATCH
path: "/api/v1/avatars/{avatar_id}"
tags: ["Avatars"]
---

# Partial update for an avatar's editable identity fields.

`PATCH /api/v1/avatars/{avatar_id}`

## Request

- Base URL: `https://api.avatarfactory.io` (the document declares no server; this is the origin it was published from)
- URL: `https://api.avatarfactory.io/api/v1/avatars/{avatar_id}`
- Auth: one of:
  - API key in cookie `session`
  - HTTP bearer

<br/>Powers the pencil-edit affordances on ``/avatars/:id``. Body<br/>contract is defined by ``UpdateAvatarRequest`` (extra-forbid, length<br/>bounded). Unknown fields → 422 from the schema layer; an empty body<br/>is a no-op that returns the current detail bundle with 200<br/>(idempotent).<br/><br/>On success the response mirrors ``GET /api/v1/avatars/<id>/detail``<br/>so the SPA can drop the new payload straight into its react-query<br/>cache via ``setQueryData`` without an extra round trip.<br/><br/>Backend note on "niche": the avatar model exposes ``category_id``<br/>(FK to ``account_categories``), not a free-text niche column. The<br/>SPA's ``resolveCategoryId(niche, categories)`` helper turns the<br/>wizard's niche key into a ``category_id`` before posting; this<br/>endpoint accepts ``category_id`` directly. The string "niche" is<br/>rejected (extra-forbid) so the SPA cannot silently fail-silent on<br/>a misnamed key.

## Path parameters

- `avatar_id` integer, required

## Request body

- UpdateAvatarRequest: Payload for ``PATCH /api/v1/avatars/<id>``. Partial update for the editable identity fields surfaced as pencil icons on the avatar-detail page. Every field is optional; the empty body is a no-op (returns the current state with 200). Unknown fields are rejected (``extra="forbid"``) so the SPA can't silently drift the schema. Length limits are conservative defaults matching the design-preview text-field affordances; tune later as the form grows. Out-of-scope (not editable via this endpoint, each has its own dedicated flow): - ``face_image`` → ``/api/v1/avatars/<id>/face`` (upload) - ``voice_id`` → voice-generation pipeline (wizard step-voice) - ``heygen_avatar_id`` → HeyGen post-save jobs "niche" is a frontend-only concept; this endpoint accepts the canonical ``category_id`` (FK to ``account_categories``). The SPA's ``resolveCategoryId(nicheKey, categories)`` turns the wizard niche into a category id before posting.
  - `category_id` integer, nullable
  - `description` string, nullable
  - `forbidden_topics` string, nullable
  - `image_prompt` string, nullable
  - `name` string, nullable
  - `signature_phrases` string, nullable
  - `story` string, nullable
  - `target_audience` string, nullable
  - `voice_description` string, nullable

## Response `200`

OK

- AvatarDetailResponse: Full bundle the avatar detail page needs. Combines the avatar row, its scenes/images split, recent videos, social accounts, the reference-pack summary, and the integration health flags the page used to receive via ``render_template`` kwargs.
  - `age_band` string, nullable
  - `archived` integer
  - `auto_publish` integer
  - `bio` string, nullable
  - `category` CategoryRow
    - `color` string, nullable
    - `id` integer, required
    - `name` string, required
  - `category_id` integer, nullable
  - `content_pillars` AvatarPillar[]
    - `description` string, nullable
    - `id` integer, required
    - `name` string, required
    - `weight` integer
  - `controversy_level` integer, nullable
  - `created_at` string, date-time, nullable
  - `currently_lives_in` string, nullable
  - `default_engine` string, nullable
  - `default_text_style` string, nullable
  - `description` string, nullable
  - `face_focus` FaceFocusPoint: Where a square or circular crop of a face image has to look. Percentages of the FULL stored frame, so the SPA can hand them straight to a CSS ``object-position``. Written by ``app.modules.avatars.face_focus``; absent (``None``) when no face was detected, and the shared `AvatarFace` component then falls back to its upper-third default.
    - `x` number, required
    - `y` number, required
  - `face_image` string, nullable
  - `first_outfit_status` string, nullable
  - `forbidden_topics` string, nullable
  - `gemini_ready` boolean
  - `gender` string, nullable
  - `heygen_avatar_id` string, nullable
  - `heygen_expression` string, nullable
  - `heygen_sync_error` string, nullable
  - `heygen_sync_status` string, nullable
  - `heygen_talking_style` string, nullable
  - `id` integer, required
  - `image_prompt` string, nullable
  - `images` AvatarSceneItem[]
    - `auto_saved` integer
    - `created_at` string, date-time, nullable
    - `filename` string, nullable
    - `heygen_photo_id` string, nullable
    - `id` integer, required
    - `is_primary` integer
    - `kind` string
    - `scene_title` string, nullable
  - `instagram_display_name` string, nullable
  - `instagram_handle` string, nullable
  - `is_template` boolean
  - `motion_prompt` string, nullable
  - `name` string, required
  - `native_language` string, nullable
  - `origin` string, nullable
  - `outfit_count` integer
  - `posting_frequency` integer, nullable
  - `posting_times` string, nullable
  - `profession` string, nullable
  - `reference_summary` AvatarReferenceSummary
    - `canonical_count` integer
    - `filled_count` integer
    - `has_any` boolean
  - `scene_guidelines` string, nullable
  - `scenes` AvatarSceneItem[]
    - `auto_saved` integer
    - `created_at` string, date-time, nullable
    - `filename` string, nullable
    - `heygen_photo_id` string, nullable
    - `id` integer, required
    - `is_primary` integer
    - `kind` string
    - `scene_title` string, nullable
  - `signature_phrases` string, nullable
  - `social_accounts` AvatarSocialAccount[]
    - `account_name` string, nullable
    - `connected_at` string, date-time, nullable
    - `id` integer, required
    - `platform` string, nullable
  - `speaking_style` string, nullable
  - `story` string, nullable
  - `target_audience` string, nullable
  - `tiktok_handle` string, nullable
  - `timezone` string, nullable
  - `updated_at` string, date-time, nullable
  - `videos` AvatarVideoSummary[]
    - `background_id` integer, nullable
    - `background_image` string, nullable
    - `background_name` string, nullable
    - `created_at` string, date-time, nullable
    - `duration_seconds` number, nullable
    - `final_video_file` string, nullable
    - `id` integer, required
    - `raw_video_file` string, nullable
    - `status` string, nullable
    - `title` string, nullable
  - `voice_description` string, nullable
  - `voice_id` string, nullable
  - `voice_integration_id` integer, nullable
  - `voice_model` string, nullable
  - `voice_name` string, nullable
  - `voice_similarity` number, nullable
  - `voice_speed` number, nullable
  - `voice_stability` number, nullable
  - `voice_style` number, nullable

## Other responses

- `404`: Not Found
- `422`: Unprocessable Entity

---

[API](https://skmtc.dev/avatarfactory/apis/avatarfactory-api.md) · [All operations](https://skmtc.dev/avatarfactory/apis/avatarfactory-api/llms.txt) · [OpenAPI document](https://skmtc.dev/avatarfactory/apis/avatarfactory-api/revisions/66fc7aa3f296?raw)
