← Back to index Logout

Global Identities

GET /api/v1/identities

Auth: Required Scope: identity-level

List all registered identities with pagination.

Example

curl -s "$API/api/v1/identities?page=1&page_size=20" -H "X-API-Key: $KEY" | jq '{count, identities: [.identities[] | {name}]}'

GET /api/v1/identity/:identity_uuid

Auth: Required Scope: identity-level

Get detailed information for a specific identity, including metadata and TMDb references.

Example

curl -s "$API/api/v1/identity/$IDENTITY_UUID" -H "X-API-Key: $KEY"

Response (200)

{
  "success": true,
  "identity_uuid": "a9a901056d6b46ff92da0c3c1a57dff4",
  "name": "Cary Grant",
  "identity_type": "people",
  "source": "tmdb",
  "status": "confirmed",
  "tmdb_id": 112,
  "tmdb_profile": "{output}/identities/{identity_uuid}/profile.jpg",
  "metadata": {},
  "reference_data": {},
  "created_at": "2026-05-16T12:00:00Z",
  "updated_at": null
}
Field Type Description
identity_uuid string Identity identifier
name string Identity name
identity_type string "people" or null
source string .json, auto, tmdb, user_defined, or merged
status string "confirmed", "pending", or "inactive"
tmdb_id integer TMDb person ID (only if source = tmdb)
tmdb_profile string Local profile image path ({output}/identities/{uuid}/profile.jpg)
metadata object Metadata JSON (tmdb_character, cast_order, etc.)
created_at string Creation timestamp

DELETE /api/v1/identity/:identity_uuid

Auth: Required Scope: identity-level

Delete an identity permanently. All face detections bound to this identity are unbound (identity_id set to NULL). The identity JSON file is deleted from disk.

History & Undo/Redo

Every DELETE records a full snapshot of the identity and its unbound faces. See 14_identity_history.md for:

Note: Delete undo/redo reuses the same endpoints as PATCH undo/redo. The endpoint automatically detects whether the identity was deleted (undo) or needs to be re-deleted (redo) based on the history record.


PATCH /api/v1/identity/:identity_uuid

Auth: Required Scope: identity-level

Partially update an identity. Only provided fields are modified. The name field is a display label and may repeat across identities (removed UNIQUE constraint). Aliases for multilingual display are stored in metadata.aliases (see BCP 47 reference below).

Request (JSON, all fields optional)

Field Type Description
name string New display name
metadata object Merged into existing metadata. Use "aliases" key for locale-tagged names
status string "confirmed", "pending", or "skipped"
identity_type string "people", "brand", "object", "concept", etc.

Example

# Update name and add aliases
curl -s -X PATCH "$API/api/v1/identity/$IDENTITY_UUID" \
  -H "X-API-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "John Smith",
    "metadata": {
      "aliases": [
        {"locale": "en", "name": "John Smith"},
        {"locale": "zh-TW", "name": "約翰·史密斯"},
        {"locale": "ja", "name": "ジョン・スミス"}
      ]
    }
  }'

# Update status only
curl -s -X PATCH "$API/api/v1/identity/$IDENTITY_UUID" \
  -H "X-API-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{"status": "confirmed"}'

Response (200)

{
  "success": true,
  "identity_uuid": "a9a901056d6b46ff92da0c3c1a57dff4",
  "updated_fields": ["name", "metadata"]
}

Error Responses

HTTP When
404 Identity not found
500 Database error

History & Undo/Redo

Every bind records a before/after snapshot. See 14_identity_history.md for:


Metadata (Embedded JSON)

The identities.metadata column is a JSONB field that stores arbitrary structured data alongside the identity's core fields (name, status, identity_type). No schema is enforced — any valid JSON object is accepted.

Merge Behavior

Operation Strategy Example
PATCH Shallow top-level merge: COALESCE(metadata,'{}'::jsonb) \|\| $1::jsonb Sending {"tmdb_rating": 8.5} only adds/overwrites tmdb_rating; all other existing keys are preserved.
mergeinto Recursive deep merge — nested sub-keys are merged individually, not replaced wholesale Target has {"tmdb": {"biography": "..."}}, source has {"tmdb": {"birthday": "1904-01-18"}} → result is {"tmdb": {"biography": "...", "birthday": "1904-01-18"}}.
Upload (POST) Direct overwrite — the entire metadata field is replaced with the request value.

Validation

Scenario Result
PATCH with non-object metadata (string, array, number, null) 400 Bad Request: "metadata must be a JSON object"
mergeinto with non-object metadata Accepted (mergeinto validates at application level)
Upload with non-object metadata Accepted (upload replaces directly)

Conventional Keys

Key Type Writer Purpose
aliases [{locale, name}] PATCH, mergeinto Multilingual display names (see Alias System)
merged_into {uuid, at} mergeinto Marks an identity as merged (undo mechanism reads this)
tmdb_* various TMDb probe Movie metadata (biography, birthday, known_for, etc.). Written only when MOMENTRY_TMDB_PROBE_ENABLED=true.
source string mergeinto Tagged on aliases/metadata when added by merge ("merge" value)

Custom keys are fully supported — no registration required.

Search Coverage

The identity search endpoint (GET /api/v1/identity/search) matches across three scopes:

  1. i.name — exact and ILIKE against display name
  2. jsonb_array_elements(i.metadata->'aliases')->>'name' — locale-tagged alias names
  3. i.metadata::text ILIKE $1 — raw string search across the entire JSON blob (all keys, all values)

This means searching for "1904-01-18" or "biography" will match identities whose metadata contains those strings anywhere.

History Snapshots

Every identity_history record captures the full metadata in both before_snapshot and after_snapshot (as part of the complete identity JSONB dump). Undo restores the identity row — including metadata — to the before_snapshot state.

For merge operations, the MongoDB merge history records metadata_fields_added and metadata_fields_added_paths (dot-separated paths like "tmdb.biography"). Merge undo removes only those specific paths, preserving subsequent manual edits to other metadata keys.

Best Practices

Guideline Reason
Deep nesting is allowed in metadata All metadata merge operations use jsonb_deep_merge() — nested sub-keys are merged recursively, not replaced wholesale
Use aliases for display names Frontend has built-in locale fallback logic (see Alias System)
Avoid >1MB per identity Metadata is included in search indexing (metadata::text ILIKE); large blobs degrade query performance
Don't rely on metadata ordering JSONB preserves insertion order but PostgreSQL does not guarantee it across operations
No LLM/Gemma4 agent writes to metadata Only API endpoints (PATCH, mergeinto, upload) and TMDb probe modify identities.metadata

POST /api/v1/identity/:identity_uuid/bind/trace

Auth: Required Scope: identity-level

Bind all face detections of a trace to an identity. Updates all rows in face_detections with the matching file_uuid and trace_id.

Request Parameters

Field Type Required Description
file_uuid string Yes File where trace exists
trace_id integer Yes Trace ID (from face_detections.trace_id)

Side Effects

Example

curl -s -X POST "$API/api/v1/identity/$IDENTITY_UUID/bind/trace" \
  -H "X-API-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{"file_uuid": "'"$FILE_UUID"'", "trace_id": 919}'

Response (200)

{
  "success": true,
  "message": "Bound trace 919 of aeed71342... to Cary Grant",
  "data": { "rows_affected": 53 }
}

Error Responses

HTTP When
404 Identity not found
500 Database error

History & Undo/Redo

Trace bind operations share the same history/undo/redo system as single-face binds. See 14_identity_history.md for endpoints.


GET /api/v1/identity/:identity_uuid/traces

Auth: Required Scope: identity-level

Get paginated face traces (continuous tracking segments) associated with this identity across all files.

Query Parameters

Field Type Required Default Description
page integer No 1 Page number
page_size integer No 20 Items per page

Example

curl -s "$API/api/v1/identity/$IDENTITY_UUID/traces?page=1&page_size=3" \
  -H "X-API-Key: $KEY" | jq '{total, total_faces, traces}'

Response (200)

{
  "success": true,
  "identity_uuid": "a9a901056d6b46ff92da0c3c1a57dff4",
  "name": "Cary Grant",
  "total": 18,
  "page": 1,
  "page_size": 3,
  "total_faces": 542,
  "traces": [
    {
      "file_uuid": "aeed71342a899fe4b4c57b7d41bcb692",
      "trace_id": 906,
      "frame_count": 52,
      "first_frame": 37974,
      "last_frame": 38127,
      "first_sec": 1519.0,
      "last_sec": 1525.1,
      "avg_confidence": 0.8254
    }
  ]
}
Field Type Description
success bool Always true
identity_uuid string Identity UUID
name string Identity display name
total integer Total number of traces (across all pages)
total_faces integer Sum of all face detections in returned traces
traces[].file_uuid string File where trace exists
traces[].trace_id integer Trace tracking ID
traces[].frame_count integer Number of frames in this trace
traces[].first_frame integer Start frame number
traces[].last_frame integer End frame number
traces[].first_sec float Start time in seconds
traces[].last_sec float End time in seconds
traces[].avg_confidence float Average detection confidence (0.0–1.0)

Error Responses

HTTP When
404 Identity not found
500 Database error

POST /api/v1/identity/:identity_uuid/unbind

Auth: Required Scope: identity-level

Unbind a face detection from an identity. Removes the identity association from the face record.

Side Effects

History & Undo/Redo

Unbind records a before/after snapshot. See 14_identity_history.md for:


POST /api/v1/identity/:identity_uuid/mergeinto

Auth: Required Scope: identity-level

Transfer all face bindings from this identity to another identity, then optionally delete or mark the source as merged.

Two Merge Cases

Case Description Undo/Redo Support
stranger → identity Merge an auto-generated stranger identity into a known identity (TMDb or user-defined) ✅ 24hr undo/redo
identity A → identity B Merge two known identities (e.g., duplicate entries) ✅ 24hr undo/redo

Request Parameters

Field Type Required Default Description
into_uuid string Yes Target identity UUID to merge into
keep_history bool No true Keep source identity record with status='merged' (true) or delete it (false)

Side Effects

Example

curl -s -X POST "$API/api/v1/identity/$SOURCE_UUID/mergeinto" \
  -H "X-API-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{"into_uuid": "'"$TARGET_UUID"'", "keep_history": true}'

Response (200)

{
  "success": true,
  "message": "Merged 'stranger_13894' into 'Louis Viret' (52 faces transferred, history kept)",
  "data": {
    "merge_id": "550e8400-e29b-41d4-a716-446655440000",
    "faces_transferred": 52,
    "aliases_added": 1,
    "metadata_fields_added": 2
  }
}
Field Type Description
merge_id string Unique merge operation ID (for undo)
faces_transferred integer Number of face detections transferred
aliases_added integer Number of aliases added to target
metadata_fields_added integer Number of metadata fields added to target

Error Responses

HTTP When
404 Source or target identity not found
500 Database error

POST /api/v1/identity/merge/:merge_id/undo

Auth: Required Scope: identity-level

Undo a merge operation within 24 hours. Restores the source identity and reverts face bindings.

Undo Behavior

Action Description
Restore source identity If keep_history=true: restore status to confirmed
If keep_history=false: recreate identity from MongoDB snapshot
Restore faces Transfer faces back to source identity
Remove aliases from target Remove aliases with source: "merge" tag
Remove metadata fields from target Remove fields that were added from source
Preserve manual changes Keep aliases/metadata manually added after merge

Example

curl -s -X POST "$API/api/v1/identity/merge/550e8400-e29b-41d4-a716-446655440000/undo" \
  -H "X-API-Key: $KEY"

Response (200)

{
  "success": true,
  "message": "Undo merge completed: 'stranger_13894' restored, 52 faces reverted",
  "data": {
    "source_identity_restored": {
      "uuid": "a9a90105...",
      "name": "stranger_13894",
      "status": "confirmed"
    },
    "faces_reverted": 52,
    "aliases_removed_from_target": 1,
    "metadata_fields_removed_from_target": 2
  }
}

Error Responses

HTTP When
400 Undo deadline expired (>24hr) or already undone
404 Merge record not found
500 Database error

POST /api/v1/identity/merge/:merge_id/redo

Auth: Required Scope: identity-level

Redo a previously undone merge operation. See 14_identity_history.md for full details.


GET /api/v1/identity/merge/history

Auth: Required Scope: identity-level

Query merge history records from MongoDB.

Query Parameters

Field Type Required Default Description
source_uuid string No Filter by source identity UUID
target_uuid string No Filter by target identity UUID
merge_id string No Filter by specific merge ID
undone bool No Filter by undone status
page int No 1 Page number
page_size int No 20 Items per page

Example

curl -s "$API/api/v1/identity/merge/history?page=1&page_size=10" \
  -H "X-API-Key: $KEY"

Response (200)

{
  "success": true,
  "total": 5,
  "page": 1,
  "page_size": 10,
  "results": [
    {
      "merge_id": "550e8400-e29b-41d4-a716-446655440000",
      "source_name": "stranger_13894",
      "target_name": "Louis Viret",
      "faces_transferred": 52,
      "merged_at": "2026-05-27T10:00:00Z",
      "undo_deadline": "2026-05-28T10:00:00Z",
      "undone": false,
      "undo_expired": false
    }
  ]
}
Field Type Description
merge_id string Unique merge operation ID
source_name string Source identity name
target_name string Target identity name
faces_transferred integer Number of faces transferred
merged_at datetime When merge occurred
undo_deadline datetime 24hr deadline for undo
undone bool Whether merge was undone
undo_expired bool Whether undo deadline passed

GET /api/v1/identities/search

Auth: Required Scope: global / file-level

Search identity name → find associated chunks. Searches identity name and aliases, returns identities with their associated text chunks.

Query Parameters

Field Type Required Default Description
q string Yes Search text (ILIKE match on name and aliases)
file_uuid string No Restrict to specific file. If omitted, searches all files (global search)
limit integer No 50 Max results

Example (Global Search)

curl -s "$API/api/v1/identities/search?q=Audrey" -H "X-API-Key: $KEY"

Example (File-specific Search)

curl -s "$API/api/v1/identities/search?q=Audrey&file_uuid=$FILE_UUID" -H "X-API-Key: $KEY"

Response (200)

{
  "success": true,
  "total": 5,
  "results": [
    {
      "identity_id": 9,
      "name": "Audrey Hepburn",
      "source": "tmdb",
      "tmdb_id": 1932,
      "file_uuid": "a6fb22eebefaef17e62af874997c5944",
      "trace_id": 41,
      "chunk_id": "llm_parent_..._204_207",
      "start_time": 204.162,
      "text_content": "...confrontation..."
    }
  ]
}
Field Type Description
results[].identity_id integer Identity ID
results[].name string Identity name
results[].source string Identity source (tmdb, user_defined, etc.)
results[].tmdb_id integer TMDb person ID (if source = tmdb)
results[].file_uuid string File where identity appears
results[].trace_id integer Face trace ID
results[].chunk_id string Associated chunk ID
results[].start_time float Chunk start time
results[].text_content string Chunk text content


POST /api/v1/identity/upload

Auth: Required Scope: identity-level

Upload an identity.json file to create or update an identity. Accepts the same format as the identity.json files stored on disk.

If an identity with the same identity_uuid already exists, it will be updated with the new values.

Request

The request body is an IdentityFile object:

Field Type Required Description
identity_uuid string Yes Identity identifier
name string Yes Identity display name
identity_type string No "people" or null
source string No .json, auto, tmdb, user_defined, or merged
status string No "confirmed", "pending", or "inactive"
tmdb_id integer No TMDb person ID
tmdb_profile string No TMDb profile image URL
metadata object No Arbitrary metadata JSON
file_bindings array No Array of { file_uuid, trace_ids, face_count } (informational)

Example

curl -s -X POST "$API/api/v1/identity/upload" \
  -H "X-API-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "version": 1,
    "identity_uuid": "a9a901056d6b46ff92da0c3c1a57dff4",
    "name": "Cary Grant",
    "identity_type": "people",
    "source": ".json",
    "status": "confirmed",
    "metadata": {},
    "file_bindings": []
  }'

Response (200)

{
  "success": true,
  "identity_uuid": "a9a901056d6b46ff92da0c3c1a57dff4",
  "name": "Cary Grant",
  "message": "Identity uploaded successfully"
}


POST /api/v1/identity/:identity_uuid/profile-image

Auth: Required Scope: identity-level

Upload a profile image (JPEG or PNG) for an identity. The image is saved to {output}/identities/{uuid}/profile.{ext}.

Uses multipart/form-data with field name image.

Example

curl -s -X POST "$API/api/v1/identity/$IDENTITY_UUID/profile-image" \
  -H "X-API-Key: $KEY" \
  -F "image=@/path/to/photo.jpg"

Response (200)

{
  "success": true,
  "identity_uuid": "a9a901056d6b46ff92da0c3c1a57dff4",
  "path": "/path/to/output/identities/.../profile.jpg",
  "message": "Profile image saved: profile.jpg"
}

Error Responses

HTTP When
400 Missing image field or unsupported format
404 Identity not found
415 Unsupported image type (use JPEG or PNG)

GET /api/v1/identity/:identity_uuid/profile-image

Auth: Required Scope: identity-level

Retrieve the profile image for an identity. Returns the raw image data with appropriate Content-Type header.

curl -s "$API/api/v1/identity/$IDENTITY_UUID/profile-image" \
  -H "X-API-Key: $KEY" -o profile.jpg
Response Header Value
content-type image/jpeg or image/png

Identity Related Data

GET /api/v1/identity/:identity_uuid/files

Auth: Required Scope: identity-level

List all files containing this identity.

Example

curl -s "$API/api/v1/identity/$IDENTITY_UUID/files" \
  -H "X-API-Key: $KEY"

Response (200)

{
  "success": true,
  "identity_uuid": "a9a90105-6d6b-46ff-92da-0c3c1a57dff4",
  "total": 3,
  "files": [
    {
      "file_uuid": "d3f9ae8e471a1fc4d47022c66091b920",
      "file_name": "video1.mp4",
      "face_count": 142,
      "first_appearance": 4.17,
      "last_appearance": 208.33
    }
  ]
}

GET /api/v1/identity/:identity_uuid/chunks

Auth: Required Scope: identity-level

List all chunks associated with this identity (chunks where the identity's face appears).

Query Parameters

Field Type Required Default Description
page integer No 1 Page number
page_size integer No 20 Items per page

Example

curl -s "$API/api/v1/identity/$IDENTITY_UUID/chunks?page=1&page_size=50" \
  -H "X-API-Key: $KEY"

Response (200)

{
  "success": true,
  "identity_uuid": "a9a90105-6d6b-46ff-92da-0c3c1a57dff4",
  "total": 45,
  "page": 1,
  "page_size": 20,
  "chunks": [
    {
      "chunk_id": "chunk_1",
      "file_uuid": "d3f9ae8e471a1fc4d47022c66091b920",
      "start_time": 4.17,
      "end_time": 8.33,
      "text": "[4s-8s] Hello, how are you?",
      "chunk_type": "story_child"
    }
  ]
}

GET /api/v1/identity/:identity_uuid/faces

Auth: Required Scope: identity-level

List all face detections for this identity.

Query Parameters

Field Type Required Default Description
page integer No 1 Page number
page_size integer No 50 Items per page

Example

curl -s "$API/api/v1/identity/$IDENTITY_UUID/faces?page=1&page_size=100" \
  -H "X-API-Key: $KEY"

Response (200)

{
  "success": true,
  "identity_uuid": "a9a90105-6d6b-46ff-92da-0c3c1a57dff4",
  "total": 1420,
  "page": 1,
  "page_size": 50,
  "faces": [
    {
      "face_id": "face_100",
      "file_uuid": "d3f9ae8e471a1fc4d47022c66091b920",
      "frame_number": 1200,
      "timestamp": 50.0,
      "bbox": [100, 50, 300, 400],
      "confidence": 0.95,
      "trace_id": 2
    }
  ]
}

GET /api/v1/identity/:identity_uuid/status

Auth: Required Scope: identity-level

Get processing/status info for an identity.

Example

curl -s "$API/api/v1/identity/$IDENTITY_UUID/status" \
  -H "X-API-Key: $KEY"

Response (200)

{
  "success": true,
  "identity_uuid": "a9a90105-6d6b-46ff-92da-0c3c1a57dff4",
  "name": "Audrey Hepburn",
  "status": "confirmed",
  "face_count": 1420,
  "file_count": 3,
  "has_embedding": true,
  "has_profile_image": true
}

GET /api/v1/identity/:identity_uuid/json

Auth: Required Scope: identity-level

Get the raw identity JSON file (same format as identity.json on disk).

Example

curl -s "$API/api/v1/identity/$IDENTITY_UUID/json" \
  -H "X-API-Key: $KEY"

Response (200)

{
  "version": 1,
  "identity_uuid": "a9a90105-6d6b-46ff-92da-0c3c1a57dff4",
  "name": "Audrey Hepburn",
  "identity_type": "people",
  "source": "tmdb",
  "status": "confirmed",
  "tmdb_id": 1234,
  "tmdb_profile": "https://image.tmdb.org/...",
  "metadata": {},
  "file_bindings": [
    {"file_uuid": "d3f9ae8e...", "trace_ids": [0, 1, 2], "face_count": 142}
  ]
}


POST /api/v1/file/:file_uuid/pending-person

Auth: Required Scope: file-level

Create a manually managed "pending person" under a specific file. A pending person is an identity with status='pending' and source='manual', used for unmatched traces that the user wants to manually label before a full identity resolution.

Optionally binds a list of trace IDs to this new identity.

Request

{
  "trace_ids": [100, 150, 200],
  "name": "Mystery Man #1"
}
Field Type Required Default Description
trace_ids array[int] No [] Trace IDs to bind to this pending person
name string No "Person N" Human-readable name. Auto-generated if omitted

Example

# Create pending person with name and no traces
curl -s -X POST "$API/api/v1/file/$FILE_UUID/pending-person" \
  -H "X-API-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Unknown Woman #2", "trace_ids": []}'

# Create pending person with auto-name and bind traces
curl -s -X POST "$API/api/v1/file/$FILE_UUID/pending-person" \
  -H "X-API-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{"trace_ids": [100, 150, 200]}'

Response (200)

{
  "success": true,
  "message": "Created pending person: Mystery Man #1 (uuid: 4d96b25b-68f0-4c52-b238-d69f7dfd588b)",
  "data": {
    "identity_uuid": "4d96b25b-68f0-4c52-b238-d69f7dfd588b",
    "identity_id": 55,
    "name": "Mystery Man #1",
    "bound_traces": 0
  }
}
Field Type Description
identity_uuid string UUID of the newly created pending identity
identity_id integer Internal ID of the new identity
name string Display name
bound_traces integer Number of traces bound

Side Effects


GET /api/v1/file/:file_uuid/pending-persons

Auth: Required Scope: file-level

List all pending persons for a file.

Example

curl -s "$API/api/v1/file/$FILE_UUID/pending-persons" \
  -H "X-API-Key: $KEY"

Response (200)

{
  "success": true,
  "message": "Found 2 pending persons for c36f35685177c981aa139b66bbbccc5b",
  "data": [
    {
      "identity_uuid": "232ecd08-a2bf-4bd0-bd25-0bd8fb7a7dae",
      "identity_id": 56,
      "name": "Person 2",
      "created_at": "2026-06-23 17:13:23",
      "trace_count": 3,
      "bound_traces": null
    }
  ]
}
Field Type Description
identity_uuid string Identity UUID
identity_id integer Internal identity ID
name string Display name
created_at string Creation timestamp
trace_count integer Number of face traces bound to this pending person
bound_traces array[int] List of bound trace IDs (currently null, reserved for future expansion)

Notes


Alias System (BCP 47 Locale Tags)

Identity aliases support multilingual display names. Aliases are stored in metadata.aliases as an array of {locale, name} objects.

BCP 47 Locale Tags Reference

Locale Tag Example
English en John Smith
Traditional Chinese zh-TW 約翰·史密斯
Simplified Chinese zh-CN 约翰·史密斯
Japanese ja ジョン・スミス
Korean ko 존 스미스
Cantonese yue 約翰·史密夫
French fr John Smith (French spelling)
Spanish es Juan Smith
Arabic ar جون سميث
Russian ru Джон Смит
Thai th จอห์น สมิธ

BCP 47 is the IETF standard for language tags. Format: language (e.g. en, ja) or language-Region (e.g. zh-TW, zh-CN). Region suffix distinguishes regional variants.

Frontend Display Logic

function getDisplayName(identity, preferredLocale) {
  // 1. Exact locale match
  const match = identity.metadata?.aliases?.find(a => a.locale === preferredLocale);
  if (match) return match.name;

  // 2. Language-only match (zh-TW → zh)
  const lang = preferredLocale.split('-')[0];
  const langMatch = identity.metadata?.aliases?.find(a => a.locale.startsWith(lang));
  if (langMatch) return langMatch.name;

  // 3. Fallback to identity.name
  return identity.name;
}

Updating Aliases via PATCH

PATCH /api/v1/identity/:identity_uuid
{
  "metadata": {
    "aliases": [
      {"locale": "en", "name": "John Smith"},
      {"locale": "zh-TW", "name": "約翰·史密斯"}
    ]
  }
}

This replaces the entire aliases array. To add to existing aliases, include all existing entries in the request.


Updated: 2026-06-20 — Added identity files, chunks, faces, status, and JSON endpoints