705 lines
35 KiB
HTML
705 lines
35 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.</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>400</code></td>
|
|
<td>No fields to update or invalid UUID format</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>404</code></td>
|
|
<td>Identity not found</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<hr />
|
|
<h3><code>GET /api/v1/identity/:identity_uuid/files</code></h3>
|
|
<p><strong>Auth</strong>: Required
|
|
<strong>Scope</strong>: identity-level</p>
|
|
<p>Get all files where this identity appears. Returns per-file summary including face count, confidence, and appearance time range.</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>-H<span class="w"> </span><span class="s2">"X-API-Key: </span><span class="nv">$KEY</span><span class="s2">"</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>Get all face detection records associated with 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">/faces"</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>
|
|
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th>Field</th>
|
|
<th>Type</th>
|
|
<th>Description</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>file_uuid</code></td>
|
|
<td>string</td>
|
|
<td>File where face was detected</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>frame_number</code></td>
|
|
<td>integer</td>
|
|
<td>Frame number of detection</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>face_id</code></td>
|
|
<td>string</td>
|
|
<td>Face ID (format: <code>face_{frame_number}</code>)</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>confidence</code></td>
|
|
<td>float</td>
|
|
<td>Detection confidence</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<hr />
|
|
<h3><code>GET /api/v1/identity/:identity_uuid/chunks</code></h3>
|
|
<p><strong>Auth</strong>: Required
|
|
<strong>Scope</strong>: identity-level</p>
|
|
<p>Get all text chunks (sentences) spoken while this identity's face was on screen. Useful for finding what a person said.</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">/chunks"</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">"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">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</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">"bd80fec92b0b6963d177a2c55bf713e2"</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">"bd80fec92b0b6963d177a2c55bf713e2_2"</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">"sentence"</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="nt">"start_frame"</span><span class="p">:</span><span class="w"> </span><span class="mi">5103</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="nt">"end_frame"</span><span class="p">:</span><span class="w"> </span><span class="mi">5127</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="nt">"fps"</span><span class="p">:</span><span class="w"> </span><span class="mf">24.0</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">212.64</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">213.64</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">"[213s-214s] Cary Grant: \"Olá!\""</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>file_uuid</code></td>
|
|
<td>string</td>
|
|
<td>File identifier</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>chunk_id</code></td>
|
|
<td>string</td>
|
|
<td>Sentence chunk identifier</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>start_frame</code></td>
|
|
<td>integer</td>
|
|
<td>Frame-accurate start position</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>end_frame</code></td>
|
|
<td>integer</td>
|
|
<td>Frame-accurate end position</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>fps</code></td>
|
|
<td>float</td>
|
|
<td>Frames per second</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>start_time</code></td>
|
|
<td>float</td>
|
|
<td>Start time in seconds</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>end_time</code></td>
|
|
<td>float</td>
|
|
<td>End time in seconds</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>text_content</code></td>
|
|
<td>string</td>
|
|
<td>Spoken text content</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<hr />
|
|
<h3><code>POST /api/v1/identity/:identity_uuid/bind</code></h3>
|
|
<p><strong>Auth</strong>: Required
|
|
<strong>Scope</strong>: identity-level</p>
|
|
<p>Bind a face detection to an identity. Associates the face trace with the identity for future search and recognition.</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 face is detected</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>face_id</code></td>
|
|
<td>string</td>
|
|
<td>Yes</td>
|
|
<td>Face ID (format: <code>{frame}_{idx}</code>)</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/</span><span class="nv">$IDENTITY_UUID</span><span class="s2">/bind"</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">'", "face_id": "1_5"}'</span>
|
|
</code></pre></div>
|
|
|
|
<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>
|
|
<hr />
|
|
<h3><code>GET /api/v1/identities/search</code></h3>
|
|
<p><strong>Auth</strong>: Required
|
|
<strong>Scope</strong>: identity-level</p>
|
|
<p>Search identities by name (ILIKE search). Returns matching identity records.</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/search?q=Cary"</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>
|
|
|
|
<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>Identity name</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>source</code></td>
|
|
<td>string</td>
|
|
<td>Identity source</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>tmdb_id</code></td>
|
|
<td>integer</td>
|
|
<td>TMDb ID (if source = tmdb)</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>file_uuid</code></td>
|
|
<td>string</td>
|
|
<td>Associated file</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>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>*Updated: 2026-05-22</p>
|
|
</div>
|
|
</body>
|
|
</html> |