# Momentry Core API v1.0.0

**Release**: v1.0.0  
**Last Updated**: 2026-05-06  
**Base URL**: `http://{host}:{port}` (dev: 3003, prod: 3002)

---

## Authentication

### API Key (Protected Routes)

```
Header: X-API-Key: <your_api_key>
```

Protected routes require a valid API key in the `X-API-Key` header. Unauthorized requests return `401 Unauthorized`.

### Login (Unprotected)

```
POST /api/v1/auth/login
Content-Type: application/json

{
  "username": "string",
  "password": "string"
}
```

Response `200`:
```json
{
  "success": true,
  "message": "Login successful",
  "api_key": "muser_xxx_xxx",
  "user": { "id": 1, "name": "string" }
}
```

---

## 1. File Management

### 1.1 Register File

Registers a video file into the system. Runs ffprobe probe + scene detection synchronously.

```
POST /api/v1/files/register
X-API-Key: <key>
Content-Type: application/json

{
  "file_path": "/path/to/video.mp4",
  "pattern": null,
  "user_id": null
}
```

Response `200`:
```json
{
  "success": true,
  "file_uuid": "32-char-hex-string",
  "file_name": "video.mp4",
  "file_path": "/path/to/video.mp4",
  "file_type": "video",
  "duration": 6879.0,
  "width": 1920,
  "height": 1080,
  "fps": 25.0,
  "total_frames": 171975,
  "registration_time": "2026-05-06T12:00:00Z",
  "already_exists": false,
  "message": "File registered successfully"
}
```

### 1.2 Unregister File

```
POST /api/v1/unregister
X-API-Key: <key>
Content-Type: application/json

{
  "uuid": "32-char-hex-string",
  "file_path": null,
  "pattern": null
}
```

Response `200`:
```json
{
  "success": true,
  "uuid": "32-char-hex-string",
  "message": "File unregistered successfully",
  "deleted_face_detections": 6186,
  "deleted_processor_results": 42,
  "deleted_chunks": 10546
}
```

### 1.3 Scan Files

Scans the configured watch directory and reports all files found.

```
GET /api/v1/files/scan
X-API-Key: <key>
```

Response `200`:
```json
{
  "files": [
    {
      "name": "video.mp4",
      "path": "/data/demo/video.mp4",
      "size": 1600000000,
      "is_registered": true,
      "file_uuid": "32-char-hex-string"
    }
  ],
  "total": 22,
  "registered_count": 20,
  "unregistered_count": 2
}
```

### 1.4 File Probe

Returns ffprobe metadata for a registered video.

```
GET /api/v1/file/{file_uuid}/probe
X-API-Key: <key>
```

Response `200`:
```json
{
  "file_uuid": "32-char-hex-string",
  "file_name": "video.mp4",
  "duration": 6785.0,
  "width": 1920,
  "height": 1080,
  "fps": 25.0,
  "total_frames": 169625,
  "cached": true,
  "format": "mov,mp4,m4a,3gp,3g2,mj2",
  "streams": [
    { "index": 0, "codec_type": "video", "codec_name": "av1", "width": 1920, "height": 1080 },
    { "index": 1, "codec_type": "audio", "codec_name": "opus", "sample_rate": 48000, "channels": 2 }
  ]
}
```

### 1.5 Trigger Processing

Triggers video processing pipeline for the specified processors.

```
POST /api/v1/file/{file_uuid}/process
X-API-Key: <key>
Content-Type: application/json

{
  "processors": ["asr", "cut", "yolo", "ocr", "face", "pose", "asrx"]
}
```

Response `200`:
```json
{
  "job_id": 139,
  "file_uuid": "32-char-hex-string",
  "status": "PENDING",
  "pids": [],
  "message": "Processing triggered for video.mp4"
}
```

### 1.6 List Pre-Chunks

Lists pre-chunks (raw processor output) for a video with pagination.

```
GET /api/v1/file/{file_uuid}/chunks
X-API-Key: <key>
Query: ?processor_type=face&page=1&page_size=20
```

Response `200`:
```json
{
  "pre_chunks": [
    {
      "id": 537507,
      "processor_type": "asr",
      "coordinate_type": "time",
      "coordinate_index": 0,
      "start_frame": null,
      "end_frame": null,
      "start_time": 1.66,
      "end_time": 18.95,
      "fps": 24.0,
      "data": { "text": "Hello and welcome...", "language": "en" },
      "created_at": "2026-05-06T12:00:00.000000Z"
    }
  ],
  "count": 3,
  "page": 1,
  "page_size": 20
}
```

### 1.7 List Jobs

```
GET /api/v1/jobs
X-API-Key: <key>
Query: ?page=1&page_size=10&status=completed
```

Response `200`:
```json
{
  "jobs": [
    {
      "id": 139,
      "uuid": "32-char-hex-string",
      "status": "completed",
      "current_processor": null
    }
  ],
  "count": 1,
  "page": 1,
  "page_size": 10
}
```

### 1.8 Get Progress

```
GET /api/v1/progress/{uuid}
X-API-Key: <key>
```

Response `200`:
```json
{
  "file_uuid": "32-char-hex-string",
  "overall_progress": 100.0,
  "processors": [
    { "type": "asr", "status": "completed", "progress": 100.0 },
    { "type": "face", "status": "completed", "progress": 100.0 }
  ]
}
```

---

## 2. Videos List (Unprotected)

### 2.1 List Videos

```
GET /api/v1/files
Query: ?page=1&page_size=10&uuid=xxx
```

Response `200`:
```json
{
  "success": true,
  "total": 25,
  "page": 1,
  "page_size": 10,
  "data": [
    {
      "file_uuid": "32-char-hex-string",
      "file_name": "video.mp4",
      "file_path": "/data/demo/video.mp4",
      "duration": 6785.0,
      "status": "completed",
      "created_at": "2026-05-06T12:00:00Z"
    }
  ]
}
```

### 2.2 Get File Detail

```
GET /api/v1/file/{file_uuid}
```

Response `200`:
```json
{
  "success": true,
  "file_uuid": "32-char-hex-string",
  "file_name": "video.mp4",
  "file_path": "/data/demo/video.mp4",
  "metadata": {},
  "created_at": "2026-05-06T12:00:00Z"
}
```

### 2.3 Get File Identities

```
GET /api/v1/file/{file_uuid}/identities
Query: ?page=1&page_size=20
```

---

## 3. Media & Video Streaming

### 3.1 Stream Video

Streams video with HTTP range support for seeking.

```
GET /api/v1/file/{file_uuid}/video
Headers: Range: bytes=0-1000000
```

Returns `video/mp4` binary with `206 Partial Content` if Range header provided.

### 3.2 BBOX Overlay Video

Returns video with face bounding boxes overlaid.

```
GET /api/v1/file/{file_uuid}/video/bbox
Query: ?start=0&end=300&face_uuid=xxx
```

Returns `video/mp4` binary with red bboxes drawn at frame intervals.

### 3.3 Trace Video

Returns video highlighting a specific face trace with text label.

```
GET /api/v1/file/{file_uuid}/trace/{trace_id}/video
Query: ?padding=1
```

Returns `video/mp4` binary. Shows face trace with ID label held at last detection position.

### 3.4 Thumbnail

Extracts a single frame as JPEG thumbnail.

```
GET /api/v1/file/{file_uuid}/thumbnail
Query: ?frame=840&x=0&y=0&w=100&h=100
```

Returns `image/jpeg` binary.

---

## 4. Identity Management

### 4.1 List Identities (Protected)

```
GET /api/v1/identities
X-API-Key: <key>
Query: ?page=1&page_size=20
```

Response `200`:
```json
{
  "identities": [
    {
      "identity_uuid": "uuid-string",
      "name": "Cary Grant",
      "identity_type": "actor",
      "face_count": 120,
      "confidence": 0.95
    }
  ],
  "count": 41,
  "page": 1,
  "page_size": 20
}
```

### 4.2 Create Identity

```
POST /api/v1/identity
X-API-Key: <key>
Content-Type: application/json

{
  "face_json_path": "/path/to/face.json",
  "identity_name": "Cary Grant"
}
```

### 4.3 Get Identity Detail (Unprotected)

```
GET /api/v1/identity/{identity_uuid}
```

Response `200`:
```json
{
  "success": true,
  "uuid": "identity-uuid",
  "name": "Cary Grant",
  "identity_type": "actor",
  "source": "tmdb",
  "status": "active",
  "metadata": {},
  "reference_data": {},
  "tmdb_id": 1234,
  "tmdb_profile": "/path/to/profile.jpg",
  "created_at": "2026-01-01T00:00:00Z",
  "updated_at": "2026-05-06T00:00:00Z"
}
```

> `tmdb_id` 和 `tmdb_profile` 只有在 `identity_type` 為 `"actor"` 時才會出現。其他類型（如 `"stranger"`）無此欄位。


### 4.4 Delete Identity

```
DELETE /api/v1/identity/{identity_uuid}
```

Returns `204 No Content`.

### 4.5 Get Identity Files

```
GET /api/v1/identity/{identity_uuid}/files
Query: ?page=1&page_size=20
```

### 4.6 Get Identity Chunks

```
GET /api/v1/identity/{identity_uuid}/chunks
Query: ?page=1&page_size=20
```

### 4.7 List Face Candidates (Protected)

```
GET /api/v1/faces/candidates
X-API-Key: <key>
Query: ?file_uuid=xxx&min_confidence=0.5&page=1&page_size=20
```

### 4.8 Bind Face to Identity

```
POST /api/v1/identity/{identity_uuid}/bind
X-API-Key: <key>
Content-Type: application/json

{
  "file_uuid": "32-char-hex-string",
  "face_id": "face_123"
}
```

Response `200`:
```json
{
  "success": true,
  "message": "Face bound to identity",
  "data": { "rows_affected": 1 }
}
```

### 4.9 Unbind Face from Identity

```
POST /api/v1/identity/{identity_uuid}/unbind
X-API-Key: <key>
Content-Type: application/json

{
  "file_uuid": "32-char-hex-string",
  "face_id": "face_123"
}
```

### 4.10 Merge Identities

```
POST /api/v1/identity/{from_uuid}/mergeinto
X-API-Key: <key>
Content-Type: application/json

{
  "into_uuid": "target-identity-uuid",
  "keep_history": true
}
```

---

## 5. Search

### 5.1 Universal Search

Multi-type search across chunks, frames, and persons.

```
POST /api/v1/search/universal
Content-Type: application/json

{
  "query": "Cary Grant",
  "uuid": "32-char-hex-string",
  "types": ["chunk", "frame", "person"],
  "time_range": null,
  "filters": null,
  "limit": 10,
  "offset": 0
}
```

Response `200`:
```json
{
  "query": "Cary Grant",
  "results": [
    {
      "type": "chunk",
      "chunk_id": "chunk_123",
      "score": 0.9,
      "text": "[59s-77s] Cast: Cary Grant, Walter Matthau.",
      "start_time": 59.0,
      "end_time": 77.0,
      "start_frame": 1475,
      "end_frame": 1925,
      "fps": 25.0,
      "speaker_id": null,
      "metadata": {}
    }
  ],
  "total": 3,
  "took_ms": 45
}
```

### 5.2 Smart Search

LLM-powered search with query understanding.

```
POST /api/v1/search/smart
Content-Type: application/json

{
  "uuid": "32-char-hex-string",
  "query": "who said how do you shave in there?",
  "limit": 10
}
```

Response `200`:
```json
{
  "query": "who said how do you shave in there?",
  "results": [
    {
      "chunk_id": "chunk_123",
      "type": "sentence",
      "score": 0.95,
      "text": "[2035s-2038s] Cary Grant: \"how do you shave in there?\"",
      "start_time": 2035.09,
      "end_time": 2037.62,
      "start_frame": 50877,
      "end_frame": 50940,
      "fps": 25.0
    }
  ],
  "strategy": "semantic"
}
```

### 5.3 Frame Search

Search individual video frames by object class, OCR text, or face.

```
POST /api/v1/search/frames
Content-Type: application/json

{
  "uuid": "32-char-hex-string",
  "object_class": "person",
  "ocr_text": "welcome",
  "face_id": null,
  "time_range": null,
  "limit": 20
}
```

Response `200`:
```json
{
  "frames": [
    {
      "frame_number": 54,
      "timestamp": 2.16,
      "score": 0.85,
      "objects": ["person"],
      "ocr_texts": ["welcome"],
      "faces": ["face_1"],
      "pose_persons": []
    }
  ],
  "total": 1
}
```

### 5.4 Visual Chunk Search

Searches for visual chunks (time segments with object detections) matching criteria.

```
POST /api/v1/search/visual
Content-Type: application/json

{
  "uuid": "32-char-hex-string",
  "criteria": {
    "min_unique_classes": 2,
    "required_classes": ["person", "car"]
  }
}
```

Response `200`:
```json
{
  "chunks": [
    {
      "chunk_id": "vis_001",
      "start_time": 120.0,
      "end_time": 135.0,
      "start_frame": 3000,
      "end_frame": 3375,
      "fps": 25.0,
      "object_classes": ["person", "car"],
      "total_objects": 5
    }
  ],
  "total": 1
}
```

### 5.5 Visual Chunk Search by Class

```
POST /api/v1/search/visual/class
Content-Type: application/json

{
  "uuid": "32-char-hex-string",
  "object_class": "car",
  "min_count": 1,
  "max_count": 10
}
```

Response `200`:
```json
{
  "chunks": [
    {
      "chunk_id": "vis_001",
      "start_time": 120.0,
      "end_time": 135.0,
      "start_frame": 3000,
      "end_frame": 3375,
      "fps": 25.0,
      "object_class": "car",
      "count": 3
    }
  ],
  "total": 1
}
```

### 5.6 Visual Chunk Search by Density

```
POST /api/v1/search/visual/density
Content-Type: application/json

{
  "uuid": "32-char-hex-string",
  "min_density": 0.1,
  "max_density": 0.8
}
```

Response `200`:
```json
{
  "chunks": [
    {
      "chunk_id": "vis_001",
      "start_time": 120.0,
      "end_time": 135.0,
      "start_frame": 3000,
      "end_frame": 3375,
      "fps": 25.0,
      "density": 0.35
    }
  ],
  "total": 1
}
```

### 5.7 Visual Chunk Stats

```
POST /api/v1/search/visual/stats
Content-Type: application/json

{
  "uuid": "32-char-hex-string"
}
```

Response `200`:
```json
{
  "uuid": "32-char-hex-string",
  "stats": {
    "total_chunks": 45,
    "total_frames": 18000,
    "unique_classes": ["person", "car", "dog"],
    "class_counts": { "person": 120, "car": 30, "dog": 5 }
  }
}
```

### 5.8 Visual Chunk Search by Combination

```
POST /api/v1/search/visual/combination
Content-Type: application/json

{
  "uuid": "32-char-hex-string",
  "combination": [["person", 1], ["car", 1]]
}
```

Response `200`:
```json
{
  "chunks": [
    {
      "chunk_id": "vis_001",
      "start_time": 120.0,
      "end_time": 135.0,
      "start_frame": 3000,
      "end_frame": 3375,
      "fps": 25.0,
      "combination": [["person", 2], ["car", 1]],
      "total_objects": 3
    }
  ],
  "total": 1
}
```

---

## 6. Agents

### 6.1 Translate Text

```
POST /api/v1/agents/translate
Content-Type: application/json

{
  "text": "Hello world",
  "target_language": "zh-TW",
  "source_language": null
}
```

Response `200`:
```json
{
  "success": true,
  "translated_text": "你好世界",
  "source_language_detected": "en",
  "model_used": "gemma4"
}
```

### 6.2 5W1H Analyze

Generates 5W1H+ summary for scenes in a video using LLM.

```
POST /api/v1/agents/5w1h/analyze
Content-Type: application/json

{
  "file_uuid": "32-char-hex-string",
  "scene_group_size": 7,
  "model": "gemma-4-31B-it-Q5_K_M.gguf"
}
```

Response `200`:
```json
{
  "success": true,
  "file_uuid": "32-char-hex-string",
  "summaries": [
    {
      "scene_number": 1,
      "start_time": 59.0,
      "end_time": 302.0,
      "summary": "Cary Grant and Audrey Hepburn engage in a tense conversation...",
      "who": "Cary Grant, Audrey Hepburn",
      "what": "Conversation about a mysterious situation",
      "where": "Paris apartment",
      "when": "1963",
      "why": "To uncover the truth about the stolen money",
      "how": "Through dialogue and interrogation"
    }
  ],
  "processing_status": { "status": "completed", "progress": 100.0 }
}
```

### 6.3 5W1H Batch

```
POST /api/v1/agents/5w1h/batch
Content-Type: application/json

{
  "file_uuids": ["uuid1", "uuid2"],
  "scene_group_size": 7
}
```

### 6.4 5W1H Status

```
GET /api/v1/agents/5w1h/status
```

Response `200`:
```json
{
  "success": true,
  "videos": []
}
```

### 6.5 Identity Analyze

```
POST /api/v1/agents/identity/analyze
Content-Type: application/json

{
  "file_uuid": "32-char-hex-string",
  "use_llm": true,
  "model": "gemma-4-31B-it-Q5_K_M.gguf"
}
```

### 6.6 Suggest Merges

```
POST /api/v1/agents/identity/suggest
Content-Type: application/json

{
  "file_uuid": "32-char-hex-string"
}
```

### 6.7 Identity Agent Status

```
GET /api/v1/agents/identity/status
```

### 6.8 Suggest Clustering

```
POST /api/v1/agents/suggest/clustering
Content-Type: application/json

{
  "file_uuid": "32-char-hex-string",
  "min_cluster_size": 3,
  "similarity_threshold": 0.7
}
```

### 6.9 Suggest Merge

```
POST /api/v1/agents/suggest/merge
Content-Type: application/json

{
  "identity_id": "identity-uuid",
  "similarity_threshold": 0.75
}
```

---

## 7. System & Configuration

### 7.1 Health

```
GET /health
```

Response `200`:
```json
{
  "status": "ok",
  "version": "1.0.0",
  "uptime_ms": 1397142
}
```

### 7.2 Detailed Health

```
GET /health/detailed
```

Response `200`:
```json
{
  "status": "ok",
  "version": "1.0.0",
  "uptime_ms": 1397142,
  "services": {
    "postgres": { "status": "ok" },
    "redis": { "status": "ok" },
    "qdrant": { "status": "ok" },
    "mongodb": { "status": "ok" }
  }
}
```

### 7.3 Ingest Stats

```
GET /api/v1/stats/ingest
```

Response `200`:
```json
{
  "total_videos": 25,
  "total_chunks": 10546,
  "sentence_chunks": 7547,
  "cut_chunks": 0,
  "time_chunks": 0,
  "searchable_chunks": 4,
  "chunks_with_visual": 0,
  "chunks_with_summary": 0,
  "pending_videos": 1
}
```

### 7.4 SFTPGo Status

```
GET /api/v1/stats/sftpgo
```

### 7.5 Inference Health

```
GET /api/v1/stats/inference
```

Response `200`:
```json
{
  "embedding": {
    "engine": "EmbeddingGemma 300M (Python MPS)",
    "model": "embeddinggemma-300m",
    "status": "ok",
    "latency_ms": 10,
    "error": null
  },
  "llm": {
    "engine": "llama-server (Gemma4 26B MoE)",
    "model": "google_gemma-4-26B-A4B-it-Q5_K_M.gguf",
    "status": "ok",
    "latency_ms": 0,
    "error": null
  }
}
```

### 7.6 Cache Toggle

```
POST /api/v1/config/cache
X-API-Key: <key>
Content-Type: application/json

{
  "enabled": true
}
```

---

## 8. Resource Management (Unprotected)

### 8.1 List Resources

```
GET /api/v1/resources
```

### 8.2 Register Resource

```
POST /api/v1/resource/register
Content-Type: application/json

{
  "resource_id": "worker-01",
  "resource_type": "processor",
  "category": "ml",
  "capabilities": ["face", "asr"],
  "config": {},
  "metadata": {}
}
```

### 8.3 Resource Heartbeat

```
POST /api/v1/resource/heartbeat
Content-Type: application/json

{
  "resource_id": "worker-01",
  "status": "running"
}
```

---

## 9. Auth Endpoints

### 9.1 Login

```
POST /api/v1/auth/login
Content-Type: application/json

{
  "username": "admin",
  "password": "password"
}
```

Response `200` (success):
```json
{
  "success": true,
  "message": "Login successful",
  "api_key": "muser_xxx_xxx",
  "user": { "id": 1, "name": "admin" }
}
```

Response `200` (failure):
```json
{
  "success": false,
  "message": "Invalid username or password",
  "api_key": null,
  "user": null
}
```

### 9.2 Logout

```
POST /api/v1/auth/logout
```

Response `200`:
```json
{ "success": true }
```

---

## Common Error Responses

### 401 Unauthorized
```json
No body (empty response)
```

### 404 Not Found
```json
No body (empty response)
```

### 500 Internal Server Error
```json
No body (empty response)
```

Or error text for agent endpoints:
```json
{ "error": "error description" }
```

---

## Processor Reference

| Processor | Script | Description | Dependencies | Default |
|-----------|--------|-------------|-------------|---------|
| `asr` | `asr_processor.py` | Speech-to-text (faster-whisper) | None | Yes |
| `asrx` | `asrx_processor.py` | Speaker diarization | asr | Yes |
| `cut` | `cut_processor.py` | Scene detection (PySceneDetect) | None | Yes |
| `yolo` | `yolo_processor.py` | Object detection (YOLO) | None | Yes |
| `ocr` | `ocr_processor.py` | Text recognition | None | Yes |
| `face` | `face_processor.py` | Face detection + recognition (Vision + FaceNet) | None | Yes |
| `pose` | `pose_processor.py` | Pose estimation | None | Yes |
| `visual_chunk` | — | Visual object-based chunking | yolo | No |
| `story` | — | Narrative generation | asr + asrx + cut + yolo + face | No |

---

## Post-Processing Pipeline

After all specified processors complete, the system triggers:

| Step | Trigger | Description |
|------|---------|-------------|
| **Rule 1 Chunking** | ASR + ASRX completed | Converts ASR segments into `sentence` chunks in `dev.chunks` |
| **Face Trace** | Face completed | Runs `store_traced_faces.py` to assign trace_ids, stores in `dev.face_detections` |
| **Qdrant Face Sync** | After Face Trace | Syncs face embeddings to Qdrant `_face` collection |
| **Rule 3 Scene Chunking** | All processors completed | Groups sentence chunks by scene boundaries, generates LLM 5W1H summaries |
| **5W1H Agent** | After Rule 3 | Generates 5W1H+ analysis for each scene |

---

## Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `DATABASE_URL` | `postgres://accusys@localhost:5432/momentry` | PostgreSQL connection |
| `DATABASE_SCHEMA` | `public` | Database schema name |
| `MOMENTRY_SERVER_PORT` | `3002` (prod) / `3003` (dev) | API server port |
| `MOMENTRY_REDIS_PREFIX` | `momentry:` / `momentry_dev:` | Redis key prefix |
| `MOMENTRY_API_KEY` | — | API key for authentication |
| `MOMENTRY_OUTPUT_DIR` | `~/momentry/output` | Output JSON directory |
| `MOMENTRY_SCRIPTS_DIR` | `./scripts` | Python scripts directory |
| `MOMENTRY_PYTHON_PATH` | `python3` | Python interpreter path |
| `MOMENTRY_LLM_SUMMARY_URL` | `http://127.0.0.1:8081/v1/chat/completions` | LLM endpoint for 5W1H |
| `MOMENTRY_LLM_SUMMARY_MODEL` | `gemma4` | LLM model name for summaries |
| `MOMENTRY_LLM_SUMMARY_ENABLED` | `true` | Enable/disable LLM summaries |
| `REDIS_URL` | `redis://:accusys@localhost:6379` | Redis connection |

---

## Status Codes

| Code | Description |
|------|-------------|
| 200 | Success |
| 204 | No Content (DELETE success) |
| 206 | Partial Content (video range requests) |
| 400 | Bad Request |
| 401 | Unauthorized (missing/invalid API key) |
| 404 | Not Found |
| 500 | Internal Server Error |
