- Identity agent: per-face max matching, multi-round with derived seeds from high-confidence faces, angle diversity filter (cosine sim < 0.90) - Pending person API: POST /file/:file_uuid/pending-person + GET /file/:file_uuid/pending-persons with status=pending, source=manual - Update API docs (07_identity.md)
1744 lines
87 KiB
HTML
1744 lines
87 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>07 Identity - Momentry API Docs</title>
|
||
<style>
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f5f5; color: #333; padding: 40px; }
|
||
.container { max-width: 960px; margin: 0 auto; background: white; border-radius: 12px; box-shadow: 0 2px 12px rgba(0,0,0,0.08); padding: 40px; }
|
||
h1 { font-size: 24px; margin: 24px 0 12px; }
|
||
h2 { font-size: 20px; margin: 20px 0 10px; color: #222; }
|
||
h3 { font-size: 16px; margin: 16px 0 8px; color: #444; }
|
||
p { line-height: 1.6; margin: 8px 0; }
|
||
table { border-collapse: collapse; width: 100%; margin: 12px 0; font-size: 14px; }
|
||
th, td { border: 1px solid #ddd; padding: 8px 12px; text-align: left; }
|
||
th { background: #f0f0f0; font-weight: 600; }
|
||
code { background: #f0f0f0; padding: 2px 6px; border-radius: 3px; font-size: 13px; }
|
||
pre { background: #f8f8f8; border: 1px solid #ddd; border-radius: 6px; padding: 12px; overflow-x: auto; margin: 12px 0; }
|
||
pre code { background: none; padding: 0; }
|
||
a { color: #0066cc; }
|
||
.back { display: inline-block; margin-bottom: 20px; color: #666; }
|
||
.back:hover { color: #333; }
|
||
.topbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
|
||
.logout-btn { font-size: 13px; color: #999; text-decoration: none; }
|
||
.logout-btn:hover { color: #cc0000; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<div class="topbar">
|
||
<a class="back" href="index.html">← Back to index</a>
|
||
<a class="logout-btn" href="#" onclick="fetch('/api/v1/auth/logout',{method:'POST'}).then(()=>window.location.reload());return false">Logout</a>
|
||
</div>
|
||
<!-- module: identity -->
|
||
<!-- description: Global identities — CRUD, detail, files, faces, bind, unbind, search -->
|
||
<!-- depends: 01_auth -->
|
||
|
||
<h2>Global Identities</h2>
|
||
<h3><code>GET /api/v1/identities</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>List all registered identities with pagination.</p>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identities?page=1&page_size=20"</span><span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span><span class="w"> </span><span class="p">|</span><span class="w"> </span>jq<span class="w"> </span><span class="s1">'{count, identities: [.identities[] | {name}]}'</span>
|
||
</code></pre></div>
|
||
|
||
<hr />
|
||
<h3><code>GET /api/v1/identity/:identity_uuid</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Get detailed information for a specific identity, including metadata and TMDb references.</p>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">"</span><span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Response (200)</h4>
|
||
<div class="codehilite"><pre><span></span><code><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"identity_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a9a901056d6b46ff92da0c3c1a57dff4"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Cary Grant"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"identity_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"people"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"source"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tmdb"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"confirmed"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"tmdb_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">112</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"tmdb_profile"</span><span class="p">:</span><span class="w"> </span><span class="s2">"{output}/identities/{identity_uuid}/profile.jpg"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"metadata"</span><span class="p">:</span><span class="w"> </span><span class="p">{},</span>
|
||
<span class="w"> </span><span class="nt">"reference_data"</span><span class="p">:</span><span class="w"> </span><span class="p">{},</span>
|
||
<span class="w"> </span><span class="nt">"created_at"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-05-16T12:00:00Z"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"updated_at"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>identity_uuid</code></td>
|
||
<td>string</td>
|
||
<td>Identity identifier</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>name</code></td>
|
||
<td>string</td>
|
||
<td>Identity name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>identity_type</code></td>
|
||
<td>string</td>
|
||
<td><code>"people"</code> or null</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>source</code></td>
|
||
<td>string</td>
|
||
<td><code>.json</code>, <code>auto</code>, <code>tmdb</code>, <code>user_defined</code>, or <code>merged</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>status</code></td>
|
||
<td>string</td>
|
||
<td><code>"confirmed"</code>, <code>"pending"</code>, or <code>"inactive"</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>tmdb_id</code></td>
|
||
<td>integer</td>
|
||
<td>TMDb person ID (only if source = tmdb)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>tmdb_profile</code></td>
|
||
<td>string</td>
|
||
<td>Local profile image path (<code>{output}/identities/{uuid}/profile.jpg</code>)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>metadata</code></td>
|
||
<td>object</td>
|
||
<td>Metadata JSON (tmdb_character, cast_order, etc.)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>created_at</code></td>
|
||
<td>string</td>
|
||
<td>Creation timestamp</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h3><code>DELETE /api/v1/identity/:identity_uuid</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Delete an identity permanently. All face detections bound to this identity are unbound (<code>identity_id</code> set to <code>NULL</code>). The identity JSON file is deleted from disk.</p>
|
||
<h4>History & Undo/Redo</h4>
|
||
<p>Every DELETE records a full snapshot of the identity and its unbound faces. See <a href="14_identity_history.md#4-delete-history--undoredo"><code>14_identity_history.md</code></a> for:</p>
|
||
<ul>
|
||
<li>Undo via <code>POST /api/v1/identity/:identity_uuid/undo</code> — recreates identity and re-binds faces</li>
|
||
<li>Redo via <code>POST /api/v1/identity/:identity_uuid/redo</code> — re-deletes the identity</li>
|
||
</ul>
|
||
<p><strong>Note</strong>: 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.</p>
|
||
<hr />
|
||
<h3><code>PATCH /api/v1/identity/:identity_uuid</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Partially update an identity. Only provided fields are modified. The <code>name</code> field is a display label and may repeat across identities (removed UNIQUE constraint). Aliases for multilingual display are stored in <code>metadata.aliases</code> (see BCP 47 reference below).</p>
|
||
<h4>Request (JSON, all fields optional)</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>name</code></td>
|
||
<td>string</td>
|
||
<td>New display name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>metadata</code></td>
|
||
<td>object</td>
|
||
<td>Merged into existing metadata. Use <code>"aliases"</code> key for locale-tagged names</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>status</code></td>
|
||
<td>string</td>
|
||
<td><code>"confirmed"</code>, <code>"pending"</code>, or <code>"skipped"</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>identity_type</code></td>
|
||
<td>string</td>
|
||
<td><code>"people"</code>, <code>"brand"</code>, <code>"object"</code>, <code>"concept"</code>, etc.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code><span class="c1"># Update name and add aliases</span>
|
||
curl<span class="w"> </span>-s<span class="w"> </span>-X<span class="w"> </span>PATCH<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"Content-Type: application/json"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-d<span class="w"> </span><span class="s1">'{</span>
|
||
<span class="s1"> "name": "John Smith",</span>
|
||
<span class="s1"> "metadata": {</span>
|
||
<span class="s1"> "aliases": [</span>
|
||
<span class="s1"> {"locale": "en", "name": "John Smith"},</span>
|
||
<span class="s1"> {"locale": "zh-TW", "name": "約翰·史密斯"},</span>
|
||
<span class="s1"> {"locale": "ja", "name": "ジョン・スミス"}</span>
|
||
<span class="s1"> ]</span>
|
||
<span class="s1"> }</span>
|
||
<span class="s1"> }'</span>
|
||
|
||
<span class="c1"># Update status only</span>
|
||
curl<span class="w"> </span>-s<span class="w"> </span>-X<span class="w"> </span>PATCH<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"Content-Type: application/json"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-d<span class="w"> </span><span class="s1">'{"status": "confirmed"}'</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Response (200)</h4>
|
||
<div class="codehilite"><pre><span></span><code><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"identity_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a9a901056d6b46ff92da0c3c1a57dff4"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"updated_fields"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"name"</span><span class="p">,</span><span class="w"> </span><span class="s2">"metadata"</span><span class="p">]</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Error Responses</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>HTTP</th>
|
||
<th>When</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>404</code></td>
|
||
<td>Identity not found</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>500</code></td>
|
||
<td>Database error</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>History & Undo/Redo</h4>
|
||
<p>Every bind records a before/after snapshot. See <a href="14_identity_history.md#2-bindunbindtrace-history--undoredo"><code>14_identity_history.md</code></a> for:</p>
|
||
<ul>
|
||
<li><code>POST /api/v1/identity/:identity_uuid/bind/undo</code> — Revert a bind</li>
|
||
<li><code>POST /api/v1/identity/:identity_uuid/bind/redo</code> — Reapply an undone bind</li>
|
||
<li><code>GET /api/v1/identity/:identity_uuid/bind/history</code> — Query bind operations</li>
|
||
</ul>
|
||
<hr />
|
||
<h2>Metadata (Embedded JSON)</h2>
|
||
<p>The <code>identities.metadata</code> column is a <strong>JSONB</strong> 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.</p>
|
||
<h3>Merge Behavior</h3>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Operation</th>
|
||
<th>Strategy</th>
|
||
<th>Example</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>PATCH</strong></td>
|
||
<td>Shallow top-level merge: <code>COALESCE(metadata,'{}'::jsonb) \|\| $1::jsonb</code></td>
|
||
<td>Sending <code>{"tmdb_rating": 8.5}</code> only adds/overwrites <code>tmdb_rating</code>; all other existing keys are preserved.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>mergeinto</strong></td>
|
||
<td>Recursive deep merge — nested sub-keys are merged individually, not replaced wholesale</td>
|
||
<td>Target has <code>{"tmdb": {"biography": "..."}}</code>, source has <code>{"tmdb": {"birthday": "1904-01-18"}}</code> → result is <code>{"tmdb": {"biography": "...", "birthday": "1904-01-18"}}</code>.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Upload (<code>POST</code>)</strong></td>
|
||
<td>Direct overwrite — the entire <code>metadata</code> field is replaced with the request value.</td>
|
||
<td></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h3>Validation</h3>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Scenario</th>
|
||
<th>Result</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>PATCH with non-object metadata (<code>string</code>, <code>array</code>, <code>number</code>, <code>null</code>)</td>
|
||
<td><code>400 Bad Request: "metadata must be a JSON object"</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>mergeinto with non-object metadata</td>
|
||
<td>Accepted (mergeinto validates at application level)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Upload with non-object metadata</td>
|
||
<td>Accepted (upload replaces directly)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h3>Conventional Keys</h3>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Key</th>
|
||
<th>Type</th>
|
||
<th>Writer</th>
|
||
<th>Purpose</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>aliases</code></td>
|
||
<td><code>[{locale, name}]</code></td>
|
||
<td>PATCH, mergeinto</td>
|
||
<td>Multilingual display names (see <a href="#alias-system-bcp-47-locale-tags">Alias System</a>)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>merged_into</code></td>
|
||
<td><code>{uuid, at}</code></td>
|
||
<td>mergeinto</td>
|
||
<td>Marks an identity as merged (undo mechanism reads this)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>tmdb_*</code></td>
|
||
<td>various</td>
|
||
<td>TMDb probe</td>
|
||
<td>Movie metadata (biography, birthday, known_for, etc.). Written only when <code>MOMENTRY_TMDB_PROBE_ENABLED=true</code>.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>source</code></td>
|
||
<td>string</td>
|
||
<td>mergeinto</td>
|
||
<td>Tagged on aliases/metadata when added by merge (<code>"merge"</code> value)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Custom keys are fully supported — no registration required.</p>
|
||
<h3>Search Coverage</h3>
|
||
<p>The identity search endpoint (<code>GET /api/v1/identity/search</code>) matches across three scopes:</p>
|
||
<ol>
|
||
<li><code>i.name</code> — exact and ILIKE against display name</li>
|
||
<li><code>jsonb_array_elements(i.metadata->'aliases')->>'name'</code> — locale-tagged alias names</li>
|
||
<li><code>i.metadata::text ILIKE $1</code> — raw string search across the entire JSON blob (all keys, all values)</li>
|
||
</ol>
|
||
<p>This means searching for <code>"1904-01-18"</code> or <code>"biography"</code> will match identities whose metadata contains those strings anywhere.</p>
|
||
<h3>History Snapshots</h3>
|
||
<p>Every <code>identity_history</code> record captures the <strong>full metadata</strong> in both <code>before_snapshot</code> and <code>after_snapshot</code> (as part of the complete identity JSONB dump). Undo restores the identity row — including metadata — to the <code>before_snapshot</code> state.</p>
|
||
<p>For merge operations, the MongoDB merge history records <code>metadata_fields_added</code> and <code>metadata_fields_added_paths</code> (dot-separated paths like <code>"tmdb.biography"</code>). Merge undo removes only those specific paths, preserving subsequent manual edits to other metadata keys.</p>
|
||
<h3>Best Practices</h3>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Guideline</th>
|
||
<th>Reason</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Deep nesting is allowed in metadata</td>
|
||
<td>All metadata merge operations use <code>jsonb_deep_merge()</code> — nested sub-keys are merged recursively, not replaced wholesale</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Use <code>aliases</code> for display names</td>
|
||
<td>Frontend has built-in locale fallback logic (see <a href="#alias-system-bcp-47-locale-tags">Alias System</a>)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Avoid >1MB per identity</td>
|
||
<td>Metadata is included in search indexing (<code>metadata::text ILIKE</code>); large blobs degrade query performance</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Don't rely on metadata ordering</td>
|
||
<td>JSONB preserves insertion order but PostgreSQL does not guarantee it across operations</td>
|
||
</tr>
|
||
<tr>
|
||
<td>No LLM/Gemma4 agent writes to metadata</td>
|
||
<td>Only API endpoints (PATCH, mergeinto, upload) and TMDb probe modify <code>identities.metadata</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h3><code>POST /api/v1/identity/:identity_uuid/bind/trace</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Bind all face detections of a trace to an identity. Updates all rows in <code>face_detections</code> with the matching <code>file_uuid</code> and <code>trace_id</code>.</p>
|
||
<h4>Request Parameters</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Required</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>file_uuid</code></td>
|
||
<td>string</td>
|
||
<td>Yes</td>
|
||
<td>File where trace exists</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>trace_id</code></td>
|
||
<td>integer</td>
|
||
<td>Yes</td>
|
||
<td>Trace ID (from <code>face_detections.trace_id</code>)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Side Effects</h4>
|
||
<ul>
|
||
<li>清除該 trace 所有 face detection rows 的 <code>stranger_id</code>(設為 NULL)</li>
|
||
<li>不影響 <code>identities</code> 表中原有的 stranger auto-identity 記錄</li>
|
||
</ul>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">/bind/trace"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"Content-Type: application/json"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-d<span class="w"> </span><span class="s1">'{"file_uuid": "'</span><span class="s2">"</span><span class="nv">$FILE_UUID</span><span class="s2">"</span><span class="s1">'", "trace_id": 919}'</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Response (200)</h4>
|
||
<div class="codehilite"><pre><span></span><code><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Bound trace 919 of aeed71342... to Cary Grant"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nt">"rows_affected"</span><span class="p">:</span><span class="w"> </span><span class="mi">53</span><span class="w"> </span><span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Error Responses</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>HTTP</th>
|
||
<th>When</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>404</code></td>
|
||
<td>Identity not found</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>500</code></td>
|
||
<td>Database error</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>History & Undo/Redo</h4>
|
||
<p>Trace bind operations share the same history/undo/redo system as single-face binds. See <a href="14_identity_history.md#2-bindunbindtrace-history--undoredo"><code>14_identity_history.md</code></a> for endpoints.</p>
|
||
<hr />
|
||
<h3><code>GET /api/v1/identity/:identity_uuid/traces</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Get paginated face traces (continuous tracking segments) associated with this identity across all files.</p>
|
||
<h4>Query Parameters</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Required</th>
|
||
<th>Default</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>page</code></td>
|
||
<td>integer</td>
|
||
<td>No</td>
|
||
<td><code>1</code></td>
|
||
<td>Page number</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>page_size</code></td>
|
||
<td>integer</td>
|
||
<td>No</td>
|
||
<td><code>20</code></td>
|
||
<td>Items per page</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">/traces?page=1&page_size=3"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span><span class="w"> </span><span class="p">|</span><span class="w"> </span>jq<span class="w"> </span><span class="s1">'{total, total_faces, traces}'</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Response (200)</h4>
|
||
<div class="codehilite"><pre><span></span><code><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"identity_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a9a901056d6b46ff92da0c3c1a57dff4"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Cary Grant"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"total"</span><span class="p">:</span><span class="w"> </span><span class="mi">18</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"page"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"page_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"total_faces"</span><span class="p">:</span><span class="w"> </span><span class="mi">542</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"traces"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"file_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"aeed71342a899fe4b4c57b7d41bcb692"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"trace_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">906</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"frame_count"</span><span class="p">:</span><span class="w"> </span><span class="mi">52</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"first_frame"</span><span class="p">:</span><span class="w"> </span><span class="mi">37974</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"last_frame"</span><span class="p">:</span><span class="w"> </span><span class="mi">38127</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"first_sec"</span><span class="p">:</span><span class="w"> </span><span class="mf">1519.0</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"last_sec"</span><span class="p">:</span><span class="w"> </span><span class="mf">1525.1</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"avg_confidence"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.8254</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="p">]</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>success</code></td>
|
||
<td>bool</td>
|
||
<td>Always <code>true</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>identity_uuid</code></td>
|
||
<td>string</td>
|
||
<td>Identity UUID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>name</code></td>
|
||
<td>string</td>
|
||
<td>Identity display name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>total</code></td>
|
||
<td>integer</td>
|
||
<td>Total number of traces (across all pages)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>total_faces</code></td>
|
||
<td>integer</td>
|
||
<td>Sum of all face detections in returned traces</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>traces[].file_uuid</code></td>
|
||
<td>string</td>
|
||
<td>File where trace exists</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>traces[].trace_id</code></td>
|
||
<td>integer</td>
|
||
<td>Trace tracking ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>traces[].frame_count</code></td>
|
||
<td>integer</td>
|
||
<td>Number of frames in this trace</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>traces[].first_frame</code></td>
|
||
<td>integer</td>
|
||
<td>Start frame number</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>traces[].last_frame</code></td>
|
||
<td>integer</td>
|
||
<td>End frame number</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>traces[].first_sec</code></td>
|
||
<td>float</td>
|
||
<td>Start time in seconds</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>traces[].last_sec</code></td>
|
||
<td>float</td>
|
||
<td>End time in seconds</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>traces[].avg_confidence</code></td>
|
||
<td>float</td>
|
||
<td>Average detection confidence (0.0–1.0)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Error Responses</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>HTTP</th>
|
||
<th>When</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>404</code></td>
|
||
<td>Identity not found</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>500</code></td>
|
||
<td>Database error</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h3><code>POST /api/v1/identity/:identity_uuid/unbind</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Unbind a face detection from an identity. Removes the identity association from the face record.</p>
|
||
<h4>Side Effects</h4>
|
||
<ul>
|
||
<li>只清除 <code>identity_id</code>(設為 NULL),<strong>不會恢復 <code>stranger_id</code></strong></li>
|
||
<li>被 unbind 的 face 不會自動成為 stranger</li>
|
||
<li>要重新標記為 stranger 需重新跑 Agent API(<code>identity/analyze</code>)</li>
|
||
</ul>
|
||
<h4>History & Undo/Redo</h4>
|
||
<p>Unbind records a before/after snapshot. See <a href="14_identity_history.md#2-bindunbindtrace-history--undoredo"><code>14_identity_history.md</code></a> for:</p>
|
||
<ul>
|
||
<li><code>POST /api/v1/identity/:identity_uuid/bind/undo</code> — Revert an unbind</li>
|
||
<li><code>POST /api/v1/identity/:identity_uuid/bind/redo</code> — Reapply an undone unbind</li>
|
||
</ul>
|
||
<hr />
|
||
<h3><code>POST /api/v1/identity/:identity_uuid/mergeinto</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Transfer all face bindings from this identity to another identity, then optionally delete or mark the source as merged.</p>
|
||
<h4>Two Merge Cases</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Case</th>
|
||
<th>Description</th>
|
||
<th>Undo/Redo Support</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>stranger → identity</strong></td>
|
||
<td>Merge an auto-generated stranger identity into a known identity (TMDb or user-defined)</td>
|
||
<td>✅ 24hr undo/redo</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>identity A → identity B</strong></td>
|
||
<td>Merge two known identities (e.g., duplicate entries)</td>
|
||
<td>✅ 24hr undo/redo</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Request Parameters</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Required</th>
|
||
<th>Default</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>into_uuid</code></td>
|
||
<td>string</td>
|
||
<td>Yes</td>
|
||
<td>—</td>
|
||
<td>Target identity UUID to merge into</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>keep_history</code></td>
|
||
<td>bool</td>
|
||
<td>No</td>
|
||
<td><code>true</code></td>
|
||
<td>Keep source identity record with <code>status='merged'</code> (<code>true</code>) or delete it (<code>false</code>)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Side Effects</h4>
|
||
<ul>
|
||
<li>轉移所有 <code>face_detections.identity_id</code> 到目標 identity</li>
|
||
<li>同時清除所有被轉移 rows 的 <code>stranger_id</code></li>
|
||
<li>將 source name 加入 target aliases (with <code>source: "merge"</code> tag)</li>
|
||
<li>將 source aliases 加入 target aliases (if not already present)</li>
|
||
<li>將 source metadata fields 加入 target metadata (if not already present)</li>
|
||
<li><code>keep_history: true</code>(預設):source identity 設為 <code>status='merged'</code>,保留記錄</li>
|
||
<li><code>keep_history: false</code>:<strong>刪除</strong> source identity 及其 identity JSON 檔案</li>
|
||
<li><strong>記錄 merge history 到 MongoDB</strong>(支援 undo/redo)</li>
|
||
</ul>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$SOURCE_UUID</span><span class="s2">/mergeinto"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"Content-Type: application/json"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-d<span class="w"> </span><span class="s1">'{"into_uuid": "'</span><span class="s2">"</span><span class="nv">$TARGET_UUID</span><span class="s2">"</span><span class="s1">'", "keep_history": true}'</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Response (200)</h4>
|
||
<div class="codehilite"><pre><span></span><code><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Merged 'stranger_13894' into 'Louis Viret' (52 faces transferred, history kept)"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"merge_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"550e8400-e29b-41d4-a716-446655440000"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"faces_transferred"</span><span class="p">:</span><span class="w"> </span><span class="mi">52</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"aliases_added"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"metadata_fields_added"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>merge_id</code></td>
|
||
<td>string</td>
|
||
<td>Unique merge operation ID (for undo)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>faces_transferred</code></td>
|
||
<td>integer</td>
|
||
<td>Number of face detections transferred</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>aliases_added</code></td>
|
||
<td>integer</td>
|
||
<td>Number of aliases added to target</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>metadata_fields_added</code></td>
|
||
<td>integer</td>
|
||
<td>Number of metadata fields added to target</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Error Responses</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>HTTP</th>
|
||
<th>When</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>404</code></td>
|
||
<td>Source or target identity not found</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>500</code></td>
|
||
<td>Database error</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h3><code>POST /api/v1/identity/merge/:merge_id/undo</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Undo a merge operation within 24 hours. Restores the source identity and reverts face bindings.</p>
|
||
<h4>Undo Behavior</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Action</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Restore source identity</td>
|
||
<td>If <code>keep_history=true</code>: restore status to <code>confirmed</code><br>If <code>keep_history=false</code>: recreate identity from MongoDB snapshot</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Restore faces</td>
|
||
<td>Transfer faces back to source identity</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Remove aliases from target</td>
|
||
<td>Remove aliases with <code>source: "merge"</code> tag</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Remove metadata fields from target</td>
|
||
<td>Remove fields that were added from source</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Preserve manual changes</strong></td>
|
||
<td>Keep aliases/metadata manually added after merge</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/merge/550e8400-e29b-41d4-a716-446655440000/undo"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Response (200)</h4>
|
||
<div class="codehilite"><pre><span></span><code><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Undo merge completed: 'stranger_13894' restored, 52 faces reverted"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"source_identity_restored"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a9a90105..."</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"stranger_13894"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"confirmed"</span>
|
||
<span class="w"> </span><span class="p">},</span>
|
||
<span class="w"> </span><span class="nt">"faces_reverted"</span><span class="p">:</span><span class="w"> </span><span class="mi">52</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"aliases_removed_from_target"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"metadata_fields_removed_from_target"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Error Responses</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>HTTP</th>
|
||
<th>When</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>400</code></td>
|
||
<td>Undo deadline expired (>24hr) or already undone</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>404</code></td>
|
||
<td>Merge record not found</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>500</code></td>
|
||
<td>Database error</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h3><code>POST /api/v1/identity/merge/:merge_id/redo</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Redo a previously undone merge operation. See <a href="14_identity_history.md#post-apiv1identitymergemerge_idredo"><code>14_identity_history.md</code></a> for full details.</p>
|
||
<hr />
|
||
<h3><code>GET /api/v1/identity/merge/history</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Query merge history records from MongoDB.</p>
|
||
<h4>Query Parameters</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Required</th>
|
||
<th>Default</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>source_uuid</code></td>
|
||
<td>string</td>
|
||
<td>No</td>
|
||
<td>—</td>
|
||
<td>Filter by source identity UUID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>target_uuid</code></td>
|
||
<td>string</td>
|
||
<td>No</td>
|
||
<td>—</td>
|
||
<td>Filter by target identity UUID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>merge_id</code></td>
|
||
<td>string</td>
|
||
<td>No</td>
|
||
<td>—</td>
|
||
<td>Filter by specific merge ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>undone</code></td>
|
||
<td>bool</td>
|
||
<td>No</td>
|
||
<td>—</td>
|
||
<td>Filter by undone status</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>page</code></td>
|
||
<td>int</td>
|
||
<td>No</td>
|
||
<td>1</td>
|
||
<td>Page number</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>page_size</code></td>
|
||
<td>int</td>
|
||
<td>No</td>
|
||
<td>20</td>
|
||
<td>Items per page</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/merge/history?page=1&page_size=10"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Response (200)</h4>
|
||
<div class="codehilite"><pre><span></span><code><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"total"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"page"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"page_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"results"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"merge_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"550e8400-e29b-41d4-a716-446655440000"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"source_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"stranger_13894"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"target_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Louis Viret"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"faces_transferred"</span><span class="p">:</span><span class="w"> </span><span class="mi">52</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"merged_at"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-05-27T10:00:00Z"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"undo_deadline"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-05-28T10:00:00Z"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"undone"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"undo_expired"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="p">]</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>merge_id</code></td>
|
||
<td>string</td>
|
||
<td>Unique merge operation ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>source_name</code></td>
|
||
<td>string</td>
|
||
<td>Source identity name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>target_name</code></td>
|
||
<td>string</td>
|
||
<td>Target identity name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>faces_transferred</code></td>
|
||
<td>integer</td>
|
||
<td>Number of faces transferred</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>merged_at</code></td>
|
||
<td>datetime</td>
|
||
<td>When merge occurred</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>undo_deadline</code></td>
|
||
<td>datetime</td>
|
||
<td>24hr deadline for undo</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>undone</code></td>
|
||
<td>bool</td>
|
||
<td>Whether merge was undone</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>undo_expired</code></td>
|
||
<td>bool</td>
|
||
<td>Whether undo deadline passed</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h3><code>GET /api/v1/identities/search</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: global / file-level</p>
|
||
<p>Search identity name → find associated chunks. Searches identity name and aliases, returns identities with their associated text chunks.</p>
|
||
<h4>Query Parameters</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Required</th>
|
||
<th>Default</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>q</code></td>
|
||
<td>string</td>
|
||
<td>Yes</td>
|
||
<td>—</td>
|
||
<td>Search text (ILIKE match on name and aliases)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>file_uuid</code></td>
|
||
<td>string</td>
|
||
<td>No</td>
|
||
<td>—</td>
|
||
<td>Restrict to specific file. If omitted, searches all files (global search)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>limit</code></td>
|
||
<td>integer</td>
|
||
<td>No</td>
|
||
<td>50</td>
|
||
<td>Max results</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Example (Global Search)</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identities/search?q=Audrey"</span><span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Example (File-specific Search)</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identities/search?q=Audrey&file_uuid=</span><span class="nv">$FILE_UUID</span><span class="s2">"</span><span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Response (200)</h4>
|
||
<div class="codehilite"><pre><span></span><code><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"total"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"results"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"identity_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">9</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Audrey Hepburn"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"source"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tmdb"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"tmdb_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1932</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"file_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a6fb22eebefaef17e62af874997c5944"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"trace_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">41</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"chunk_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"llm_parent_..._204_207"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"start_time"</span><span class="p">:</span><span class="w"> </span><span class="mf">204.162</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"text_content"</span><span class="p">:</span><span class="w"> </span><span class="s2">"...confrontation..."</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="p">]</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>results[].identity_id</code></td>
|
||
<td>integer</td>
|
||
<td>Identity ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>results[].name</code></td>
|
||
<td>string</td>
|
||
<td>Identity name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>results[].source</code></td>
|
||
<td>string</td>
|
||
<td>Identity source (<code>tmdb</code>, <code>user_defined</code>, etc.)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>results[].tmdb_id</code></td>
|
||
<td>integer</td>
|
||
<td>TMDb person ID (if source = tmdb)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>results[].file_uuid</code></td>
|
||
<td>string</td>
|
||
<td>File where identity appears</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>results[].trace_id</code></td>
|
||
<td>integer</td>
|
||
<td>Face trace ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>results[].chunk_id</code></td>
|
||
<td>string</td>
|
||
<td>Associated chunk ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>results[].start_time</code></td>
|
||
<td>float</td>
|
||
<td>Chunk start time</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>results[].text_content</code></td>
|
||
<td>string</td>
|
||
<td>Chunk text content</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<hr />
|
||
<h3><code>POST /api/v1/identity/upload</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Upload an identity.json file to create or update an identity. Accepts the same format as the identity.json files stored on disk.</p>
|
||
<p>If an identity with the same <code>identity_uuid</code> already exists, it will be updated with the new values.</p>
|
||
<h4>Request</h4>
|
||
<p>The request body is an <code>IdentityFile</code> object:</p>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Required</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>identity_uuid</code></td>
|
||
<td>string</td>
|
||
<td>Yes</td>
|
||
<td>Identity identifier</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>name</code></td>
|
||
<td>string</td>
|
||
<td>Yes</td>
|
||
<td>Identity display name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>identity_type</code></td>
|
||
<td>string</td>
|
||
<td>No</td>
|
||
<td><code>"people"</code> or null</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>source</code></td>
|
||
<td>string</td>
|
||
<td>No</td>
|
||
<td><code>.json</code>, <code>auto</code>, <code>tmdb</code>, <code>user_defined</code>, or <code>merged</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>status</code></td>
|
||
<td>string</td>
|
||
<td>No</td>
|
||
<td><code>"confirmed"</code>, <code>"pending"</code>, or <code>"inactive"</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>tmdb_id</code></td>
|
||
<td>integer</td>
|
||
<td>No</td>
|
||
<td>TMDb person ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>tmdb_profile</code></td>
|
||
<td>string</td>
|
||
<td>No</td>
|
||
<td>TMDb profile image URL</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>metadata</code></td>
|
||
<td>object</td>
|
||
<td>No</td>
|
||
<td>Arbitrary metadata JSON</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>file_bindings</code></td>
|
||
<td>array</td>
|
||
<td>No</td>
|
||
<td>Array of <code>{ file_uuid, trace_ids, face_count }</code> (informational)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/upload"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"Content-Type: application/json"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-d<span class="w"> </span><span class="s1">'{</span>
|
||
<span class="s1"> "version": 1,</span>
|
||
<span class="s1"> "identity_uuid": "a9a901056d6b46ff92da0c3c1a57dff4",</span>
|
||
<span class="s1"> "name": "Cary Grant",</span>
|
||
<span class="s1"> "identity_type": "people",</span>
|
||
<span class="s1"> "source": ".json",</span>
|
||
<span class="s1"> "status": "confirmed",</span>
|
||
<span class="s1"> "metadata": {},</span>
|
||
<span class="s1"> "file_bindings": []</span>
|
||
<span class="s1"> }'</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Response (200)</h4>
|
||
<div class="codehilite"><pre><span></span><code><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"identity_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a9a901056d6b46ff92da0c3c1a57dff4"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Cary Grant"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Identity uploaded successfully"</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<hr />
|
||
<hr />
|
||
<h3><code>POST /api/v1/identity/:identity_uuid/profile-image</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Upload a profile image (JPEG or PNG) for an identity. The image is saved to <code>{output}/identities/{uuid}/profile.{ext}</code>.</p>
|
||
<p>Uses <code>multipart/form-data</code> with field name <code>image</code>.</p>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">/profile-image"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-F<span class="w"> </span><span class="s2">"image=@/path/to/photo.jpg"</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Response (200)</h4>
|
||
<div class="codehilite"><pre><span></span><code><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"identity_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a9a901056d6b46ff92da0c3c1a57dff4"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"path"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/path/to/output/identities/.../profile.jpg"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Profile image saved: profile.jpg"</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Error Responses</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>HTTP</th>
|
||
<th>When</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>400</code></td>
|
||
<td>Missing image field or unsupported format</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>404</code></td>
|
||
<td>Identity not found</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>415</code></td>
|
||
<td>Unsupported image type (use JPEG or PNG)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h3><code>GET /api/v1/identity/:identity_uuid/profile-image</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Retrieve the profile image for an identity. Returns the raw image data with appropriate Content-Type header.</p>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">/profile-image"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span><span class="w"> </span>-o<span class="w"> </span>profile.jpg
|
||
</code></pre></div>
|
||
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Response Header</th>
|
||
<th>Value</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>content-type</code></td>
|
||
<td><code>image/jpeg</code> or <code>image/png</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<hr />
|
||
<h2>Identity Related Data</h2>
|
||
<h3><code>GET /api/v1/identity/:identity_uuid/files</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>List all files containing this identity.</p>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">/files"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Response (200)</h4>
|
||
<div class="codehilite"><pre><span></span><code><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"identity_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a9a90105-6d6b-46ff-92da-0c3c1a57dff4"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"total"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"files"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"file_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"d3f9ae8e471a1fc4d47022c66091b920"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"file_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"video1.mp4"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"face_count"</span><span class="p">:</span><span class="w"> </span><span class="mi">142</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"first_appearance"</span><span class="p">:</span><span class="w"> </span><span class="mf">4.17</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"last_appearance"</span><span class="p">:</span><span class="w"> </span><span class="mf">208.33</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="p">]</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<hr />
|
||
<h3><code>GET /api/v1/identity/:identity_uuid/chunks</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>List all chunks associated with this identity (chunks where the identity's face appears).</p>
|
||
<h4>Query Parameters</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Required</th>
|
||
<th>Default</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>page</code></td>
|
||
<td>integer</td>
|
||
<td>No</td>
|
||
<td>1</td>
|
||
<td>Page number</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>page_size</code></td>
|
||
<td>integer</td>
|
||
<td>No</td>
|
||
<td>20</td>
|
||
<td>Items per page</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">/chunks?page=1&page_size=50"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Response (200)</h4>
|
||
<div class="codehilite"><pre><span></span><code><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"identity_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a9a90105-6d6b-46ff-92da-0c3c1a57dff4"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"total"</span><span class="p">:</span><span class="w"> </span><span class="mi">45</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"page"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"page_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">20</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"chunks"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"chunk_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"chunk_1"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"file_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"d3f9ae8e471a1fc4d47022c66091b920"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"start_time"</span><span class="p">:</span><span class="w"> </span><span class="mf">4.17</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"end_time"</span><span class="p">:</span><span class="w"> </span><span class="mf">8.33</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"text"</span><span class="p">:</span><span class="w"> </span><span class="s2">"[4s-8s] Hello, how are you?"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"chunk_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"story_child"</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="p">]</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<hr />
|
||
<h3><code>GET /api/v1/identity/:identity_uuid/faces</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>List all face detections for this identity.</p>
|
||
<h4>Query Parameters</h4>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Required</th>
|
||
<th>Default</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>page</code></td>
|
||
<td>integer</td>
|
||
<td>No</td>
|
||
<td>1</td>
|
||
<td>Page number</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>page_size</code></td>
|
||
<td>integer</td>
|
||
<td>No</td>
|
||
<td>50</td>
|
||
<td>Items per page</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">/faces?page=1&page_size=100"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Response (200)</h4>
|
||
<div class="codehilite"><pre><span></span><code><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"identity_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a9a90105-6d6b-46ff-92da-0c3c1a57dff4"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"total"</span><span class="p">:</span><span class="w"> </span><span class="mi">1420</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"page"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"page_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">50</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"faces"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"face_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"face_100"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"file_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"d3f9ae8e471a1fc4d47022c66091b920"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"frame_number"</span><span class="p">:</span><span class="w"> </span><span class="mi">1200</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="mf">50.0</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"bbox"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mi">100</span><span class="p">,</span><span class="w"> </span><span class="mi">50</span><span class="p">,</span><span class="w"> </span><span class="mi">300</span><span class="p">,</span><span class="w"> </span><span class="mi">400</span><span class="p">],</span>
|
||
<span class="w"> </span><span class="nt">"confidence"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.95</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"trace_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="p">]</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<hr />
|
||
<h3><code>GET /api/v1/identity/:identity_uuid/status</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Get processing/status info for an identity.</p>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">/status"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Response (200)</h4>
|
||
<div class="codehilite"><pre><span></span><code><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"identity_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a9a90105-6d6b-46ff-92da-0c3c1a57dff4"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Audrey Hepburn"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"confirmed"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"face_count"</span><span class="p">:</span><span class="w"> </span><span class="mi">1420</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"file_count"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"has_embedding"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"has_profile_image"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<hr />
|
||
<h3><code>GET /api/v1/identity/:identity_uuid/json</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: identity-level</p>
|
||
<p>Get the raw identity JSON file (same format as identity.json on disk).</p>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/identity/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">/json"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Response (200)</h4>
|
||
<div class="codehilite"><pre><span></span><code><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"version"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"identity_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"a9a90105-6d6b-46ff-92da-0c3c1a57dff4"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Audrey Hepburn"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"identity_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"people"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"source"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tmdb"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"confirmed"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"tmdb_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1234</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"tmdb_profile"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://image.tmdb.org/..."</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"metadata"</span><span class="p">:</span><span class="w"> </span><span class="p">{},</span>
|
||
<span class="w"> </span><span class="nt">"file_bindings"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||
<span class="w"> </span><span class="p">{</span><span class="nt">"file_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"d3f9ae8e..."</span><span class="p">,</span><span class="w"> </span><span class="nt">"trace_ids"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">],</span><span class="w"> </span><span class="nt">"face_count"</span><span class="p">:</span><span class="w"> </span><span class="mi">142</span><span class="p">}</span>
|
||
<span class="w"> </span><span class="p">]</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<hr />
|
||
<hr />
|
||
<h3><code>POST /api/v1/file/:file_uuid/pending-person</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: file-level</p>
|
||
<p>Create a manually managed "pending person" under a specific file. A pending person is an identity with <code>status='pending'</code> and <code>source='manual'</code>, used for unmatched traces that the user wants to manually label before a full identity resolution.</p>
|
||
<p>Optionally binds a list of trace IDs to this new identity.</p>
|
||
<h4>Request</h4>
|
||
<div class="codehilite"><pre><span></span><code><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"trace_ids"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="mi">100</span><span class="p">,</span><span class="w"> </span><span class="mi">150</span><span class="p">,</span><span class="w"> </span><span class="mi">200</span><span class="p">],</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Mystery Man #1"</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Required</th>
|
||
<th>Default</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>trace_ids</code></td>
|
||
<td>array[int]</td>
|
||
<td>No</td>
|
||
<td><code>[]</code></td>
|
||
<td>Trace IDs to bind to this pending person</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>name</code></td>
|
||
<td>string</td>
|
||
<td>No</td>
|
||
<td><code>"Person N"</code></td>
|
||
<td>Human-readable name. Auto-generated if omitted</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code><span class="c1"># Create pending person with name and no traces</span>
|
||
curl<span class="w"> </span>-s<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/file/</span><span class="nv">$FILE_UUID</span><span class="s2">/pending-person"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"Content-Type: application/json"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-d<span class="w"> </span><span class="s1">'{"name": "Unknown Woman #2", "trace_ids": []}'</span>
|
||
|
||
<span class="c1"># Create pending person with auto-name and bind traces</span>
|
||
curl<span class="w"> </span>-s<span class="w"> </span>-X<span class="w"> </span>POST<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/file/</span><span class="nv">$FILE_UUID</span><span class="s2">/pending-person"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"Content-Type: application/json"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-d<span class="w"> </span><span class="s1">'{"trace_ids": [100, 150, 200]}'</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Response (200)</h4>
|
||
<div class="codehilite"><pre><span></span><code><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Created pending person: Mystery Man #1 (uuid: 4d96b25b-68f0-4c52-b238-d69f7dfd588b)"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"identity_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"4d96b25b-68f0-4c52-b238-d69f7dfd588b"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"identity_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">55</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Mystery Man #1"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"bound_traces"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>identity_uuid</code></td>
|
||
<td>string</td>
|
||
<td>UUID of the newly created pending identity</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>identity_id</code></td>
|
||
<td>integer</td>
|
||
<td>Internal ID of the new identity</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>name</code></td>
|
||
<td>string</td>
|
||
<td>Display name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>bound_traces</code></td>
|
||
<td>integer</td>
|
||
<td>Number of traces bound</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Side Effects</h4>
|
||
<ul>
|
||
<li>Creates an <code>identities</code> row with <code>status='pending'</code>, <code>source='manual'</code>, <code>file_uuid=<file_uuid></code></li>
|
||
<li>If <code>trace_ids</code> provided: <code>UPDATE face_detections SET identity_id = ...</code> for matching traces</li>
|
||
<li>If <code>trace_ids</code> provided: TKG face_track nodes get <code>identity_id</code> / <code>identity_name</code> in properties</li>
|
||
<li>Identity JSON file synced to disk</li>
|
||
</ul>
|
||
<hr />
|
||
<h3><code>GET /api/v1/file/:file_uuid/pending-persons</code></h3>
|
||
<p><strong>Auth</strong>: Required
|
||
<strong>Scope</strong>: file-level</p>
|
||
<p>List all pending persons for a file.</p>
|
||
<h4>Example</h4>
|
||
<div class="codehilite"><pre><span></span><code>curl<span class="w"> </span>-s<span class="w"> </span><span class="s2">"</span><span class="nv">$API</span><span class="s2">/api/v1/file/</span><span class="nv">$FILE_UUID</span><span class="s2">/pending-persons"</span><span class="w"> </span><span class="se">\</span>
|
||
<span class="w"> </span>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</span>
|
||
</code></pre></div>
|
||
|
||
<h4>Response (200)</h4>
|
||
<div class="codehilite"><pre><span></span><code><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"success"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Found 2 pending persons for c36f35685177c981aa139b66bbbccc5b"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||
<span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"identity_uuid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"232ecd08-a2bf-4bd0-bd25-0bd8fb7a7dae"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"identity_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">56</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Person 2"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"created_at"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2026-06-23 17:13:23"</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"trace_count"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span>
|
||
<span class="w"> </span><span class="nt">"bound_traces"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="w"> </span><span class="p">]</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Field</th>
|
||
<th>Type</th>
|
||
<th>Description</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><code>identity_uuid</code></td>
|
||
<td>string</td>
|
||
<td>Identity UUID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>identity_id</code></td>
|
||
<td>integer</td>
|
||
<td>Internal identity ID</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>name</code></td>
|
||
<td>string</td>
|
||
<td>Display name</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>created_at</code></td>
|
||
<td>string</td>
|
||
<td>Creation timestamp</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>trace_count</code></td>
|
||
<td>integer</td>
|
||
<td>Number of face traces bound to this pending person</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>bound_traces</code></td>
|
||
<td>array[int]</td>
|
||
<td>List of bound trace IDs (currently null, reserved for future expansion)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h4>Notes</h4>
|
||
<ul>
|
||
<li>Pending persons are normal <code>identities</code> rows with <code>status='pending'</code> — they can be promoted to confirmed via <code>PATCH /api/v1/identity/:identity_uuid</code> (<code>{"status": "confirmed"}</code>)</li>
|
||
<li>They can be merged into known identities via <code>POST /api/v1/identity/:identity_uuid/mergeinto</code></li>
|
||
<li>Use <code>GET /api/v1/identity/:identity_uuid/traces</code> to get detailed trace info for each pending person</li>
|
||
</ul>
|
||
<hr />
|
||
<h2>Alias System (BCP 47 Locale Tags)</h2>
|
||
<p>Identity aliases support multilingual display names. Aliases are stored in <code>metadata.aliases</code> as an array of <code>{locale, name}</code> objects.</p>
|
||
<h3>BCP 47 Locale Tags Reference</h3>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Locale</th>
|
||
<th>Tag</th>
|
||
<th>Example</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>English</td>
|
||
<td><code>en</code></td>
|
||
<td>John Smith</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Traditional Chinese</td>
|
||
<td><code>zh-TW</code></td>
|
||
<td>約翰·史密斯</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Simplified Chinese</td>
|
||
<td><code>zh-CN</code></td>
|
||
<td>约翰·史密斯</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Japanese</td>
|
||
<td><code>ja</code></td>
|
||
<td>ジョン・スミス</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Korean</td>
|
||
<td><code>ko</code></td>
|
||
<td>존 스미스</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Cantonese</td>
|
||
<td><code>yue</code></td>
|
||
<td>約翰·史密夫</td>
|
||
</tr>
|
||
<tr>
|
||
<td>French</td>
|
||
<td><code>fr</code></td>
|
||
<td>John Smith (French spelling)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Spanish</td>
|
||
<td><code>es</code></td>
|
||
<td>Juan Smith</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Arabic</td>
|
||
<td><code>ar</code></td>
|
||
<td>جون سميث</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Russian</td>
|
||
<td><code>ru</code></td>
|
||
<td>Джон Смит</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Thai</td>
|
||
<td><code>th</code></td>
|
||
<td>จอห์น สมิธ</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>BCP 47 is the IETF standard for language tags. Format: <code>language</code> (e.g. <code>en</code>, <code>ja</code>) or <code>language-Region</code> (e.g. <code>zh-TW</code>, <code>zh-CN</code>). Region suffix distinguishes regional variants.</p>
|
||
<h3>Frontend Display Logic</h3>
|
||
<div class="codehilite"><pre><span></span><code><span class="kd">function</span><span class="w"> </span><span class="nx">getDisplayName</span><span class="p">(</span><span class="nx">identity</span><span class="p">,</span><span class="w"> </span><span class="nx">preferredLocale</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="c1">// 1. Exact locale match</span>
|
||
<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">match</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">identity</span><span class="p">.</span><span class="nx">metadata</span><span class="o">?</span><span class="p">.</span><span class="nx">aliases</span><span class="o">?</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="nx">a</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="nx">a</span><span class="p">.</span><span class="nx">locale</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="nx">preferredLocale</span><span class="p">);</span>
|
||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">match</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">match</span><span class="p">.</span><span class="nx">name</span><span class="p">;</span>
|
||
|
||
<span class="w"> </span><span class="c1">// 2. Language-only match (zh-TW → zh)</span>
|
||
<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">lang</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">preferredLocale</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s1">'-'</span><span class="p">)[</span><span class="mf">0</span><span class="p">];</span>
|
||
<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">langMatch</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">identity</span><span class="p">.</span><span class="nx">metadata</span><span class="o">?</span><span class="p">.</span><span class="nx">aliases</span><span class="o">?</span><span class="p">.</span><span class="nx">find</span><span class="p">(</span><span class="nx">a</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="nx">a</span><span class="p">.</span><span class="nx">locale</span><span class="p">.</span><span class="nx">startsWith</span><span class="p">(</span><span class="nx">lang</span><span class="p">));</span>
|
||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">langMatch</span><span class="p">)</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">langMatch</span><span class="p">.</span><span class="nx">name</span><span class="p">;</span>
|
||
|
||
<span class="w"> </span><span class="c1">// 3. Fallback to identity.name</span>
|
||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">identity</span><span class="p">.</span><span class="nx">name</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<h3>Updating Aliases via PATCH</h3>
|
||
<div class="codehilite"><pre><span></span><code><span class="err">PATCH</span><span class="w"> </span><span class="err">/api/v</span><span class="mi">1</span><span class="err">/ide</span><span class="kc">nt</span><span class="err">i</span><span class="kc">t</span><span class="err">y/</span><span class="p">:</span><span class="err">ide</span><span class="kc">nt</span><span class="err">i</span><span class="kc">t</span><span class="err">y_uuid</span>
|
||
<span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"metadata"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span>
|
||
<span class="w"> </span><span class="nt">"aliases"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>
|
||
<span class="w"> </span><span class="p">{</span><span class="nt">"locale"</span><span class="p">:</span><span class="w"> </span><span class="s2">"en"</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"John Smith"</span><span class="p">},</span>
|
||
<span class="w"> </span><span class="p">{</span><span class="nt">"locale"</span><span class="p">:</span><span class="w"> </span><span class="s2">"zh-TW"</span><span class="p">,</span><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"約翰·史密斯"</span><span class="p">}</span>
|
||
<span class="w"> </span><span class="p">]</span>
|
||
<span class="w"> </span><span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div>
|
||
|
||
<p>This <strong>replaces</strong> the entire <code>aliases</code> array. To add to existing aliases, include all existing entries in the request.</p>
|
||
<hr />
|
||
<p><em>Updated: 2026-06-20 — Added identity files, chunks, faces, status, and JSON endpoints</em></p>
|
||
</div>
|
||
</body>
|
||
</html> |