GET /api/v1/identitiesAuth: Required Scope: identity-level
List all registered identities with pagination.
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_uuidAuth: Required Scope: identity-level
Get detailed information for a specific identity, including metadata and TMDb references.
curl -s "$API/api/v1/identity/$IDENTITY_UUID" -H "X-API-Key: $KEY"
{
"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_uuidAuth: 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.
Every DELETE records a full snapshot of the identity and its unbound faces. See 14_identity_history.md for:
POST /api/v1/identity/:identity_uuid/undo — recreates identity and re-binds facesPOST /api/v1/identity/:identity_uuid/redo — re-deletes the identityNote: 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_uuidAuth: 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).
| 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. |
# 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"}'
{
"success": true,
"identity_uuid": "a9a901056d6b46ff92da0c3c1a57dff4",
"updated_fields": ["name", "metadata"]
}
| HTTP | When |
|---|---|
404 |
Identity not found |
500 |
Database error |
Every bind records a before/after snapshot. See 14_identity_history.md for:
POST /api/v1/identity/:identity_uuid/bind/undo — Revert a bindPOST /api/v1/identity/:identity_uuid/bind/redo — Reapply an undone bindGET /api/v1/identity/:identity_uuid/bind/history — Query bind operationsThe 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.
| 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. |
| 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) |
| 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.
The identity search endpoint (GET /api/v1/identity/search) matches across three scopes:
i.name — exact and ILIKE against display namejsonb_array_elements(i.metadata->'aliases')->>'name' — locale-tagged alias namesi.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.
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.
| 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/traceAuth: 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.
| Field | Type | Required | Description |
|---|---|---|---|
file_uuid |
string | Yes | File where trace exists |
trace_id |
integer | Yes | Trace ID (from face_detections.trace_id) |
stranger_id(設為 NULL)identities 表中原有的 stranger auto-identity 記錄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}'
{
"success": true,
"message": "Bound trace 919 of aeed71342... to Cary Grant",
"data": { "rows_affected": 53 }
}
| HTTP | When |
|---|---|
404 |
Identity not found |
500 |
Database error |
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/tracesAuth: Required Scope: identity-level
Get paginated face traces (continuous tracking segments) associated with this identity across all files.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
page |
integer | No | 1 |
Page number |
page_size |
integer | No | 20 |
Items per page |
curl -s "$API/api/v1/identity/$IDENTITY_UUID/traces?page=1&page_size=3" \
-H "X-API-Key: $KEY" | jq '{total, total_faces, traces}'
{
"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) |
| HTTP | When |
|---|---|
404 |
Identity not found |
500 |
Database error |
POST /api/v1/identity/:identity_uuid/unbindAuth: Required Scope: identity-level
Unbind a face detection from an identity. Removes the identity association from the face record.
identity_id(設為 NULL),不會恢復 stranger_ididentity/analyze)Unbind records a before/after snapshot. See 14_identity_history.md for:
POST /api/v1/identity/:identity_uuid/bind/undo — Revert an unbindPOST /api/v1/identity/:identity_uuid/bind/redo — Reapply an undone unbindPOST /api/v1/identity/:identity_uuid/mergeintoAuth: Required Scope: identity-level
Transfer all face bindings from this identity to another identity, then optionally delete or mark the source as merged.
| 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 |
| 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) |
face_detections.identity_id 到目標 identitystranger_idsource: "merge" tag)keep_history: true(預設):source identity 設為 status='merged',保留記錄keep_history: false:刪除 source identity 及其 identity JSON 檔案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}'
{
"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 |
| HTTP | When |
|---|---|
404 |
Source or target identity not found |
500 |
Database error |
POST /api/v1/identity/merge/:merge_id/undoAuth: Required Scope: identity-level
Undo a merge operation within 24 hours. Restores the source identity and reverts face bindings.
| Action | Description |
|---|---|
| Restore source identity | If keep_history=true: restore status to confirmedIf 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 |
curl -s -X POST "$API/api/v1/identity/merge/550e8400-e29b-41d4-a716-446655440000/undo" \
-H "X-API-Key: $KEY"
{
"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
}
}
| 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/redoAuth: Required Scope: identity-level
Redo a previously undone merge operation. See 14_identity_history.md for full details.
GET /api/v1/identity/merge/historyAuth: Required Scope: identity-level
Query merge history records from MongoDB.
| 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 |
curl -s "$API/api/v1/identity/merge/history?page=1&page_size=10" \
-H "X-API-Key: $KEY"
{
"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/searchAuth: Required Scope: global / file-level
Search identity name → find associated chunks. Searches identity name and aliases, returns identities with their associated text chunks.
| 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 |
curl -s "$API/api/v1/identities/search?q=Audrey" -H "X-API-Key: $KEY"
curl -s "$API/api/v1/identities/search?q=Audrey&file_uuid=$FILE_UUID" -H "X-API-Key: $KEY"
{
"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/uploadAuth: 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.
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) |
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": []
}'
{
"success": true,
"identity_uuid": "a9a901056d6b46ff92da0c3c1a57dff4",
"name": "Cary Grant",
"message": "Identity uploaded successfully"
}
POST /api/v1/identity/:identity_uuid/profile-imageAuth: 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.
curl -s -X POST "$API/api/v1/identity/$IDENTITY_UUID/profile-image" \
-H "X-API-Key: $KEY" \
-F "image=@/path/to/photo.jpg"
{
"success": true,
"identity_uuid": "a9a901056d6b46ff92da0c3c1a57dff4",
"path": "/path/to/output/identities/.../profile.jpg",
"message": "Profile image saved: profile.jpg"
}
| 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-imageAuth: 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 |
GET /api/v1/identity/:identity_uuid/filesAuth: Required Scope: identity-level
List all files containing this identity.
curl -s "$API/api/v1/identity/$IDENTITY_UUID/files" \
-H "X-API-Key: $KEY"
{
"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/chunksAuth: Required Scope: identity-level
List all chunks associated with this identity (chunks where the identity's face appears).
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
page |
integer | No | 1 | Page number |
page_size |
integer | No | 20 | Items per page |
curl -s "$API/api/v1/identity/$IDENTITY_UUID/chunks?page=1&page_size=50" \
-H "X-API-Key: $KEY"
{
"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/facesAuth: Required Scope: identity-level
List all face detections for this identity.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
page |
integer | No | 1 | Page number |
page_size |
integer | No | 50 | Items per page |
curl -s "$API/api/v1/identity/$IDENTITY_UUID/faces?page=1&page_size=100" \
-H "X-API-Key: $KEY"
{
"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/statusAuth: Required Scope: identity-level
Get processing/status info for an identity.
curl -s "$API/api/v1/identity/$IDENTITY_UUID/status" \
-H "X-API-Key: $KEY"
{
"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/jsonAuth: Required Scope: identity-level
Get the raw identity JSON file (same format as identity.json on disk).
curl -s "$API/api/v1/identity/$IDENTITY_UUID/json" \
-H "X-API-Key: $KEY"
{
"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-personAuth: 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.
{
"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 |
# 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]}'
{
"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 |
identities row with status='pending', source='manual', file_uuid=<file_uuid>trace_ids provided: UPDATE face_detections SET identity_id = ... for matching tracestrace_ids provided: TKG face_track nodes get identity_id / identity_name in propertiesGET /api/v1/file/:file_uuid/pending-personsAuth: Required Scope: file-level
List all pending persons for a file.
curl -s "$API/api/v1/file/$FILE_UUID/pending-persons" \
-H "X-API-Key: $KEY"
{
"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) |
identities rows with status='pending' — they can be promoted to confirmed via PATCH /api/v1/identity/:identity_uuid ({"status": "confirmed"})POST /api/v1/identity/:identity_uuid/mergeintoGET /api/v1/identity/:identity_uuid/traces to get detailed trace info for each pending personIdentity aliases support multilingual display names. Aliases are stored in metadata.aliases as an array of {locale, name} objects.
| 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.
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;
}
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