{"openapi":"3.1.0","info":{"title":"Welcome to Imentiv AI","description":"\n\nImentiv AI is an advanced Emotion AI platform that leverages deep learning to analyze human emotions across multiple media formats, including video, audio, images, and text. By interpreting facial expressions, vocal tones, and linguistic cues, it provides detailed emotional insights to support use cases such as content creation, marketing optimization, education, mental health assessments, and more.\n\nImentiv AI offers a suite of Emotion Recognition APIs that allow developers and businesses to seamlessly integrate advanced emotion analysis into their applications, enabling deeper insights into user engagement and emotional responses.\n\n## Required Headers\nAll requests to the API must include your API key:\n\n```python\nheaders = {\n    \"X-API-Key\": get_secret(\"imentiv-api-key\"),\n}\n```\n\n## Key Features of Imentiv AI APIs\n\n### 1. Video Emotion API\n**Functionality:** Analyzes emotions in videos, whether uploaded directly or via YouTube links.\n\n**Insights Provided:**\n- Frame-by-frame and actor-by-actor emotion analysis\n- Valence-arousal mapping to determine emotional intensity and positivity/negativity\n- Personality trait analysis using the Big Five (OCEAN) model\n- Emotion graphs and wheels to visualize emotional dynamics\n\n**Use Cases:** Suitable for researchers analyzing bulk videos, advertising campaigns, webinars, online meetings, and other video-based content to optimize engagement and content strategy.\n\n### 2. Image Emotion API\n**Functionality:** Detects and analyzes emotions in images.\n\n**Insights Provided:**\n- Face detection and recognition\n- Emotion analysis for each detected face\n- Overall emotional tone of the image\n\n**Use Cases:** Enhances image analysis for advertising, market research, psychological studies, and social media content.\n\n### 3. Audio Emotion API\n**Functionality:** Analyzes emotions in audio recordings, either via direct uploads or by extracting audio from YouTube videos.\n\n**Insights Provided:**\n- Differentiation between multiple speakers in a conversation\n- Emotion detection from vocal cues (e.g., happiness, sadness, anger, surprise)\n- Transcription and detection of up to 28 nuanced emotions (e.g., admiration, curiosity, disappointment, pride)\n- Audio summaries and detailed analytics\n\n**Use Cases:** Provides emotional insights for podcasts, voice messages, call analysis, and other audio-based content.\n\n### 4. Text Emotion API\n**Functionality:** Analyzes emotions in textual content.\n\n**Insights Provided:**\n- Detection of 28 distinct emotions, including curiosity, disappointment, and excitement\n- Overall sentiment analysis and identification of dominant emotions at sentence level\n\n**Use Cases:** Integrates into chatbots, social media platforms, and other text-based applications for sentiment and emotion analysis.\n\n---\n\n## Authorization\n\nLearn how to obtain your API keys to access Imentiv AI APIs.\n\n### Generate API Key:\n\n1. **Sign up** at [imentiv.ai](https://imentiv.ai) and log in to your account.\n2. Navigate to the **'My Profile'** tab to obtain your API key.\n3. Use this API key to authorize your API calls.\n\n> **Note:** Keep your API key secure and do not share it publicly. Include it in your API requests using the `Authorization` header or as specified in the endpoint documentation.\n        ","version":"1.0.0"},"paths":{"/v2/videos":{"post":{"tags":["Video Emotion API"],"summary":"Upload Video","description":"Analyze emotions and facial expressions from video content.\n\nSubmit a video from multiple sources and receive detailed emotion analysis\nof all detected faces throughout the video timeline.\n\n**Input Sources:**\n- **YouTube**: Provide any YouTube video URL\n- **Cloud Storage**: Dropbox, Google Drive, or other cloud service links\n- **Social Media**: Instagram, Facebook, Twitter (X) videos\n- **Direct URL**: Any publicly accessible video link\n- **File Upload**: Upload video files directly from your device\n\n**Analyze Specific Segments:**\nFocus on specific portions using `start_millis` and `end_millis` parameters.\nThis helps target key moments and optimize credit usage.\n\n**How It Works:**\n1. Submit your video and receive an immediate response with a video ID\n2. Video is queued for processing (typically completes within minutes)\n3. Optionally receive a webhook notification via `callback_url` when complete\n4. Retrieve results using the video ID\n\n**Supported Formats:**\nMP4, AVI, MOV, WebM, MPEG\n\n**Credit Usage:**\nBilled on success; the amount is recorded after the run and is not quoted\non this request. Each video is billed at least 10 credits. A 402 is\nreturned before processing starts when admission is refused.\n\n**Moments:**\nSet `moments` to run Moments on this video as part of the same request. It\nis billed with the analysis, and a single 402 covers both: when your balance\ncannot cover them together, nothing is charged and the video is not queued.\nIf the video's first analysis fails, Moments is cancelled and you are not\nbilled for it; once the video has been reprocessed, a later failure does not\ncancel it. Leave it off to request Moments later.","operationId":"upload_video_v2_videos_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_video_v2_videos_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoEmotionResponseV2"}}}},"422":{"description":"Request validation failed (`{\"detail\": str}` only), or `processing_fps_over_budget`: the video is too long to process at its frame rate. Nothing was charged. The body then also offers the frame rates that fit (`recommended_processing_fps`, `fits_processing_fps`) and each tier's longest video (`duration_s`, `max_duration_by_fps`), so the client can suggest a lower frame rate or a trim.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProcessingFpsOverBudgetError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.write"]}]}},"/v1/videos":{"get":{"tags":["Video Emotion API"],"summary":"List Videos","description":"Retrieves a paginated list of uploaded video files associated with the authenticated user.","operationId":"list_videos_v1_videos_get","parameters":[{"name":"offset_video_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The ID of the reference video from which to paginate forward/backward","title":"Offset Video Id"},"description":"The ID of the reference video from which to paginate forward/backward"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","description":"Number of results to return (default: 10)","default":10,"title":"Page Size"},"description":"Number of results to return (default: 10)"},{"name":"direction","in":"query","required":false,"schema":{"type":"string","description":"Pagination direction: forward (default) or backward","default":"forward","title":"Direction"},"description":"Pagination direction: forward (default) or backward"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v1/videos/{video_id}":{"get":{"tags":["Video Emotion API"],"summary":"Get Video Emotion Insights","description":"Get a single video details by ID","operationId":"get_video_emotion_insights_v1_videos__video_id__get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the video to be fetched","title":"Video Id"},"description":"The unique ID of the video to be fetched"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoResponse"}}}},"401":{"description":"Missing, malformed or rejected credentials — no `X-API-Key` / `Authorization: Bearer <jwt>`, an expired token, or a caller that lacks the role the route requires. Note that this API answers *authorization* failures with 401 as well as 403 (see the 403 entry where a route declares both).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"No such resource, or it is not visible to this caller. Ownership failures are reported as 404 rather than 403 so the API does not confirm the existence of another account's records.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]},"delete":{"tags":["Video Emotion API"],"summary":"Delete Video","description":"Permanently deletes a video and its emotional insights. This action is irreversible.\n\nDeletion completes asynchronously: the video disappears from listings immediately,\nand its data is permanently removed shortly after. The response status is `deleting`.","operationId":"delete_video_v1_videos__video_id__delete","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the video to be deleted.","title":"Video Id"},"description":"The unique ID of the video to be deleted."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoDeleteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["Video Emotion API"],"summary":"Reprocess Video","description":"Reprocess an already uploaded and completed video file.\nThis is useful when insights need to be regenerated or logic has been updated on the backend.\nReprocessing can optionally notify your system via a callback URL.\n\n`language` is optional. Without it the video keeps the language it already has (detected from the audio when\nit has none). With it, the video and its companion audio are processed in that language, and the video\nremembers it for later reprocesses. It is checked before anything is charged, so an unsupported code is refused\nwith 422 `invalid_language` and costs nothing. It is only used by a request that starts a run (a failed video,\nor `reprocess=true`); on a request that does not, it is ignored.\n\nThe video's companion audio is transcribed again in that language, as part of the video's charge: a `failed`\none as before, and a `completed` one too (its language-range edits are discarded). One that is still\n`processing` is not restarted; fix it with `PUT /v1/audios/{audio_id}` once it has finished. One busy with a\nlanguage-range job is refused with 409 `audio_busy`. `GET` on the video returns the stored `multilingual` and\n`expected_languages`, but not the stored language.\n\n`multilingual` and `expected_languages` are the language hints (`expected_languages` is the set of languages the\naudio may contain, up to 5; see the field descriptions). Each is optional and, unlike on the audio endpoint, an\nomitted one keeps what the video has: `multilingual=false` turns the switch off and an empty `expected_languages`\nremoves the set, which is how a video returns to unrestricted detection. They are checked with `language`\nbefore anything is charged (an invalid one is 422 `invalid_language` and costs nothing), and `language` must be\nin the set the video will have: naming only `language` outside the stored set is refused the same way, so send\nthe set too (or an empty one) to change both. Naming any of the three also re-runs a `completed` companion\naudio, and the companion is given the video's resulting hints.","operationId":"reprocess_video_v1_videos__video_id__put","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the video to reprocess","title":"Video Id"},"description":"Unique identifier of the video to reprocess"}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_reprocess_video_v1_videos__video_id__put"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReprocessVideoResponse"}}}},"422":{"description":"Request validation failed (`{\"detail\": str}` only), or `processing_fps_over_budget`: the video is too long to process at its frame rate. Nothing was charged. The body then also offers the frame rates that fit (`recommended_processing_fps`, `fits_processing_fps`) and each tier's longest video (`duration_s`, `max_duration_by_fps`), so the client can suggest a lower frame rate or a trim.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProcessingFpsOverBudgetError"}}}}}}},"/v2/videos/{video_id}/multimodal-analytics":{"get":{"tags":["Video Emotion API"],"summary":"Get Video Multimodal Insights","description":"Get complete multimodal analytics for a video including:\n- Video emotion analysis (face-by-face)\n- Audio emotion analysis (speaker-by-speaker)\n- Text sentiment analysis\n- Detected faces and speakers\n- Overall summaries","operationId":"get_video_multimodal_insights_v2_videos__video_id__multimodal_analytics_get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the video","title":"Video Id"},"description":"The unique ID of the video"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FullVideoAnalyticsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v2/videos/{video_id}/emotions/average":{"get":{"tags":["Video Emotion API"],"summary":"Get Average Emotions","description":"Calculate and retrieve average emotion scores for a video with optional face filtering.\n\nThis endpoint computes the average emotion percentages across all frames in a video.\nWhen a face_id is provided, it calculates the average emotions for only that specific face.\n\n**Behavior:**\n- **Without face_id**: Returns overall average emotion score for entire video (all faces combined)\n- **With face_id**: Calculates average emotion scores for the specified face\n\n**Parameters:**\n- `video_id`: Unique identifier for the video\n- `face_id`: (Optional) Calculate average emotions for only this face ID\n\n**Response:**\nReturns emotion scores as percentages (values sum to 1.0):\n- `angry`: Percentage of anger emotion\n- `disgust`: Percentage of disgust emotion\n- `fear`: Percentage of fear emotion\n- `happy`: Percentage of happiness emotion\n- `sad`: Percentage of sadness emotion\n- `surprise`: Percentage of surprise emotion\n- `neutral`: Percentage of neutral emotion\n\n**Use Cases:**\n- **Overall video analysis**: Understand the general emotional tone of the entire video\n- **Individual tracking**: Track emotional patterns of specific people throughout the video\n- **Comparative analysis**: Compare emotional profiles between different faces in the same video\n\n**Example Usage:**\n- Overall average: `GET /v2/videos/123/emotions/average`\n- Face-specific: `GET /v2/videos/123/emotions/average?face_id=1`\n\n**Note:** Face-specific calculations require the face to appear in at least one frame.","operationId":"get_average_emotions_v2_videos__video_id__emotions_average_get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique video ID of the video for which average emotions are to be calculated","title":"Video Id"},"description":"The unique video ID of the video for which average emotions are to be calculated"},{"name":"face_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Optional face ID to calculate average emotions for a specific face","title":"Face Id"},"description":"Optional face ID to calculate average emotions for a specific face"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmotionScores"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v2/videos/{video_id}/annotated_video":{"get":{"tags":["Video Emotion API"],"summary":"Get Annotated Video","description":"Get annotated video with emotion labels and bounding boxes overlaid on each frame.\n\n**How It Works:**\n- First request initiates video generation and returns a status response\n- Poll this endpoint to check generation progress\n- When ready, returns an authorized URL for downloading the video\n\n**Response Types:**\n\n1. **Status Response** (JSON) - While generating:\n   ```json\n   {\n     \"video_id\": \"abc123\",\n     \"message\": \"Annotated video is currently being generated...\",\n     \"status\": \"pending\"\n   }\n   ```\n\n2. **Download URL Response** (JSON) - When complete:\n   ```json\n   {\n     \"video_id\": \"abc123\",\n     \"download_url\": \"https://...\",\n     \"filename\": \"My_Video_abc123.mp4\",\n     \"expires_in_minutes\": 60,\n     \"expires_at\": \"2026-01-09T12:00:00Z\",\n     \"note\": \"Download URLs are short-lived. Request a new URL if a download fails.\"\n   }\n   ```\n\n**Parameters:**\n- `video_id`: Video identifier\n- `callback_url`: Optional webhook URL to receive notification when generation completes\n\n**Prerequisites:**\nVideo analysis must be complete before requesting annotated video.\n\n**Security:**\n- New videos are stored privately; the returned URL is what authorizes the download\n- Treat `download_url` as short-lived — fetch it promptly and request a new one\n  rather than storing or sharing it\n- Old videos may use public URLs (backward compatibility)\n\n**Example Workflow:**\n\n```python\n# 1. Request annotated video (with optional callback URL)\nGET /v2/videos/abc123/annotated_video?callback_url=https://example.com/webhook\nResponse: {\"status\": \"pending\", \"message\": \"...\"}\n\n# 2. Check status (poll until ready)\nGET /v2/videos/abc123/annotated_video\nResponse: {\"status\": \"in progress\", \"message\": \"...\"}\n\n# 3. Get download URL when ready\nGET /v2/videos/abc123/annotated_video\nResponse: {\"video_id\": \"abc123\", \"download_url\": \"https://...\", ...}\n\n# 4. Download video from the returned URL\nGET <download_url>\n```","operationId":"get_annotated_video_v2_videos__video_id__annotated_video_get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique video ID of the video","title":"Video Id"},"description":"The unique video ID of the video"},{"name":"callback_url","in":"query","required":false,"schema":{"type":"string","description":"Optional callback URL to notify when annotated video generation is complete. Must be an https URL on a publicly reachable host.","default":"","title":"Callback Url"},"description":"Optional callback URL to notify when annotated video generation is complete. Must be an https URL on a publicly reachable host."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/SignedDownloadUrlResponse"},{"$ref":"#/components/schemas/AnnotatedVideoResponse"}],"title":"Response Get Annotated Video V2 Videos  Video Id  Annotated Video Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v1/videos/{video_id}/valence_arousal":{"get":{"tags":["Video Emotion API"],"summary":"Get Valence Arousal","description":"Get emotion intensities of a video based on video_id.\n\n`valence` and `arousal` are the averages over all faces in all frames (or over one face's frames\nwhen `face_id` is given); `intensity` is the length of that averaged (valence, arousal) vector.\n`circumplex_version` names the coordinate table the numbers come from.","operationId":"get_valence_arousal_v1_videos__video_id__valence_arousal_get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique video ID of the video whose emotion intensity is to be fetched","title":"Video Id"},"description":"The unique video ID of the video whose emotion intensity is to be fetched"},{"name":"face_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Restrict the result to this face: the average is taken over the frames the face appears in. Omit it (or pass 0) to average over every face. Responds 404 if the video has no such face or the face appears in no frame.","title":"Face Id"},"description":"Restrict the result to this face: the average is taken over the frames the face appears in. Omit it (or pass 0) to average over every face. Responds 404 if the video has no such face or the face appears in no frame."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValenceArousalIntensity"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v1/videos/{video_id}/frames":{"get":{"tags":["Video Emotion API"],"summary":"Get Multiple Frame","description":"Per-frame face detections.\n\nPer-face emotion scores are on `processed_frames[].faces[].emotions`. Each face also carries\n`valence`, `arousal` and `intensity` computed from those scores, so you do not need one\nvalence/arousal request per face per frame. They equal what\n`GET /videos/{video_id}/frames/{frame_id}/valence_arousal?face_id=` returns for that face.\nThe top-level `circumplex_version` names the coordinate table behind them.\n\nTo reproduce the clip-wide `GET /videos/{video_id}/valence_arousal` result from this list, average\n`valence` and `arousal` over the faces first, then take the length of that averaged vector; averaging the\nper-face `intensity` values gives a different number (the average per-face intensity). This only holds for\nthe full, unfiltered listing (every frame, no `face_ids`): a window (`start_frame`/`length`) or a face\nsubset has a different set of faces to average over, so its mean is not the clip figure.","operationId":"get_multiple_frame_v1_videos__video_id__frames_get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique video ID of the video for which the frames to be fetched","title":"Video Id"},"description":"The unique video ID of the video for which the frames to be fetched"},{"name":"start_frame","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Frame number to start from (inclusive). Defaults to the start of the video.","title":"Start Frame"},"description":"Frame number to start from (inclusive). Defaults to the start of the video."},{"name":"length","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Number of frames to return from start_frame. Omit (or 0) for every stored frame. Page long videos: each frame also carries image URLs.","title":"Length"},"description":"Number of frames to return from start_frame. Omit (or 0) for every stored frame. Page long videos: each frame also carries image URLs."},{"name":"face_ids","in":"query","required":false,"schema":{"type":"string","description":"Optional comma-separated list of face IDs (e.g., '1,2,3')","default":"","title":"Face Ids"},"description":"Optional comma-separated list of face IDs (e.g., '1,2,3')"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MultipleFramesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v1/videos/{video_id}/frames/{frame_id}":{"get":{"tags":["Video Emotion API"],"summary":"Get Single Frame","description":"One stored frame. Per-face emotion scores are on `faces[].emotions`; `faces[].valence`, `arousal` and\n`intensity` are computed from them, as in the frames list.","operationId":"get_single_frame_v1_videos__video_id__frames__frame_id__get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique video ID of the video for which the frame to be fetched.","title":"Video Id"},"description":"The unique video ID of the video for which the frame to be fetched."},{"name":"frame_id","in":"path","required":true,"schema":{"type":"string","description":"The unique frame ID of the frame to be fetched.","title":"Frame Id"},"description":"The unique frame ID of the frame to be fetched."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FramesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v1/videos/{video_id}/frames/{frame_id}/dominant_emotion":{"get":{"tags":["Video Emotion API"],"summary":"Retrieve Domination Emotion For A Specific Frame","description":"This API endpoint is designed to fetch and return the dominant emotion of frames within a video based on its video_id.\nIf face_id is provided, the dominant emotion of a specific face will be the output.","operationId":"retrieve_domination_emotion_for_a_specific_frame_v1_videos__video_id__frames__frame_id__dominant_emotion_get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the video from which the frame’s dominant emotion is to be fetched.","title":"Video Id"},"description":"Unique identifier of the video from which the frame’s dominant emotion is to be fetched."},{"name":"frame_id","in":"path","required":true,"schema":{"type":"string","description":"The unique id of the frame from which the dominant emotion is to be fetched.","title":"Frame Id"},"description":"The unique id of the frame from which the dominant emotion is to be fetched."},{"name":"face_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The id of the face whose dominant emotion is to be retrieved. If not provided, the dominant emotion for the entire frame will be returned.","title":"Face Id"},"description":"The id of the face whose dominant emotion is to be retrieved. If not provided, the dominant emotion for the entire frame will be returned."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FrameEmotionResponseData"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v1/videos/{video_id}/frames/{frame_id}/valence_arousal":{"get":{"tags":["Video Emotion API"],"summary":"Retrieve Emotion Intensity","description":"Retrieve Emotion Intensity for a Specific Frame","operationId":"retrieve_emotion_intensity_v1_videos__video_id__frames__frame_id__valence_arousal_get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique video ID of the video for which the frame's valence, arousal, and intensity are to be fetched.","title":"Video Id"},"description":"The unique video ID of the video for which the frame's valence, arousal, and intensity are to be fetched."},{"name":"frame_id","in":"path","required":true,"schema":{"type":"string","description":"The unique frame ID of the frame from which to fetch the valence, arousal, and intensity.","title":"Frame Id"},"description":"The unique frame ID of the frame from which to fetch the valence, arousal, and intensity."},{"name":"face_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The ID of the face whose valence, arousal, and intensity are to be retrieved. If not provided, these values for the entire frame will be returned.","title":"Face Id"},"description":"The ID of the face whose valence, arousal, and intensity are to be retrieved. If not provided, these values for the entire frame will be returned."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValenceArousalIntensity"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v1/videos/{video_id}/frames/{frame_id}/faces/{face_id}/emotions":{"put":{"tags":["Video Emotion API"],"summary":"Update Emotion Per Frame","description":"Update the emotion of a specific face in a frame","operationId":"update_emotion_per_frame_v1_videos__video_id__frames__frame_id__faces__face_id__emotions_put","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique video ID of the video.","title":"Video Id"},"description":"The unique video ID of the video."},{"name":"frame_id","in":"path","required":true,"schema":{"type":"string","description":"The unique frame ID of the frame.","title":"Frame Id"},"description":"The unique frame ID of the frame."},{"name":"face_id","in":"path","required":true,"schema":{"type":"string","description":"The unique face ID of the face in the frame.","title":"Face Id"},"description":"The unique face ID of the face in the frame."}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_update_emotion_per_frame_v1_videos__video_id__frames__frame_id__faces__face_id__emotions_put"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateEmotionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/videos/{video_id}/frames/{frame_id}/faces/{face_id}/note":{"put":{"tags":["Video Emotion API"],"summary":"Edit Face Note","description":"Update the note associated with a detected face in a frame.","operationId":"edit_face_note_v1_videos__video_id__frames__frame_id__faces__face_id__note_put","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique video id of the video.","title":"Video Id"},"description":"The unique video id of the video."},{"name":"frame_id","in":"path","required":true,"schema":{"type":"string","description":"The unique id of the frame inside the video.","title":"Frame Id"},"description":"The unique id of the frame inside the video."},{"name":"face_id","in":"path","required":true,"schema":{"type":"string","description":"The unique id of the face inside the frame. Use '0' to add a note to the entire frame instead of a specific face.","title":"Face Id"},"description":"The unique id of the face inside the frame. Use '0' to add a note to the entire frame instead of a specific face."}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_edit_face_note_v1_videos__video_id__frames__frame_id__faces__face_id__note_put"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FrameNoteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/videos/{video_id}/frames/faces":{"put":{"tags":["Video Emotion API"],"summary":"Update Multiple Faces","description":"Update Face ID in multiple frames of a video","operationId":"update_multiple_faces_v1_videos__video_id__frames_faces_put","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique video ID of the video in which face ID to be changed","title":"Video Id"},"description":"The unique video ID of the video in which face ID to be changed"}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_update_multiple_faces_v1_videos__video_id__frames_faces_put"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MultiFaceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/videos/{video_id}/faces":{"put":{"tags":["Video Emotion API"],"summary":"Merge Face","description":"Merge multiple face detections into a single face to remove duplicates from a video.","operationId":"merge_face_v1_videos__video_id__faces_put","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique video ID of the video.","title":"Video Id"},"description":"The unique video ID of the video."}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_merge_face_v1_videos__video_id__faces_put"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MergeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Video Emotion API"],"summary":"Delete Faces","description":"Delete specified faces from a video while ensuring at least one face remains.\n\nPeople linked to a deleted face lose that link; a link to a speaker is kept.\nRepeated ids in one request count once.\n\nErrors: `404` when a face id is not on the video (this includes a face that\nwas already deleted, so a retried delete can treat it as done); `409` with\ndetail `last_face` when the request would delete every face. Neither\nchanges anything.","operationId":"delete_faces_v1_videos__video_id__faces_delete","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique identifier of the video from which faces will be deleted.","title":"Video Id"},"description":"The unique identifier of the video from which faces will be deleted."}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_delete_faces_v1_videos__video_id__faces_delete"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteFacesResponse"}}}},"401":{"description":"Missing, malformed or rejected credentials — no `X-API-Key` / `Authorization: Bearer <jwt>`, an expired token, or a caller that lacks the role the route requires. Note that this API answers *authorization* failures with 401 as well as 403 (see the 403 entry where a route declares both).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"No such resource, or it is not visible to this caller. Ownership failures are reported as 404 rather than 403 so the API does not confirm the existence of another account's records.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"409":{"description":"`last_face`: deleting these faces would leave the video with none. Nothing was deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Video Emotion API"],"summary":"Add Face","description":"Create a placeholder face on a video file. The new face has no detection\ndata — pair it with a speaker via Match Faces & Speakers when the AI\nmissed a person. Auto-allocates `face_id` from a monotonic counter\npersisted on the doc (never reuses ids freed by delete). Cached CSV/PDF\nreports are invalidated since the report enumerates faces.\n\nReturns the auto-allocated `{face_id, name}` so the FE can append the\nnew tab without a re-read.","operationId":"add_face_v1_videos__video_id__faces_post","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique video ID.","title":"Video Id"},"description":"The unique video ID."}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_add_face_v1_videos__video_id__faces_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddFaceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/videos/{video_id}/faces/names":{"put":{"tags":["Video Emotion API"],"summary":"Update Faces Names","description":"Updates the names of multiple faces in a video.","operationId":"update_faces_names_v1_videos__video_id__faces_names_put","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique video ID of the video.","title":"Video Id"},"description":"The unique video ID of the video."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FaceNameItem"},"description":"List of face id and name pairs.","title":"Face Updates"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/NameUpdateResponse"},"title":"Response Update Faces Names V1 Videos  Video Id  Faces Names Put"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/videos/highlights":{"post":{"tags":["Video Emotion API"],"summary":"Process Highlight","description":"Trigger automatic highlight video generation using emotion-based analysis.\n\nAnalyzes video frames to identify emotionally significant moments and generates\na highlight reel. The process runs asynchronously.\n\nArgs:\n    video_id: The unique video ID for highlight generation\n    user_info: Authenticated user information\n    api_info: API request metadata including domain\n\nReturns:\n    HighlightGenerateResponse: Contains video_id, processing status, and message\n    \nRaises:\n    InvalidParams: If video_id is missing\n    InvalidUser: If user doesn't own the video\n    CreditExpired: If user has insufficient credits\n    VideoNotFound: If video doesn't exist or is not completed\n\nCredit Usage:\n    10 credits per minute of video duration.\n    Enterprise plans: 5 credits per minute.\n    First use is free (feature credit).","operationId":"process_highlight_v1_videos_highlights_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_process_highlight_v1_videos_highlights_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HighlightGenerateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/videos/{video_id}/highlights":{"get":{"tags":["Video Emotion API"],"summary":"Fetch Highlight Video","description":"API endpoint for highlight generation of a video.","operationId":"fetch_highlight_video_v1_videos__video_id__highlights_get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The video id to get the highlight video url","title":"Video Id"},"description":"The video id to get the highlight video url"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HighlightResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]},"put":{"tags":["Video Emotion API"],"summary":"Update Highlight Video","description":"Generate custom highlight video using user-defined time segments.\n\nCreates a highlight reel by concatenating specific video segments chosen by the user.\nThe process runs asynchronously.\n\nArgs:\n    video_id: The unique video ID for which highlights are being generated\n    segments: List of time segments with start_millis and end_millis timestamps\n    user_info: Authenticated user information\n\nReturns:\n    HighlightGenerateResponse: Contains video_id, status=\"queue\", and processing message\n    \nRaises:\n    InvalidParams: If segments list is empty\n    InvalidUser: If user doesn't own the video\n    UserVideoException: If segment timestamps are invalid:\n        - Both start and end are 0\n        - Start equals end\n        - Start is greater than or equal to end\n        - Timestamps are out of video duration bounds\n    CreditExpired: If user has insufficient credits\n    VideoNotFound: If video doesn't exist or is not completed\n\nCredit Usage:\n    10 credits per minute of video duration.\n    Enterprise plans: 5 credits per minute.","operationId":"update_highlight_video_v1_videos__video_id__highlights_put","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique video id of the video for which the update highlight video to be edited.","title":"Video Id"},"description":"The unique video id of the video for which the update highlight video to be edited."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SegmentResponse"},"description":"List of video highlight segments","title":"Segments"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HighlightGenerateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/videos/{video_id}/highlight_video":{"get":{"tags":["Video Emotion API"],"summary":"Download Highlight Video","description":"Download the highlight video.","operationId":"download_highlight_video_v1_videos__video_id__highlight_video_get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The video id to download the highlight video.","title":"Video Id"},"description":"The video id to download the highlight video."}],"responses":{"200":{"description":"Successful Response","content":{"video/mp4":{}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v1/videos/{video_id}/metadata":{"put":{"tags":["Video Emotion API"],"summary":"Edit Title Or Description By Id","description":"Edit the title or description of a video based on the provided video ID.","operationId":"edit_title_or_description_by_id_v1_videos__video_id__metadata_put","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"Provide the video ID to edit or title and description of the audio","title":"Video Id"},"description":"Provide the video ID to edit or title and description of the audio"}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_edit_title_or_description_by_id_v1_videos__video_id__metadata_put"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoEditResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/videos/{video_id}/report":{"post":{"tags":["Video Emotion API"],"summary":"Generate Video Emotion Analysis Report","description":"Initiates the generation of an analysis report for the specified video.\nThe user will be notified at the provided callback URL once the process is complete.","operationId":"generate_video_emotion_analysis_report_v1_videos__video_id__report_post","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique video ID of the video whose analysis report is to be generated.","title":"Video Id"},"description":"The unique video ID of the video whose analysis report is to be generated."}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_generate_video_emotion_analysis_report_v1_videos__video_id__report_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateCSVResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Video Emotion API"],"summary":"Download Video Emotion Analysis Report","description":"Download a videos processed analysis details based on the provided video ID.","operationId":"download_video_emotion_analysis_report_v1_videos__video_id__report_get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"Provide the video ID to download analysis","title":"Video Id"},"description":"Provide the video ID to download analysis"}],"responses":{"200":{"description":"Successful Response","content":{"application/zip":{}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/audios":{"post":{"tags":["Audio Emotion API"],"summary":"Upload Audio","description":"Analyze emotions and speech patterns from audio content.\n\nSubmit audio from multiple sources and receive detailed emotion analysis,\nspeaker identification, and transcription with emotional insights.\n\n**Input Sources:**\n- **YouTube**: Provide any YouTube video URL (audio will be extracted)\n- **Instagram**: Instagram Reels and video posts (public content)\n- **Twitter/X**: Twitter video posts and media content\n- **Facebook**: Facebook video posts (public content)\n- **Cloud Storage**: Dropbox, Google Drive, or other cloud service links\n- **Direct URL**: Any publicly accessible audio or video link\n- **File Upload**: Upload audio files directly from your device\n\n**Audio Extraction:**\nVideo files are supported for URLs only (YouTube, Instagram, Twitter, Facebook,\nDropbox, direct links) - the audio track will be automatically extracted and analyzed.\nFor file uploads, use audio formats only (MP3, WAV, AAC, M4A).\n\n**Analyze Specific Segments:**\nFocus on specific portions using `start_millis` and `end_millis` parameters.\nThis helps target key moments and optimize credit usage.\n\n**How It Works:**\n1. Submit your audio and receive an immediate response with an audio ID\n2. Audio is queued for processing (typically completes within minutes)\n3. Optionally receive a webhook notification via `callback_url` when complete\n4. Retrieve results using the audio ID\n\n**Supported Formats:**\n- File Upload: MP3, WAV, AAC, M4A\n- Media URLs: MP3, WAV, AAC, M4A, MP4, AVI, MOV (audio extracted from video URLs)\n\n**Credit Usage:**\nBilled on success; the amount is recorded after the run and is not quoted\non this request. Each audio is billed at least 5 credits. A 402 is\nreturned before processing starts when admission is refused.\n\n**Extra Parameters:**\n- **diarization** (bool): Enable or disable speaker diarization for identifying different speakers in the audio. Default is True. Disabling it reduces the per-second credit cost.\n- **text_emotion** (bool): Enable or disable transcript emotion analysis for the transcribed text. Default is True. Requires `transcription=True`; if `transcription` is False this is forced off. Disabling it reduces the per-second credit cost. Supplying a `transcript` is the exception: the words already exist, so `text_emotion` is honoured as you sent it.\n- **transcription** (bool): Enable or disable transcript / ASR generation. Default is True. When False, no transcript is produced and `text_emotion` is forced off. Disabling it reduces the per-second credit cost.\n- **language** (str, optional): Language code for the audio (e.g., 'en'). If not provided, language will be auto-detected.\n- **multilingual** (bool): Opt-in mixed-language transcription. Default is False. Once the audio completes, the lines that are not in its main language are also translated into the main language and billed as a translation, on top of transcription; `auto_translation` on the audio reports it.\n- **expected_languages** (str, optional): Comma-separated codes of the languages the audio may contain, the main one included (e.g., 'en,ja'), up to 5. Detection is restricted to them. If `language` is also sent it must be one of them. An invalid code is refused with 422 `invalid_language` before anything is charged.\n\n**Bringing your own transcript:**\nPass `transcript` (and optionally `transcript_format`) to skip our speech-to-text\nentirely. Your speaker turns become the diarization and your speaker names are used\nverbatim, so the analysis comes back attributed to real participants rather than\n\"Speaker 1\" / \"Speaker 2\" — and you are not billed for the transcription stage.\nA `start_millis`/`end_millis` trim is rejected: we would not have media your\ntimestamps line up with. A `media_url` source is accepted — the download runs\nfirst and your transcript is applied once processing completes; that\nresponse's `status` is `queue`. After that apply we deliver `callback_url`\n(success or failure). A file or presigned upload applies in the same\nrequest and reports `processing`. The response reports `speaker_count` so you\ncan see how many speakers we read out of what you sent.\n\n**Recording type:**\nPass `audio_type=contact_center_call` to tag a support/sales call at upload.\n`POST /v1/audios` does not accept `audio_type` — tag afterwards with\n`PUT /v1/audios/{id}/audio-type`. A call uploaded as video starts generic;\nPUT the companion audio id after processing completes (`POST /v2/videos`\ndoes not take `audio_type` either).","operationId":"upload_audio_v2_audios_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_audio_v2_audios_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudioEmotionResponseV2"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.write"]}]}},"/v1/audios":{"get":{"tags":["Audio Emotion API"],"summary":"List Audios","description":"API to get audios processed by user with pagination.","operationId":"list_audios_v1_audios_get","parameters":[{"name":"offset_audio_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The ID of the reference audio from which to paginate forward/backward","title":"Offset Audio Id"},"description":"The ID of the reference audio from which to paginate forward/backward"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","description":"Number of results to return (default: 20, max: 100)","default":10,"title":"Page Size"},"description":"Number of results to return (default: 20, max: 100)"},{"name":"direction","in":"query","required":false,"schema":{"type":"string","description":"Pagination direction: forward (default) or backward","default":"forward","title":"Direction"},"description":"Pagination direction: forward (default) or backward"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v1/audios/{audio_id}":{"get":{"tags":["Audio Emotion API"],"summary":"Get Audio Emotion Insights","description":"Retrieve Single Audio based on audio_id.","operationId":"get_audio_emotion_insights_v1_audios__audio_id__get","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the uploaded audio","title":"Audio Id"},"description":"The unique ID of the uploaded audio"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Audio"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]},"put":{"tags":["Audio Emotion API"],"summary":"Reprocess Audio","description":"Reprocess an audio file based on audio id.\n\nThe three speech-analysis flags are optional and default to \"leave whatever\nis stored alone\", so existing callers are unaffected. Supplying them is how\nmedia uploaded with speech analysis skipped gets analyzed later (#840).\n\n`language` is optional too. Without it the language is detected from the audio, as before; with it\nthe audio is processed in that language. It is checked before anything is charged, so an unsupported\ncode is refused with 422 `invalid_language` and costs nothing. It is only used by a request that\nstarts a run (a failed audio, or `reprocess=true`); on a request that does not, it is ignored.\nFor a standalone audio it applies to this run only: a later reprocess that omits it auto-detects again.\nFor a video's companion audio the choice is also recorded on the video, so a later reprocess of the\nvideo re-sends it; a later audio reprocess that omits `language` re-detects the audio but leaves the\nvideo's recorded language alone.\n\n`multilingual` and `expected_languages` are the language hints (`expected_languages` is the set of languages\nthe audio may contain, up to 5; see the field descriptions). They are checked with `language`, before anything\nis charged: an invalid one is refused with 422 `invalid_language` and costs nothing. Unlike `language` they are\nnot sticky: a request that starts a run and omits them runs without a hint, and clears the audio's stored ones.\nFor a video's companion audio, a request that names any of `language`, `multilingual` or `expected_languages`\nalso replaces the video's hints with the audio's resulting ones, so naming only `language` clears the video's\nhints; a request that names none leaves the video's alone.","operationId":"reprocess_audio_v1_audios__audio_id__put","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the audio to reprocess","title":"Audio Id"},"description":"Unique identifier of the audio to reprocess"}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_reprocess_audio_v1_audios__audio_id__put"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudioReprocessResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Audio Emotion API"],"summary":"Delete An Audio","description":"Deletes a specific audio file and all associated emotion analysis data. This action is irreversible.\n\nDeletion completes asynchronously: the audio disappears from listings immediately,\nand its data is permanently removed shortly after. The response status is `deleting`.","operationId":"delete_an_audio_v1_audios__audio_id__delete","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the audio to be deleted","title":"Audio Id"},"description":"Unique identifier of the audio to be deleted"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudioDeleteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/audios/{audio_id}/multimodal-analytics":{"get":{"tags":["Audio Emotion API"],"summary":"Get Audio Multimodal Insights","description":"Get complete multimodal analytics for an audio file including:\n- Audio emotion analysis (speaker-by-speaker)\n- Text sentiment analysis from transcription\n- Detected speakers\n- Overall summaries","operationId":"get_audio_multimodal_insights_v2_audios__audio_id__multimodal_analytics_get","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the audio","title":"Audio Id"},"description":"The unique ID of the audio"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FullAudioAnalyticsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v1/audios/{audio_id}/{segment_index}":{"delete":{"tags":["Audio Emotion API"],"summary":"Delete Audio Segment","description":"Deletes a specific segment of an audio file based on the segment index. After deletion,\nthe remaining segments are reindexed to fill the gap — e.g., if segment 1 is deleted, segment 2 becomes 1","operationId":"delete_audio_segment_v1_audios__audio_id___segment_index__delete","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the audio file","title":"Audio Id"},"description":"Unique identifier of the audio file"},{"name":"segment_index","in":"path","required":true,"schema":{"type":"integer","description":"The index of the segment to delete (0-based indexing)","title":"Segment Index"},"description":"The index of the segment to delete (0-based indexing)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudioSegmentDeleteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/audios/{audio_id}/transcript":{"put":{"tags":["Audio Emotion API"],"summary":"Update Audio Transcripts Segments","description":"Update the transcript and timestamp data for specific segments within an audio file.\nThis is useful when refining speaker diarization, correcting transcription errors,\nor uploading external transcript data.","operationId":"update_audio_transcripts_segments_v1_audios__audio_id__transcript_put","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the audio file","title":"Audio Id"},"description":"Unique identifier of the audio file"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TranscriptSegment"},"description":"List of transcript segments to update.","title":"Transcript Segments"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TranscriptSegment"},"title":"Response Update Audio Transcripts Segments V1 Audios  Audio Id  Transcript Put"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/audios/{audio_id}/speakers/names":{"put":{"tags":["Audio Emotion API"],"summary":"Update Speaker Names","description":"Update the names of detected speakers in an audio file. This is useful for labeling anonymized\nspeaker IDs with human-readable names (e.g., \"John\", \"Customer\", \"Agent\").","operationId":"update_speaker_names_v1_audios__audio_id__speakers_names_put","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the audio file","title":"Audio Id"},"description":"Unique identifier of the audio file"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SpeakerUpdate"},"description":"List of speaker ID and name pairs to update","title":"Speaker Updates"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpeakerUpdateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/audios/{audio_id}/metadata":{"put":{"tags":["Audio Emotion API"],"summary":"Edit Title Or Description","description":"Update the metadata of an audio file.\nThis endpoint allows you to edit the title (which is auto-generated from the file name by default)\nand add or update a description.","operationId":"edit_title_or_description_v1_audios__audio_id__metadata_put","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"Provide the audio ID to edit the title or description of the audio","title":"Audio Id"},"description":"Provide the audio ID to edit the title or description of the audio"}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_edit_title_or_description_v1_audios__audio_id__metadata_put"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudioEditResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/audios/{audio_id}/report":{"get":{"tags":["Audio Emotion API"],"summary":"Download Audio Emotion Report","description":"Download a detailed report of the emotional insights extracted from a specific audio file.\nThe report includes average emotions, segment-level analysis, speaker information,\nand summary—formatted as a downloadable file (e.g., PDF or CSV).","operationId":"download_audio_emotion_report_v1_audios__audio_id__report_get","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the audio file","title":"Audio Id"},"description":"Unique identifier of the audio file"}],"responses":{"200":{"description":"Successful Response","content":{"application/zip":{}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/audios/{audio_id}/speakers":{"delete":{"tags":["Audio Emotion API"],"summary":"Delete Speakers","description":"Delete one or more speakers from an audio file while ensuring at least one\nspeaker remains. Drops affected segments from segment_emotions, recomputes\nthe audio's average emotions, nulls speaker references on transcript\nsentences (text is preserved), and clears speaker references on linked\nvideo faces. Cached CSV/PDF reports are invalidated.","operationId":"delete_speakers_v1_audios__audio_id__speakers_delete","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"The unique audio ID.","title":"Audio Id"},"description":"The unique audio ID."}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_delete_speakers_v1_audios__audio_id__speakers_delete"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteSpeakersResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["Audio Emotion API"],"summary":"Merge Speakers","description":"Merge multiple speakers into a single selected speaker on an audio document.\n\nThe merged speakers' segments are relabeled to selected_speaker_id and the\nmerged-away speaker entries are removed from the audio. If the audio is\nlinked to a video, faces matched to a merged-away speaker are unlinked\n(speaker_id cleared) and any face matched to the surviving speaker has its\npersonality_traits reset to pending — speaker merge is intentionally NOT\na face-merge cascade. See module.audios.controller.merge_speakers_in_audio\nfor full semantics, including the symmetric face-merge fix tracked in #439.","operationId":"merge_speakers_v1_audios__audio_id__speakers_put","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"The unique audio ID.","title":"Audio Id"},"description":"The unique audio ID."}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_merge_speakers_v1_audios__audio_id__speakers_put"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MergeSpeakersResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Audio Emotion API"],"summary":"Add Speaker","description":"Create a placeholder speaker on an audio file. The new speaker has zero\nsegments — sentence ownership gets assigned via the existing transcript\neditor. Auto-allocates `speaker_id` from a monotonic counter persisted on\nthe doc (never reuses ids freed by delete). Cached CSV/PDF reports are\ninvalidated since the report enumerates speakers.\n\nReturns the auto-allocated `{speaker_id, name}` so the FE can append the\nnew tab without a re-read.","operationId":"add_speaker_v1_audios__audio_id__speakers_post","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"The unique audio ID.","title":"Audio Id"},"description":"The unique audio ID."}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_add_speaker_v1_audios__audio_id__speakers_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddSpeakerResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/audios/{audio_id}/speakers/roles":{"put":{"tags":["Audio Emotion API"],"summary":"Update Speaker Roles","description":"Set declared speaker roles (agent, customer, system, or media) by speaker_id.\n\n``system`` and ``media`` mark known non-human audio, such as a voice\nassistant or music. They are not a party. Send ``null`` to clear.\n\nMerge-by-id: a role write does not clobber ``name``, and a rename does not\nclobber ``role``. Unknown ids 422 the whole batch. Audio must be\n``completed``. Owner or ``media:write``.\n\nA re-diarising reprocess, a trim, or a transcript replace drops these\nroles (#1361 writes a role-less speakers list before dispatch). Re-apply\nthem afterwards.\n\nThe names PUT response is still ``{speaker_id, name}`` only — if D1\nrefreshes local state from it, role chips vanish until reload. This\nresponse returns the full speaker list including ``role``.","operationId":"update_speaker_roles_v1_audios__audio_id__speakers_roles_put","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the audio file","title":"Audio Id"},"description":"Unique identifier of the audio file"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SpeakerRoleUpdate"},"description":"List of speaker ID and role pairs. role is required; send null to clear.","title":"Speaker Updates"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpeakerRoleUpdateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/audios/{audio_id}/audio-type":{"put":{"tags":["Audio Emotion API"],"summary":"Update Audio Type","description":"Set the declared recording type (not a file format).\n\n``generic`` is a generic recording (no stance pass). ``contact_center_call``\nmarks a support/sales call so speaker roles and stance analysis can apply.\nOwner or a caller holding ``media:write``. Audio must be ``completed``.\n\nA call uploaded as video starts generic on the companion audio; PUT this\naudio id after processing completes — ``POST /v2/videos`` does not take\n``audio_type``. ``POST /v1/audios`` also has no ``audio_type`` field; v1\nuploads must use this PUT to tag. ``llm_audio_policy`` is not\nclient-writable; it is restamped from the workspace setting. Reverting to\n``generic`` clears stored ``stance``.","operationId":"update_audio_type_v1_audios__audio_id__audio_type_put","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the audio file","title":"Audio Id"},"description":"Unique identifier of the audio file"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudioTypeUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudioTypeUpdateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/texts":{"post":{"tags":["Text Emotion API"],"summary":"Upload Text","description":"Analyze emotions and sentiment from text content.\n\nSubmit text directly, provide a cloud storage URL, or upload a text file (.txt, .srt, .pdf).\n\n**Input Modes (provide exactly one):**\n- **text**: Raw text content for direct analysis\n- **text_url**: URL to a text file from Dropbox, Google Drive, AWS S3, or any public URL\n- **text_file**: Direct file upload (.txt, .srt, .pdf)\n\n**How It Works:**\n1. Submit your text (or file URL / file upload) and receive an immediate response with a text ID\n2. For file URLs/uploads: the file is downloaded/read, text extracted, and word count calculated\n3. Text is queued for processing (typically completes within seconds)\n4. Optionally receive a webhook notification via `callback_url` when complete\n5. Retrieve results using the text ID\n\n**Supported File Formats (for text_url and text_file):**\n- `.txt` — Plain text files\n- `.srt` — Subtitle files (timestamps are stripped, only text is extracted)\n- `.pdf` — PDF documents (text extracted from all pages)\n\n**Credit Usage:**\nBilled on success; the amount is recorded after the run and is not quoted\non this request. Each text analysis is billed at least 2 credits. A 402 is\nreturned before processing starts when admission is refused.","operationId":"upload_text_v2_texts_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_text_v2_texts_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TextEmotionResponseV2"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.write"]}]}},"/v1/texts":{"get":{"tags":["Text Emotion API"],"summary":"List Texts","description":"Retrieves a paginated list of all uploaded or processed images.\nUse this endpoint to browse through text records based on their ID, status, and title.","operationId":"list_texts_v1_texts_get","parameters":[{"name":"start_after","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The ID of the reference text from which to paginate forward/backward","title":"Start After"},"description":"The ID of the reference text from which to paginate forward/backward"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","description":"Number of results to return (default: 10)","default":10,"title":"Page Size"},"description":"Number of results to return (default: 10)"},{"name":"direction","in":"query","required":false,"schema":{"type":"string","description":"Pagination direction: forward (default) or backward","default":"forward","title":"Direction"},"description":"Pagination direction: forward (default) or backward"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v1/texts/{text_id}":{"get":{"tags":["Text Emotion API"],"summary":"Get Text Emotion Insights","description":"Retrieves detailed emotion analysis results for a specific uploaded text.\nIncludes paragraph wise analysis, and supports 28 emotions.","operationId":"get_text_emotion_insights_v1_texts__text_id__get","parameters":[{"name":"text_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the text.","title":"Text Id"},"description":"The unique ID of the text."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FetchTextResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]},"delete":{"tags":["Text Emotion API"],"summary":"Delete Text","description":"Deletes a specific text and all associated emotion analysis data from the system.\nThis action is irreversible.","operationId":"delete_text_v1_texts__text_id__delete","parameters":[{"name":"text_id","in":"path","required":true,"schema":{"type":"string","description":"The unique text ID of the text.","title":"Text Id"},"description":"The unique text ID of the text."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteTextResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["Text Emotion API"],"summary":"Reprocess Text","description":"Reprocesses a text that has either failed or completed.\nYou can trigger the reprocessing by setting reprocess to True.\nThis will reanalyze the image and provide updated insights","operationId":"reprocess_text_v1_texts__text_id__put","parameters":[{"name":"text_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the text.","title":"Text Id"},"description":"The unique ID of the text."}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_reprocess_text_v1_texts__text_id__put"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TextReprocessResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/images":{"post":{"tags":["Image Emotion API"],"summary":"Upload Image","description":"Analyze emotions and facial expressions from image content.\n\nSubmit an image from multiple sources and receive detailed emotion analysis\nof all detected faces in the image.\n\n**Input Sources:**\n- **Cloud Storage**: Dropbox or other cloud service links\n- **Direct URL**: Any publicly accessible image link\n- **File Upload**: Upload image files directly from your device\n\n**How It Works:**\n1. Submit your image and receive an immediate response with an image ID\n2. Image is queued for processing (typically completes within seconds)\n3. Optionally receive a webhook notification via `callback_url` when complete\n4. Retrieve results using the image ID\n\n**Supported Formats:**\nJPG, JPEG, PNG, WEBP\n\n**Credit Usage:**\n- 10 credits per image\n- Enterprise plans: 5 credits per image","operationId":"upload_image_v2_images_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_image_v2_images_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageEmotionResponseV2"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.write"]}]}},"/v1/images":{"get":{"tags":["Image Emotion API"],"summary":"List Images","description":"Retrieves a paginated list of all uploaded or processed images.\nUse this endpoint to browse through image records based on their ID, status, and title.","operationId":"list_images_v1_images_get","parameters":[{"name":"start_after","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ID of the last image from the previous page. Fetches results after this ID.","title":"Start After"},"description":"ID of the last image from the previous page. Fetches results after this ID."},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","description":"Number of results to return per page. The default is 10.","default":10,"title":"Page Size"},"description":"Number of results to return per page. The default is 10."},{"name":"direction","in":"query","required":false,"schema":{"type":"string","description":"Pagination direction: 'forward' or 'backward'. Default is 'forward'","default":"forward","title":"Direction"},"description":"Pagination direction: 'forward' or 'backward'. Default is 'forward'"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v1/images/{image_id}":{"get":{"tags":["Image Emotion API"],"summary":"Get Image Emotion Analysis","description":"Retrieves detailed emotion analysis results and metadata for a specific uploaded image.\nIncludes detected faces, their emotional scores, image dimensions, and annotated visualizations.","operationId":"get_image_emotion_analysis_v1_images__image_id__get","parameters":[{"name":"image_id","in":"path","required":true,"schema":{"type":"string","description":"Unique ID of the image to retrieve.","title":"Image Id"},"description":"Unique ID of the image to retrieve."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageDBResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]},"delete":{"tags":["Image Emotion API"],"summary":"Delete Image","description":"Deletes a specific image and all associated emotion analysis data from the system. This action is irreversible.","operationId":"delete_image_v1_images__image_id__delete","parameters":[{"name":"image_id","in":"path","required":true,"schema":{"type":"string","description":"Unique ID of the image","title":"Image Id"},"description":"Unique ID of the image"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageDeleteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["Image Emotion API"],"summary":"Reprocess Image","description":"Reprocesses an image that has either failed or completed.\nYou can trigger the reprocessing by setting reprocess to True.\nThis will reanalyze the image and provide updated insights.","operationId":"reprocess_image_v1_images__image_id__put","parameters":[{"name":"image_id","in":"path","required":true,"schema":{"type":"string","description":"The unique image ID of the image.","title":"Image Id"},"description":"The unique image ID of the image."}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_reprocess_image_v1_images__image_id__put"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageReprocessResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/images/{image_id}/annotated_image":{"get":{"tags":["Image Emotion API"],"summary":"Send Image","description":"Downloads the annotated version of the image, which highlights detected faces and their corresponding emotions.\n\nAuthentication is required. A share link already receives the annotated image URL on the image detail.","operationId":"send_image_v1_images__image_id__annotated_image_get","parameters":[{"name":"image_id","in":"path","required":true,"schema":{"type":"string","description":"Unique ID of the image.","title":"Image Id"},"description":"Unique ID of the image."}],"responses":{"200":{"description":"Successful Response","content":{"image/jpeg":{}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v1/images/{image_id}/faces/{face_id}/note":{"put":{"tags":["Image Emotion API"],"summary":"Add Note To Specific Face","description":"Adds or updates a note attached to a specific face detected in an image.","operationId":"add_note_to_specific_face_v1_images__image_id__faces__face_id__note_put","parameters":[{"name":"image_id","in":"path","required":true,"schema":{"type":"string","description":"The unique image ID of the image","title":"Image Id"},"description":"The unique image ID of the image"},{"name":"face_id","in":"path","required":true,"schema":{"type":"string","description":"Unique ID of the face within the image","title":"Face Id"},"description":"Unique ID of the face within the image"}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_add_note_to_specific_face_v1_images__image_id__faces__face_id__note_put"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageNoteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/images/{image_id}/metadata":{"put":{"tags":["Image Emotion API"],"summary":"Edit Image Metadata","description":"Edit image title and description of the uploaded image.\n\nA ``callback_url``, if supplied, must be an https URL on a publicly reachable host.","operationId":"edit_image_metadata_v1_images__image_id__metadata_put","parameters":[{"name":"image_id","in":"path","required":true,"schema":{"type":"string","description":"Provide the image ID to edit the title or description of the image","title":"Image Id"},"description":"Provide the image ID to edit the title or description of the image"}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_edit_image_metadata_v1_images__image_id__metadata_put"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageEditResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/images/{image_id}/report":{"get":{"tags":["Image Emotion API"],"summary":"Download Image Emotion Report","description":"Downloads the emotion analysis insights for a specific image in CSV format.\nThe report includes detected faces and their associated emotion scores.","operationId":"download_image_emotion_report_v1_images__image_id__report_get","parameters":[{"name":"image_id","in":"path","required":true,"schema":{"type":"string","description":"Unique ID of the image","title":"Image Id"},"description":"Unique ID of the image"}],"responses":{"200":{"description":"Successful Response","content":{"application/octet-stream":{}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/reports/pdf/{media_id}":{"get":{"tags":["Report API"],"summary":"Generate Pdf Report","description":"Get PDF report if available, or trigger generation if not.\n\n- Returns the PDF if it is already generated\n- Triggers generation and returns 202 if not available\n- Returns processing status if generation is already in progress\n- Returns 409 with body {\"detail\": \"media_unavailable\"} when the owner's\n  account is gone. Retrying does not help.","operationId":"generate_pdf_report_v2_reports_pdf__media_id__get","parameters":[{"name":"media_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the media","title":"Media Id"},"description":"Unique identifier of the media"},{"name":"media_type","in":"query","required":true,"schema":{"type":"string","description":"Type: Only 'video' is allowed at present","title":"Media Type"},"description":"Type: Only 'video' is allowed at present"},{"name":"callback_url","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Callback URL for completion notification. Must be an https URL on a publicly reachable host.","title":"Callback Url"},"description":"Callback URL for completion notification. Must be an https URL on a publicly reachable host."}],"responses":{"200":{"description":"PDF stream when the artifact is ready; JSON {id, message, status} when this request triggered generation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateReportResponse"}},"application/pdf":{}}},"202":{"description":"Generation already running.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateReportResponse"}}}},"409":{"description":"The owner's account is gone, so the report cannot be generated. The body is {\"detail\": \"media_unavailable\"}. Retrying does not help.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/{media_type}/{media_id}/insights":{"post":{"tags":["Insights API"],"summary":"Send message to Insights","description":"Send a message to Insights and receive an AI-generated response about the media's emotional analysis.\n\n## Streaming (Recommended)\n\nSet `stream=true` (default) to receive the response as Server-Sent Events (SSE) with progressive text chunks. This is the **recommended** approach as it provides real-time feedback and avoids potential timeout issues with longer responses.\n\n**SSE Events:**\n- `stream_start`: Initial event confirming the connection\n- `metadata`: Contains `conversation_id` and `message_id`\n- `stream_chunk`: Progressive text chunks from AI\n- `stream_end`: Final event with `full_text`, `token_usage`, and `credits_charged` (flat per-reasoning-mode rate billed for this turn)\n- `done`: Completion signal\n- `error`: Error event if processing fails (includes `error_id` and `message`). Insufficient-credits failures use `error_id: 1402` (matches HTTP 402 in non-streaming mode)\n\n**Error Handling in Streaming Mode:**\n- Errors that occur **before** the stream starts (e.g., authentication failures, validation errors) are returned as standard HTTP 4xx/5xx responses with JSON bodies.\n- Errors that occur **after** the stream has started (e.g., during AI processing) are delivered as SSE `error` events.\n\n## Non-Streaming\n\nSet `stream=false` to receive the complete response as JSON. **Note:** This mode waits for the full AI response before returning, which may result in longer response times or timeouts for complex queries. Use streaming mode for a better experience.\n\n## Requirements\n\n- **Message Length:** Maximum 10,000 characters\n- **Media Status:** Media processing must be completed (status: \"completed\")\n- **Analysis Data:** Emotional analysis data must be available. If it is not ready, you'll receive error code 1601. The body includes `data.modalities`: each required analysis is `ready`, `building`, `waiting_source`, or `failed`. Retrying may clear a transient `failed`. `building` and `waiting_source` may resolve once work finishes; a `failed` modality that stays failed has no file to wait for.\n\n## Conversation Management\n\nThe system maintains one conversation per user per media item. Your conversation history is preserved across multiple requests — no need to manage conversation IDs manually.\n\n## Supported Media Types\n\nvideo, audio, image, text","operationId":"send_insights_message","parameters":[{"name":"media_type","in":"path","required":true,"schema":{"type":"string","title":"Media Type"}},{"name":"media_id","in":"path","required":true,"schema":{"type":"string","title":"Media Id"}},{"name":"stream","in":"query","required":false,"schema":{"type":"boolean","description":"Enable SSE streaming (true) or return full JSON response (false)","default":true,"title":"Stream"},"description":"Enable SSE streaming (true) or return full JSON response (false)"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AIInsightsChatRequest"}}}},"responses":{"200":{"description":"AI response (SSE stream or JSON)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AIInsightsChatResponse"},"example":{"conversation_id":"user123_01","message_id":1,"response":"Based on the video analysis, the dominant emotions are...","token_usage":{"input_tokens":1500,"output_tokens":350},"is_quota_exceeded":false,"credits_charged":1}},"text/event-stream":{"example":"event: stream_start\ndata: {\"status\": \"initializing\"}\n\nevent: metadata\ndata: {\"message_id\": 1, \"conversation_id\": \"user123_01\"}\n\nevent: stream_chunk\ndata: {\"text\": \"Based on the video analysis, \"}\n\nevent: stream_chunk\ndata: {\"text\": \"the dominant emotions are...\"}\n\nevent: stream_end\ndata: {\"message_id\": 1, \"full_text\": \"Based on...\", \"token_usage\": {\"input_tokens\": 1500, \"output_tokens\": 350}, \"credits_charged\": 1}\n\nevent: done\ndata: {}\n"}}},"400":{"description":"Bad Request - Analysis data not yet available","content":{"application/json":{"example":{"error_id":1601,"message":"CSV data not available for video. Retrying may clear a transient failure. building or waiting_source may resolve once work finishes; a failed modality that stays failed has no file to wait for.","timestamp":"2024-01-15T10:30:00.000000+00:00","data":{"modalities":{"face":"building","audio":"ready","transcript":"waiting_source"}}}}}},"401":{"description":"Unauthorized - Authentication failed","content":{"application/json":{"example":{"detail":"Invalid User or Not Authorized"}}}},"402":{"description":"Payment Required - User does not have enough credits for the requested reasoning mode. Flat per-mode rate (1 / 5 / 20 for standard / pro / ultra) — see `services.ai_insights_credit_policy`. Top up to continue. In SSE mode this surfaces as an `error` event with the same `error_id`.","content":{"application/json":{"example":{"error_id":1402,"message":"Insufficient credits — your balance is -12. Add credits to start anything new.","timestamp":"2024-01-15T10:30:00.000000+00:00"}}}},"404":{"description":"Not Found - Media not found","content":{"application/json":{"example":{"detail":"Video not found"}}}},"422":{"description":"Validation Error - Invalid input parameters","content":{"application/json":{"schema":{"type":"object","properties":{"detail":{"type":"string","description":"Error message describing the validation failure"}},"required":["detail"]},"examples":{"invalid_media_type":{"summary":"Invalid media type","value":{"detail":"Invalid media_type. Must be one of: audio, image, text, video"}},"media_not_completed":{"summary":"Media processing not completed","value":{"detail":"Media processing not completed. Current status: processing"}},"message_too_long":{"summary":"Message exceeds length limit","value":{"detail":"'message' string should have at most 10000 characters"}}}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"example":{"detail":"Internal Server Error"}}}}}},"get":{"tags":["Insights API"],"summary":"Get Insights conversation history","description":"Retrieve the conversation history for a specific media item's Insights session.\n\n## Response\n\nReturns all messages (user questions and AI responses) in chronological order.\n\n**Empty History:** If no conversation exists yet, returns an empty `messages` array with a conversation_id. The conversation will be created automatically when you send your first message via POST.\n\n## Supported Media Types\n\nvideo, audio, image, text","operationId":"get_insights_history","parameters":[{"name":"media_type","in":"path","required":true,"schema":{"type":"string","title":"Media Type"}},{"name":"media_id","in":"path","required":true,"schema":{"type":"string","title":"Media Id"}}],"responses":{"200":{"description":"Conversation history retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AIInsightsHistoryResponse"},"examples":{"with_messages":{"summary":"Conversation with messages","value":{"conversation_id":"user123_01","messages":[{"role":"user","text":"What emotions were detected?","message_id":1,"timestamp":"2024-01-15T10:30:00.000000"},{"role":"ai_assistant","text":"Based on the analysis, the dominant emotions are...","message_id":1,"timestamp":"2024-01-15T10:30:05.000000"}]}},"empty_history":{"summary":"No conversation yet","value":{"conversation_id":"user123_01","messages":[]}}}}}},"401":{"description":"Unauthorized - Authentication failed","content":{"application/json":{"example":{"detail":"Invalid User or Not Authorized"}}}},"404":{"description":"Not Found - Media not found","content":{"application/json":{"example":{"detail":"Video not found"}}}},"422":{"description":"Validation Error - Invalid input parameters","content":{"application/json":{"schema":{"type":"object","properties":{"detail":{"type":"string","description":"Error message describing the validation failure"}},"required":["detail"]},"example":{"detail":"Invalid media_type. Must be one of: audio, image, text, video"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]},"delete":{"tags":["Insights API"],"summary":"Delete Insights conversation","description":"Delete the conversation history for a specific media item.\n\nThis permanently removes all messages and context for the current user's Insights session with this media item. Other users' conversations are not affected.\n\nAfter deletion, the next POST request will start a fresh conversation.\n\n## Supported Media Types\n\nvideo, audio, image, text","operationId":"delete_insights_conversation","parameters":[{"name":"media_type","in":"path","required":true,"schema":{"type":"string","title":"Media Type"}},{"name":"media_id","in":"path","required":true,"schema":{"type":"string","title":"Media Id"}}],"responses":{"204":{"description":"Conversation deleted successfully"},"401":{"description":"Unauthorized - Authentication failed","content":{"application/json":{"example":{"detail":"Invalid User or Not Authorized"}}}},"404":{"description":"Not Found - Media not found","content":{"application/json":{"example":{"detail":"Video not found"}}}},"422":{"description":"Validation Error - Invalid input parameters","content":{"application/json":{"schema":{"type":"object","properties":{"detail":{"type":"string","description":"Error message describing the validation failure"}},"required":["detail"]},"example":{"detail":"Invalid media_type. Must be one of: audio, image, text, video"}}}}}}},"/v1/texts/{text_id}/emotions":{"put":{"tags":["Text Emotion API"],"summary":"Edit Text","description":"This end-point allows a user to edit the existing text and reprocess it to generate emotion insights.","operationId":"edit_text_v1_texts__text_id__emotions_put","parameters":[{"name":"text_id","in":"path","required":true,"schema":{"type":"string","description":"The unique text ID of the text.","title":"Text Id"},"description":"The unique text ID of the text."}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_edit_text_v1_texts__text_id__emotions_put"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TextReprocessResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/texts/{text_id}/report":{"get":{"tags":["Text Emotion API"],"summary":"Download Text Emotion Report","description":"Downloads the emotion analysis insights for a specific text in CSV format based on the provided text id.","operationId":"download_text_emotion_report_v1_texts__text_id__report_get","parameters":[{"name":"text_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the text.","title":"Text Id"},"description":"The unique ID of the text."}],"responses":{"200":{"description":"Successful Response","content":{"application/octet-stream":{}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/audios/{audio_id}/transcript/replace":{"put":{"tags":["Audio Emotion API"],"summary":"Replace Audio Transcript","operationId":"replace_audio_transcript_v1_audios__audio_id__transcript_replace_put","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the audio file","title":"Audio Id"},"description":"Unique identifier of the audio file"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplaceTranscriptRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplaceTranscriptResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/videos/{video_id}/trim":{"put":{"tags":["Video Emotion API"],"summary":"Trim Video Endpoint","description":"Crop an uploaded video by trimming time off the beginning and/or the end.\n\nOn a processed video this drops the cut-out portion's faces, speakers,\ntranscript, and facial/audio/text emotion data by re-analyzing only the\nkept window (timestamps re-based to the new start). The video re-enters\n`processing`; poll the status endpoint for completion.","operationId":"trim_video_endpoint_v1_videos__video_id__trim_put","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the video to crop","title":"Video Id"},"description":"Unique identifier of the video to crop"}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_trim_video_endpoint_v1_videos__video_id__trim_put"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrimVideoResponse"}}}},"422":{"description":"Request validation failed (`{\"detail\": str}` only), or `processing_fps_over_budget`: the video is too long to process at its frame rate. Nothing was charged. The body then also offers the frame rates that fit (`recommended_processing_fps`, `fits_processing_fps`) and each tier's longest video (`duration_s`, `max_duration_by_fps`), so the client can suggest a lower frame rate or a trim.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProcessingFpsOverBudgetError"}}}}}}},"/v2/insights/digests":{"post":{"tags":[],"summary":"Create Digest","description":"Digest one document into a bounded summary with verified quotes.\n\nSend the document as ordered chunks. Each quote in the result is an exact span of\nthe chunks it cites. A repeat of an identical request (same chunks, focus and\n`digest_version`) returns the stored digest at no charge.\n\nAnswers 200 with `status: ready` when the digest is available within `wait_s`,\notherwise 202 with `status: running`; poll `GET /v2/insights/digests/{digest_key}`.\nA digest that stopped before finishing reports `failed` with code `interrupted`:\nsend this request again, since document text is never stored. The result is kept\nfor `ttl_days`. A successful digest is billed once; a failed one is not billed.","operationId":"create_digest_v2_insights_digests_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DigestRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DigestResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/insights/digests/{digest_key}":{"get":{"tags":[],"summary":"Read Digest","description":"Read one of your digests: `ready`, `running` (202) or `failed`.","operationId":"read_digest_v2_insights_digests__digest_key__get","parameters":[{"name":"digest_key","in":"path","required":true,"schema":{"type":"string","title":"Digest Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DigestResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/insights/briefs":{"post":{"tags":[],"summary":"Create Brief","description":"Combine several of your ready digests into one cross-document brief.\n\nChunk ids must be unique across the documents in one brief (prefix them with a\ndocument id). A digest that is not ready answers 409 `digest_not_ready`. Cached,\npolled and billed like a digest.","operationId":"create_brief_v2_insights_briefs_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BriefRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BriefResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/insights/briefs/{brief_key}":{"get":{"tags":[],"summary":"Read Brief","description":"Read one of your briefs: `ready`, `running` (202) or `failed`.","operationId":"read_brief_v2_insights_briefs__brief_key__get","parameters":[{"name":"brief_key","in":"path","required":true,"schema":{"type":"string","title":"Brief Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BriefResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/insights/conversations":{"post":{"tags":[],"summary":"Create Conversation","description":"Create a conversation: an instruction, an output schema and typed context, sent once.\n\nReturns a new `conversation_id` on every call. The context is prepared once so each\nevaluate sends only the turn. `media_binding` attaches per-turn emotion evidence from\none of your live realtime sessions or one of your own processed recordings; a\nrecording must be fully processed (409 `media_not_ready` otherwise), and one with\nseveral subjects needs a selection per modality. Alternatively, a recording binding may\nname one to four `people`, each with a unique role label (1–64 trimmed characters) and\nexplicit `subjects`. Each person's readings stay separate. `people` and binding-level\n`subjects` are mutually exclusive; overlapping subject IDs within a modality are 422.\nIn people mode omitted modalities remain unbound, even with a sole speaker. This also\nworks on each entry of `media_bindings`. Labels and selections persist for the conversation lifetime.\n\nWith `retention: ephemeral` no turn's input or output is stored, only its usage. The\nconversation's own instruction, schema and sections are kept until it expires or is\ndeleted, so put per-turn content in evaluate, not here.\n\nAlso available on a user's delegated access token with the `insights.write` scope. The\nconversation then belongs to that user and your app: only your app's tokens for that\nuser can evaluate or delete it. It must be bound to one of that user's own processed\nrecordings, which also needs `media.read` (403 naming the scope otherwise); a\nconversation with no binding, or bound to a live session, is refused with 403. Each\nturn is billed to that user, or to their team workspace where the deployment is that\nworkspace's own.\n\nA delegated token may instead send `media_bindings`: one to 200 of that user's processed\nrecordings, each with its own `media_id` (distinct ids; more than 200, a repeat, or sending\n`media_binding` as well is 422). The conversation then reads several recordings at once, and\neach turn chooses which with `media_ids`. A recording is readable by that user as its owner:\ntheir own, or those of the team workspace the token's grant is bound to. Every id is checked\nbefore anything is read or charged, and one that is missing or not readable, among ones that\nare, fails the whole request with 404 `binding_not_found`; then `media.read` is needed (403\nnaming the scope), and every recording must be fully processed (409 `media_not_ready`, with\neach unfinished `media_id` and its status). `media_bindings` is refused with 403 on an API\nkey. Turns on recordings of the user's own are billed to that user. A grant bound to a team\nworkspace is billed to that workspace where the workspace has its own dedicated deployment, and\nis refused with 403 anywhere else. The whole selected cohort must become ready within one\nreadiness budget; incomplete preparation answers 409 `media_not_ready` without creating a conversation.\nIf recording preparation is temporarily unavailable, the request answers 503 `preparation_busy`;\nretry after a short wait.","operationId":"create_conversation_v2_insights_conversations_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateConversationRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateConversationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["insights.write","media.read"]}]}},"/v2/insights/conversations/{conversation_id}":{"delete":{"tags":[],"summary":"Delete Conversation","description":"Delete a conversation, its stored turns and its prepared context.\n\nAlso available on a user's delegated access token with the `insights.write` scope, for\na conversation your app created for that user.","operationId":"delete_conversation_v2_insights_conversations__conversation_id__delete","parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationDeleted"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["insights.write"]}]}},"/v2/insights/conversations/{conversation_id}/evaluate":{"post":{"tags":[],"summary":"Evaluate Conversation","description":"Evaluate one turn and return an output object valid against the conversation's schema.\n\nSynchronous. Live and unbound conversations answer within a short deadline suited to\nlive use, or fail with 504 `evaluate_timeout`; a recording-bound conversation has a\nlonger one, since a recording's evidence may have to be loaded first. For a bound\nconversation send `window` (milliseconds on the session or recording clock) and the\n`modalities` to read; `evidence_pending` says the live window was still filling.\n\nA `turn_key` is charged at most once. Repeating a finished turn replays its output\nfree of charge in a `session` conversation (409 `turn_key_conflict` if the request\ndiffers) and is refused with 409 `turn_key_used` in an `ephemeral` one. A repeat\nwhile the first is still running is 409 `turn_in_progress`. A failed turn is not\nbilled and its key can be retried. Rarely, a turn is answered but cannot be\nrecorded; a repeat of its key is then evaluated again after a short wait, and is\nstill charged once. Several turns of one conversation may run at once.\n\nAlso available on a user's delegated access token with the `insights.write` scope, for\na conversation your app created for that user; the token must still hold `media.read`\n(403 naming the scope otherwise), each turn must request at least one modality (403\notherwise), and a recording that user no longer owns is 404 `binding_not_found`. None of\nthese is billed. The turn is billed to that user, or to their team workspace where the\ndeployment is that workspace's own.\n\nOn a conversation bound with `media_bindings`, `media_ids` chooses which of the bound\nrecordings this turn reads (distinct ids, at most 200, each one bound: another id is 422 with\n`field: media_ids.{i}`; absent means all of them). `window` is applied on each chosen\nrecording's own clock, a recording that needs a subject says which one in `media_id`, and\n`modalities_used` reports what was read across them. Every chosen recording is checked again\non every turn: one that was deleted, or is no longer readable by the token, is 404\n`binding_not_found` before any work is done or charged. Only a conversation bound to\nrecordings takes `media_ids`.\n\nBindings with `people` supply separately labelled evidence for every selected person.\nUnbound modalities have no samples and never inherit another person's speaker. Transcript\nsegments are limited to explicitly bound, requested voice/text speakers. Missing evidence\nis not a negative finding. Larger prompts may increase the turn's usage; billing remains\nonce per turn. The evidence size limit covers all people and selected recordings together\n(413 `payload_too_large`, `field: recording_evidence`).\n\nSelected recordings keep their own clocks; a window may extend beyond a shorter recording.\nThe cohort must become ready within the turn budget (409 `media_not_ready` otherwise).\nIf recording preparation is temporarily unavailable, the turn answers 503 `preparation_busy`\nwithout a charge; retry after a short wait.\nGenerated evidence and retained evidence each have an aggregate 256,000 UTF-8 byte limit;\nexceeding either is 413 `payload_too_large` with `field: recording_evidence`, before model work.\nEvidence is never truncated or a selected recording silently omitted to fit these limits.","operationId":"evaluate_conversation_v2_insights_conversations__conversation_id__evaluate_post","parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EvaluateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EvaluateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["insights.write","media.read"]}]}},"/v2/insights/expert/{skill}":{"post":{"tags":[],"summary":"Run Expert Skill","description":"Run one emotion expert skill on a window of a processed recording the user may read.\n\nTakes a user's delegated access token with the `insights.write` scope; an API key or a\nbrowser session is refused with 403. The recording must be readable by that user, which\nalso needs `media.read` (403 naming the scope otherwise): their own, or, on a grant bound to\ntheir team workspace, one that workspace owns. A recording they may not read and a missing\none are the same 404 `binding_not_found`. The call is billed to that user, or to their team\nworkspace where the deployment is that workspace's own, with your app recorded on the\ncharge, and the stored result belongs to that user and your app.\n\nSkills: `interpret_window` (what the subject appears to feel, optionally for a\n`question`), `assess_response` (whether the subject responded to a `stimulus`, against\na `baseline`), `explain_moment` (why a detected moment, `moment_id`, was flagged; videos\nonly), `compare` (two to four `people` side by side) and `narrate` (the emotional arc of\nthe window, optionally for a `focus`). A field that belongs to another skill is 422.\n\nThe recording must be fully processed (409 `media_not_ready` otherwise), and one with\nseveral people needs a subject per requested modality (422 `subject_required`).\n`situation` sections are treated as data about the situation, never as instructions.\n\nEvery finding cites evidence on the recording: evidence windows and moments. Confidence\nis capped by how reliable the emotion models are for each state, language and\ncondition; `evidence_quality` says why. `insufficient_evidence` and `out_of_scope` are\nresults (200), not errors. `explain_moment` answers 409 `moments_not_ready` while the\nrecording's moments are computed, and 422 `moments_unavailable` when they will not be.\nA call that does not finish in time is 504 `evaluate_timeout`.\n\nA call that produces a new result is billed once, after the response. A repeat of an\nidentical request from the same app with the same `expert_version` replays the stored\nresult free of charge (`replayed: true`); while the first is still running a repeat is 409\n`expert_in_progress`. Another app's identical request is evaluated, and billed, on its own.\nFour results are free: a request outside the skills' scope, a window with no usable evidence,\nand an answer that failed citation grounding or the output schema. A stored answer is billed\nwhatever its status, including `insufficient_evidence` and `out_of_scope` answers and one\nwhose findings calibration then withholds. Failed calls are not billed. Stored results are kept for 30 days and\ndeleted with the recording or the account; there is no per-result delete.","operationId":"run_expert_skill_v2_insights_expert__skill__post","parameters":[{"name":"skill","in":"path","required":true,"schema":{"enum":["interpret_window","assess_response","explain_moment","compare","narrate"],"type":"string","title":"Skill"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpertSkillRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpertResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2Delegated":["insights.write","media.read"]}]}},"/v2/insights/expert/{skill}/batch":{"post":{"tags":[],"summary":"Run Expert Batch","description":"Run one expert skill on up to ten windows of recordings the user may read, at once.\n\nTakes a user's delegated access token with the `insights.write` scope, like a single\ncall (readable recordings and billing as there); an API key or a browser session is\nrefused with 403.\n\nEach item takes the fields of a single call, including its own `media_binding`, except\n`product`, `language` and `condition`, which the batch shares (recordings in different\nlanguages belong in separate batches). A batch reads at most four distinct recordings\n(422 `invalid_request` on `items` beyond that). Every recording must be readable by the\ntoken's user before any is evaluated: one that is missing or not readable by them is a 404\n`binding_not_found` for the whole request, and a token without `media.read` is a 403\nnaming the scope; nothing is evaluated or billed then.\n\nThe items run concurrently; the entries come back in request order, each naming its\n`media_binding` and carrying its own `result` or `error`, charge, `replayed` flag and\n`model_ids`, and `credits_charged` is their sum. A problem with the request as a whole\n(validation, authentication, a recording that is not readable) is the usual error response;\na failure of one item is that\nentry's `error`, e.g. `evaluate_timeout`, `expert_in_progress`, `media_not_ready` or\n`moments_not_ready` for an item whose recording is still being processed (its `field` names\n`items.{i}.media_binding`, and the items on other recordings still run), or a subject,\nperson or moment of that item the recording does not have (`subject_required`,\n`moment_not_found`, `invalid_request` naming the item's field), with the same fields a single call's error\ncarries. A failed item is not charged and its siblings still run. If the balance cannot\ncover the items that need a new evaluation, those items answer `insufficient_credits`\ntogether; replays and requests outside the skills' scope are still answered. Identical\nitems are evaluated once and charged once: the first carries the charge, the others\nare `replayed`.","operationId":"run_expert_batch_v2_insights_expert__skill__batch_post","parameters":[{"name":"skill","in":"path","required":true,"schema":{"enum":["interpret_window","assess_response","explain_moment","compare","narrate"],"type":"string","title":"Skill"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpertBatchRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpertBatchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2Delegated":["insights.write","media.read"]}]}},"/v2/videos/{video_id}/non-human-spans":{"put":{"tags":["Video Emotion API"],"summary":"Put Video Non Human Spans","description":"Replace known non-human audio, such as a voice assistant or music.\n\nThe body is the whole list. ``[]`` clears it. Every span must lie within the\nvideo. 409 ``video_duration_unknown`` until the duration is known. 422\n``non_human_span_out_of_range`` when a span ends after the video, 422\n``non_human_spans_clock_unsupported`` when the video was analysed from\n``start_millis`` > 0, and 422 ``non_human_spans_too_large`` when the stored\nlist exceeds its size cap.\n\nSending this list does not by itself remove a speaker from analysis results.\nA span is recorded time. A speaker marked ``system`` or ``media`` on the\naudio is what leaves Moments.\n\nOwner or ``media:write``. Not available to a delegated token. No charge.","operationId":"put_video_non_human_spans_v2_videos__video_id__non_human_spans_put","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the video.","title":"Video Id"},"description":"The unique ID of the video."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NonHumanSpansRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NonHumanSpansResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/videos/{video_id}":{"get":{"tags":["Video Emotion API"],"summary":"Get Video Details","description":"Retrieve all details for a specific video directly from the database.\n\nReturns a complete standardized response with all possible fields.\nFields that don't exist in the database are returned as null.\n\n**Access Control:**\n- Authenticated users can access their own videos\n- Admins can access all videos\n- Sample videos and shared videos are publicly accessible","operationId":"get_video_details_v2_videos__video_id__get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the video.","title":"Video Id"},"description":"The unique ID of the video."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoDetailResponseV2"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]},"delete":{"tags":["Video Emotion API"],"summary":"Delete Video V2","description":"Permanently delete a video, its stored media and its analyses. This action is irreversible.\n\nOnly the video's owner can delete it. Deletion completes asynchronously: the video\ndisappears from listings immediately, and its data is permanently removed shortly\nafter. The response status is `deleting`, and repeating the request while it is in\nprogress returns the same response.","operationId":"delete_video_v2_v2_videos__video_id__delete","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the video to delete.","title":"Video Id"},"description":"The unique ID of the video to delete."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoDeleteResponse"}}}},"401":{"description":"Missing, malformed or rejected credentials — no `X-API-Key` / `Authorization: Bearer <jwt>`, an expired token, or a caller that lacks the role the route requires. Note that this API answers *authorization* failures with 401 as well as 403 (see the 403 entry where a route declares both).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"No such resource, or it is not visible to this caller. Ownership failures are reported as 404 rather than 403 so the API does not confirm the existence of another account's records.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.delete"]}]}},"/v2/videos/{video_id}/playback-url":{"get":{"tags":["Video Emotion API"],"summary":"Get Video Playback Url Route","description":"Get a short-lived URL for playing back a video.\n\nThe returned `url` streams the stored video inline in a browser or media player.\nIt is short-lived: request a new one when `expires_at` passes or when a request\nmade with it is refused, and do not store or share it. The video's `url` and\n`media_source_url` fields are references to the stored media, not guaranteed to\nbe readable; this endpoint is how to play it.\n\n**Access Control:** the same as the video details. The owner and admins can\nalways get a playback URL. While a video is shared, or is a sample, anyone can,\nso sharing a video lets anyone with its link play it back.\n\n**Errors:**\n- **401**: not authorized to access this video\n- **404**: video not found\n- **409** `media_not_stored`: the video has no stored copy to play (for example, a\n  video submitted by URL whose copy is not ready yet)\n- **503** `media_storage_unavailable`: temporarily unavailable; try again shortly","operationId":"get_video_playback_url_route_v2_videos__video_id__playback_url_get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the video.","title":"Video Id"},"description":"The unique ID of the video."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackUrlResponse"}}}},"401":{"description":"Missing, malformed or rejected credentials — no `X-API-Key` / `Authorization: Bearer <jwt>`, an expired token, or a caller that lacks the role the route requires. Note that this API answers *authorization* failures with 401 as well as 403 (see the 403 entry where a route declares both).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"No such resource, or it is not visible to this caller. Ownership failures are reported as 404 rather than 403 so the API does not confirm the existence of another account's records.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"409":{"description":"`media_not_stored`: there is no stored copy to play yet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"503":{"description":"`media_storage_unavailable`: try again shortly.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v2/videos/{video_id}/faces/{face_id}/image-url":{"get":{"tags":["Video Emotion API"],"summary":"Get Video Face Image Url Route","description":"Get a short-lived URL for a face's image.\n\nThe returned `url` serves the stored image of one detected face, ready to use as an\nimage source. It is short-lived: request a new one when `expires_at` passes or when a\nrequest made with it is refused, and do not store or share it. A face's `image` field\nin the video details is a stored reference, not guaranteed to be readable; this\nendpoint is how to show it.\n\n**Access Control:** the same as the video details. The owner and admins can always get\na face image URL. While a video is shared, or is a sample, anyone can, so sharing a\nvideo lets anyone with its link see its faces.\n\n**Errors:**\n- **401**: not authorized to access this video\n- **404**: video or face not found\n- **409** `media_not_stored`: the face has no stored image (for example, a face added\n  manually, or one whose image is not ready yet), or its image could not be looked up\n  just now; retrying may succeed\n- **503** `media_storage_unavailable`: the URL could not be issued; try again shortly","operationId":"get_video_face_image_url_route_v2_videos__video_id__faces__face_id__image_url_get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the video.","title":"Video Id"},"description":"The unique ID of the video."},{"name":"face_id","in":"path","required":true,"schema":{"type":"integer","minimum":1,"description":"The ID of the face within the video.","title":"Face Id"},"description":"The ID of the face within the video."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FaceImageUrlResponse"}}}},"401":{"description":"Missing, malformed or rejected credentials — no `X-API-Key` / `Authorization: Bearer <jwt>`, an expired token, or a caller that lacks the role the route requires. Note that this API answers *authorization* failures with 401 as well as 403 (see the 403 entry where a route declares both).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"No such resource, or it is not visible to this caller. Ownership failures are reported as 404 rather than 403 so the API does not confirm the existence of another account's records.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"409":{"description":"`media_not_stored`: this face has no stored image yet, or its image could not be looked up just now. Retrying may succeed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"503":{"description":"`media_storage_unavailable`: try again shortly.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v2/videos/{video_id}/transcript":{"put":{"tags":["Video Emotion API"],"summary":"Put Video Provider Transcript","description":"Attach a transcript produced elsewhere (for example by a meeting provider) to a video.\n\nThe transcript is stored beside the video's own transcript and does not replace it: the\nanalysis, speakers and every other result keep using the video's own transcript, and the\nattached one is read back with `GET /v2/videos/{video_id}/transcript?transcript=provider`.\nSpeaker display names sent in `speakers` are stored with the attached transcript only; they\ndo not rename speakers on the video.\n\nOnly one transcript can be attached per video. Sending again replaces it, whatever its\n`source`. Sending the same content again changes nothing and returns the original\n`attached_at`, so a request can be retried safely. `replaced` is true only when this request\nreplaced a different transcript that was already attached.\n\n`start_ms` and `end_ms` are on the video's own timeline; no offset is applied. Segments are\nstored and returned in `start_ms` order. At least one segment is required.\n\nThe request body is limited to 1 MiB, the transcript to 10,000 segments, and the stored\ncontent (segments and speaker names) to 640 KiB; over any limit the response is 413 and\nnothing is stored. The video's owner and administrators can attach a transcript. No charge is\napplied.","operationId":"put_video_provider_transcript_v2_videos__video_id__transcript_put","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the video.","title":"Video Id"},"description":"The unique ID of the video."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderTranscriptRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderTranscriptPutResponse"}}}},"401":{"description":"Missing, malformed or rejected credentials — no `X-API-Key` / `Authorization: Bearer <jwt>`, an expired token, or a caller that lacks the role the route requires. Note that this API answers *authorization* failures with 401 as well as 403 (see the 403 entry where a route declares both).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"No such resource, or it is not visible to this caller. Ownership failures are reported as 404 rather than 403 so the API does not confirm the existence of another account's records.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Request validation failed. The app flattens FastAPI's validation error list into a single `detail` string, so the body is `{\"detail\": \"'field' <message>\"}` and NOT FastAPI's default `HTTPValidationError`. Declaring this overrides the auto-generated 422 — see the module docstring.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"413":{"description":"`provider_transcript_too_large`: the request body, the number of segments or the stored transcript content is over the limit. Nothing was stored.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.write"]}]},"get":{"tags":["Video Emotion API"],"summary":"Get Video Transcript","description":"Read a video's own transcript, or the transcript attached to it.\n\n`transcript=machine` (the default) returns the video's own transcript with its speaker labels;\nit is empty until that transcript exists. `transcript=provider` returns what was attached\nwith `PUT`, its `source`, its speaker display names and `attached_at`, or 404 when nothing\nis attached. The response names which transcript it holds in `transcript`.\n\nThe video's owner and administrators can read it; a shared link does not grant access to a\ntranscript.","operationId":"get_video_transcript_v2_videos__video_id__transcript_get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the video.","title":"Video Id"},"description":"The unique ID of the video."},{"name":"transcript","in":"query","required":false,"schema":{"enum":["machine","provider"],"type":"string","description":"`machine` (default) is the video's own transcript; `provider` is the one attached with `PUT`.","default":"machine","title":"Transcript"},"description":"`machine` (default) is the video's own transcript; `provider` is the one attached with `PUT`."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptResponse"}}}},"401":{"description":"Missing, malformed or rejected credentials — no `X-API-Key` / `Authorization: Bearer <jwt>`, an expired token, or a caller that lacks the role the route requires. Note that this API answers *authorization* failures with 401 as well as 403 (see the 403 entry where a route declares both).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"No such resource, or it is not visible to this caller. Ownership failures are reported as 404 rather than 403 so the API does not confirm the existence of another account's records.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Request validation failed. The app flattens FastAPI's validation error list into a single `detail` string, so the body is `{\"detail\": \"'field' <message>\"}` and NOT FastAPI's default `HTTPValidationError`. Declaring this overrides the auto-generated 422 — see the module docstring.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v2/videos/{video_id}/face_analysis":{"get":{"tags":["Video Emotion API"],"summary":"Get Video Face Analysis","description":"Per-frame gaze, eye, blink, liveness, and lip signals — no frame images.\n\nSame authorization as other video GETs (owner, admin, shared, or sample).\n\nEach `frames[]` row is one face on one frame. Fields the pipeline did not\ncompute are omitted.\n\nBlink rate is not returned. Count `False`→`True` transitions on `blink`\nand divide by the face's duration in minutes. The eye-tracking heatmap is\nnot a stored dataset — it is a density view of `gaze_yaw` × `gaze_pitch`.\n\nPage with `start_frame` + `length` on long videos. `length=0` (default) pulls\nevery frame from frames-service, same as `GET /v1/videos/{id}/frames`.","operationId":"get_video_face_analysis_v2_videos__video_id__face_analysis_get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique video ID of the video to fetch face analysis from","title":"Video Id"},"description":"The unique video ID of the video to fetch face analysis from"},{"name":"face_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Optional face ID to restrict the series to one face","title":"Face Id"},"description":"Optional face ID to restrict the series to one face"},{"name":"start_frame","in":"query","required":false,"schema":{"type":"integer","description":"First frame number to include (inclusive). Ignored when length is 0.","default":0,"title":"Start Frame"},"description":"First frame number to include (inclusive). Ignored when length is 0."},{"name":"length","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Max frames to fetch by frame number. 0 = the whole video. Prefer a bound on long videos.","default":0,"title":"Length"},"description":"Max frames to fetch by frame number. 0 = the whole video. Prefer a bound on long videos."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoFaceAnalysisResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v2/videos/{video_id}/moments/resolve":{"get":{"tags":["Video Emotion API"],"summary":"Resolve Video Moments Api","description":"Follow moment ids from an earlier computation to the latest stored moments.\n\nSame authorization as other video GETs (owner, admin, shared, or sample).\nThis call does not start a computation. It answers from the latest stored\nmoments even when those are stale.\n\n`moments_status` and `stale` mean the same thing as on\n`GET /v2/videos/{video_id}/moments`. `generation` and `profile_version`\nidentify that stored computation, together with `computed_at`: `generation`\nand `profile_version` can stay the same across a recomputation, and\n`computed_at` changes whenever the stored moments do.\n\nEach `results` row echoes the id that was asked:\n\n- `current` — the id is in the latest stored moments. `current_id` is that id.\n- `superseded` — the id was followed to an id in the latest stored moments.\n  That is the best overlapping moment for the same subject and kind, which can\n  be a moment that also absorbed a neighbor. Check the span before treating it\n  as the same event. A moment tied to a stimulus is followed only to a moment\n  for that same stimulus.\n- `gone` — nothing for the same subject and kind overlapped enough.\n  `current_id` is null. This is not proof the event ceased to exist: a subject\n  change, for example, has no eligible overlap.\n- `unknown` — the id is in neither the latest stored moments nor a computation\n  this answer can follow. `current_id` is null. Ids from older computations\n  may be reported as unknown.","operationId":"resolve_video_moments_api_v2_videos__video_id__moments_resolve_get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique video ID of the video to resolve moments for","title":"Video Id"},"description":"The unique video ID of the video to resolve moments for"},{"name":"ids","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Moment ids to follow, repeated once per id. Send 1 to 200 ids. Zero ids, more than 200, or an id longer than 256 characters is rejected after the video is authorized. A blank id is reported unknown. Duplicates are kept in order. An id from an older computation may be unknown.","title":"Ids"},"description":"Moment ids to follow, repeated once per id. Send 1 to 200 ids. Zero ids, more than 200, or an id longer than 256 characters is rejected after the video is authorized. A blank id is reported unknown. Duplicates are kept in order. An id from an older computation may be unknown."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MomentResolveResponse"}}}},"422":{"description":"`ids` is missing, empty, longer than 200, or an id is longer than 256 characters. The check runs after the video is authorized. The body is `{\"detail\": \"<message>\"}`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v2/videos/{video_id}/moments/request":{"post":{"tags":["Video Emotion API"],"summary":"Request Video Moments Api","description":"Request Moments for a processed video. Credits are charged once per video.\n\nMoments are not computed for a video until they are requested here. The video must be\ncompleted (`409` otherwise). A successful request charges the credits once, queues the\ncomputation and returns the same body as `GET /v2/videos/{video_id}/moments`\n(`status: computing`, then `ready` as that call is polled).\n\nRequesting is idempotent: when Moments was already requested for the video, this returns\n`200` with the current moments and charges nothing. Moments stay with the video through\nedits, trimming and reprocessing; recomputing them after such changes is free.\n\nWith too few credits the call answers `402` and nothing is requested. Credits are\ncharged once per video, whatever the number of attempts. If a call fails with a server\nerror after the charge, repeat it: it completes the request and charges nothing more.\nA second request for the same video made while the first is still being processed\nanswers `409`; retry it.\n\nRequires the same access as the video's other charged analyses (the owner or a member\nallowed to write) and, for a delegated app, the `media.write` scope.","operationId":"request_video_moments_api_v2_videos__video_id__moments_request_post","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique video ID of the video to request moments for","title":"Video Id"},"description":"The unique video ID of the video to request moments for"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoMomentsResponse"}}}},"401":{"description":"Missing, malformed or rejected credentials — no `X-API-Key` / `Authorization: Bearer <jwt>`, an expired token, or a caller that lacks the role the route requires. Note that this API answers *authorization* failures with 401 as well as 403 (see the 403 entry where a route declares both).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"No such resource, or it is not visible to this caller. Ownership failures are reported as 404 rather than 403 so the API does not confirm the existence of another account's records.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"402":{"description":"Not enough credits, or a workspace budget refuses the charge. Nothing was charged and Moments was not requested.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"409":{"description":"Either the video has not finished processing, and the body is what `GET /v2/videos/{video_id}/moments` returns for it (`status: unavailable`); or a request for the same video is already being processed, and the body is `{\"detail\": ...}`: retry it.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/VideoMomentsResponse"},{"$ref":"#/components/schemas/ErrorDetail"}]}}}},"503":{"description":"The credits or budgets that would pay for the request could not be checked. Nothing was charged; retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.write"]}]}},"/v2/videos/{video_id}/moments":{"get":{"tags":["Video Emotion API"],"summary":"Get Video Moments Api","description":"Notable moments in a processed video, per face or speaker.\n\nSame authorization as other video GETs (owner, admin, shared, or sample).\n\nMoments are computed asynchronously once the video, its audio and its transcript\nhave finished processing, and again after edits that change them (face merges,\nspeaker links, linked people, reprocessing). This call never waits for that:\n\n- `status: ready` returns the moments. With `stale: true` the video changed since\n  they were computed; these are the previous moments and a recomputation is queued.\n- `status: computing` means the first computation is queued or running. Poll again.\n- `status: failed` means the last computation failed for the video as it is now.\n- `status: unavailable` means the video has not finished processing, or Moments has\n  not been requested for it. In the second case `reason` is `not_requested`; request it\n  with `POST /v2/videos/{video_id}/moments/request`. Moments already computed for a\n  video are still returned here, but the AI insights conversation does not use them\n  until Moments has been requested for the video.\n\n`coverage` says, per subject and kind, whether that kind of moment could be looked\nfor. No moments of a kind means none happened only where its coverage is `measured`.\nJoin `moments[].subject` to `subjects[].id` for the face, speaker and linked person.\nNew kinds and coverage reasons may appear at any time; treat unknown values as opaque.\n\n`generation` and `profile_version` say which computation these moments belong to.\nCompare them together with `computed_at`: a recomputation can leave `generation`\nand `profile_version` unchanged while `computed_at` moves. `profile_version` is\nthe same value as `profile.version` when a profile is present.\n\n`visibility` says when each face was on screen, as half-open spans of video time;\ntime outside every span is time the face was not visible. `uninterrupted: false`\nmeans brief absences inside a span were joined over. A reaction whose response\nwindow includes time the face was not visible carries `evidence.partly_seen: true`.\n`visibility` is null when it is not included with these moments: an older video or\nresult, no usable visibility data, or left out to keep the result within its size\nlimit. Null never means fully seen, and a subject missing from it has unknown\nvisibility.","operationId":"get_video_moments_api_v2_videos__video_id__moments_get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"The unique video ID of the video to fetch moments for","title":"Video Id"},"description":"The unique video ID of the video to fetch moments for"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoMomentsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v2/videos/{video_id}/speaker-face-match":{"put":{"tags":["Video Emotion API"],"summary":"Update Speaker Face Match","description":"Manually match detected faces to identified speakers with bidirectional name synchronization.\n\nThis endpoint enables client-driven multimodal person identification by allowing you to manually\nspecify which detected faces (from video analysis) correspond to which identified speakers \n(from audio analysis). Each mapping synchronizes identity information across video and audio \nmodalities while managing personality analysis data.\n\n## Features\n\n- **Face-to-Speaker Mapping**: Links face IDs (from video) to speaker IDs (from audio)\n- **Bidirectional Name Sync**: Automatically synchronizes names between faces and speakers\n- **Duplicate Prevention**: Validates that each face and speaker is mapped only once\n- **Personality Data Cleanup**: Clears existing personality analysis when mappings change\n- **Batch Operations**: Process multiple mappings in a single request\n\n## Name Synchronization\n\nThe `name` field in each mapping supports bidirectional synchronization:\n\n- **Empty/null/whitespace name** (`\"\"`, `null`, or `\"   \"`): adopts the speaker's\n  name only when the face has no name yet or still carries a generated\n  placeholder label. A face that already has a real name keeps it.\n- **Provided name** (e.g., `\"Alice\"`): Updates both the face name AND the speaker name\n  in their respective documents\n\nThis ensures consistency across audio and video data while giving you control over\nwhether to use existing speaker names or provide new ones.\n\n## Request Body\n\n```json\n{\n  \"mappings\": [\n    {\n      \"face_id\": 1,\n      \"speaker_id\": 0,\n      \"name\": \"Alice Johnson\"\n    },\n    {\n      \"face_id\": 2,\n      \"speaker_id\": 1,\n      \"name\": \"\"\n    }\n  ]\n}\n```\n\nEach mapping object contains:\n- `face_id` (int, required): ID of detected face from video analysis\n- `speaker_id` (int, required): ID of identified speaker from audio analysis\n- `name` (string, optional): Person's name for synchronization\n\n## Response\n\nReturns a confirmation response:\n```json\n{\n  \"status\": \"completed\",\n  \"message\": \"Speaker-face matching completed successfully\"\n}\n```\n\n## Prerequisites\n\n- Video must be in \"completed\" status\n- Video must have associated audio document (multimodal analysis)\n- Face IDs must exist in the video's face detections\n- Speaker IDs must exist in the audio's speaker identifications\n\n## Use Cases\n\n1. **Manual Initial Matching**: Manually map speakers to faces after video processing completes\n2. **Name Updates**: Update names while preserving analysis data\n3. **Correction of Auto-Matching**: Override or correct automatic face-to-speaker associations\n4. **Custom Mappings**: Specify mappings based on client-side knowledge or user input\n5. **Prepare for Personality Analysis**: Define mappings before triggering personality analysis\n6. **Batch Processing**: Map multiple face-speaker pairs in a single API call\n\n## Validation Rules\n\n- ✅ Each `face_id` must appear only once in the request (no duplicate faces)\n- ✅ Each `speaker_id` must appear only once in the request (no duplicate speakers)\n- ✅ All `face_id` values must exist in the video document\n- ✅ All `speaker_id` values must exist in the audio document\n- ✅ User must own the video (admin users can access any video)\n\n## Important Notes\n\n- **Manual Operation**: This endpoint requires the client to explicitly provide all \n  face-to-speaker mappings. For automatic AI-driven matching, use the POST endpoint.\n- **Personality Data**: When mappings are created or updated, any existing\n  personality analysis data for those faces is cleared and must be re-analyzed\n- **Audio Document Updates**: If you provide names, the audio document's speaker\n  names are automatically updated to match\n- **Idempotent**: Calling this endpoint multiple times with the same mappings\n  will update the entities (not create duplicates)\n\n## Example Scenarios\n\n### Scenario 1: Create New Mappings\n```json\nPUT /v2/videos/abc123/speaker-face-match\n{\n  \"mappings\": [\n    {\"face_id\": 1, \"speaker_id\": 0, \"name\": \"Alice\"},\n    {\"face_id\": 2, \"speaker_id\": 1, \"name\": \"Bob\"}\n  ]\n}\n```\n\n### Scenario 2: Use Existing Speaker Names\n```json\nPUT /v2/videos/abc123/speaker-face-match\n{\n  \"mappings\": [\n    {\"face_id\": 1, \"speaker_id\": 0, \"name\": \"\"},\n    {\"face_id\": 2, \"speaker_id\": 1, \"name\": \"\"}\n  ]\n}\n```\nNames will be fetched from the audio document's speaker records for faces that\nstill have a placeholder name. A face that already has a real name keeps it.\n\n### Scenario 3: Update Names\n```json\nPUT /v2/videos/abc123/speaker-face-match\n{\n  \"mappings\": [\n    {\"face_id\": 1, \"speaker_id\": 0, \"name\": \"Alice Johnson\"}\n  ]\n}\n```\nUpdates both the face name and speaker name to \"Alice Johnson\".\n\n## Error Responses\n\n- **400 Bad Request**: Empty mappings, duplicate face/speaker IDs, or missing audio ID\n- **401 Unauthorized**: Missing authentication, or user doesn't own the video\n- **404 Not Found**: Face ID or speaker ID not found in respective documents\n- **422 Unprocessable Entity**: Video processing not completed","operationId":"update_speaker_face_match_v2_videos__video_id__speaker_face_match_put","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the video. Video must be in 'completed' status with associated audio analysis.","title":"Video Id"},"description":"Unique identifier of the video. Video must be in 'completed' status with associated audio analysis."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SpeakerFaceMatchRequest"},"description":"List of face-to-speaker mappings. Each mapping contains: face_id (detected face from video), speaker_id (identified speaker from audio), and optional name. Name behavior: empty/null/whitespace adopts the speaker's name only for a placeholder face name; a real face name is kept. A provided value updates both face and speaker. Validates against duplicate face_id and speaker_id values.","title":"Mappings"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpeakerFaceMatchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Video Emotion API"],"summary":"Trigger Speaker Face Match","description":"Trigger automatic AI-driven speaker-to-face matching for a video.\n\nThis endpoint first runs a deterministic name-channel pre-pass that joins\nface names (typically OCR-read face-tile names, but any real name already\npresent on an unmatched face) against provider speaker names (exact,\nfuzzy, and truncated-prefix tiers). If every **non-synthetic** speaker\n(screen-share/system tracks excluded) ends up decisively linked, the match\nis marked complete immediately and the lip-sync worker is skipped. A\ngeneric diarization label (e.g. `\"Speaker 3\"`) still counts as a\nnon-synthetic speaker that must be linked — the pre-pass cannot name-link\nit, so it forces the worker path. Otherwise, any decisive links found are\napplied and this endpoint falls back to the asynchronous AI worker as\nbefore, which may still refine or overwrite them.\n\n## Request Body (optional)\n\n- `target_speaker_ids`: when omitted (or the request has no body at all),\n  behavior is unchanged — every speaker is cleared and eligible for\n  re-derivation, exactly as before this parameter existed. When provided\n  as a non-empty list of speaker IDs, only those speakers are cleared and\n  re-derived; every other speaker's bound face is left untouched, and no\n  face currently held by a non-target (\"protected\") speaker can be stolen\n  by a target speaker's resolved match — that guarantee holds for both\n  this endpoint's synchronous pre-pass and the asynchronous worker's\n  later callback. A speaker with no bound face can be targeted.\n\n## Prerequisites\n\n- User must be the video owner or an admin\n- Video must be in 'completed' status\n- Audio must be in 'completed' status\n- Video must have an associated audio_id\n\n## Response\n\n```json\n{\n  \"video_id\": \"abc123\",\n  \"audio_id\": \"xyz789\",\n  \"status\": \"processing\",\n  \"message\": \"Speaker-face match worker triggered successfully\"\n}\n```\n\n`status` is `\"completed\"` instead of `\"processing\"` when the name-channel\npre-pass alone resolved every non-synthetic speaker (or, for a scoped\nrequest, every targeted speaker) and the worker was skipped.\n\nCompleted audio with no eligible speakers returns `completed` and `reason: \"no_speakers\"`\nimmediately, leaving bindings and personality unchanged. A synthetic-only requested scope\nalso completes immediately; the reason is omitted if other eligible speakers exist.\nPending transcript replacement returns 409.\n\n## Error Responses\n\n- **401 Unauthorized**: User is not authenticated or not authorized to access this video\n- **404 Not Found**: Video not found\n- **409 Conflict**: Video or audio processing not completed, or a suggestions run is in flight\n- **400 Bad Request**: Audio ID missing in video document, `target_speaker_ids` is an\n  empty list, or `target_speaker_ids` contains a speaker ID not present in the audio\n- **422 Unprocessable Entity**: Request body includes the removed `target_face_ids` field","operationId":"trigger_speaker_face_match_v2_videos__video_id__speaker_face_match_post","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the video. Video must be in 'completed' status with associated audio.","title":"Video Id"},"description":"Unique identifier of the video. Video must be in 'completed' status with associated audio."}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoSpeakerFaceMatchTriggerRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoSpeakerFaceMatchTriggerResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/videos/{video_id}/speaker-face-match/suggestions":{"post":{"tags":["Video Emotion API"],"summary":"Trigger Speaker Face Match Suggestions","description":"Trigger automatic speaker-to-face matching in **suggestions** mode.\n\nSame body, auth, and prerequisites as `POST .../speaker-face-match`, but\ndoes **not** write `face.speaker_id`, `face.accuracy`, or\n`face.personality_traits`. The worker (or a fully-resolved name-channel\npre-pass) stores an advisory block on `video.speaker_face_match.suggestions`\nthat the client commits verbatim through the existing PUT.\n\nPoll `GET .../statuses?speaker_face_match_status=true`. The stored in-flight\nvalue is `\"queue\"` (the trigger **response** `status` is `\"processing\"`);\na finished suggest run writes `\"completed\"` or `\"failed\"`. `GET\n.../speaker-face-match/suggestions` returns 404 when no block exists. Stop polling\nwhen the statuses `states.speaker_face_match_status.terminal` flag is true, including\n`failed` with `speaker_face_match_reason: \"timed_out\"`.\n\nNo eligible speakers in the requested scope completes immediately with `items: []`\nand leaves bindings and personality unchanged. `reason: \"no_speakers\"` is included\nonly when the whole audio has no eligible speakers. Pending transcript replacement returns 409.\n\nReturns **409** if a non-stale run is already in flight (`status==\"queue\"`\nand `queued_at` within 15 minutes). `queued_at` is stamped only on\nsuggest-mode triggers. A historical or commit-mode `\"queue\"` with no\n`queued_at` (commit-mode write-back never terminals that field) is treated\nas stale and does not 409.\n\nA commit-mode POST issued while a suggest run is in flight also 409s, so\nan impatient click cannot convert the in-flight preview into a personality-\nwiping commit. After the stale window that 409 lifts, a late suggest-era\nworker `PUT .../matched_speakers` can still arrive. If a commit POST has\nalready cleared `mode` (and possibly personality), that late PUT takes\nthe **commit** branch and mutates `faces[]` with the old preview's matches.\nThere is no run-id on the worker contract to ignore it.\n\n## Error Responses\n\n- **401 Unauthorized**: User is not authenticated or not authorized to access this video\n- **404 Not Found**: Video not found\n- **409 Conflict**: Video or audio processing not completed, or a match run is already in flight\n- **400 Bad Request**: Audio ID missing in video document, `target_speaker_ids` is an\n  empty list, or `target_speaker_ids` contains a speaker ID not present in the audio\n- **422 Unprocessable Entity**: Request body includes the removed `target_face_ids` field","operationId":"trigger_speaker_face_match_suggestions_v2_videos__video_id__speaker_face_match_suggestions_post","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the video. Video must be in 'completed' status with associated audio.","title":"Video Id"},"description":"Unique identifier of the video. Video must be in 'completed' status with associated audio."}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoSpeakerFaceMatchTriggerRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoSpeakerFaceMatchTriggerResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Video Emotion API"],"summary":"Get Speaker Face Match Suggestions Route","description":"Return the stored speaker-face match suggestions for a video.\n\nOne item per in-scope speaker when eligible speakers were matched. Unresolved speakers have `face_id` and\n`accuracy` null and a `reasoning` explaining why. Never includes the\ninternal `match_evidence` dict.\n\n404 when no suggestions block exists (no run yet, in-flight after a\ntrigger cleared the previous block, or after face/speaker add/delete/merge\ninvalidation). A completed run that resolved nothing still 200s with\nall-null rows. If the requested scope has no eligible speakers, returns `items: []`\ninstead, with `reason: \"no_speakers\"` when the whole audio has no eligible speakers.\nA timed-out run can still have no block; use the statuses terminal flag to stop polling.\n\nAuth matches the trigger POST: owner or admin only.\n\n## Error Responses\n\n- **401 Unauthorized**: User is not authenticated or not authorized to access this video\n- **404 Not Found**: Video not found, or no suggestions block is stored","operationId":"get_speaker_face_match_suggestions_route_v2_videos__video_id__speaker_face_match_suggestions_get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the video.","title":"Video Id"},"description":"Unique identifier of the video."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpeakerFaceMatchSuggestionsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/videos/{video_id}/personality/analyze":{"post":{"tags":["Video Emotion API"],"summary":"Trigger Personality Analysis","description":"Trigger personality analysis for a specific face in a video.\n\nInitiates asynchronous personality trait assessment based on facial expressions and,\nwhen available, vocal characteristics from linked audio speaker data.\n\n## Analysis Types\n\n- **overall**: Analyzes personality traits across the entire video\n- **by_segment**: Analyzes personality traits by time segments\n- **combined**: Performs both overall and segment analysis\n\nOverall and segment analyses are independent - triggering one does not affect the other.\n\n## Multimodal Analysis\n\n- **Face only**: Analyzes facial expressions when no speaker is linked\n- **Face + Audio**: Combines facial and vocal characteristics when face is linked to a speaker\n\n## Prerequisites\n\n- Video must be completed\n- Face ID must exist in the video\n- If face has speaker mapping: audio must be completed\n- User must have sufficient credits\n\n## Request\n\n```json\n{\n  \"face_id\": 1,\n  \"analysis_type\": \"overall\",\n  \"callback_url\": \"https://example.com/webhook\"\n}\n```\n\n## Response\n\n```json\n{\n  \"video_id\": \"abc123\",\n  \"face_id\": 1,\n  \"speaker_id\": 2,\n  \"analysis_type\": \"overall\",\n  \"status\": \"processing\",\n  \"message\": \"Personality analysis triggered successfully\"\n}\n```\n\n## Credit Usage\n\nCredits are billed per minute of video duration based on `analysis_type`.\n\n| analysis_type | Personal | Enterprise |\n|---|---|---|\n| overall | 30 credits/min | 15 credits/min |\n| by_segment | 50 credits/min | 25 credits/min |\n| combined | 60 credits/min | 30 credits/min |\n\n## Notes\n\n- Analysis runs asynchronously in the background\n- Use callback_url for webhook notifications when complete\n- Credits are deducted when analysis is triggered\n- Retrieve results using the video analytics endpoints","operationId":"trigger_personality_analysis_v2_videos__video_id__personality_analyze_post","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the video. Video must be in 'completed' status with detected faces.","title":"Video Id"},"description":"Unique identifier of the video. Video must be in 'completed' status with detected faces."}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_trigger_personality_analysis_v2_videos__video_id__personality_analyze_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PersonalityTriggerResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.write"]}]}},"/v2/videos/{video_id}/faces/{face_id}/personality-analysis":{"get":{"tags":["Video Emotion API"],"summary":"Get Personality Analysis","description":"Retrieve OCEAN personality analysis results for a specific face in a video.\n\nThis endpoint provides comprehensive Big Five (OCEAN) personality trait analysis for a specific\nface detected in a video. It retrieves both processing status and completed analysis data from\ndual data sources, returning conditional data based on completion status.","operationId":"get_personality_analysis_v2_videos__video_id__faces__face_id__personality_analysis_get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the video containing the face.","title":"Video Id"},"description":"Unique identifier of the video containing the face."},{"name":"face_id","in":"path","required":true,"schema":{"type":"integer","minimum":1,"description":"Face identifier for which to retrieve personality analysis.","title":"Face Id"},"description":"Face identifier for which to retrieve personality analysis."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PersonalityAnalysisResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v2/audios/{audio_id}":{"get":{"tags":["Audio Emotion API"],"summary":"Get Audio Details","description":"Retrieve all details for a specific audio directly from the database.\n\nReturns a complete standardized response with all possible fields.\nFields that don't exist in the database are returned as null.\n\n**Access Control:**\n- Authenticated users can access their own audios\n- Admins can access all audios\n- Sample audios and shared audios are publicly accessible","operationId":"get_audio_details_v2_audios__audio_id__get","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the audio.","title":"Audio Id"},"description":"The unique ID of the audio."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudioDetailResponseV2"}}}},"401":{"description":"Missing, malformed or rejected credentials — no `X-API-Key` / `Authorization: Bearer <jwt>`, an expired token, or a caller that lacks the role the route requires. Note that this API answers *authorization* failures with 401 as well as 403 (see the 403 entry where a route declares both).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"No such resource, or it is not visible to this caller. Ownership failures are reported as 404 rather than 403 so the API does not confirm the existence of another account's records.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]},"delete":{"tags":["Audio Emotion API"],"summary":"Delete Audio V2","description":"Permanently delete an audio, its stored media and its analyses. This action is irreversible.\n\nOnly the audio's owner can delete it. Deletion completes asynchronously: the audio\ndisappears from listings immediately, and its data is permanently removed shortly\nafter. The response status is `deleting`, and repeating the request while it is in\nprogress returns the same response.","operationId":"delete_audio_v2_v2_audios__audio_id__delete","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the audio to delete.","title":"Audio Id"},"description":"The unique ID of the audio to delete."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudioDeleteResponse"}}}},"401":{"description":"Missing, malformed or rejected credentials — no `X-API-Key` / `Authorization: Bearer <jwt>`, an expired token, or a caller that lacks the role the route requires. Note that this API answers *authorization* failures with 401 as well as 403 (see the 403 entry where a route declares both).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"No such resource, or it is not visible to this caller. Ownership failures are reported as 404 rather than 403 so the API does not confirm the existence of another account's records.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.delete"]}]}},"/v2/audios/{audio_id}/playback-url":{"get":{"tags":["Audio Emotion API"],"summary":"Get Audio Playback Url Route","description":"Get a short-lived URL for playing back an audio.\n\nThe returned `url` streams the stored audio inline in a browser or media player.\nIt is short-lived: request a new one when `expires_at` passes or when a request\nmade with it is refused, and do not store or share it. The audio's `url` and\n`media_source_url` fields are references to the stored media, not guaranteed to\nbe readable; this endpoint is how to play it.\n\n**Access Control:** the same as the audio details. The owner and admins can\nalways get a playback URL. While an audio (or the video it belongs to) is shared,\nor is a sample, anyone can, so sharing lets anyone with its link play it back.\n\n**Errors:**\n- **401**: not authorized to access this audio\n- **404**: audio not found\n- **409** `media_not_stored`: the audio has no stored copy to play (for example, an\n  audio submitted by URL whose copy is not ready yet)\n- **503** `media_storage_unavailable`: temporarily unavailable; try again shortly","operationId":"get_audio_playback_url_route_v2_audios__audio_id__playback_url_get","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the audio.","title":"Audio Id"},"description":"The unique ID of the audio."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackUrlResponse"}}}},"401":{"description":"Missing, malformed or rejected credentials — no `X-API-Key` / `Authorization: Bearer <jwt>`, an expired token, or a caller that lacks the role the route requires. Note that this API answers *authorization* failures with 401 as well as 403 (see the 403 entry where a route declares both).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"No such resource, or it is not visible to this caller. Ownership failures are reported as 404 rather than 403 so the API does not confirm the existence of another account's records.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"409":{"description":"`media_not_stored`: there is no stored copy to play yet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"503":{"description":"`media_storage_unavailable`: try again shortly.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v2/audios/{audio_id}/transcript":{"put":{"tags":["Audio Emotion API"],"summary":"Put Audio Provider Transcript","description":"Attach a transcript produced elsewhere (for example by a meeting provider) to an audio.\n\nThe transcript is stored beside the audio's own transcript and does not replace it: the\nanalysis, speakers and every other result keep using the audio's own transcript, and the\nattached one is read back with `GET /v2/audios/{audio_id}/transcript?transcript=provider`.\nSpeaker display names sent in `speakers` are stored with the attached transcript only; they\ndo not rename speakers on the audio.\n\nOnly one transcript can be attached per audio. Sending again replaces it, whatever its\n`source`. Sending the same content again changes nothing and returns the original\n`attached_at`, so a request can be retried safely. `replaced` is true only when this request\nreplaced a different transcript that was already attached.\n\n`start_ms` and `end_ms` are on the audio's own timeline; no offset is applied. Segments are\nstored and returned in `start_ms` order. At least one segment is required.\n\nThe request body is limited to 1 MiB, the transcript to 10,000 segments, and the stored\ncontent (segments and speaker names) to 640 KiB; over any limit the response is 413 and\nnothing is stored. The audio's owner and administrators can attach a transcript. No charge is\napplied.","operationId":"put_audio_provider_transcript_v2_audios__audio_id__transcript_put","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the audio.","title":"Audio Id"},"description":"The unique ID of the audio."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderTranscriptRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderTranscriptPutResponse"}}}},"401":{"description":"Missing, malformed or rejected credentials — no `X-API-Key` / `Authorization: Bearer <jwt>`, an expired token, or a caller that lacks the role the route requires. Note that this API answers *authorization* failures with 401 as well as 403 (see the 403 entry where a route declares both).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"No such resource, or it is not visible to this caller. Ownership failures are reported as 404 rather than 403 so the API does not confirm the existence of another account's records.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Request validation failed. The app flattens FastAPI's validation error list into a single `detail` string, so the body is `{\"detail\": \"'field' <message>\"}` and NOT FastAPI's default `HTTPValidationError`. Declaring this overrides the auto-generated 422 — see the module docstring.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"413":{"description":"`provider_transcript_too_large`: the request body, the number of segments or the stored transcript content is over the limit. Nothing was stored.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.write"]}]},"get":{"tags":["Audio Emotion API"],"summary":"Get Audio Transcript","description":"Read an audio's own transcript, or the transcript attached to it.\n\n`transcript=machine` (the default) returns the audio's own transcript with its speaker labels;\nit is empty until that transcript exists. `transcript=provider` returns what was attached\nwith `PUT`, its `source`, its speaker display names and `attached_at`, or 404 when nothing\nis attached. The response names which transcript it holds in `transcript`.\n\nThe audio's owner and administrators can read it; a shared link does not grant access to a\ntranscript.","operationId":"get_audio_transcript_v2_audios__audio_id__transcript_get","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the audio.","title":"Audio Id"},"description":"The unique ID of the audio."},{"name":"transcript","in":"query","required":false,"schema":{"enum":["machine","provider"],"type":"string","description":"`machine` (default) is the audio's own transcript; `provider` is the one attached with `PUT`.","default":"machine","title":"Transcript"},"description":"`machine` (default) is the audio's own transcript; `provider` is the one attached with `PUT`."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptResponse"}}}},"401":{"description":"Missing, malformed or rejected credentials — no `X-API-Key` / `Authorization: Bearer <jwt>`, an expired token, or a caller that lacks the role the route requires. Note that this API answers *authorization* failures with 401 as well as 403 (see the 403 entry where a route declares both).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"No such resource, or it is not visible to this caller. Ownership failures are reported as 404 rather than 403 so the API does not confirm the existence of another account's records.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Request validation failed. The app flattens FastAPI's validation error list into a single `detail` string, so the body is `{\"detail\": \"'field' <message>\"}` and NOT FastAPI's default `HTTPValidationError`. Declaring this overrides the auto-generated 422 — see the module docstring.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v2/audios/{audio_id}/speaker-name-inference":{"post":{"tags":["Audio Emotion API"],"summary":"Trigger Speaker Name Inference Route","description":"Trigger a manual, credit-billed, speaker-scoped re-run of transcript-based\nspeaker-name inference for an audio.\n\nThe name signal itself is face-independent: a quote-grounded model pass over the\ndiarized transcript that proposes a name per targeted speaker from self-intros\n(\"Hi, I'm Mark\") or third-person mentions (\"Thanks, Priya\"), each verified\ndeterministically against the real transcript text before being persisted onto\nthe speaker row and transcript sentences. For audio that belongs to a completed\nvideo, a face already matched to that `speaker_id` is updated only when it does\nnot already carry an OCR-derived name. Interactive rename and a suggestion\ncommit via `PUT /v1/audios/{audio_id}/speakers/names` may overwrite an OCR-derived\nface name; this persist path does not.\n\nThis endpoint mirrors the automatic, free, one-shot webhook hook, but without\neither of its restrictions: it does not require the audio to be freshly unnamed\n(targeting an already-named speaker is a legitimate, billed re-run — e.g. the name\nwas wrong, or wiped), and it never touches a speaker outside\n`target_speaker_ids`, named or not.\n\nThis call is synchronous: it runs in-request (bounded by an internal timeout) and\nreturns the final result rather than a processing/queued status, unlike\n`speaker-face-match`. A caller must be prepared to hold the connection open for\nthe duration of the model call.\n\n## Request Body (required)\n\n- `target_speaker_ids`: required, non-empty list of speaker ids to (re-)name. Every\n  speaker not in this list is left byte-for-byte untouched, even if the model's\n  transcript reasoning would otherwise have proposed something for them.\n- `infer_roles`: optional, default false. When true, the same model pass also\n  proposes `agent` or `customer` per targeted speaker, grounded in a quote from\n  that speaker's own words. Independent of the name verdict. Roles are never\n  written; commit them via `PUT /v1/audios/{audio_id}/speakers/roles`. Targeting\n  a speaker for a role also targets them for a rename; callers who want a role\n  proposal without a possible rename should use\n  `.../speaker-name-inference/suggestions`. A speaker whose stored role is\n  already `agent` or `customer` is not offered a new role (`role_already_set`);\n  clear the role and re-run to get a fresh proposal. Omitted and false are the\n  same request.\n\n## Prerequisites\n\n- User must be the audio owner or hold `MEDIA_WRITE`\n- Audio must be in 'completed' status. If the audio has a parent video, that video\n  must also be completed (a paid name can otherwise be overwritten with\n  `Face {id}` when speaker-face matching writes back)\n- The audio must be eligible for transcript-based name inference: not a\n  meeting-platform ingest, no provider-supplied transcript/diarization, and no\n  `speech_segments` pointer. A supplied or replaced transcript\n  (`PUT /v1/audios/{audio_id}/transcript/replace`, or a URL+transcript upload)\n  writes that pointer permanently, so Identify-people Suggest-names returns 400\n  for those items rather than running. The transcript itself must already be\n  available\n- Caller must have a positive credit balance. This call is billed on success;\n  the amount is not quoted here. A 402 is returned before the model runs when\n  admission is refused\n\n## Response\n\nReturns at least one result row per requested `target_speaker_id`, each either an\naccepted rename (`name`, `quote`, `pattern`) or a rejection (`reason`) — including a\nrow for any targeted speaker the model could not resolve. Duplicate model rows for\nthe same id, if any, are not collapsed. When `infer_roles` is true, each row also\ncarries an independent role verdict (`role_accepted`, and on accept `role` plus\n`role_quote`). Roles are not mutually exclusive across speakers. `video_id` is set\nwhen this audio has a parent video; it is `null` for audio-only media.\n\n## Error Responses\n\n- **401 Unauthorized**: User is not authenticated or not authorized to access this audio\n- **404 Not Found**: Audio not found\n- **409 Conflict**: Audio processing not completed, or parent video not completed\n- **400 Bad Request**: Transcript not ready, `target_speaker_ids` is empty, contains\n  a speaker ID not present in the audio or naming a synthetic track, the audio (or\n  its parent video) is not eligible for this feature, or `video_id` points at a\n  missing video\n- **402 Payment Required**: Admission refused. Detail is the admission\n  `refusal_message`. The wording differs for a zero vs. negative balance vs.\n  a window refusal (buying credits does not fix the latter)\n- **422 Unprocessable Entity**: `target_speaker_ids` is missing/null, or contains a\n  non-integer entry; or `infer_roles` is null or not a boolean\n- **502 Bad Gateway**: The model call failed or returned an unparseable response.\n  No charge is applied","operationId":"trigger_speaker_name_inference_route_v2_audios__audio_id__speaker_name_inference_post","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the audio. Audio must be in 'completed' status.","title":"Audio Id"},"description":"Unique identifier of the audio. Audio must be in 'completed' status."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpeakerNameInferenceRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpeakerNameInferenceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/audios/{audio_id}/speaker-name-inference/suggestions":{"post":{"tags":["Audio Emotion API"],"summary":"Trigger Speaker Name Inference Suggestions Route","description":"Trigger speaker-name inference in **suggestions** mode: same model pass and\nevidence grounding as `POST .../speaker-name-inference`, same result rows, writes\n**nothing**.\n\n`audios.speakers[].name` is left byte-identical. No `manual_name_inference_runs`\naudit append and no Deal-Person linking fire. The client reviews `results` and\ncommits accepted names through existing rename endpoints that do **not** charge:\n\n- `PUT /v1/audios/{audio_id}/speakers/names` — names only, including speakers with\n  no matched face. Unlike the committing inference POST, a commit here may\n  overwrite an OCR-derived face name on a completed parent video. Unknown\n  `speaker_id`s are skipped with a 200; there is no `audio.status == \"completed\"`\n  check and no parent-video check. A suggestion committed after a speaker\n  merge/delete can silently no-op. A name committed while the parent video is\n  still processing can be overwritten with `Face {id}` when speaker-face\n  matching writes back. This is the primary commit path for both audio-only\n  media and video-backed audio.\n- `PUT /v2/videos/{video_id}/speaker-face-match` (`name` field; requires `face_id`)\n  — updates both face and speaker. Only available when this audio has a parent\n  video. Speakers with no matched face are out of scope. Unknown `speaker_id` /\n  `face_id` raise `InvalidSpeaker` / `InvalidFace`; the video must be\n  `completed`. A stale id is a 4xx, not a silent skip.\n\nNeither PUT re-runs this endpoint's validation (no synthetic-track refusal, no\nduplicate-name guard). They also do **not** restore the Deal-Person linking or\n`manual_name_inference_runs` stamp that the committing POST performs\n(follow-up: #1375). Identify-people Save via this route then a PUT is a smaller\nfeature than the committing POST.\n\nDuplicate-name grounding is per call against **persisted** speaker names. Because\nthis path writes nothing, two sequential per-speaker suggestion calls can both\naccept the same name (e.g. `\"Alice\"`), and neither PUT dedupes.\n\nEvery successful call is billed. The amount is not quoted on the request; it is\nrecorded after the run. A 502 (model failure or unparseable response) is not\nbilled. The call is not idempotent: a double click is two model calls and two\ncharges. The client must disable the control while in flight and must not blindly\nretry a gateway timeout — the request may still complete and charge after the\nclient has given up.\n\nBody and auth match the sibling POST. Audio must be completed; unlike the\ncommitting POST, an in-flight parent is not a 409. Synchronous: holds the\nconnection for the model call. An all-rejected result is a successful billed run.\n\n## Request Body (required)\n\n- `target_speaker_ids`: required, non-empty list of speaker ids to (re-)name.\n- `infer_roles`: optional, default false. When true, the same model pass also\n  proposes `agent` or `customer` per targeted speaker, grounded in a quote from\n  that speaker's own words. Independent of the name verdict. Roles are never\n  written; commit them via `PUT /v1/audios/{audio_id}/speakers/roles`. A speaker\n  whose stored role is already `agent` or `customer` is not offered a new role\n  (`role_already_set`); clear the role and re-run to get a fresh proposal.\n  Omitted and false are the same request. This is the path for a role proposal\n  without a possible rename.\n\n## Prerequisites\n\n- User must be the audio owner or hold `MEDIA_WRITE`\n- Audio must be in 'completed' status. An in-flight parent video does not\n  409 this path: this call writes nothing. Committing a suggestion via PUT\n  while the parent is still processing is a documented race (see the\n  commit-path bullet above)\n- The audio must be eligible for transcript-based name inference (non-meeting\n  upload with no provider-supplied transcript/diarization and no `speech_segments`\n  pointer) and its transcript must already be available. A supplied or replaced\n  transcript is a permanent 400, not a retryable state\n- Caller must have a positive credit balance. This call is billed on success;\n  the amount is not quoted here. A 402 is returned before the model runs when\n  admission is refused\n\n## Response\n\nReturns at least one result row per requested `target_speaker_id`, each either an\naccepted proposal (`name`, `quote`, `pattern`) or a rejection (`reason`) — including\na row for any targeted speaker the model could not resolve. Duplicate model rows for\nthe same id, if any, are not collapsed. When `infer_roles` is true, each row also\ncarries an independent role verdict (`role_accepted`, and on accept `role` plus\n`role_quote`). Roles are not mutually exclusive across speakers. `video_id` is set\nwhen this audio has a parent video; it is `null` for audio-only media.\n\n## Error Responses\n\n- **401 Unauthorized**: User is not authenticated or not authorized to access this audio\n- **404 Not Found**: Audio not found\n- **409 Conflict**: Audio processing not completed\n- **400 Bad Request**: Transcript not ready, `target_speaker_ids` is empty, contains\n  a speaker ID not present in the audio or naming a synthetic track, the audio (or\n  its parent video) is not eligible for this feature, or `video_id` points at a\n  missing video\n- **402 Payment Required**: Admission refused. Detail is the admission\n  `refusal_message`. The wording differs for a zero vs. negative balance vs.\n  a window refusal (buying credits does not fix the latter)\n- **422 Unprocessable Entity**: `target_speaker_ids` is missing/null, or contains a\n  non-integer entry; or `infer_roles` is null or not a boolean\n- **502 Bad Gateway**: The model call failed or returned an unparseable response.\n  No charge is applied","operationId":"trigger_speaker_name_inference_suggestions_route_v2_audios__audio_id__speaker_name_inference_suggestions_post","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the audio. Audio must be in 'completed' status.","title":"Audio Id"},"description":"Unique identifier of the audio. Audio must be in 'completed' status."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpeakerNameInferenceRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpeakerNameInferenceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Audio Emotion API"],"summary":"Get Speaker Name Inference Suggestions Route","description":"Return the stored speaker-name inference suggestions for an audio.\n\nPopulated automatically after a contact-center recording finishes\nprocessing, or after the recording is tagged as a contact-center call.\nRow shape matches `POST .../speaker-name-inference/suggestions`. No charge\nis applied for this stored block.\n\n404 when no suggestions block is stored, when the speaker set has changed\nsince it was generated, when an accepted name has been committed (that\nwrite clears the block; nothing re-runs it except tagging the audio as a\ncontact-center call again), or when the audio is not a contact-center\ncall. An all-rejected completed run still 200s. Committing a role does\nnot update this proposal.\n\nAuth matches the trigger POST: owner or a caller holding `media:write`.\nThis is not a share-link surface.\n\n## Error Responses\n\n- **401 Unauthorized**: User is not authenticated or not authorized to access this audio\n- **404 Not Found**: Audio not found, no suggestions block is stored, the\n  speaker set changed since generation, an accepted name was committed, or\n  the audio is not a contact-center call","operationId":"get_speaker_name_inference_suggestions_route_v2_audios__audio_id__speaker_name_inference_suggestions_get","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"Unique identifier of the audio.","title":"Audio Id"},"description":"Unique identifier of the audio."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpeakerNameInferenceStoredSuggestionsResponse"}}}},"401":{"description":"Missing, malformed or rejected credentials — no `X-API-Key` / `Authorization: Bearer <jwt>`, an expired token, or a caller that lacks the role the route requires. Note that this API answers *authorization* failures with 401 as well as 403 (see the 403 entry where a route declares both).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"No such resource, or it is not visible to this caller. Ownership failures are reported as 404 rather than 403 so the API does not confirm the existence of another account's records.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/audios/{audio_id}/translations":{"get":{"tags":["Audio Emotion API"],"summary":"List Audio Translations","description":"List the stored translations of an audio's transcript.\n\nThe audio's owner sees every translation in every status, with its charge fields. Anyone\nelse who can see the audio (it is shared, or a sample) sees `ready` translations only, without\n`error_code`, charge fields or `retry_kind`. `source_revision` is the revision a new\ntranslation would accept (send it when requesting one), or null when a new translation could\nnot start (no usable transcript, sample media, audio not yet `completed`, all-blank or\nover-limit transcript, or a non-positive estimate).","operationId":"list_audio_translations_v2_audios__audio_id__translations_get","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the audio.","title":"Audio Id"},"description":"The unique ID of the audio."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranslationIndexOut"}}}},"401":{"description":"Missing, malformed or rejected credentials — no `X-API-Key` / `Authorization: Bearer <jwt>`, an expired token, or a caller that lacks the role the route requires. Note that this API answers *authorization* failures with 401 as well as 403 (see the 403 entry where a route declares both).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"No such resource, or it is not visible to this caller. Ownership failures are reported as 404 rather than 403 so the API does not confirm the existence of another account's records.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Request validation failed. The app flattens FastAPI's validation error list into a single `detail` string, so the body is `{\"detail\": \"'field' <message>\"}` and NOT FastAPI's default `HTTPValidationError`. Declaring this overrides the auto-generated 422 — see the module docstring.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v2/audios/{audio_id}/translations/{language}":{"get":{"tags":["Audio Emotion API"],"summary":"Get Audio Translation","description":"Read one stored translation.\n\nAccess is the same as for the list. A translation that is not `ready` is not found for a\ncaller who is not the audio's owner.","operationId":"get_audio_translation_v2_audios__audio_id__translations__language__get","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the audio.","title":"Audio Id"},"description":"The unique ID of the audio."},{"name":"language","in":"path","required":true,"schema":{"type":"string","description":"The target language as a BCP-47 tag, for example `es` or `pt-BR`.","title":"Language"},"description":"The target language as a BCP-47 tag, for example `es` or `pt-BR`."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranslationOut"}}}},"401":{"description":"Missing, malformed or rejected credentials — no `X-API-Key` / `Authorization: Bearer <jwt>`, an expired token, or a caller that lacks the role the route requires. Note that this API answers *authorization* failures with 401 as well as 403 (see the 403 entry where a route declares both).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"404":{"description":"No such resource, or it is not visible to this caller. Ownership failures are reported as 404 rather than 403 so the API does not confirm the existence of another account's records.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}},"422":{"description":"Request validation failed. The app flattens FastAPI's validation error list into a single `detail` string, so the body is `{\"detail\": \"'field' <message>\"}` and NOT FastAPI's default `HTTPValidationError`. Declaring this overrides the auto-generated 422 — see the module docstring.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDetail"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v2/images/{image_id}":{"get":{"tags":["Image Emotion API"],"summary":"Get Image Details","description":"Retrieve all details for a specific image directly from the database.\n\nReturns a complete standardized response with all possible fields.\nFields that don't exist in the database are returned as null.\n\n**Access Control:**\n- Authenticated users can access their own images\n- Admins can access all images\n- Sample images and shared images are publicly accessible","operationId":"get_image_details_v2_images__image_id__get","parameters":[{"name":"image_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the image.","title":"Image Id"},"description":"The unique ID of the image."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageDetailResponseV2"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v2/{media_type}/{media_id}/statuses":{"get":{"tags":["Media API"],"summary":"Get Media Statuses","description":"Get status fields for a specific media item, and the `states` to wait on.\n\nUse query parameters to select which status fields to include. `media_id`, `media_type` and\nthe raw `status` are always returned. `states` describes each requested lifecycle field in one\nvocabulary; it is `{}` when none was requested.\n\n**Waiting.** Poll this route and stop when every field you care about has `terminal: true`.\n`terminal` is true for `completed`, `failed` and `skipped`. **Treat any state you do not\nrecognise as not finished**: new states may be added. `progress` is a fraction from 0 to 1,\nor null where none is reported. `terminal` describes the current operation only: a `skipped`\nhighlight becomes `pending` once you request one, so trigger before you wait. Use your own\ndeadline as well, and treat a 404 as \"gone\".\n\n**`states` values per field:**\n\n| Field | Possible states | Terminal | `progress` |\n|---|---|---|---|\n| `status` | `pending`, `processing`, `completed`, `failed` | `completed`, `failed` | `1.0` when completed, otherwise the reported percentage, or null |\n| `csv_status`, `pdf_status`, `summary_status` | `pending`, `processing`, `completed`, `failed` (a `processing` CSV or PDF whose worker is gone is reported `pending`) | `completed`, `failed` | `1.0` when completed, otherwise null |\n| `highlight_status` | `pending`, `processing`, `completed`, `failed`, `skipped` (never requested) | `completed`, `failed`, `skipped` | `1.0` when completed, otherwise null |\n| `annotated_video_status` | `pending`, `processing`, `completed`, `failed` | `completed`, `failed` | `1.0` when completed, otherwise the reported percentage, or null |\n| `personality_status` | `pending`, `processing`, `completed`, `failed`, `skipped` (no audio) | `completed`, `failed`, `skipped` | `1.0` when completed, otherwise the reported percentage, or null |\n| `frame_upload_status` | `pending`, `processing`, `completed`, `failed`, `skipped` (frame upload is retired; no value was ever stored) | `completed`, `failed`, `skipped` | `1.0` when completed, otherwise the reported percentage, or null |\n| `psychologist_review_status`, `speaker_face_match_status` | `pending`, `processing`, `completed`, `failed` | `completed`, `failed` | `1.0` when completed, otherwise null |\n| `face_deletion_status`, `speaker_deletion_status` | `pending`, `processing`, `completed`, `failed` | `completed`, `failed` | `1.0` when completed, otherwise null |\n\nSpeaker-face matching includes `speaker_face_match_reason` when applicable: `no_speakers`\nfor empty completion, or `timed_out` for an expired suggestions run. Stop polling when\n`states.speaker_face_match_status.terminal` is true.\n\nA missing or unrecognised stored value is `unknown` and not terminal, except `frame_upload_status`: an absent value is `skipped` (frame upload is retired; see the table and the note below).\n\n**Not in `states`.** `share_status` and `speech_status` are flags rather than progress, and the\npercentage fields (`process_status`, `frame_upload_progress`, `annotated_video_percentage`,\n`personality_processing`) are measurements that feed `progress`. The per-face\n`personality_statuses` list is returned as before; inspect its entries yourself.\n`highlight_status` reports `pending` in the raw field for a highlight that was never\nrequested, while `states.highlight_status` reports `skipped`. Likewise `frame_upload_status`\nreports `pending` in the raw field when no value was ever stored (frame upload is retired, so\nnone will be), while `states.frame_upload_status` reports `skipped`.\n\n**Raw values** (unchanged): `completed`, `processing`, `pending`, `failed`, plus `no audio`\nfor `personality_status`.\n\n**Auto-included Fields:**\n- `annotated_video_status` → also includes `annotated_video_percentage`, `annotated_video_stage` (where the job is)\n  and `annotated_video_error` (`{code, message, retryable}` once it has failed). A job whose worker is gone is reported\n  `failed` here, with a typed error; requesting the annotated video again starts a new one.\n- `personality_status` → also includes `personality_processing`\n- `frame_upload_status` → also includes `frame_upload_progress`\n- `highlight_status` → also includes `highlight_url` and `highlight_segments`\n\n**Available Status Fields:**\n- General: `status`, `process_status`, `csv_status`, `pdf_status`, `summary_status`, `share_status`\n- Video-specific: `highlight_status`, `annotated_video_status`, `personality_status`,\n  `personality_statuses`, `frame_upload_status`, `face_deletion_status`, `speaker_face_match_status`\n- Audio-related: `speech_status`, `speaker_deletion_status`\n- Review: `psychologist_review_status`","operationId":"get_media_statuses_v2__media_type___media_id__statuses_get","parameters":[{"name":"media_type","in":"path","required":true,"schema":{"type":"string","description":"The type of media (video, audio, text, or image)","examples":["video"],"title":"Media Type"},"description":"The type of media (video, audio, text, or image)"},{"name":"media_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the media item","examples":["abc123-def456-789"],"title":"Media Id"},"description":"The unique ID of the media item"},{"name":"status","in":"query","required":false,"schema":{"type":"boolean","description":"Include overall processing status","default":false,"title":"Status"},"description":"Include overall processing status"},{"name":"csv_status","in":"query","required":false,"schema":{"type":"boolean","description":"Include CSV export status","default":false,"title":"Csv Status"},"description":"Include CSV export status"},{"name":"highlight_status","in":"query","required":false,"schema":{"type":"boolean","description":"Include highlight reel status (video only)","default":false,"title":"Highlight Status"},"description":"Include highlight reel status (video only)"},{"name":"pdf_status","in":"query","required":false,"schema":{"type":"boolean","description":"Include PDF report status","default":false,"title":"Pdf Status"},"description":"Include PDF report status"},{"name":"annotated_video_status","in":"query","required":false,"schema":{"type":"boolean","description":"Include annotated video status (video only)","default":false,"title":"Annotated Video Status"},"description":"Include annotated video status (video only)"},{"name":"annotated_video_percentage","in":"query","required":false,"schema":{"type":"boolean","description":"Include annotated video progress percentage (video only)","default":false,"title":"Annotated Video Percentage"},"description":"Include annotated video progress percentage (video only)"},{"name":"personality_status","in":"query","required":false,"schema":{"type":"boolean","description":"Include overall personality analysis status (video only)","default":false,"title":"Personality Status"},"description":"Include overall personality analysis status (video only)"},{"name":"personality_statuses","in":"query","required":false,"schema":{"type":"boolean","description":"Include per-face personality analysis statuses (video only)","default":false,"title":"Personality Statuses"},"description":"Include per-face personality analysis statuses (video only)"},{"name":"speech_status","in":"query","required":false,"schema":{"type":"boolean","description":"Include speech transcription status","default":false,"title":"Speech Status"},"description":"Include speech transcription status"},{"name":"frame_upload_status","in":"query","required":false,"schema":{"type":"boolean","description":"Include frame upload status (video only)","default":false,"title":"Frame Upload Status"},"description":"Include frame upload status (video only)"},{"name":"frame_upload_progress","in":"query","required":false,"schema":{"type":"boolean","description":"Include frame upload progress percentage (video only)","default":false,"title":"Frame Upload Progress"},"description":"Include frame upload progress percentage (video only)"},{"name":"process_status","in":"query","required":false,"schema":{"type":"boolean","description":"Include processing percentage","default":false,"title":"Process Status"},"description":"Include processing percentage"},{"name":"summary_status","in":"query","required":false,"schema":{"type":"boolean","description":"Include summary generation status","default":false,"title":"Summary Status"},"description":"Include summary generation status"},{"name":"psychologist_review_status","in":"query","required":false,"schema":{"type":"boolean","description":"Include psychologist review status","default":false,"title":"Psychologist Review Status"},"description":"Include psychologist review status"},{"name":"face_deletion_status","in":"query","required":false,"schema":{"type":"boolean","description":"Include face deletion status (video only)","default":false,"title":"Face Deletion Status"},"description":"Include face deletion status (video only)"},{"name":"speaker_deletion_status","in":"query","required":false,"schema":{"type":"boolean","description":"Include speaker deletion status (audio only)","default":false,"title":"Speaker Deletion Status"},"description":"Include speaker deletion status (audio only)"},{"name":"share_status","in":"query","required":false,"schema":{"type":"boolean","description":"Include share status","default":false,"title":"Share Status"},"description":"Include share status"},{"name":"speaker_face_match_status","in":"query","required":false,"schema":{"type":"boolean","description":"Include speaker-face match status (video only)","default":false,"title":"Speaker Face Match Status"},"description":"Include speaker-face match status (video only)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaStatusResponse"},"examples":{"video_processing":{"summary":"A video that is still being analysed","value":{"media_id":"abc123-def456","media_type":"video","status":"processing","personality_status":"pending","personality_processing":"0","states":{"status":{"state":"processing","terminal":false,"progress":0.45},"personality_status":{"state":"pending","terminal":false,"progress":0.0}}}},"video_completed_personality_skipped":{"summary":"A finished video whose personality analysis was skipped (no audio)","value":{"media_id":"abc123-def456","media_type":"video","status":"completed","personality_status":"no audio","states":{"status":{"state":"completed","terminal":true,"progress":1.0},"personality_status":{"state":"skipped","terminal":true,"progress":null}}}},"audio_failed":{"summary":"An audio item whose analysis failed","value":{"media_id":"xyz789-uvw012","media_type":"audio","status":"failed","states":{"status":{"state":"failed","terminal":true,"progress":null}}}}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v2/texts/{text_id}":{"get":{"tags":["Text Emotion API"],"summary":"Get Text Details","description":"Retrieve all details for a specific text directly from the database.\n\nReturns a complete standardized response with all possible fields.\nFields that don't exist in the database are returned as null.\n\n**Access Control:**\n- Authenticated users can access their own texts\n- Admins can access all texts\n- Sample texts and shared texts are publicly accessible","operationId":"get_text_details_v2_texts__text_id__get","parameters":[{"name":"text_id","in":"path","required":true,"schema":{"type":"string","description":"The unique ID of the text.","title":"Text Id"},"description":"The unique ID of the text."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TextDetailResponseV2"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.read"]}]}},"/v2/realtime/session":{"post":{"tags":["Realtime Emotion API"],"summary":"Create realtime emotion session","description":"Create a new session for real-time video/audio emotion analysis via WebRTC.","operationId":"create_realtime_session_v2_realtime_session_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicCreateSessionRequest"}}}},"responses":{"200":{"description":"A slot was free: the session exists and is ready for the WebRTC offer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSessionResponse"}}}},"202":{"description":"Every session slot is busy and this request is holding a place in the queue. No session exists yet, so there is nothing to connect to. Claim the returned session_id every `poll_after_ms` (POST .../admission) until it answers 200 — each claim also holds the place, so a caller that stops claiming is dropped from the queue. DELETE .../admission to leave early.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionQueuedResponse"}}}},"503":{"description":"At capacity and no place in the queue was available (the queue is full, or a held place expired). The body carries `{\"code\": \"at_capacity\"}`. This is a capacity signal, not a fault — retry later rather than treating it as an error."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["realtime.write"]}]}},"/v2/realtime/session/{session_id}/admission":{"post":{"tags":["Realtime Emotion API"],"summary":"Claim a queued realtime session","description":"Ask whether a queued session has been admitted yet, and start it if so. Also holds the caller's place in the queue.","operationId":"claim_realtime_admission_v2_realtime_session__session_id__admission_post","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","description":"The queued session ID to claim","title":"Session Id"},"description":"The queued session ID to claim"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicCreateSessionRequest"}}}},"responses":{"200":{"description":"A slot was free: the session exists and is ready for the WebRTC offer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSessionResponse"}}}},"202":{"description":"Every session slot is busy and this request is holding a place in the queue. No session exists yet, so there is nothing to connect to. Claim the returned session_id every `poll_after_ms` (POST .../admission) until it answers 200 — each claim also holds the place, so a caller that stops claiming is dropped from the queue. DELETE .../admission to leave early.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionQueuedResponse"}}}},"503":{"description":"At capacity and no place in the queue was available (the queue is full, or a held place expired). The body carries `{\"code\": \"at_capacity\"}`. This is a capacity signal, not a fault — retry later rather than treating it as an error."},"410":{"description":"The server holding this session restarted or went away, and the session ended with it. The body carries `{\"code\": \"session_node_gone\"}`. Start a new session; retrying this one will not help."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["realtime.write"]}]},"delete":{"tags":["Realtime Emotion API"],"summary":"Leave the realtime queue","description":"Give up a queued place in line so the next waiter advances immediately.","operationId":"release_realtime_admission_v2_realtime_session__session_id__admission_delete","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","description":"The queued session ID to release","title":"Session Id"},"description":"The queued session ID to release"}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["realtime.write"]}]}},"/v2/realtime/session/{session_id}/end":{"post":{"tags":["Realtime Emotion API"],"summary":"End realtime emotion session","description":"End an active realtime emotion session and get session summary.","operationId":"end_realtime_session_v2_realtime_session__session_id__end_post","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","description":"The session ID to end","title":"Session Id"},"description":"The session ID to end"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EndSessionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["realtime.write"]}]}},"/v2/realtime/session/{session_id}/credits":{"get":{"tags":["Realtime Emotion API"],"summary":"Get realtime session credits","description":"Get what a realtime session cost, and whether the figures are final.","operationId":"get_realtime_session_credits_v2_realtime_session__session_id__credits_get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","description":"The session ID","title":"Session Id"},"description":"The session ID"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionCreditsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["realtime.write"]}]}},"/v2/realtime/session/{session_id}/window":{"get":{"tags":["Realtime Emotion API"],"summary":"Read a window of a realtime session","description":"Read what a session you started produced over one span: the transcript, and the emotion\nstatistics of the streams you ask for.\n\nUse it to follow a session from your server without holding the results socket open. Reading a window\ndoes not cost credits of its own, but it is how the session is billed when you do not hold the socket: the\nsession's time is charged, at the same per-second rate, up to its present whenever it is read and when it is\nended. A caller that never reads gets no output and pays nothing. A session whose balance has run out is\nended and answered 402.\n\nEvery time is in milliseconds on one session clock, shared by every stream and by the words; `origin_epoch_ms`\nis the Unix time of its zero. The span is half-open, `[start_ms, end_ms)`. An empty or partial window is a 200:\nsilence is data. While a stream carries the `pending` flag more of the span may still arrive, so ask again; stop\nonce it is gone.\n\nA session started with `emotion_analysis_enabled` false ran no emotion model: asking it for\na stream is a 400, and the transcript is always available. Available while the session is\nlive and for a short time after it ends.\n\n**Authentication:** Requires valid API key in X-API-Key header.","operationId":"get_realtime_session_window_v2_realtime_session__session_id__window_get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","description":"The session ID","title":"Session Id"},"description":"The session ID"},{"name":"start_ms","in":"query","required":true,"schema":{"type":"integer","minimum":0,"description":"Start of the span, in milliseconds on the session clock.","title":"Start Ms"},"description":"Start of the span, in milliseconds on the session clock."},{"name":"end_ms","in":"query","required":true,"schema":{"type":"integer","minimum":1,"description":"End of the span (exclusive). Must be greater than `start_ms`.","title":"End Ms"},"description":"End of the span (exclusive). Must be greater than `start_ms`."},{"name":"modalities","in":"query","required":false,"schema":{"type":"array","items":{"enum":["face","voice","text"],"type":"string"},"description":"Emotion streams to include, repeated: `face`, `voice`, `text`. Omit for the transcript only.","default":[],"title":"Modalities"},"description":"Emotion streams to include, repeated: `face`, `voice`, `text`. Omit for the transcript only."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RealtimeWindow"}}}},"410":{"description":"The server holding this session restarted or went away, and the session ended with it. The body carries `{\"code\": \"session_node_gone\"}`. Start a new session; retrying this one will not help."},"400":{"description":"A stream was asked for that the session was not started for (`{\"detail\": {\"code\": \"modality_not_admitted\"}}`): any emotion stream when the session was started with `emotion_analysis_enabled` false, or `face` on a voice-only session."},"404":{"description":"Not a session you started, or one that has not started yet."},"402":{"description":"The credits for the session have run out (`{\"detail\": {\"code\": \"insufficient_credits\"}}`). The session has been ended."},"409":{"description":"The span starts after the session's present, or has been dropped from every stream asked for (`{\"detail\": {\"code\": \"window_out_of_range\"}}`)."},"503":{"description":"The window could not be read right now (`{\"detail\": {\"code\": \"window_unavailable\"}}`). Ask again."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["realtime.write"]}]}},"/v2/realtime/webrtc/offer":{"post":{"tags":["Realtime Emotion API"],"summary":"Forward WebRTC offer","description":"Forward WebRTC SDP offer to establish media connection.","operationId":"forward_webrtc_offer_endpoint_v2_realtime_webrtc_offer_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebRTCOfferRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebRTCOfferResponse"}}}},"410":{"description":"The server holding this session restarted or went away, and the session ended with it. The body carries `{\"code\": \"session_node_gone\"}`. Start a new session; retrying this one will not help."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["realtime.write"]}]}},"/v2/realtime/webrtc/ice":{"post":{"tags":["Realtime Emotion API"],"summary":"Forward ICE candidate","description":"Forward WebRTC ICE candidate for connection establishment.","operationId":"forward_ice_candidate_endpoint_v2_realtime_webrtc_ice_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ICECandidateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ICECandidateResponse"}}}},"410":{"description":"The server holding this session restarted or went away, and the session ended with it. The body carries `{\"code\": \"session_node_gone\"}`. Start a new session; retrying this one will not help."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["realtime.write"]}]}},"/v2/realtime/health":{"get":{"tags":["Realtime Emotion API"],"summary":"Check realtime service health","description":"Check the health status of the imentiv-realtime service.","operationId":"check_health_v2_realtime_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthCheckResponse"}}}}}}},"/v2/projects/{project_type}":{"post":{"tags":["Projects"],"summary":"Create Project Endpoint","description":"Create a new project.","operationId":"create_project_endpoint_v2_projects__project_type__post","parameters":[{"name":"project_type","in":"path","required":true,"schema":{"type":"string","description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404.","enum":["product_testing","recruitment","sales_deal","financial_research","collection"],"title":"Project Type"},"description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["projects.write"]}]},"get":{"tags":["Projects"],"summary":"List Projects Endpoint","description":"List projects for the authenticated user.","operationId":"list_projects_endpoint_v2_projects__project_type__get","parameters":[{"name":"project_type","in":"path","required":true,"schema":{"type":"string","description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404.","enum":["product_testing","recruitment","sales_deal","financial_research","collection"],"title":"Project Type"},"description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404."},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Page Size"}},{"name":"start_after","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Project ID to paginate after","title":"Start After"},"description":"Project ID to paginate after"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["projects.read"]}]}},"/v2/projects/{project_type}/samples":{"get":{"tags":["Projects"],"summary":"List Sample Projects Endpoint","description":"List the public sample projects for a use case.\n\nUnauthenticated by design, mirroring the sample-media endpoint\n(`GET /v1/utils/sample_media`) — this is what lets a logged-out visitor\ndiscover the demo project. Only docs flagged `tags_map.sample: true` are\never returned, and the payload is a display-only subset that omits the\nauthoring account (see `SampleProjectResponse`).\n\nDeclared above `GET /{project_type}/{project_id}` on purpose: FastAPI\nmatches in declaration order, so the reverse would capture `samples` as a\nproject ID.","operationId":"list_sample_projects_endpoint_v2_projects__project_type__samples_get","parameters":[{"name":"project_type","in":"path","required":true,"schema":{"type":"string","description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404.","enum":["product_testing","recruitment","sales_deal","financial_research","collection"],"title":"Project Type"},"description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SampleProjectListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/projects/{project_type}/{project_id}":{"get":{"tags":["Projects"],"summary":"Get Project Endpoint","description":"Get a project by ID with enriched video details.\n\nSupports tag filtering via `filter[tags][<key>]=<value>` query\nparams (AND across keys). Filtering happens in-memory after fetch\n— fine at the ≤1000-videos-per-project scale. Omit all\n`filter[tags]` params to get the unfiltered list.","operationId":"get_project_endpoint_v2_projects__project_type___project_id__get","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","description":"Project ID","title":"Project Id"},"description":"Project ID"},{"name":"project_type","in":"path","required":true,"schema":{"type":"string","description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404.","enum":["product_testing","recruitment","sales_deal","financial_research","collection"],"title":"Project Type"},"description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrichedProjectResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["projects.read"]}]},"put":{"tags":["Projects"],"summary":"Update Project Endpoint","description":"Update a project.","operationId":"update_project_endpoint_v2_projects__project_type___project_id__put","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","description":"Project ID","title":"Project Id"},"description":"Project ID"},{"name":"project_type","in":"path","required":true,"schema":{"type":"string","description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404.","enum":["product_testing","recruitment","sales_deal","financial_research","collection"],"title":"Project Type"},"description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProjectRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Projects"],"summary":"Delete Project Endpoint","description":"Delete a project and all its linked videos asynchronously.\n\nReturns 202 immediately with an empty body; the cascade runs in a\nbackground task. Clients poll GET on the project to detect completion:\na 404 means the project document is gone (the user-visible signal that\n\"the project is deleted\"). Underlying per-video blob/storage cleanup\nmay continue asynchronously for a short while after that point —\n`perform_video_deletion` schedules its own sub-tasks for files /\nfolders / frames cleanup, which run after the cascade returns. None\nof that is user-visible.\n\nPer-video failures abort the cascade and leave the project doc in\nplace so the user's polling eventually times out and they retry.\n\nA workspace's project is deleted by its creator and by the workspace's owners and admins. It is refused with\n`403`, and nothing is deleted, when the project holds an item that the caller may not delete: ask an owner or\nadmin of the workspace. An item the project does not own is unlinked, never deleted.\n\nNote: this is a hard, fully-cascading delete. There is no \"preserve\nvideos\" mode — for product_testing, a video stripped of its project\ncontext (task, analysis) is meaningless to the user. If a future\nproject type ever needs \"delete project but keep videos\" semantics,\nadd it then with concrete requirements.","operationId":"delete_project_endpoint_v2_projects__project_type___project_id__delete","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","description":"Project ID","title":"Project Id"},"description":"Project ID"},{"name":"project_type","in":"path","required":true,"schema":{"type":"string","description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404.","enum":["product_testing","recruitment","sales_deal","financial_research","collection"],"title":"Project Type"},"description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404."}],"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["projects.delete"]}]}},"/v2/projects/{project_type}/{project_id}/videos":{"post":{"tags":["Projects"],"summary":"Link Video Endpoint","description":"Link an existing Imentiv video to this project.\n\nA project and the items in it have the same owner, so the video has to be owned like the project: one the\nproject's workspace owns for a workspace's project, one you own for a personal project. Any other video is\nrefused with `409` and the code `media_owner_mismatch`.","operationId":"link_video_endpoint_v2_projects__project_type___project_id__videos_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","description":"Project ID","title":"Project Id"},"description":"Project ID"},{"name":"project_type","in":"path","required":true,"schema":{"type":"string","description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404.","enum":["product_testing","recruitment","sales_deal","financial_research","collection"],"title":"Project Type"},"description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkVideoRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["projects.write","media.write"]}]}},"/v2/projects/{project_type}/{project_id}/videos/{video_id}":{"delete":{"tags":["Projects"],"summary":"Unlink Video Endpoint","description":"Remove a video link from this project. Does NOT delete the video itself.\n\nRemoving the video does not restore people that were carried into the\nproject. Where no person remains, the names shown are the names stored\non each face and speaker.","operationId":"unlink_video_endpoint_v2_projects__project_type___project_id__videos__video_id__delete","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","description":"Project ID","title":"Project Id"},"description":"Project ID"},{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"Video ID to unlink","title":"Video Id"},"description":"Video ID to unlink"},{"name":"project_type","in":"path","required":true,"schema":{"type":"string","description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404.","enum":["product_testing","recruitment","sales_deal","financial_research","collection"],"title":"Project Type"},"description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["projects.write","media.write"]}]}},"/v2/projects/{project_type}/{project_id}/videos/batch":{"post":{"tags":["Projects"],"summary":"Batch Import Videos Endpoint","description":"Import multiple videos from URLs (Dropbox, YouTube, Google Drive, etc.).\n\nEach URL is submitted to the video processing pipeline and linked to the project.\nMax 30 URLs per batch.","operationId":"batch_import_videos_endpoint_v2_projects__project_type___project_id__videos_batch_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","description":"Project ID","title":"Project Id"},"description":"Project ID"},{"name":"project_type","in":"path","required":true,"schema":{"type":"string","description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404.","enum":["product_testing","recruitment","sales_deal","financial_research","collection"],"title":"Project Type"},"description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchVideoImportRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchVideoImportResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/projects/{project_type}/{project_id}/context/upload-url":{"post":{"tags":["Projects"],"summary":"Get Context Upload Url Endpoint","description":"Get a signed GCS upload URL for a context document file.","operationId":"get_context_upload_url_endpoint_v2_projects__project_type___project_id__context_upload_url_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","description":"Project ID","title":"Project Id"},"description":"Project ID"},{"name":"project_type","in":"path","required":true,"schema":{"type":"string","description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404.","enum":["product_testing","recruitment","sales_deal","financial_research","collection"],"title":"Project Type"},"description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextUploadURLRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextUploadURLResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/projects/{project_type}/{project_id}/context":{"post":{"tags":["Projects"],"summary":"Add Context Doc Endpoint","description":"Add a context document (file already uploaded to GCS, or pasted text).","operationId":"add_context_doc_endpoint_v2_projects__project_type___project_id__context_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","description":"Project ID","title":"Project Id"},"description":"Project ID"},{"name":"project_type","in":"path","required":true,"schema":{"type":"string","description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404.","enum":["product_testing","recruitment","sales_deal","financial_research","collection"],"title":"Project Type"},"description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextDocCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ContextDocCreatedChargedResponse"},{"$ref":"#/components/schemas/ContextDocCreatedResponse"}],"title":"Response Add Context Doc Endpoint V2 Projects  Project Type   Project Id  Context Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/projects/{project_type}/{project_id}/context/{doc_id}":{"delete":{"tags":["Projects"],"summary":"Delete Context Doc Endpoint","description":"Delete a single context document.","operationId":"delete_context_doc_endpoint_v2_projects__project_type___project_id__context__doc_id__delete","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","description":"Project ID","title":"Project Id"},"description":"Project ID"},{"name":"doc_id","in":"path","required":true,"schema":{"type":"string","description":"Context document ID","title":"Doc Id"},"description":"Context document ID"},{"name":"project_type","in":"path","required":true,"schema":{"type":"string","description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404.","enum":["product_testing","recruitment","sales_deal","financial_research","collection"],"title":"Project Type"},"description":"Project type: `product_testing`, `recruitment`, `sales_deal`, `financial_research`, `collection`. `collection` is a general-purpose project that holds videos and people, with no use-case-specific features: insight cards and the Compare artifact cannot be generated for it (422). An unknown project type returns 404."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextDocDeleteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/videos/{video_id}/persons":{"get":{"tags":["Media Persons"],"summary":"List Video Persons","description":"People named on this video.\n\nIncludes someone named here who is also attached to the video's audio.\nA person with no face or speaker yet is included. People stored on the\naudio are not a separate list: a linked audio uses this video.","operationId":"list_video_persons_v2_videos__video_id__persons_get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","title":"Video Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaPersonListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Media Persons"],"summary":"Create Video Person","description":"Name a person on this video. You can attach faces and speakers now or later.\n\nThe name is also saved on those faces and speakers, and on their transcript lines.","operationId":"create_video_person_v2_videos__video_id__persons_post","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","title":"Video Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMediaPersonRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaPerson"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/videos/{video_id}/persons/{person_id}":{"get":{"tags":["Media Persons"],"summary":"Get Video Person","description":"One person named on this video.","operationId":"get_video_person_v2_videos__video_id__persons__person_id__get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","title":"Video Id"}},{"name":"person_id","in":"path","required":true,"schema":{"type":"string","title":"Person Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaPerson"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Media Persons"],"summary":"Rename Video Person","description":"Change the display name of a person named on this video.\n\nFaces, speakers, and transcript lines that still show the previous name are updated.","operationId":"rename_video_person_v2_videos__video_id__persons__person_id__patch","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","title":"Video Id"}},{"name":"person_id","in":"path","required":true,"schema":{"type":"string","title":"Person Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenameMediaPersonRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaPerson"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Media Persons"],"summary":"Delete Video Person","description":"Remove a person from this video.","operationId":"delete_video_person_v2_videos__video_id__persons__person_id__delete","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","title":"Video Id"}},{"name":"person_id","in":"path","required":true,"schema":{"type":"string","title":"Person Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaPersonDeleteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/videos/{video_id}/persons/{person_id}/appearances":{"post":{"tags":["Media Persons"],"summary":"Link Video Appearance","description":"Attach a face or a speaker on this video to a person.\n\nThat face or speaker, and the speaker's transcript lines, are updated to this person's name.","operationId":"link_video_appearance_v2_videos__video_id__persons__person_id__appearances_post","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","title":"Video Id"}},{"name":"person_id","in":"path","required":true,"schema":{"type":"string","title":"Person Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaAppearanceInput"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaPerson"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Media Persons"],"summary":"Unlink Video Appearance","description":"Detach a face or a speaker. The person stays on the video.\n\nPass either id, or both. Detaching a speaker leaves the face in place\nwhen they were attached together.","operationId":"unlink_video_appearance_v2_videos__video_id__persons__person_id__appearances_delete","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","title":"Video Id"}},{"name":"person_id","in":"path","required":true,"schema":{"type":"string","title":"Person Id"}},{"name":"face_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"description":"Face to detach.","title":"Face Id"},"description":"Face to detach."},{"name":"speaker_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Speaker to detach.","title":"Speaker Id"},"description":"Speaker to detach."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaPerson"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/audios/{audio_id}/persons":{"get":{"tags":["Media Persons"],"summary":"List Audio Persons","description":"People named on this audio.\n\nA standalone audio lists its own people. An audio that belongs to a video\nlists that video's people who have a speaker on it, and each row names the\nvideo as its home. A face with no speaker, and a person with no attachment\nyet, appear only on the video.","operationId":"list_audio_persons_v2_audios__audio_id__persons_get","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","title":"Audio Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaPersonListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Media Persons"],"summary":"Create Audio Person","description":"Name a person on a standalone audio. An audio that belongs to a video is rejected.\n\nThe name is also saved on the speakers you attach, and on their transcript lines.","operationId":"create_audio_person_v2_audios__audio_id__persons_post","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","title":"Audio Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMediaPersonRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaPerson"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/audios/{audio_id}/persons/{person_id}":{"get":{"tags":["Media Persons"],"summary":"Get Audio Person","description":"One person visible on this audio. The home may be the parent video.","operationId":"get_audio_person_v2_audios__audio_id__persons__person_id__get","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","title":"Audio Id"}},{"name":"person_id","in":"path","required":true,"schema":{"type":"string","title":"Person Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaPerson"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Media Persons"],"summary":"Rename Audio Person","description":"Change the display name. Rejected when this audio belongs to a video.\n\nSpeakers and transcript lines that still show the previous name are updated.","operationId":"rename_audio_person_v2_audios__audio_id__persons__person_id__patch","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","title":"Audio Id"}},{"name":"person_id","in":"path","required":true,"schema":{"type":"string","title":"Person Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenameMediaPersonRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaPerson"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Media Persons"],"summary":"Delete Audio Person","description":"Remove a person from a standalone audio.","operationId":"delete_audio_person_v2_audios__audio_id__persons__person_id__delete","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","title":"Audio Id"}},{"name":"person_id","in":"path","required":true,"schema":{"type":"string","title":"Person Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaPersonDeleteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/audios/{audio_id}/persons/{person_id}/appearances":{"post":{"tags":["Media Persons"],"summary":"Link Audio Appearance","description":"Attach a speaker on a standalone audio. A face is rejected.\n\nThat speaker and its transcript lines are updated to this person's name.","operationId":"link_audio_appearance_v2_audios__audio_id__persons__person_id__appearances_post","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","title":"Audio Id"}},{"name":"person_id","in":"path","required":true,"schema":{"type":"string","title":"Person Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaAppearanceInput"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaPerson"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Media Persons"],"summary":"Unlink Audio Appearance","description":"Detach a speaker from a person on a standalone audio.","operationId":"unlink_audio_appearance_v2_audios__audio_id__persons__person_id__appearances_delete","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","title":"Audio Id"}},{"name":"person_id","in":"path","required":true,"schema":{"type":"string","title":"Person Id"}},{"name":"face_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"description":"Face to detach.","title":"Face Id"},"description":"Face to detach."},{"name":"speaker_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"description":"Speaker to detach.","title":"Speaker Id"},"description":"Speaker to detach."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaPerson"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/faces/similarity":{"post":{"tags":["Faces"],"summary":"Face Similarity","description":"Compare faces across your videos.\n\nScores each probe face against each gallery face and returns a similarity per pair.\nBoth lists are explicit: there is no search across all of your videos. Only videos\nyou own can be compared. Shared or sample videos you can view, and other members'\nvideos, are reported as `not_found`, the same as a video that does not exist.\n\nUp to 16 probes and 512 gallery faces per call. A face listed twice within a list is\ncompared once, and a face is never paired with itself.\n\nA face that cannot be scored is listed under `incomparable` with a reason instead of\na score. The response is still 200, with an empty `pairs` when nothing could be\nscored. Scores are only computed between faces analysed with the same model version,\nnamed by `model_id` and `embedding_version`.\n\nThe response never contains face embeddings or any other biometric template, only\nids and scores. Similarity is a score, not a match decision. No charge is applied.","operationId":"face_similarity_v2_faces_similarity_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FaceSimilarityRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FaceSimilarityResponse"}}}},"503":{"description":"Face data could not be read in time. Retry after the `Retry-After` delay."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/webhooks/settings":{"get":{"tags":["Webhooks"],"summary":"Get your webhook settings","description":"Your signing-secret status and payload format. The secret itself is never returned here.","operationId":"get_webhook_settings_v2_webhooks_settings_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSettings"}}}}}},"put":{"tags":["Webhooks"],"summary":"Choose your webhook payload format","description":"Set the payload format for events created from now on.\n\n`legacy` is the format sent before payload versions existed and is only available to accounts\nthat already receive it. Events already created keep the format they were created with.","operationId":"put_webhook_settings_v2_webhooks_settings_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSettingsUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSettings"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/webhooks/secret":{"post":{"tags":["Webhooks"],"summary":"Reveal or rotate your webhook signing secret","description":"Return your signing secret, in full, in this response only.\n\nEvery delivery is signed from your first event. If you have not read your secret yet, this call\nreturns it without changing it, so you can verify deliveries you have already received. If you\nhave, this call creates a new secret; the previous one keeps signing for 24 hours, and during\nthat time each delivery carries two `v1` signatures so you can switch over without dropping\nevents.","operationId":"post_webhook_secret_v2_webhooks_secret_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSecret"}}}}}}},"/v2/webhooks/deliveries":{"get":{"tags":["Webhooks"],"summary":"List recent webhook deliveries","description":"Your most recent webhook deliveries, newest first, kept for 30 days.\n\nEach entry shows every attempt: the status your endpoint answered with, or why there was no\nanswer, and when the next attempt is due. Your full URL is never returned, only its host.","operationId":"get_webhook_deliveries_v2_webhooks_deliveries_get","parameters":[{"name":"resource_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"description":"Only deliveries for this resource.","title":"Resource Id"},"description":"Only deliveries for this resource."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveries"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/videos/{video_id}/tags":{"post":{"tags":["Video Emotion API"],"summary":"Add Video Tag","description":"Add or replace a user-source tag on a video.\n\nAccepts `label.*` (free labels — normalized lowercase + slugify) and\nregistered `user.*` typed keys. Returns 409 if an existing\nimporter/system row sits at the target key.","operationId":"add_video_tag_v2_videos__video_id__tags_post","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"ID of the video.","title":"Video Id"},"description":"ID of the video."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagWriteRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/StringTag"},{"$ref":"#/components/schemas/EnumTag"},{"$ref":"#/components/schemas/IntegerTag"},{"$ref":"#/components/schemas/BoolTag"},{"$ref":"#/components/schemas/DateTag"},{"$ref":"#/components/schemas/GeoTag"},{"$ref":"#/components/schemas/SetTag"},{"$ref":"#/components/schemas/NoneTag"}],"discriminator":{"propertyName":"value_type","mapping":{"string":"#/components/schemas/StringTag","enum":"#/components/schemas/EnumTag","integer":"#/components/schemas/IntegerTag","boolean":"#/components/schemas/BoolTag","date":"#/components/schemas/DateTag","location_admin":"#/components/schemas/GeoTag","set":"#/components/schemas/SetTag","none":"#/components/schemas/NoneTag"}},"title":"Response Add Video Tag V2 Videos  Video Id  Tags Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.write"]}]}},"/v2/images/{image_id}/tags":{"post":{"tags":["Image Emotion API"],"summary":"Add Image Tag","description":"Add or replace a user-source tag on an image.\n\nAccepts `label.*` (free labels — normalized lowercase + slugify) and\nregistered `user.*` typed keys. Returns 409 if an existing\nimporter/system row sits at the target key.","operationId":"add_image_tag_v2_images__image_id__tags_post","parameters":[{"name":"image_id","in":"path","required":true,"schema":{"type":"string","description":"ID of the image.","title":"Image Id"},"description":"ID of the image."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagWriteRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/StringTag"},{"$ref":"#/components/schemas/EnumTag"},{"$ref":"#/components/schemas/IntegerTag"},{"$ref":"#/components/schemas/BoolTag"},{"$ref":"#/components/schemas/DateTag"},{"$ref":"#/components/schemas/GeoTag"},{"$ref":"#/components/schemas/SetTag"},{"$ref":"#/components/schemas/NoneTag"}],"discriminator":{"propertyName":"value_type","mapping":{"string":"#/components/schemas/StringTag","enum":"#/components/schemas/EnumTag","integer":"#/components/schemas/IntegerTag","boolean":"#/components/schemas/BoolTag","date":"#/components/schemas/DateTag","location_admin":"#/components/schemas/GeoTag","set":"#/components/schemas/SetTag","none":"#/components/schemas/NoneTag"}},"title":"Response Add Image Tag V2 Images  Image Id  Tags Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.write"]}]}},"/v2/audios/{audio_id}/tags":{"post":{"tags":["Audio Emotion API"],"summary":"Add Audio Tag","description":"Add or replace a user-source tag on an audio.\n\nAccepts `label.*` (free labels — normalized lowercase + slugify) and\nregistered `user.*` typed keys. Returns 409 if an existing\nimporter/system row sits at the target key.","operationId":"add_audio_tag_v2_audios__audio_id__tags_post","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"ID of the audio.","title":"Audio Id"},"description":"ID of the audio."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagWriteRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/StringTag"},{"$ref":"#/components/schemas/EnumTag"},{"$ref":"#/components/schemas/IntegerTag"},{"$ref":"#/components/schemas/BoolTag"},{"$ref":"#/components/schemas/DateTag"},{"$ref":"#/components/schemas/GeoTag"},{"$ref":"#/components/schemas/SetTag"},{"$ref":"#/components/schemas/NoneTag"}],"discriminator":{"propertyName":"value_type","mapping":{"string":"#/components/schemas/StringTag","enum":"#/components/schemas/EnumTag","integer":"#/components/schemas/IntegerTag","boolean":"#/components/schemas/BoolTag","date":"#/components/schemas/DateTag","location_admin":"#/components/schemas/GeoTag","set":"#/components/schemas/SetTag","none":"#/components/schemas/NoneTag"}},"title":"Response Add Audio Tag V2 Audios  Audio Id  Tags Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.write"]}]}},"/v2/texts/{text_id}/tags":{"post":{"tags":["Text Emotion API"],"summary":"Add Text Tag","description":"Add or replace a user-source tag on a text.\n\nAccepts `label.*` (free labels — normalized lowercase + slugify) and\nregistered `user.*` typed keys. Returns 409 if an existing\nimporter/system row sits at the target key.","operationId":"add_text_tag_v2_texts__text_id__tags_post","parameters":[{"name":"text_id","in":"path","required":true,"schema":{"type":"string","description":"ID of the text.","title":"Text Id"},"description":"ID of the text."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagWriteRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/StringTag"},{"$ref":"#/components/schemas/EnumTag"},{"$ref":"#/components/schemas/IntegerTag"},{"$ref":"#/components/schemas/BoolTag"},{"$ref":"#/components/schemas/DateTag"},{"$ref":"#/components/schemas/GeoTag"},{"$ref":"#/components/schemas/SetTag"},{"$ref":"#/components/schemas/NoneTag"}],"discriminator":{"propertyName":"value_type","mapping":{"string":"#/components/schemas/StringTag","enum":"#/components/schemas/EnumTag","integer":"#/components/schemas/IntegerTag","boolean":"#/components/schemas/BoolTag","date":"#/components/schemas/DateTag","location_admin":"#/components/schemas/GeoTag","set":"#/components/schemas/SetTag","none":"#/components/schemas/NoneTag"}},"title":"Response Add Text Tag V2 Texts  Text Id  Tags Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.write"]}]}},"/v2/videos/{video_id}/provider-tags":{"post":{"tags":["Video Emotion API"],"summary":"Import Provider Tags","description":"Import importer-sourced tags (e.g. provider ``vpm.*``, cohort\n``demo.*`` / ``survey.*``) onto a video — the API path for external-source\ndata that ``POST /v2/videos/{id}/tags`` forbids.\n\nUnlike the user endpoint this writes **non-user-editable** registered keys\nwith an ``importer.*`` source (so customer-solutions data lands through the\nbackend — correct DB/creds + registry validation — instead of a direct\ndatabase write). Owner-or-admin only (same access check as ``POST /tags``).\n\nBounded so it can't forge data:\n  * ``source`` must be an ``importer.*`` value,\n  * every ``key`` must resolve in the registry, and\n  * every ``key``'s category must be in ``IMPORTABLE_CATEGORIES``\n    (provider / demographic / survey — external-source data nothing in\n    the pipeline owns). ``system.*`` stays rejected so an owner token\n    can't write pipeline-owned tags through this path; ``label.*`` /\n    ``user.*`` stay rejected because user data belongs on\n    ``POST /v2/videos/{id}/tags`` with user-source semantics.\n\nIdempotent — each key is an upsert via ``set_tag``.","operationId":"import_provider_tags_v2_videos__video_id__provider_tags_post","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"ID of the video.","title":"Video Id"},"description":"ID of the video."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderTagImportRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/StringTag"},{"$ref":"#/components/schemas/EnumTag"},{"$ref":"#/components/schemas/IntegerTag"},{"$ref":"#/components/schemas/BoolTag"},{"$ref":"#/components/schemas/DateTag"},{"$ref":"#/components/schemas/GeoTag"},{"$ref":"#/components/schemas/SetTag"},{"$ref":"#/components/schemas/NoneTag"}],"discriminator":{"propertyName":"value_type","mapping":{"string":"#/components/schemas/StringTag","enum":"#/components/schemas/EnumTag","integer":"#/components/schemas/IntegerTag","boolean":"#/components/schemas/BoolTag","date":"#/components/schemas/DateTag","location_admin":"#/components/schemas/GeoTag","set":"#/components/schemas/SetTag","none":"#/components/schemas/NoneTag"}}},"title":"Response Import Provider Tags V2 Videos  Video Id  Provider Tags Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/videos/{video_id}/tags/{tag_id}":{"delete":{"tags":["Video Emotion API"],"summary":"Remove Video Tag","description":"Delete a user-source tag from a video.\n\nThe row's `source` must be \"user\" AND `created_by` must match the\nrequester (or the requester must be admin). Idempotent: deleting an\nalready-absent tag returns 204.","operationId":"remove_video_tag_v2_videos__video_id__tags__tag_id__delete","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","description":"ID of the video.","title":"Video Id"},"description":"ID of the video."},{"name":"tag_id","in":"path","required":true,"schema":{"type":"string","description":"Synthetic tag id (key for scalars, key#elem for set elements).","title":"Tag Id"},"description":"Synthetic tag id (key for scalars, key#elem for set elements)."}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/images/{image_id}/tags/{tag_id}":{"delete":{"tags":["Image Emotion API"],"summary":"Remove Image Tag","description":"Delete a user-source tag from an image.\n\nThe row's `source` must be \"user\" AND `created_by` must match the\nrequester (or the requester must be admin). Idempotent: deleting an\nalready-absent tag returns 204.","operationId":"remove_image_tag_v2_images__image_id__tags__tag_id__delete","parameters":[{"name":"image_id","in":"path","required":true,"schema":{"type":"string","description":"ID of the image.","title":"Image Id"},"description":"ID of the image."},{"name":"tag_id","in":"path","required":true,"schema":{"type":"string","description":"Synthetic tag id (key for scalars, key#elem for set elements).","title":"Tag Id"},"description":"Synthetic tag id (key for scalars, key#elem for set elements)."}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/audios/{audio_id}/tags/{tag_id}":{"delete":{"tags":["Audio Emotion API"],"summary":"Remove Audio Tag","description":"Delete a user-source tag from an audio.\n\nThe row's `source` must be \"user\" AND `created_by` must match the\nrequester (or the requester must be admin). Idempotent: deleting an\nalready-absent tag returns 204.","operationId":"remove_audio_tag_v2_audios__audio_id__tags__tag_id__delete","parameters":[{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","description":"ID of the audio.","title":"Audio Id"},"description":"ID of the audio."},{"name":"tag_id","in":"path","required":true,"schema":{"type":"string","description":"Synthetic tag id (key for scalars, key#elem for set elements).","title":"Tag Id"},"description":"Synthetic tag id (key for scalars, key#elem for set elements)."}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/texts/{text_id}/tags/{tag_id}":{"delete":{"tags":["Text Emotion API"],"summary":"Remove Text Tag","description":"Delete a user-source tag from a text.\n\nThe row's `source` must be \"user\" AND `created_by` must match the\nrequester (or the requester must be admin). Idempotent: deleting an\nalready-absent tag returns 204.","operationId":"remove_text_tag_v2_texts__text_id__tags__tag_id__delete","parameters":[{"name":"text_id","in":"path","required":true,"schema":{"type":"string","description":"ID of the text.","title":"Text Id"},"description":"ID of the text."},{"name":"tag_id","in":"path","required":true,"schema":{"type":"string","description":"Synthetic tag id (key for scalars, key#elem for set elements).","title":"Tag Id"},"description":"Synthetic tag id (key for scalars, key#elem for set elements)."}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/{media_type}/upload-url":{"post":{"tags":["Media API"],"summary":"Get a presigned URL for direct file upload to cloud storage","description":"Returns a signed URL that allows the client to upload a file directly to cloud storage via HTTP PUT. This avoids sending the file through the backend, eliminating upload timeouts for large files.\n\n**Flow:**\n1. Call this endpoint to get a signed upload URL\n2. PUT the file directly to the signed URL with the required headers\n3. Call the regular POST /v2/videos or /v2/audios endpoint with the returned public_url\n\n`public_url` is a reference to submit in step 3, not a guaranteed-readable URL: media may be stored privately. To play media back, call `GET /v2/videos/{video_id}/playback-url` or `GET /v2/audios/{audio_id}/playback-url`.","operationId":"get_upload_url_v2__media_type__upload_url_post","parameters":[{"name":"media_type","in":"path","required":true,"schema":{"$ref":"#/components/schemas/MediaType"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_get_upload_url_v2__media_type__upload_url_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadUrlResponse"}}}},"402":{"description":"Refused before the URL is issued. Either the payer's balance is non-positive, or a plan usage window (daily / weekly / monthly) is exhausted. **Not** an affordability check: admission is ``balance > 0``, not ``balance >= cost``, so an account holding fewer credits than this upload will cost still receives a URL and goes into debt when the media is submitted. Credits are deducted at submit time (step 3), never here."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]},{"OAuth2Delegated":["media.write"]}]}}},"webhooks":{"video.completed":{"post":{"tags":["Webhooks"],"summary":"Video analysis completed","description":"Sent once when a video you submitted with a `callback_url` has finished successfully.\n\n**Delivery.** Imentiv sends an HTTPS `POST` with a JSON body to the `callback_url` you submitted.\nAnswer with any `2xx` status to acknowledge it. A connection error, a timeout, `408`, `429` or a\n`5xx` answer is retried with backoff for up to about 24 hours (eight attempts in all); a `Retry-After`\nheader on `429` and `503` is honoured. Any other answer is final, and redirects are not followed.\nAttempts can repeat an event, so ignore an `event_id` you have already processed.\n\n**Verifying the signature.** Every request carries\n`Imentiv-Signature: t=<unix seconds>,v1=<hex>[,v1=<hex>]`. Each `v1` is the hex HMAC-SHA256, keyed\nwith your signing secret (`whsec_...`, including the prefix), of the string `<t>.<raw request body>`.\nCompute it over the exact bytes you received, compare in constant time, and reject a `t` further from\nyour clock than the tolerance you choose (five minutes is typical). `t` is the time of that attempt,\nso it changes on a retry while the body does not. While a rotated-out secret is still valid the header\ncarries two `v1` values; accept the request if either matches. Read or rotate your secret with\n`POST /v2/webhooks/secret`; see `GET /v2/webhooks/deliveries` for the attempt log.","operationId":"video_completedvideo_completed_post","parameters":[{"description":"`t=<unix seconds>,v1=<hex>[,v1=<hex>]`. See above.","required":true,"schema":{"type":"string","title":"Imentiv-Signature","description":"`t=<unix seconds>,v1=<hex>[,v1=<hex>]`. See above."},"name":"imentiv-signature","in":"header"},{"description":"The event's `event_id`. Identical on every attempt.","required":true,"schema":{"type":"string","title":"Imentiv-Event-Id","description":"The event's `event_id`. Identical on every attempt."},"name":"imentiv-event-id","in":"header"},{"description":"1 for the first attempt, then 2, 3, ...","required":true,"schema":{"type":"integer","title":"Imentiv-Delivery-Attempt","description":"1 for the first attempt, then 2, 3, ..."},"name":"imentiv-delivery-attempt","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoCompletedEvent"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"video.failed":{"post":{"tags":["Webhooks"],"summary":"Video analysis failed","description":"Sent once when a video you submitted with a `callback_url` has failed.\n\n**Delivery.** Imentiv sends an HTTPS `POST` with a JSON body to the `callback_url` you submitted.\nAnswer with any `2xx` status to acknowledge it. A connection error, a timeout, `408`, `429` or a\n`5xx` answer is retried with backoff for up to about 24 hours (eight attempts in all); a `Retry-After`\nheader on `429` and `503` is honoured. Any other answer is final, and redirects are not followed.\nAttempts can repeat an event, so ignore an `event_id` you have already processed.\n\n**Verifying the signature.** Every request carries\n`Imentiv-Signature: t=<unix seconds>,v1=<hex>[,v1=<hex>]`. Each `v1` is the hex HMAC-SHA256, keyed\nwith your signing secret (`whsec_...`, including the prefix), of the string `<t>.<raw request body>`.\nCompute it over the exact bytes you received, compare in constant time, and reject a `t` further from\nyour clock than the tolerance you choose (five minutes is typical). `t` is the time of that attempt,\nso it changes on a retry while the body does not. While a rotated-out secret is still valid the header\ncarries two `v1` values; accept the request if either matches. Read or rotate your secret with\n`POST /v2/webhooks/secret`; see `GET /v2/webhooks/deliveries` for the attempt log.","operationId":"video_failedvideo_failed_post","parameters":[{"description":"`t=<unix seconds>,v1=<hex>[,v1=<hex>]`. See above.","required":true,"schema":{"type":"string","title":"Imentiv-Signature","description":"`t=<unix seconds>,v1=<hex>[,v1=<hex>]`. See above."},"name":"imentiv-signature","in":"header"},{"description":"The event's `event_id`. Identical on every attempt.","required":true,"schema":{"type":"string","title":"Imentiv-Event-Id","description":"The event's `event_id`. Identical on every attempt."},"name":"imentiv-event-id","in":"header"},{"description":"1 for the first attempt, then 2, 3, ...","required":true,"schema":{"type":"integer","title":"Imentiv-Delivery-Attempt","description":"1 for the first attempt, then 2, 3, ..."},"name":"imentiv-delivery-attempt","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoFailedEvent"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"audio.failed":{"post":{"tags":["Webhooks"],"summary":"Audio analysis failed","description":"Sent once when an audio you submitted with a `callback_url` has failed.\n\n**Delivery.** Imentiv sends an HTTPS `POST` with a JSON body to the `callback_url` you submitted.\nAnswer with any `2xx` status to acknowledge it. A connection error, a timeout, `408`, `429` or a\n`5xx` answer is retried with backoff for up to about 24 hours (eight attempts in all); a `Retry-After`\nheader on `429` and `503` is honoured. Any other answer is final, and redirects are not followed.\nAttempts can repeat an event, so ignore an `event_id` you have already processed.\n\n**Verifying the signature.** Every request carries\n`Imentiv-Signature: t=<unix seconds>,v1=<hex>[,v1=<hex>]`. Each `v1` is the hex HMAC-SHA256, keyed\nwith your signing secret (`whsec_...`, including the prefix), of the string `<t>.<raw request body>`.\nCompute it over the exact bytes you received, compare in constant time, and reject a `t` further from\nyour clock than the tolerance you choose (five minutes is typical). `t` is the time of that attempt,\nso it changes on a retry while the body does not. While a rotated-out secret is still valid the header\ncarries two `v1` values; accept the request if either matches. Read or rotate your secret with\n`POST /v2/webhooks/secret`; see `GET /v2/webhooks/deliveries` for the attempt log.","operationId":"audio_failedaudio_failed_post","parameters":[{"description":"`t=<unix seconds>,v1=<hex>[,v1=<hex>]`. See above.","required":true,"schema":{"type":"string","title":"Imentiv-Signature","description":"`t=<unix seconds>,v1=<hex>[,v1=<hex>]`. See above."},"name":"imentiv-signature","in":"header"},{"description":"The event's `event_id`. Identical on every attempt.","required":true,"schema":{"type":"string","title":"Imentiv-Event-Id","description":"The event's `event_id`. Identical on every attempt."},"name":"imentiv-event-id","in":"header"},{"description":"1 for the first attempt, then 2, 3, ...","required":true,"schema":{"type":"integer","title":"Imentiv-Delivery-Attempt","description":"1 for the first attempt, then 2, 3, ..."},"name":"imentiv-delivery-attempt","in":"header"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudioFailedEvent"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"AIInsightsChatMessage":{"properties":{"role":{"type":"string","title":"Role","description":"Message role: 'user' or 'ai_assistant'"},"text":{"type":"string","title":"Text","description":"Message content"},"message_id":{"type":"integer","title":"Message Id","description":"Message ID"},"timestamp":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Timestamp","description":"ISO 8601 timestamp"}},"type":"object","required":["role","text","message_id"],"title":"AIInsightsChatMessage","description":"Individual message in conversation history.\n\nAttributes:\n    role (str): Message role - \"user\" or \"ai_assistant\".\n    text (str): Message text content.\n    message_id (int): Message ID within the conversation.\n    timestamp (Optional[str]): ISO 8601 timestamp when message was created."},"AIInsightsChatRequest":{"properties":{"message":{"type":"string","maxLength":10000,"minLength":1,"title":"Message","description":"User's question or message about the media content"}},"type":"object","required":["message"],"title":"AIInsightsChatRequest","description":"Request model for sending a message to AI Insights (REST API).\n\nThe system automatically manages conversation continuity - one conversation per user per media item.\n\nAttributes:\n    message (str): The user's question or message about the media content."},"AIInsightsChatResponse":{"properties":{"conversation_id":{"type":"string","title":"Conversation Id","description":"Conversation session ID"},"message_id":{"type":"integer","title":"Message Id","description":"Message ID within conversation"},"response":{"type":"string","title":"Response","description":"AI assistant's complete response"},"token_usage":{"anyOf":[{"additionalProperties":{"type":"integer"},"type":"object"},{"type":"null"}],"title":"Token Usage","description":"Token usage (input_tokens, output_tokens)"},"is_quota_exceeded":{"type":"boolean","title":"Is Quota Exceeded","description":"Whether LLM quota exceeded","default":false},"credits_charged":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Credits Charged","description":"Credits deducted for this turn — the amount the ledger actually moved, so it may be fractional when the charge was clamped to the remaining balance. Null if charging was skipped (e.g. error path before the stream completed)."}},"type":"object","required":["conversation_id","message_id","response"],"title":"AIInsightsChatResponse","description":"Response model for AI Insights chat (non-streaming mode with ?stream=false).\n\nAttributes:\n    conversation_id (str): Unique identifier for this conversation session.\n    message_id (int): Sequential message ID within the conversation.\n    response (str): The complete AI assistant's response text.\n    token_usage (Optional[Dict[str, int]]): LLM token usage statistics\n                                             (input_tokens, output_tokens).\n    is_quota_exceeded (bool): Whether the user's LLM quota has been exceeded."},"AIInsightsHistoryResponse":{"properties":{"conversation_id":{"type":"string","title":"Conversation Id","description":"Conversation session ID"},"messages":{"items":{"$ref":"#/components/schemas/AIInsightsChatMessage"},"type":"array","title":"Messages","description":"Conversation message history"}},"type":"object","required":["conversation_id","messages"],"title":"AIInsightsHistoryResponse","description":"Response model for retrieving AI Insights conversation history.\n\nAttributes:\n    conversation_id (str): The conversation session ID.\n    messages (List[AIInsightsChatMessage]): List of messages in chronological order\n                                             (user messages and AI responses)."},"AddFaceResponse":{"properties":{"id":{"type":"string","title":"Id"},"face_id":{"type":"integer","title":"Face Id"},"name":{"type":"string","title":"Name"}},"type":"object","required":["id","face_id","name"],"title":"AddFaceResponse","description":"Response model after adding a placeholder face to a video.\n\nAttributes:\n    id (str): Video ID the face was added to.\n    face_id (int): Auto-allocated id (monotonic; never reuses ids freed by delete).\n    name (str): Persisted name (trimmed; may be empty — FE renders \"Face {id}\" fallback)."},"AddSpeakerResponse":{"properties":{"id":{"type":"string","title":"Id"},"speaker_id":{"type":"integer","title":"Speaker Id"},"name":{"type":"string","title":"Name"}},"type":"object","required":["id","speaker_id","name"],"title":"AddSpeakerResponse","description":"Response model after adding a placeholder speaker to an audio.\n\nAttributes:\n    id (str): Audio ID the speaker was added to.\n    speaker_id (int): Auto-allocated id (monotonic; never reuses ids freed by delete).\n    name (str): Persisted name (trimmed; may be empty — FE renders \"Speaker {id}\" fallback)."},"AnnotatedVideoError":{"properties":{"code":{"type":"string","title":"Code","description":"Machine-readable reason","examples":["frames_unavailable","no_frames","worker_lost"]},"message":{"type":"string","title":"Message","description":"Short human-readable reason","default":""},"retryable":{"type":"boolean","title":"Retryable","description":"Whether requesting the annotated video again can succeed","default":true}},"type":"object","required":["code"],"title":"AnnotatedVideoError","description":"Typed reason an annotated video job failed. ``retryable`` is false when asking again cannot succeed."},"AnnotatedVideoResponse":{"properties":{"video_id":{"type":"string","title":"Video Id"},"message":{"type":"string","title":"Message"},"status":{"type":"string","title":"Status"},"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Id"},"stage":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stage"},"percentage":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Percentage"}},"type":"object","required":["video_id","message","status"],"title":"AnnotatedVideoResponse","description":"Response for annotated video generation status.\n\nAttributes:\n    video_id (str): ID of the video.\n    message (str): Status message.\n    status (str): Current status of annotated video generation (pending, in progress, completed).\n    job_id (Optional[str]): Identity of the generation job this answer describes. A retry after a\n        failure gets a new one; progress and stage always belong to the job named here.\n    stage (Optional[str]): Where the job is: queued, starting, fetching_frames, downloading_source,\n        extracting_audio, rendering, muxing_audio, uploading, finalizing.\n    percentage (Optional[int]): Whole-percent progress of the render, when the job reports it."},"ArtifactError":{"properties":{"code":{"type":"string","title":"Code"},"message":{"type":"string","title":"Message"},"stage":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stage"}},"type":"object","required":["code","message"],"title":"ArtifactError"},"ArtifactUsage":{"properties":{"tokens_by_model":{"additionalProperties":{"additionalProperties":{"type":"integer"},"type":"object"},"type":"object","title":"Tokens By Model"},"credits_charged":{"type":"integer","title":"Credits Charged","description":"Credits charged for producing this result; 0 on a cached hit.","default":0}},"type":"object","title":"ArtifactUsage"},"Audio":{"properties":{"created_at":{"anyOf":[{},{"type":"null"}],"title":"Created At"},"status":{"type":"string","title":"Status"},"id":{"type":"string","title":"Id"},"model_id":{"type":"string","title":"Model Id","default":""},"url":{"type":"string","title":"Url"},"audio_duration":{"type":"number","title":"Audio Duration"},"task_completion_status":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Task Completion Status"},"user_id":{"type":"string","title":"User Id"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"average_audio_emotions":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Average Audio Emotions"},"segment_audio_emotions":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Segment Audio Emotions"},"segment_text_emotions":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Segment Text Emotions"},"summary":{"anyOf":[{"type":"string"},{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Summary"},"speakers":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Speakers"},"audio_type":{"type":"string","title":"Audio Type","default":"generic"},"llm_audio_policy":{"type":"string","title":"Llm Audio Policy","default":"off"}},"type":"object","required":["status","id","url","audio_duration","user_id"],"title":"Audio","description":"Model representing audio metadata and analysis results.\n\nAttributes:\n    created_at (Optional[Any]): Timestamp when the audio was created.\n    status (str): Current processing status of the audio.\n    id (str): Unique identifier for the audio.\n    model_id (str): Models used to process audio.\n    url (str): Storage URL of the audio file.\n    audio_duration (float): Duration of the audio in seconds.\n    task_completion_status (Optional[int]): Status code indicating task completion.\n    user_id (str): ID of the user who uploaded the audio.\n    title (Optional[str]): Optional title for the audio.\n    average_audio_emotions (Optional[dict]): Average emotion scores for the audio.\n    segment_audio_emotions (Optional[list[dict]]): Emotions per audio segment.\n    segment_text_emotions (Optional[list[dict]]): Text-based emotions per segment.\n    summary (Optional[Union[str, Dict[str, Any]]]): Text summary of the audio (string or dict with title, status, summary_body).\n    speakers (Optional[list[dict]]): Detected speaker information.\n    audio_type (str): Declared recording type. Always a string; generic when absent.\n    llm_audio_policy (str): Backend-stamped audio-LLM policy. Always a string; off when absent."},"AudioDeleteResponse":{"properties":{"id":{"type":"string","title":"Id"},"status":{"type":"string","title":"Status"}},"type":"object","required":["id","status"],"title":"AudioDeleteResponse","description":"Response model after deleting audio.\n\nInherits:\n    ResponseStatus: Generic success/error status model."},"AudioDetailResponseV2":{"properties":{"languages":{"items":{"type":"string"},"type":"array","title":"Languages","description":"Detected languages in stored dominant-first order; empty when unavailable. Short stretches may have line language marks without appearing in this list. This is an output summary, independent of the expected_languages input hint.","default":[],"readOnly":true,"examples":[["en","ja"]]},"audio_duration":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Audio Duration","description":"Audio duration in seconds"},"audio_model_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Audio Model Id","description":"Model used for audio processing"},"audio_type":{"type":"string","title":"Audio Type","description":"Declared recording type (not a file format): generic or contact_center_call. Absent on old documents is generic. Always a string; never null.","default":"generic"},"auto_translation":{"anyOf":[{"$ref":"#/components/schemas/AutoTranslation"},{"type":"null"}],"description":"What happened when this finished run was asked to translate the lines that are not in the audio's main language into it, which is done on its own for an audio uploaded with `multilingual` on (billed as a translation, on top of transcription). `{language, status}` where `status` is `requested` (a translation was asked for: its own state, `queued` to `ready` or `failed`, is on `GET /v2/audios/{audio_id}/translations`) or `skipped` (none was asked for: the audio still completed, and the translation can be requested from the translations endpoint). `reason`, for the owner only, says why a skipped one was skipped: `insufficient_credits` or `unavailable`. Null when this run asked for nothing: the audio is not `multilingual`, has no line outside its main language, or has no recorded main language; or a translation of the current transcript already exists that someone else asked for, in which case it stands for this one and its state is on the translations endpoint. A reprocess overwrites it."},"average_audio_emotions":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Average Audio Emotions","description":"Average audio emotions"},"bulk_upload_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bulk Upload Id","description":"Bulk upload batch ID"},"channel":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Channel","description":"Worker-written per-speaker channel assignment (A1). Null until written. Visible to share-link readers of GET /v2/audios/{audio_id}, same as other analysis outputs on this model."},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At","description":"Creation timestamp"},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At","description":"ISO-8601 UTC timestamp of the last change to this audio. Compare it for inequality with the value you hold to tell whether your copy is stale; it carries no ordering guarantee. It covers this audio's own fields only, not data derived from other records such as frames or analytics.","examples":["2025-12-01T14:30:00.000000+00:00"]},"csv_analysis_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Csv Analysis Url","description":"CSV analysis file URL"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Audio description"},"emotions":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Emotions","description":"Overall audio emotions"},"expected_languages":{"items":{"type":"string"},"type":"array","title":"Expected Languages","description":"The languages you said the audio may contain, as sent on upload or reprocess (Whisper codes, main language included). This is the INPUT you gave, not the languages detected in the speech. `[]` when none was given.","default":[],"examples":[["en","ja"]]},"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id","description":"Unique audio identifier"},"interpretation":{"anyOf":[{"$ref":"#/components/schemas/AudioInterpretation"},{"type":"null"}],"description":"Derived contact-centre interpretation (register, moments, tone_label, reliability). Null when audio_type is not contact_center_call. Computed on read; never stored. Share-link readers of GET /v2/audios/{audio_id} see it — same visibility as channel / stance. relative is not a distribution; do not feed it to computeEmotionVectors. Stance fields inside this object are process-time."},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language","description":"The audio's language as a BCP-47 tag made of a language, an optional script and an optional region (for example `en` or `pt-BR`), or null when none is recorded."},"language_source":{"type":"string","enum":["user","detected","unknown"],"title":"Language Source","description":"Where `language` came from: `user` when the upload sent one, `detected` when speech recognition filled it in, `unknown` otherwise (including when `language` is null). Always a string; never null.","default":"unknown"},"llm_audio_policy":{"type":"string","title":"Llm Audio Policy","description":"Backend-stamped audio-LLM policy (off, transcript_only, allowed). Not client-writable. Absent is off. Always a string; never null.","default":"off"},"media_source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Media Source","description":"Source type: youtube, dropbox, public_url, file_upload"},"media_source_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Media Source Url","description":"The URL the media was submitted with. For an upload this is a reference to the stored media, not guaranteed to be readable; use the media's `playback-url` endpoint to play it back."},"multilingual":{"type":"boolean","title":"Multilingual","description":"Whether mixed-language transcription was requested for this audio. This is the INPUT you gave, not a detection result. `false` when it was not requested. When on, the lines that are not in the audio's main language are also translated into it once the audio completes (see `auto_translation`), billed as a translation on top of transcription.","default":false,"examples":[true]},"parent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Id","description":"Parent video ID if extracted from video"},"predefined_qa":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Predefined Qa","description":"Predefined Q&A analysis"},"process_status":{"anyOf":[{"type":"number"},{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Process Status","description":"Processing progress"},"project_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id","description":"Associated project ID. Empty/absent for library audio."},"project_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Type","description":"Type of the associated project (sales_deal/...). Null for library audio."},"psychologist_review":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Psychologist Review","description":"Psychologist review content"},"psychologist_review_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Psychologist Review Status","description":"Psychologist review status"},"segment_audio_emotions":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Segment Audio Emotions","description":"Segment-level audio emotions"},"segment_text_emotions":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Segment Text Emotions","description":"Segment-level text emotions"},"share_status":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Share Status","description":"Whether audio is shared"},"diarization":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Diarization","description":"Whether diarization is enabled (defaults to true if absent from the stored document)"},"has_transcript":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Transcript","description":"Whether a transcript exists (or is being produced): our ASR ran, OR one was supplied and applied. Drive the transcript UI off THIS, not `transcription` — an imported call has transcription=false and a real transcript, so that flag hides one that is right there."},"text_speaker_id_overall_emotions":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Text Speaker Id Overall Emotions","description":"Speaker-level aggregated emotions"},"speakers":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Speakers","description":"Identified speakers"},"speech_status":{"anyOf":[{"type":"string"},{"type":"boolean"},{"type":"null"}],"title":"Speech Status","description":"Speech transcription status"},"stance":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Stance","description":"Worker-written stance measurement (C1). Null until written, and null when audio_type is not contact_center_call. Share-link readers of GET /v2/audios/{audio_id} see it when present — same visibility as the other analysis outputs on this anonymously-readable endpoint."},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","description":"Processing status"},"summary":{"anyOf":[{"type":"string"},{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Summary","description":"Audio summary or summary object"},"system_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"System Name","description":"System name that processed the audio"},"tags":{"anyOf":[{"items":{"oneOf":[{"$ref":"#/components/schemas/StringTag"},{"$ref":"#/components/schemas/EnumTag"},{"$ref":"#/components/schemas/IntegerTag"},{"$ref":"#/components/schemas/BoolTag"},{"$ref":"#/components/schemas/DateTag"},{"$ref":"#/components/schemas/GeoTag"},{"$ref":"#/components/schemas/SetTag"},{"$ref":"#/components/schemas/NoneTag"}],"discriminator":{"propertyName":"value_type","mapping":{"boolean":"#/components/schemas/BoolTag","date":"#/components/schemas/DateTag","enum":"#/components/schemas/EnumTag","integer":"#/components/schemas/IntegerTag","location_admin":"#/components/schemas/GeoTag","none":"#/components/schemas/NoneTag","set":"#/components/schemas/SetTag","string":"#/components/schemas/StringTag"}}},"type":"array"},{"type":"null"}],"title":"Tags","description":"Generalized media tags (demographic, survey, label, system). Discriminated union over value_type — see /v2/tag-keys for dimension metadata."},"tags_map":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Tags Map","description":"Tags map for categorization"},"text_emotion":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Text Emotion","description":"Whether text emotion analysis was enabled at upload time (defaults to true if absent from the stored document)"},"text_overall_emotions":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Text Overall Emotions","description":"Overall text emotions from transcript"},"text_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text Id","description":"Associated text analysis ID"},"text_model_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text Model Id","description":"Model used for text processing"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"Audio title"},"transcript_segments":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Transcript Segments","description":"Transcript segments with emotions"},"transcription":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Transcription","description":"Whether transcription/ASR was enabled at upload time (defaults to true if absent from the stored document)"},"ui_request":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Ui Request","description":"Whether request came from UI"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url","description":"Stored audio URL. A reference to the stored media, not guaranteed to be readable: use `GET /v2/audios/{audio_id}/playback-url` to play it back."},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id","description":"The user who created the item (the same value as `created_by`)."},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By","description":"The user who created the item. Never changes, including when the item is moved to a workspace."},"owner":{"anyOf":[{"$ref":"#/components/schemas/MediaOwnerOut"},{"type":"null"}],"description":"Who owns the item: the user, or the team workspace that holds it."},"video_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Video Id","description":"Associated video ID if extracted from video"},"text_word_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Text Word Count","description":"Word count of transcript"},"youtube_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube Url","description":"YouTube URL if applicable"}},"type":"object","title":"AudioDetailResponseV2","description":"Complete V2 response model for audio details with all possible fields.\nReturns null for fields that don't exist in the database, except\ndiarization/text_emotion/transcription, which default to true when\nabsent (enabled-by-default speech stages, matching the upload contract),\nand audio_type / llm_audio_policy, which always surface as strings\n(generic / off when absent).\nFields are ordered alphabetically."},"AudioEditResponse":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"}},"type":"object","title":"AudioEditResponse","description":"Response model for editing audio data.\n\nInherits:\n    ImageEditResponse: Contains status and metadata after editing."},"AudioEmotionResponseV2":{"properties":{"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"status":{"type":"string","title":"Status"},"duration":{"type":"integer","title":"Duration"},"media_source":{"type":"string","title":"Media Source"},"media_source_url":{"type":"string","title":"Media Source Url"},"media_source_type":{"type":"string","title":"Media Source Type"},"user_id":{"type":"string","title":"User Id"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"diarization":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Diarization"},"text_emotion":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Text Emotion"},"transcription":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Transcription"},"speaker_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Speaker Count"}},"type":"object","required":["id","title","status","duration","media_source","media_source_url","media_source_type","user_id"],"title":"AudioEmotionResponseV2","description":"V2 response model for audio emotion detection with URL source tracking.\n\nSupports:\n- YouTube URLs (audio extracted from video)\n- Cloud storage URLs (Dropbox, direct URLs, etc.)\n- Time parameters for all URL types\n- URL source tracking with media type detection\n\nAttributes:\n    id (str): Unique ID of the audio.\n    title (str): Title of the audio.\n    status (str): Processing status (queue, processing, done, etc.).\n    duration (int): Duration in seconds (trimmed duration if time params provided).\n    media_source (str): Source type (\"youtube\", \"dropbox\", \"public_url\", \"file_upload\").\n    media_source_url (str): Full URL as provided by user.\n    media_source_type (str): Type of source media (\"video\" or \"audio\").\n    user_id (str): ID of the user who uploaded the audio.\n    created_at (Optional[str]): Timestamp when audio was created."},"AudioFailedData":{"properties":{"id":{"type":"string","title":"Id","description":"The audio id."},"status":{"type":"string","const":"failed","title":"Status"},"error":{"type":"string","title":"Error","description":"A short, fixed description of the failure."},"error_code":{"type":"string","enum":["processing_failed","timeout"],"title":"Error Code","description":"A stable code to branch on."}},"type":"object","required":["id","status","error","error_code"],"title":"AudioFailedData"},"AudioFailedEvent":{"properties":{"event_id":{"type":"string","title":"Event Id","description":"Unique per event and identical on every delivery attempt. Use it to ignore duplicates."},"timestamp":{"type":"integer","title":"Timestamp","description":"When the event was created, in Unix seconds."},"payload_version":{"type":"string","const":"v1","title":"Payload Version","default":"v1"},"event_type":{"type":"string","const":"audio.failed","title":"Event Type"},"resource_type":{"type":"string","const":"audio","title":"Resource Type"},"data":{"$ref":"#/components/schemas/AudioFailedData"}},"type":"object","required":["event_id","timestamp","event_type","resource_type","data"],"title":"AudioFailedEvent"},"AudioInterpretation":{"properties":{"version":{"type":"string","const":"interp_v1","title":"Version","description":"Interpretation schema version.","default":"interp_v1"},"speakers":{"additionalProperties":{"$ref":"#/components/schemas/InterpretationSpeaker"},"type":"object","title":"Speakers","description":"Per-speaker register, reliability, and C1 summary. Keys are string speaker ids."},"segments":{"items":{"$ref":"#/components/schemas/InterpretationSegment"},"type":"array","title":"Segments","description":"Index-aligned with segment_emotions[] (CSV Index = i+1). Not transcript-sentence index."}},"type":"object","title":"AudioInterpretation","description":"Derived contact-centre interpretation (interp_v1). Computed on read; never stored."},"AudioReprocessResponse":{"properties":{"id":{"type":"string","title":"Id"},"status":{"type":"string","title":"Status"},"message":{"type":"string","title":"Message"}},"type":"object","required":["id","status","message"],"title":"AudioReprocessResponse","description":"Response model for audio reprocessing.\n\nInherits:\n    TextReprocessResponse: Reuse response format for audio reprocessing."},"AudioSegmentDeleteResponse":{"properties":{"id":{"type":"string","title":"Id"},"segment_id":{"type":"integer","title":"Segment Id"},"message":{"type":"string","title":"Message"}},"type":"object","required":["id","segment_id","message"],"title":"AudioSegmentDeleteResponse","description":"Response for deleting an audio segment.\n\nAttributes:\n    id (str): Audio ID the segment belongs to.\n    segment_id (int): Segment number that was deleted.\n    message (str): Status message of the deletion operation."},"AudioTypeUpdate":{"properties":{"audio_type":{"type":"string","enum":["generic","contact_center_call"],"title":"Audio Type"}},"type":"object","required":["audio_type"],"title":"AudioTypeUpdate","description":"Body for ``PUT /v1/audios/{audio_id}/audio-type``."},"AudioTypeUpdateResponse":{"properties":{"id":{"type":"string","title":"Id"},"audio_type":{"type":"string","title":"Audio Type"},"llm_audio_policy":{"type":"string","title":"Llm Audio Policy"}},"type":"object","required":["id","audio_type","llm_audio_policy"],"title":"AudioTypeUpdateResponse","description":"Echo of the public audio_type and the restamped policy after an audio-type PUT."},"AutoSpeakerFaceMatchTriggerRequest":{"properties":{"target_speaker_ids":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Target Speaker Ids"}},"type":"object","title":"AutoSpeakerFaceMatchTriggerRequest","description":"Request body for triggering the automatic speaker-face match worker.\n\nAttributes:\n    target_speaker_ids (Optional[List[int]]): When omitted (or ``null``), behavior is\n        byte-identical to today: every speaker is cleared and eligible for\n        re-derivation. When provided (must be non-empty), only the listed speakers\n        are cleared/re-derived; every other speaker's bound face is left untouched,\n        and no target speaker's resolved match can steal a face currently held by a\n        non-target (\"protected\") speaker. A speaker with no bound face can be\n        targeted — that's the whole point (#1344): it can now be reached even\n        though it has no face_id to key off of."},"AutoSpeakerFaceMatchTriggerResponse":{"properties":{"reason":{"anyOf":[{"type":"string","const":"no_speakers"},{"type":"null"}],"title":"Reason","description":"Completed audio has no eligible speakers; no matching was needed."},"video_id":{"type":"string","title":"Video Id"},"audio_id":{"type":"string","title":"Audio Id"},"status":{"type":"string","title":"Status"},"message":{"type":"string","title":"Message"}},"type":"object","required":["video_id","audio_id","status","message"],"title":"AutoSpeakerFaceMatchTriggerResponse","description":"Response model for triggering the automatic speaker-face match worker.\n\nAttributes:\n    video_id (str): Unique identifier of the video.\n    audio_id (str): Unique identifier of the audio document.\n    status (str): Status of the trigger operation (e.g., \"processing\").\n    message (str): Informational message about the operation."},"AutoTranslation":{"properties":{"language":{"type":"string","title":"Language","description":"The audio's main language the lines were to be translated into.","examples":["en"]},"status":{"type":"string","enum":["requested","skipped"],"title":"Status","description":"`requested` or `skipped`."},"reason":{"anyOf":[{"type":"string","enum":["insufficient_credits","unavailable"]},{"type":"null"}],"title":"Reason","description":"Why a `skipped` one was skipped. Present only for the owner, and only when `status` is `skipped`."}},"type":"object","required":["language","status"],"title":"AutoTranslation","description":"The automatic main-language translation of a mixed-language audio, as the last completed run left it."},"BatchVideoImportFailure":{"properties":{"url":{"type":"string","title":"Url","description":"The URL that failed to import."},"error":{"type":"string","title":"Error","description":"Why it failed (``str(exc)``, not machine-parseable)."}},"type":"object","required":["url","error"],"title":"BatchVideoImportFailure","description":"One URL the batch importer could not accept.\n\nExactly the two keys ``batch_import_videos`` appends\n(``module/project/usecase_controller.py``); it is the sole producer, so this\nis the whole shape rather than a subset of it. Modelled rather than left as a\nbare ``dict`` so generated clients get ``{url, error}`` instead of an opaque\nobject."},"BatchVideoImportRequest":{"properties":{"urls":{"items":{"type":"string"},"type":"array","maxItems":30,"minItems":1,"title":"Urls","description":"Video URLs to import"},"task":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Task","description":"Shared task label for all videos"},"person_id":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}],"title":"Person Id","description":"Optional Person ID to person-scope every imported video — uniform across all project types."}},"type":"object","required":["urls"],"title":"BatchVideoImportRequest"},"BatchVideoImportResponse":{"properties":{"imported":{"type":"integer","title":"Imported"},"failed":{"items":{"$ref":"#/components/schemas/BatchVideoImportFailure"},"type":"array","title":"Failed"},"project":{"$ref":"#/components/schemas/ProjectResponse"}},"type":"object","required":["imported","project"],"title":"BatchVideoImportResponse"},"Body_add_face_v1_videos__video_id__faces_post":{"properties":{"name":{"type":"string","title":"Name","description":"Face name (optional, max 35 chars).","default":""}},"type":"object","title":"Body_add_face_v1_videos__video_id__faces_post"},"Body_add_note_to_specific_face_v1_images__image_id__faces__face_id__note_put":{"properties":{"note":{"type":"string","title":"Note","description":"Note to be updated for the detected face in the image","default":""}},"type":"object","title":"Body_add_note_to_specific_face_v1_images__image_id__faces__face_id__note_put"},"Body_add_speaker_v1_audios__audio_id__speakers_post":{"properties":{"name":{"type":"string","title":"Name","description":"Speaker name (optional, max 35 chars).","default":""}},"type":"object","title":"Body_add_speaker_v1_audios__audio_id__speakers_post"},"Body_delete_faces_v1_videos__video_id__faces_delete":{"properties":{"face_ids":{"type":"string","title":"Face Ids","description":"Comma-separated list of face IDs to delete (e.g., 1,2,3)"}},"type":"object","required":["face_ids"],"title":"Body_delete_faces_v1_videos__video_id__faces_delete"},"Body_delete_speakers_v1_audios__audio_id__speakers_delete":{"properties":{"speaker_ids":{"type":"string","title":"Speaker Ids","description":"Comma-separated list of speaker IDs to delete (e.g., '1,2,3')."}},"type":"object","required":["speaker_ids"],"title":"Body_delete_speakers_v1_audios__audio_id__speakers_delete"},"Body_edit_face_note_v1_videos__video_id__frames__frame_id__faces__face_id__note_put":{"properties":{"note":{"type":"string","title":"Note","description":"The note to attach to the face or frame."}},"type":"object","required":["note"],"title":"Body_edit_face_note_v1_videos__video_id__frames__frame_id__faces__face_id__note_put"},"Body_edit_image_metadata_v1_images__image_id__metadata_put":{"properties":{"title":{"type":"string","title":"Title","default":""},"description":{"type":"string","title":"Description","default":""},"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url"},"image_url":{"type":"string","title":"Image Url","default":""}},"type":"object","title":"Body_edit_image_metadata_v1_images__image_id__metadata_put"},"Body_edit_text_v1_texts__text_id__emotions_put":{"properties":{"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url","description":"Optional URL to receive a webhook notification when analysis is complete. Example: 'https://example.com/callback'. Must be an https URL on a publicly reachable host."},"text":{"type":"string","title":"Text","description":"The updated text to be analyzed for emotions."}},"type":"object","required":["text"],"title":"Body_edit_text_v1_texts__text_id__emotions_put"},"Body_edit_title_or_description_by_id_v1_videos__video_id__metadata_put":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"Title to be changed for the video (Max: 150 characters). Omit to leave unchanged."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Description to be changed for the video (Max: 300 characters). Omit to leave unchanged; send an empty value to clear it."}},"type":"object","title":"Body_edit_title_or_description_by_id_v1_videos__video_id__metadata_put"},"Body_edit_title_or_description_v1_audios__audio_id__metadata_put":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"New title for the audio file. Omit to leave unchanged."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Description to add or update. Omit to leave unchanged; send an empty value to clear it."}},"type":"object","title":"Body_edit_title_or_description_v1_audios__audio_id__metadata_put"},"Body_generate_video_emotion_analysis_report_v1_videos__video_id__report_post":{"properties":{"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url","description":"The callback URL to notify when video CSV processing is completed. Provide a valid URL. Must be an https URL on a publicly reachable host."}},"type":"object","title":"Body_generate_video_emotion_analysis_report_v1_videos__video_id__report_post"},"Body_get_upload_url_v2__media_type__upload_url_post":{"properties":{"file_extension":{"type":"string","title":"File Extension","description":"File extension without the dot (e.g., 'mp4', 'mp3')."},"duration_seconds":{"type":"number","exclusiveMinimum":0.0,"title":"Duration Seconds","description":"Duration of the media file in seconds. Required for credit pre-check."},"file_size_bytes":{"type":"integer","maximum":2147483648.0,"exclusiveMinimum":0.0,"title":"File Size Bytes","description":"File size in bytes. Maximum allowed: 2 GB (2147483648 bytes)."}},"type":"object","required":["file_extension","duration_seconds","file_size_bytes"],"title":"Body_get_upload_url_v2__media_type__upload_url_post"},"Body_merge_face_v1_videos__video_id__faces_put":{"properties":{"selected_face_id":{"type":"integer","title":"Selected Face Id","description":"The unique face ID of the face in the video to be selected."},"merge_face_id":{"type":"string","title":"Merge Face Id","description":"A comma-separated list of unique face IDs of the faces in the video to be merged."}},"type":"object","required":["selected_face_id","merge_face_id"],"title":"Body_merge_face_v1_videos__video_id__faces_put"},"Body_merge_speakers_v1_audios__audio_id__speakers_put":{"properties":{"selected_speaker_id":{"type":"integer","title":"Selected Speaker Id","description":"The unique speaker ID to merge other speakers into."},"merge_speaker_id":{"type":"string","title":"Merge Speaker Id","description":"A comma-separated list of speaker IDs to merge into selected_speaker_id."}},"type":"object","required":["selected_speaker_id","merge_speaker_id"],"title":"Body_merge_speakers_v1_audios__audio_id__speakers_put"},"Body_process_highlight_v1_videos_highlights_post":{"properties":{"video_id":{"type":"string","title":"Video Id","description":"The unique video ID of the video for which the highlight to be generated."}},"type":"object","required":["video_id"],"title":"Body_process_highlight_v1_videos_highlights_post"},"Body_reprocess_audio_v1_audios__audio_id__put":{"properties":{"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url","description":"The callback url to notify audio reprocessing is completed. Give a valid URL. Must be an https URL on a publicly reachable host."},"reprocess":{"type":"boolean","title":"Reprocess","description":"Set to 'True' to reprocess an already completed audio; defaults to 'False'.","default":false},"transcription":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Transcription","description":"Turn speech-to-text on or off for this run. Omit to keep the current setting."},"diarization":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Diarization","description":"Turn speaker diarization on or off for this run. Omit to keep the current setting."},"text_emotion":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Text Emotion","description":"Turn transcript emotion analysis on or off for this run. Requires transcription (or an already-applied transcript). Omit to keep the current setting."},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language","description":"Language of the audio as a language code such as `hu` or `pt-BR` (a region is ignored, so `zh-yue` becomes `zh`: send `yue` for Cantonese). Use it when automatic detection got the language wrong. Omit to auto-detect. A later reprocess of this audio that omits it auto-detects the audio again. For a video's companion audio the choice is also recorded on the video, so a later reprocess of the video re-sends it, and omitting it here does not clear that. Unsupported codes are refused with 422 `invalid_language`. With `expected_languages` it must be one of them."},"multilingual":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Multilingual","description":"Opt in to mixed-language transcription: the speech may switch language, and each stretch is transcribed in the language it is spoken in. Once the audio completes, the lines that are not in its main language are also translated into the main language and billed as a translation, on top of transcription (`auto_translation` reports it). This is input only; the languages actually detected are reported separately. Omitted means no hint: the audio is analysed again without one, and any stored hint is cleared."},"expected_languages":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expected Languages","description":"Comma-separated codes of the languages the audio may contain, the main one included (e.g. `en,ja`), 1 to 5 after duplicates are removed. Case and whitespace are ignored, a region is dropped (`pt-BR` is `pt`) and an alias maps to the Whisper spelling (`iw` is `he`); Cantonese is `yue`. Language detection is restricted to the set, with or without `multilingual`. If `language` is also sent it must be one of them (422 `invalid_language` otherwise). If `language` is omitted, detection still runs, restricted to the set. An unknown code, or more than 5, is refused with 422 `invalid_language` before anything is charged. Omitted means no hint: the audio is analysed again without one, and any stored hint is cleared."}},"type":"object","title":"Body_reprocess_audio_v1_audios__audio_id__put"},"Body_reprocess_image_v1_images__image_id__put":{"properties":{"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url","description":"The callback url to notify image reprocessing is completed. Give a valid URL. Must be an https URL on a publicly reachable host."},"reprocess":{"type":"boolean","title":"Reprocess","description":"Set to 'True' to reprocess an already completed image; defaults to 'False'.","default":false}},"type":"object","title":"Body_reprocess_image_v1_images__image_id__put"},"Body_reprocess_text_v1_texts__text_id__put":{"properties":{"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url","description":"The url to notify when text processing is completed. Must be an https URL on a publicly reachable host."},"reprocess":{"type":"boolean","title":"Reprocess","description":"Set to 'True' to reprocess an already processed text; defaults to 'False'.","default":false}},"type":"object","title":"Body_reprocess_text_v1_texts__text_id__put"},"Body_reprocess_video_v1_videos__video_id__put":{"properties":{"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url","description":"The callback url to notify video reprocessing is completed. Give a valid URL. Must be an https URL on a publicly reachable host."},"reprocess":{"type":"boolean","title":"Reprocess","description":"Set to 'True' to reprocess an already completed video; defaults to 'False'.","default":false},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language","description":"Language of the video's audio track as a language code such as `hu` or `pt-BR` (a region is ignored, so `zh-yue` becomes `zh`: send `yue` for Cantonese). Use it when automatic detection got the language wrong. Omit to keep the video's current language (auto-detect if it has none). Unlike the audio endpoint, a later reprocess that omits it re-sends this language rather than auto-detecting again. Unsupported codes are refused with 422 `invalid_language`. With `expected_languages` (sent or stored) it must be one of them."},"multilingual":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Multilingual","description":"Opt in to mixed-language transcription: the speech may switch language, and each stretch is transcribed in the language it is spoken in. Once the audio completes, the lines that are not in its main language are also translated into the main language and billed as a translation, on top of transcription (`auto_translation` reports it). This is input only; the languages actually detected are reported separately. Omitted keeps what is stored. `false` turns it off."},"expected_languages":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expected Languages","description":"Comma-separated codes of the languages the audio may contain, the main one included (e.g. `en,ja`), 1 to 5 after duplicates are removed. Case and whitespace are ignored, a region is dropped (`pt-BR` is `pt`) and an alias maps to the Whisper spelling (`iw` is `he`); Cantonese is `yue`. Language detection is restricted to the set, with or without `multilingual`. If `language` is also sent it must be one of them (422 `invalid_language` otherwise). If `language` is omitted, detection still runs, restricted to the set. An unknown code, or more than 5, is refused with 422 `invalid_language` before anything is charged. Omitted keeps what is stored. Send the field empty to remove the stored set."}},"type":"object","title":"Body_reprocess_video_v1_videos__video_id__put"},"Body_trigger_personality_analysis_v2_videos__video_id__personality_analyze_post":{"properties":{"face_id":{"type":"integer","title":"Face Id","description":"Face identifier from video analysis to perform personality assessment on. Must exist in the video's detected faces."},"analysis_type":{"type":"string","title":"Analysis Type","description":"Type of personality analysis: 'overall', 'by_segment', or 'combined'. Default is 'overall'.","default":"overall"},"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url","description":"Optional webhook URL to receive notification when personality analysis completes. Webhook will receive the analysis results as JSON payload. Must be an https URL on a publicly reachable host."}},"type":"object","required":["face_id"],"title":"Body_trigger_personality_analysis_v2_videos__video_id__personality_analyze_post"},"Body_trim_video_endpoint_v1_videos__video_id__trim_put":{"properties":{"start_millis":{"type":"integer","minimum":0.0,"title":"Start Millis","description":"Kept-window start in ms (seconds trimmed off the beginning)."},"end_millis":{"type":"integer","exclusiveMinimum":0.0,"title":"End Millis","description":"Kept-window end in ms (> start_millis, <= video duration)."},"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url","description":"Optional callback url notified when reprocessing completes. Must be an https URL on a publicly reachable host."}},"type":"object","required":["start_millis","end_millis"],"title":"Body_trim_video_endpoint_v1_videos__video_id__trim_put"},"Body_update_emotion_per_frame_v1_videos__video_id__frames__frame_id__faces__face_id__emotions_put":{"properties":{"angry":{"type":"number","title":"Angry","default":0},"contempt":{"type":"number","title":"Contempt","default":0},"disgust":{"type":"number","title":"Disgust","default":0},"fear":{"type":"number","title":"Fear","default":0},"happy":{"type":"number","title":"Happy","default":0},"neutral":{"type":"number","title":"Neutral","default":0},"sad":{"type":"number","title":"Sad","default":0},"surprise":{"type":"number","title":"Surprise","default":0}},"type":"object","title":"Body_update_emotion_per_frame_v1_videos__video_id__frames__frame_id__faces__face_id__emotions_put"},"Body_update_multiple_faces_v1_videos__video_id__frames_faces_put":{"properties":{"frame_ids":{"type":"string","title":"Frame Ids","description":"The comma separated list of frame IDs to be updated."},"current_face_id":{"type":"integer","title":"Current Face Id","description":"The face ID to be replaced."},"new_face_id":{"type":"integer","title":"New Face Id","description":"The new face ID to replace with."}},"type":"object","required":["frame_ids","current_face_id","new_face_id"],"title":"Body_update_multiple_faces_v1_videos__video_id__frames_faces_put"},"Body_upload_audio_v2_audios_post":{"properties":{"title":{"type":"string","minLength":1,"title":"Title","description":"A descriptive title for your audio (e.g., 'Customer Call' or 'Team Meeting')."},"description":{"type":"string","title":"Description","description":"Optional notes about the audio content or context.","default":""},"media_url":{"type":"string","title":"Media Url","description":"URL to your audio/video source (YouTube, Instagram, Twitter, Facebook, Dropbox, or any direct media link). Audio will be extracted if source is video. Leave empty if uploading a file instead.","default":""},"start_millis":{"type":"number","title":"Start Millis","description":"Start point in milliseconds to begin analysis. Use 0 to start from the beginning. If provided (>0), end_millis is required with minimum 1-second (1000ms) gap.","default":0},"end_millis":{"type":"number","title":"End Millis","description":"End point in milliseconds to stop analysis. Use 0 only when analyzing the full audio (start_millis must also be 0). If start_millis is provided, end_millis is required and must be at least 1000ms greater than start_millis.","default":0},"callback_url":{"type":"string","title":"Callback Url","description":"Your webhook URL to receive a notification when analysis completes. Must be an https URL on a publicly reachable host.","default":""},"audio_file":{"type":"string","format":"binary","title":"Audio File","description":"Upload an audio file directly (mp3, wav, aac, m4a). File upload takes priority over media_url."},"diarization":{"type":"boolean","title":"Diarization","description":"Speaker diarization. Enabled by default. Disabling this stage reduces the per-second credit cost.","default":true},"text_emotion":{"type":"boolean","title":"Text Emotion","description":"Text emotion analysis. Enabled by default. Requires a transcript: ignored (forced off) when transcription is disabled and you supply no `transcript`. Supplying a `transcript` is the exception — the words already exist, so this is honoured as you sent it. Disabling this stage reduces the per-second credit cost.","default":true},"transcription":{"type":"boolean","title":"Transcription","description":"Transcript / ASR generation. Enabled by default. When disabled, no transcript is produced and text_emotion is forced off. Supplying a `transcript` overrides this field entirely: our ASR is skipped either way, but your transcript is used and text_emotion still applies. Disabling this stage reduces the per-second credit cost.","default":true},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language","description":"Language code for analysis. Auto-detected if not provided. With `expected_languages` it must be one of them."},"multilingual":{"type":"boolean","title":"Multilingual","description":"Opt in to mixed-language transcription: the speech may switch language, and each stretch is transcribed in the language it is spoken in. Once the audio completes, the lines that are not in its main language are also translated into the main language and billed as a translation, on top of transcription (`auto_translation` reports it). This is input only; the languages actually detected are reported separately.","default":false},"expected_languages":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expected Languages","description":"Comma-separated codes of the languages the audio may contain, the main one included (e.g. `en,ja`), 1 to 5 after duplicates are removed. Case and whitespace are ignored, a region is dropped (`pt-BR` is `pt`) and an alias maps to the Whisper spelling (`iw` is `he`); Cantonese is `yue`. Language detection is restricted to the set, with or without `multilingual`. If `language` is also sent it must be one of them (422 `invalid_language` otherwise). If `language` is omitted, detection still runs, restricted to the set. An unknown code, or more than 5, is refused with 422 `invalid_language` before anything is charged."},"transcript":{"type":"string","title":"Transcript","description":"Optional: a transcript you already have for this audio. When supplied we skip our own speech-to-text, use your transcript's speaker turns as the diarization, and name the speakers from it — so results come back with real participant names. Supplying one overrides `transcription`: our ASR is skipped either way, and you are charged less because that stage is not billed. A start_millis/end_millis trim is rejected: timestamps would not line up with the trimmed audio. A media_url source is accepted — we download it first, then apply your transcript once processing completes. That apply then delivers `callback_url` (success or failure). An uploaded file has no download wait: it applies in the same request and its callback fires normally.","default":""},"transcript_format":{"type":"string","title":"Transcript Format","description":"Format of `transcript`. 'auto' (the default) detects it. Supported: 'auto', 'text', 'tldv_json', 'teams_vtt', 'zoom_vtt', 'vtt', 'srt'. Works with tl;dv, Zoom, Microsoft Teams, Otter, Fathom, Tactiq and most .vtt/.srt exports. Google Meet's own transcript is not supported yet — it timestamps only every 5 minutes, which cannot be aligned to the audio.","default":"auto"},"transcript_offset_millis":{"type":"integer","title":"Transcript Offset Millis","description":"Milliseconds added to every timestamp in `transcript` to place it on the media timeline. Positive means the transcript starts later than the recording (transcription began after record). Negative means it starts earlier (transcription running before record). 0 (the default) is today's behaviour. A negative offset that places any turn before 0 ms is rejected. Ignored when no `transcript` is supplied.","default":0},"audio_type":{"type":"string","enum":["generic","contact_center_call"],"title":"Audio Type","description":"Declared recording type (not a file format). 'generic' (the default) is a generic recording: no stance pass. 'contact_center_call' marks a support/sales call so speaker roles and stance analysis can apply. Omit (or send empty) for generic. Unrecognised values — including the spelling 'contact_centre_call' — are rejected. A call uploaded as video starts generic; PUT the companion audio id after processing completes. POST /v1/audios does not accept audio_type — tag afterwards via PUT /v1/audios/{id}/audio-type.","default":"generic"}},"type":"object","required":["title"],"title":"Body_upload_audio_v2_audios_post"},"Body_upload_image_v2_images_post":{"properties":{"title":{"type":"string","maxLength":150,"minLength":1,"title":"Title","description":"A descriptive title for your image (e.g., 'Customer Reaction' or 'Team Photo')."},"description":{"type":"string","maxLength":300,"title":"Description","description":"Optional notes about the image content or context.","default":""},"image_url":{"type":"string","title":"Image Url","description":"URL to your image source (Dropbox or any direct image link). Leave empty if uploading a file instead.","default":""},"callback_url":{"type":"string","title":"Callback Url","description":"Your webhook URL to receive a notification when analysis completes. Must be an https URL on a publicly reachable host.","default":""},"image_file":{"type":"string","format":"binary","title":"Image File","description":"Upload an image file directly (jpg, jpeg, png, webp). File upload takes priority over image_url."}},"type":"object","required":["title"],"title":"Body_upload_image_v2_images_post"},"Body_upload_text_v2_texts_post":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"A descriptive title for your text (e.g., 'Customer Feedback' or 'Meeting Notes')."},"text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text","description":"The raw text content to be analyzed for emotions. Provide exactly one of 'text', 'text_url', or 'text_file'."},"text_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text Url","description":"Cloud storage URL to a text file (.txt, .srt, .pdf). Supported: Dropbox, Google Drive, AWS S3, or direct public URLs. Provide exactly one of 'text', 'text_url', or 'text_file'."},"text_file":{"type":"string","format":"binary","title":"Text File","description":"Upload a text file (.txt, .srt, .pdf) directly. Provide exactly one of 'text', 'text_url', or 'text_file'."},"callback_url":{"type":"string","title":"Callback Url","description":"Your webhook URL to receive a notification when analysis completes. Must be an https URL on a publicly reachable host.","default":""}},"type":"object","title":"Body_upload_text_v2_texts_post"},"Body_upload_video_v2_videos_post":{"properties":{"title":{"type":"string","minLength":1,"title":"Title","description":"A descriptive title for your video (e.g., 'Team Meeting' or 'Customer Interview')."},"description":{"type":"string","title":"Description","description":"Optional notes about the video content or context.","default":""},"video_url":{"type":"string","title":"Video Url","description":"URL to your video. Supported sources: YouTube, Dropbox, Instagram, Facebook, Twitter (X), or any direct video link. Leave empty if uploading a file instead. ","default":""},"start_millis":{"type":"number","title":"Start Millis","description":"Start point in milliseconds to begin analysis. Use 0 to start from the beginning. If provided (>0), end_millis is required with minimum 1-second (1000ms) gap.","default":0},"end_millis":{"type":"number","title":"End Millis","description":"End point in milliseconds to stop analysis. Use 0 only when analyzing the full video (start_millis must also be 0). If start_millis is provided, end_millis is required and must be at least 1000ms greater than start_millis.","default":0},"callback_url":{"type":"string","title":"Callback Url","description":"Your webhook URL to receive a notification when analysis completes. Must be an https URL on a publicly reachable host.","default":""},"video_file":{"type":"string","format":"binary","title":"Video File","description":"Upload a video file directly (mp4, avi, mov, webm, mpeg). File upload takes priority over video_url."},"project_id":{"type":"string","title":"Project Id","description":"Optional ID of a project you may modify (normally one you own). For an audience panel or user testing project, this video becomes its primary video. For a collection, this video is added to the collection; a delegated access token also needs the projects.write scope for that, and is refused with 403 insufficient_scope without it. An unknown project, or one you may not modify, returns 404. Any other project type returns 422: upload without project_id, then link the video to the project.","default":""},"processing_fps":{"anyOf":[{"type":"integer","enum":[15,10,5,1]},{"type":"null"}],"title":"Processing Fps","description":"Reduced processing frame rate (15, 10, 5, or 1 fps) that lowers the credit cost. Omit (or send empty/0) for full/native fps (no discount)."},"video_type":{"type":"string","enum":["generic","online_meeting","auto","in_vehicle"],"title":"Video Type","description":"Processing mode for face analysis. 'generic' (the default) skips on-screen name OCR. 'online_meeting' OCRs the participant-name labels a meeting client draws on each tile. 'auto' asks the library to decide from a bounded probe of the opening and reports the resolved mode back; it is opt-in and never the default, because its precision on non-meeting video is not yet characterised. 'in_vehicle' runs EXPERIMENTAL driver-state analysis (drowsiness and distraction) and requires the driver_state feature; 'auto' never resolves to it. Omit (or send empty) for generic. Unrecognised values — including the retired spelling 'meeting' — are rejected.","default":"generic"},"meeting_platform":{"anyOf":[{"type":"string","enum":["zoom","google_meet","teams","webex"]},{"type":"null"}],"title":"Meeting Platform","description":"Optional meeting-client hint (zoom, google_meet, teams, webex) that helps the OCR pipeline. Omit (or send empty) if unknown. Not required: the library is client-agnostic without it. Stored independently of video_type; the worker ignores the hint unless video_type is online_meeting."},"use_case":{"anyOf":[{"type":"string","enum":["interview","product_testing","sales_call"]},{"type":"null"}],"title":"Use Case","description":"Optional: what the recording is. 'interview': a question-and-answer session, where moments rank slow answers and drops in engagement first. 'product_testing': viewers reacting to things shown in the video, ranked by their reaction to each stimulus; it needs stimuli. 'sales_call': a sales conversation, where moments focus on the customer's reactions and shifts in momentum. Omit when unsure. A project whose type selects its own analysis, or video_type='in_vehicle', takes precedence."},"sales_call":{"type":"boolean","title":"Sales Call","description":"Place this video in your sales calls, a sales_deal project, so it is left out of the Default Library. The project is created the first time you ask. A delegated token needs only media.write. Used when project_id is omitted. A project_id this upload can link is used instead; any other project_id is still refused.","default":false},"moments":{"type":"boolean","title":"Moments","description":"Also run Moments on this video once its analysis finishes. If the video's first analysis fails, Moments is cancelled and you are not billed for it; once the video has been reprocessed, a later failure does not cancel it. Leave it off to request Moments later.","default":false},"driver_position":{"anyOf":[{"type":"string","enum":["left","right"]},{"type":"null"}],"title":"Driver Position","description":"Optional seat hint for video_type='in_vehicle': which half of the VIDEO FRAME the driver appears in ('left' or 'right') — not the vehicle's drive side. Answer it by looking at the footage; it stays correct whatever the camera orientation or mirroring, whereas a drive-side answer does not and a wrong one silently analyses the passenger. Omit if unsure: the pipeline then picks the driver from track persistence and face size alone. Ignored unless video_type is in_vehicle."},"driver_forward_gaze":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Driver Forward Gaze","description":"Optional, video_type='in_vehicle' only: the direction the driver looks when looking straight ahead at the road, so driver attention can be measured from the first frame instead of being estimated from the footage. A JSON object {\"yaw_deg\": <number>, \"pitch_deg\": <number>} in degrees, camera-relative, in the same axes and sign as the gaze_yaw / gaze_pitch values returned for faces. yaw_deg must be within [-90, 90] and pitch_deg within [-60, 60]; both keys are required and no other keys are accepted. Example: {\"yaw_deg\": -20.0, \"pitch_deg\": 5.0}. Omit (or send empty) if unknown. Rejected with any other video_type."},"camera_mount":{"anyOf":[{"type":"string","enum":["steering_column","dashboard_center","windshield","unknown"]},{"type":"null"}],"title":"Camera Mount","description":"Optional, video_type='in_vehicle' only: where the camera is mounted ('steering_column', 'dashboard_center', 'windshield' or 'unknown'). A hint for the driver's straight-ahead direction when driver_forward_gaze is not supplied; an explicit driver_forward_gaze takes precedence. 'unknown' is recorded as sent (distinct from omitting the field) and provides no hint. Omit (or send empty) if not known. Rejected with any other video_type."},"driver_forward_gaze_prior":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Driver Forward Gaze Prior","description":"Optional, video_type='in_vehicle' only: a prior for the direction the driver looks when looking straight ahead at the road — typically the driver_state forward_gaze estimate returned for an earlier video from the same camera. A JSON object {\"yaw_deg\": <number>, \"pitch_deg\": <number>, \"confidence\": <number>} in degrees, camera-relative, in the same axes and sign as the gaze_yaw / gaze_pitch values returned for faces. yaw_deg and pitch_deg must be within [-90, 90]; confidence must be greater than 0 and at most 1. The forward_gaze object returned for a face may be sent unchanged: its method is ignored and its convention must be 'public'; no other keys are accepted. An explicit driver_forward_gaze, or a camera_mount of 'steering_column', takes precedence over a prior, and a low-confidence prior may not be applied. Example: {\"yaw_deg\": -20.0, \"pitch_deg\": 5.0, \"confidence\": 0.9}. Omit (or send empty) if unknown. Rejected with any other video_type."},"diarization":{"type":"boolean","title":"Diarization","description":"Speaker diarization on the video's audio track. Enabled by default. Disabling this stage reduces the per-second credit cost.","default":true},"text_emotion":{"type":"boolean","title":"Text Emotion","description":"Text emotion analysis on the transcript. Enabled by default. Requires a transcript: ignored (forced off) when transcription is disabled and you supply no `transcript`. Supplying a `transcript` is the exception — the words already exist, so this is honoured as you sent it. Disabling this stage reduces the per-second credit cost.","default":true},"transcription":{"type":"boolean","title":"Transcription","description":"Transcript / ASR generation for the video's audio track. Enabled by default. When disabled, no transcript is produced and text_emotion is forced off. Supplying a `transcript` overrides this field entirely: our ASR is skipped either way, but your transcript is used and text_emotion still applies. Disabling this stage reduces the per-second credit cost.","default":true},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language","description":"Language code for the audio track (e.g. 'en'). Auto-detected if not provided. With `expected_languages` it must be one of them."},"multilingual":{"type":"boolean","title":"Multilingual","description":"Opt in to mixed-language transcription: the speech may switch language, and each stretch is transcribed in the language it is spoken in. Once the audio completes, the lines that are not in its main language are also translated into the main language and billed as a translation, on top of transcription (`auto_translation` reports it). This is input only; the languages actually detected are reported separately.","default":false},"expected_languages":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expected Languages","description":"Comma-separated codes of the languages the audio may contain, the main one included (e.g. `en,ja`), 1 to 5 after duplicates are removed. Case and whitespace are ignored, a region is dropped (`pt-BR` is `pt`) and an alias maps to the Whisper spelling (`iw` is `he`); Cantonese is `yue`. Language detection is restricted to the set, with or without `multilingual`. If `language` is also sent it must be one of them (422 `invalid_language` otherwise). If `language` is omitted, detection still runs, restricted to the set. An unknown code, or more than 5, is refused with 422 `invalid_language` before anything is charged."},"transcript":{"type":"string","title":"Transcript","description":"Optional: a transcript you already have for this video. When supplied we skip our own speech-to-text, use your transcript's speaker turns as the diarization, and name the speakers from it — so results come back with real participant names. Supplying one overrides `transcription`: our ASR is skipped either way, and you are charged less because that stage is not billed.","default":""},"transcript_format":{"type":"string","title":"Transcript Format","description":"Format of `transcript`. 'auto' (the default) detects it. Supported: 'auto', 'text', 'tldv_json', 'teams_vtt', 'zoom_vtt', 'vtt', 'srt'. Works with tl;dv, Zoom, Microsoft Teams, Otter, Fathom, Tactiq and most .vtt/.srt exports. Google Meet's own transcript is not supported yet — it timestamps only every 5 minutes, which cannot be aligned to the audio.","default":"auto"},"transcript_offset_millis":{"type":"integer","title":"Transcript Offset Millis","description":"Milliseconds added to every timestamp in `transcript` to place it on the media timeline. Positive means the transcript starts later than the recording (transcription began after record). Negative means it starts earlier (transcription running before record). 0 (the default) is today's behaviour. A negative offset that places any turn before 0 ms is rejected. Ignored when no `transcript` is supplied.","default":0},"stimuli":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stimuli","description":"Optional: when things the viewer reacts to appear in this video, for product testing, ad testing and reaction videos. A JSON array of objects {\"label\": str, \"kind\": \"ad_start\"|\"scene\"|\"product_reveal\"|\"price\"|\"logo\"|\"question\"|\"cta\"|\"custom\", \"start_ms\": int, \"end_ms\": int (optional), \"id\": str (optional), \"meta\": object (optional), \"category\": str (optional), \"variant\": str (optional), \"response_window_ms\": [int, int] (optional), \"attribute_during_span\": bool (optional)}, in milliseconds of video time. category is a grouping label of your own, 1-32 characters of A-Z, a-z, 0-9, '_', '.' or '-'. variant is a label for comparing versions of the same stimulus, 1-64 characters. For both, surrounding spaces are removed and case is kept, so \" Cedar \" is stored as \"Cedar\" and is a different value from \"cedar\". response_window_ms is [start, end] in milliseconds after the stimulus starts, 0 <= start < end <= 600000: a reaction that begins inside it is attributed to the stimulus; when omitted, the default window applies. attribute_during_span true also attributes a reaction that begins while the stimulus is on screen, and needs an end_ms after start_ms; false is the same as omitting it, is not stored and is returned as null. Every stimulus must start within the video and end no later than its end. At most 500 stimuli. Not accepted together with start_millis > 0. Example: [{\"label\": \"Price screen\", \"kind\": \"price\", \"start_ms\": 72000, \"end_ms\": 80000}]."},"non_human_spans":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Non Human Spans","description":"Optional: known non-human audio, such as a voice assistant or music. A JSON array of {\"start_ms\": int, \"end_ms\": int, \"role\": \"system\"|\"media\", \"label\": str (optional)}, in milliseconds of the analysed video. end_ms is after start_ms and no later than the video's end. label, when present, is 1-80 characters after surrounding spaces are removed. At most 1000 spans. Not accepted together with start_millis > 0. Omitted, blank or [] means none. Example: [{\"start_ms\": 0, \"end_ms\": 4000, \"role\": \"system\", \"label\": \"Voice assistant\"}]."}},"type":"object","required":["title"],"title":"Body_upload_video_v2_videos_post"},"BoolTag":{"properties":{"id":{"type":"string","title":"Id"},"key":{"type":"string","title":"Key"},"source":{"type":"string","title":"Source"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By"},"value_type":{"type":"string","const":"boolean","title":"Value Type"},"value_bool":{"type":"boolean","title":"Value Bool"}},"additionalProperties":false,"type":"object","required":["id","key","source","created_at","value_type","value_bool"],"title":"BoolTag"},"BoundingBox":{"properties":{"face_id":{"type":"integer","title":"Face Id","description":"Detected face identifier."},"box":{"items":{"type":"integer"},"type":"array","title":"Box","description":"Bounding box as [x, y, width, height]."},"emotions":{"additionalProperties":{"type":"number"},"type":"object","title":"Emotions","description":"Per-face emotion probability map. Typical keys: angry, contempt, disgust, fear, happy, neutral, sad, surprise. The map may be partial (a single {\"sad\": 0.9} is valid); missing keys are omitted, not zero-filled. Scores are model intensities in [0, 1].","examples":[{"angry":0.0003,"contempt":0.0383,"disgust":0.0019,"fear":4e-05,"happy":0.9505,"neutral":0.008,"sad":0.0002,"surprise":0.0008}]},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"},"face_analysis":{"anyOf":[{"$ref":"#/components/schemas/FaceAnalysis"},{"type":"null"}]},"valence":{"type":"number","title":"Valence","description":"Valence of this face on this frame, -1 (negative) to 1 (positive): the sum of each emotion score times that emotion's valence coordinate. The scores are used as given (not renormalised); an emotion with no coordinate counts as 0. Computed from `emotions`, so it always agrees with them. Recomputing it yourself from the coordinate table agrees up to floating-point rounding.","readOnly":true},"arousal":{"type":"number","title":"Arousal","description":"Arousal of this face on this frame, -1 (calm) to 1 (energetic): the sum of each emotion score times that emotion's arousal coordinate. Same conventions as `valence`.","readOnly":true},"intensity":{"type":"number","title":"Intensity","description":"Length of this face's (valence, arousal) vector on this frame: how far from neutral it reads. Not additive across faces or frames: to summarise several, average `valence` and `arousal` first and take the length of the averaged vector (opposite expressions cancel); averaging `intensity` values instead gives the average per-face intensity, a different number.","readOnly":true}},"type":"object","required":["face_id","box","emotions","valence","arousal","intensity"],"title":"BoundingBox","description":"One detected face on a frame: box, emotion probability map, optional gaze/lip signals."},"BriefRequest":{"properties":{"focus":{"anyOf":[{"$ref":"#/components/schemas/FocusIn"},{"type":"null"}],"description":"Application-authored focus. Never pass raw end-user text here."},"product":{"anyOf":[{"type":"string","pattern":"^[a-z][a-z0-9_]{0,63}$"},{"type":"null"}],"title":"Product"},"wait_s":{"type":"number","maximum":25.0,"minimum":0.0,"title":"Wait S","description":"How long to wait for a new result before answering 202 `running`.","default":20.0},"ttl_days":{"type":"integer","maximum":180.0,"minimum":1.0,"title":"Ttl Days","description":"How long a finished result is kept.","default":90},"digest_keys":{"items":{"type":"string"},"type":"array","maxItems":50,"minItems":1,"title":"Digest Keys"},"brief_version":{"type":"string","maxLength":128,"minLength":1,"title":"Brief Version"}},"additionalProperties":false,"type":"object","required":["digest_keys","brief_version"],"title":"BriefRequest"},"BriefResponse":{"properties":{"status":{"type":"string","enum":["ready","running","failed"],"title":"Status"},"brief_key":{"type":"string","title":"Brief Key"},"brief":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Brief"},"cached":{"type":"boolean","title":"Cached","default":false},"usage":{"anyOf":[{"$ref":"#/components/schemas/ArtifactUsage"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/ArtifactError"},{"type":"null"}]},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"}},"type":"object","required":["status","brief_key"],"title":"BriefResponse"},"ChunkIn":{"properties":{"chunk_id":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._:/#-]{0,127}$","title":"Chunk Id"},"version":{"type":"string","maxLength":128,"minLength":1,"title":"Version"},"page":{"anyOf":[{"type":"integer","maximum":999999999.0,"minimum":0.0},{"type":"null"}],"title":"Page"},"text":{"type":"string","title":"Text"}},"additionalProperties":false,"type":"object","required":["chunk_id","version","text"],"title":"ChunkIn"},"ContextDocCreate":{"properties":{"id":{"type":"string","title":"Id","description":"doc_id from upload-url endpoint (file) or client-generated UUID"},"name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Name"},"objective":{"type":"string","maxLength":1000,"title":"Objective","default":""},"source_type":{"type":"string","enum":["file","pasted"],"title":"Source Type"},"filename":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Filename"},"content":{"anyOf":[{"type":"string","maxLength":5000},{"type":"null"}],"title":"Content"},"target_type":{"type":"string","enum":["project","person"],"title":"Target Type","description":"Target context scope","default":"project"},"target_id":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}],"title":"Target Id","description":"The person_id if target_type is 'person'"}},"type":"object","required":["id","source_type"],"title":"ContextDocCreate"},"ContextDocCreatedChargedResponse":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"doc_count":{"type":"integer","title":"Doc Count"},"credits_charged":{"type":"number","title":"Credits Charged"},"credits_remaining":{"type":"number","title":"Credits Remaining"}},"type":"object","required":["id","name","doc_count","credits_charged","credits_remaining"],"title":"ContextDocCreatedChargedResponse","description":"``POST .../context`` when summarization charged. Credits are ``float`` (#933)."},"ContextDocCreatedResponse":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"doc_count":{"type":"integer","title":"Doc Count"}},"type":"object","required":["id","name","doc_count"],"title":"ContextDocCreatedResponse","description":"``POST .../context`` when no credit was charged (keys omitted, not null)."},"ContextDocDeleteResponse":{"properties":{"doc_count":{"type":"integer","title":"Doc Count"}},"type":"object","required":["doc_count"],"title":"ContextDocDeleteResponse","description":"``DELETE /v2/projects/{project_type}/{project_id}/context/{doc_id}``.\n\nA single key, and not the deleted id — characterized as-is."},"ContextUploadURLRequest":{"properties":{"filename":{"type":"string","maxLength":255,"minLength":1,"title":"Filename"},"content_type":{"type":"string","title":"Content Type","description":"MIME type of the file"}},"type":"object","required":["filename","content_type"],"title":"ContextUploadURLRequest"},"ContextUploadURLResponse":{"properties":{"upload_url":{"type":"string","title":"Upload Url"},"blob_path":{"type":"string","title":"Blob Path"},"doc_id":{"type":"string","title":"Doc Id"}},"type":"object","required":["upload_url","blob_path","doc_id"],"title":"ContextUploadURLResponse","description":"``POST /v2/projects/{project_type}/{project_id}/context/upload-url``."},"ConversationDeleted":{"properties":{"conversation_id":{"type":"string","title":"Conversation Id"},"deleted":{"type":"boolean","title":"Deleted","default":true}},"type":"object","required":["conversation_id"],"title":"ConversationDeleted"},"ConversationPersonIn":{"properties":{"label":{"type":"string","maxLength":64,"minLength":1,"title":"Label"},"subjects":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"integer"},{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","minItems":1}]},"propertyNames":{"enum":["face","voice","text"]},"type":"object","minProperties":1,"title":"Subjects"}},"additionalProperties":false,"type":"object","required":["label","subjects"],"title":"ConversationPersonIn","description":"One explicitly selected person, identified by a caller's role label."},"CreateConversationRequest":{"properties":{"product":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,63}$","title":"Product","description":"The calling application's tag, recorded for usage reporting only."},"external_ref":{"type":"string","maxLength":256,"title":"External Ref","description":"Caller reference, stored and never logged.","default":""},"instruction":{"$ref":"#/components/schemas/VersionedTextIn"},"output_schema":{"$ref":"#/components/schemas/OutputSchemaIn"},"sections":{"items":{"$ref":"#/components/schemas/SectionIn"},"type":"array","maxItems":64,"title":"Sections"},"media_binding":{"anyOf":[{"$ref":"#/components/schemas/RealtimeBindingIn"},{"$ref":"#/components/schemas/MediaBindingIn"},{"type":"null"}],"title":"Media Binding"},"media_bindings":{"anyOf":[{"items":{"$ref":"#/components/schemas/MediaBindingIn"},"type":"array","maxItems":200,"minItems":1},{"type":"null"}],"title":"Media Bindings","description":"Bind the conversation to one to 200 of the user's own processed recordings, each by its own `media_id` (distinct ids). Each turn then reads the recordings it selects with `media_ids`. Only on a user's delegated access token, and not together with `media_binding`."},"retention":{"type":"string","enum":["ephemeral","session"],"title":"Retention","description":"`ephemeral` stores no per-turn input or output, only usage. `session` also stores each turn's input and output so a repeated `turn_key` replays it."},"ttl_s":{"type":"integer","maximum":21600.0,"minimum":60.0,"title":"Ttl S","description":"Conversation lifetime in seconds.","default":3600}},"additionalProperties":false,"type":"object","required":["product","instruction","output_schema","retention"],"title":"CreateConversationRequest"},"CreateConversationResponse":{"properties":{"conversation_id":{"type":"string","title":"Conversation Id"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"},"cache_expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Cache Expires At"}},"type":"object","required":["conversation_id","expires_at"],"title":"CreateConversationResponse"},"CreateMediaPersonRequest":{"properties":{"name":{"type":"string","maxLength":120,"minLength":1,"title":"Name","description":"Display name."},"appearances":{"items":{"$ref":"#/components/schemas/MediaAppearanceInput"},"type":"array","maxItems":50,"title":"Appearances","description":"Faces or speakers to attach now. Omit to name the person first."}},"type":"object","required":["name"],"title":"CreateMediaPersonRequest","description":"Name someone on this video or audio. Appearances can be added later."},"CreateProjectRequest":{"properties":{"title":{"type":"string","maxLength":200,"minLength":1,"title":"Title","description":"Project name"},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"prospect":{"anyOf":[{"$ref":"#/components/schemas/ProspectInfo"},{"type":"null"}],"description":"sales_deal only: prospect company + email domains"},"stage":{"anyOf":[{"type":"string","enum":["discovery","demo","evaluation","negotiation","closed_won","closed_lost"]},{"type":"null"}],"title":"Stage","description":"sales_deal only: deal stage; defaults to 'discovery'"},"crm":{"anyOf":[{"$ref":"#/components/schemas/DealCrmInfo"},{"type":"null"}],"description":"sales_deal only: CRM position (amount, close date, next meeting, competitor)"},"template_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Template Id","description":"product_testing only: initial project-template assignment."},"company_name":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Company Name","description":"financial_research only: the tracked company's name."},"ticker":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ticker","description":"financial_research only: stock ticker, canonicalized server-side."},"exchange":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Exchange","description":"financial_research only: display-only exchange hint (e.g. NYSE) — never used for routing/lookup."},"fiscal_year_end_month":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Fiscal Year End Month","description":"financial_research only: 1-12; defaults to 12 (December) if omitted."},"link_equal_person_names":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Link Equal Person Names","description":"When enabled, adding a video or a standalone audio links a person whose name matches exactly one person already in the project. Matching is Unicode NFC, surrounding spaces stripped, then casefold. The link is made only when that person's face and speaker are not already linked to someone else, and only when every face and speaker of the media person is part of the link. People with a different name are never linked automatically. The project person's spelling is what appears on the linked face and speaker."}},"type":"object","required":["title"],"title":"CreateProjectRequest"},"CreateSessionResponse":{"properties":{"session_id":{"type":"string","title":"Session Id","description":"Unique session identifier"},"status":{"type":"string","title":"Status","description":"Session status"},"message":{"type":"string","title":"Message","description":"Status message"},"webrtc_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webrtc Url","description":"WebRTC connection URL for media streaming"},"ice_servers":{"items":{"$ref":"#/components/schemas/IceServer"},"type":"array","title":"Ice Servers","description":"ICE server configuration for WebRTC. Includes public STUN servers and TURN servers with ephemeral credentials for NAT traversal."},"ice_servers_ttl":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Ice Servers Ttl","description":"Time-to-live for TURN credentials in seconds. Clients should refresh credentials before this expires for long-running sessions. None if TURN is disabled."},"emotion_analysis_enabled":{"type":"boolean","title":"Emotion Analysis Enabled","description":"Whether emotion analysis runs for this session. False when the session was started with `emotion_analysis_enabled` false. A caller that needs it off should check this before sending media.","default":true}},"type":"object","required":["session_id","status","message"],"title":"CreateSessionResponse","description":"Response model for session creation."},"DateTag":{"properties":{"id":{"type":"string","title":"Id"},"key":{"type":"string","title":"Key"},"source":{"type":"string","title":"Source"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By"},"value_type":{"type":"string","const":"date","title":"Value Type"},"value_date":{"type":"string","format":"date-time","title":"Value Date"}},"additionalProperties":false,"type":"object","required":["id","key","source","created_at","value_type","value_date"],"title":"DateTag"},"DealCrmInfo":{"properties":{"amount":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Amount","description":"Deal value in `currency`."},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency","description":"ISO 4217 alphabetic code — REQUIRED whenever amount is set."},"expected_close_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Expected Close Date"},"next_meeting_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Next Meeting At"},"competitor":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Competitor"}},"type":"object","title":"DealCrmInfo","description":"Where the deal sits in the seller's CRM — the context that makes\nmomentum situated rather than generic (imentiv_web#2127).\n\nEvery field is optional and MANUAL in v1; a Salesforce/HubSpot sync\n(phase 2) fills these SAME fields, so nothing here should encode a\nvendor's vocabulary. `stage` is NOT here — it predates this model and\nstays a top-level project field."},"DecodingIn":{"properties":{"temperature":{"type":"number","maximum":2.0,"minimum":0.0,"title":"Temperature","default":0.0},"seed":{"anyOf":[{"type":"integer","maximum":2147483647.0,"minimum":-2147483648.0},{"type":"null"}],"title":"Seed"},"max_output_tokens":{"type":"integer","maximum":8192.0,"minimum":1.0,"title":"Max Output Tokens","default":2048}},"additionalProperties":false,"type":"object","title":"DecodingIn"},"DeleteFacesResponse":{"properties":{"id":{"type":"string","title":"Id"},"message":{"type":"string","title":"Message"}},"type":"object","required":["id","message"],"title":"DeleteFacesResponse","description":"Response model after deleting one or more faces.\n\nAttributes:\n    id (str): Video or frame ID the faces belonged to.\n    message (str): Confirmation or error message."},"DeleteSpeakersResponse":{"properties":{"id":{"type":"string","title":"Id"},"message":{"type":"string","title":"Message"}},"type":"object","required":["id","message"],"title":"DeleteSpeakersResponse","description":"Response model after deleting one or more speakers from an audio.\n\nAttributes:\n    id (str): Audio ID the speakers belonged to.\n    message (str): Confirmation message reporting how many speakers remain."},"DeleteTextResponse":{"properties":{"id":{"type":"string","title":"Id"},"status":{"type":"string","title":"Status"}},"type":"object","required":["id","status"],"title":"DeleteTextResponse","description":"Response returned after deleting a text emotion analysis result."},"DigestRequest":{"properties":{"focus":{"anyOf":[{"$ref":"#/components/schemas/FocusIn"},{"type":"null"}],"description":"Application-authored focus. Never pass raw end-user text here."},"product":{"anyOf":[{"type":"string","pattern":"^[a-z][a-z0-9_]{0,63}$"},{"type":"null"}],"title":"Product"},"wait_s":{"type":"number","maximum":25.0,"minimum":0.0,"title":"Wait S","description":"How long to wait for a new result before answering 202 `running`.","default":20.0},"ttl_days":{"type":"integer","maximum":180.0,"minimum":1.0,"title":"Ttl Days","description":"How long a finished result is kept.","default":90},"chunks":{"items":{"$ref":"#/components/schemas/ChunkIn"},"type":"array","maxItems":20000,"minItems":1,"title":"Chunks"},"digest_version":{"type":"string","maxLength":128,"minLength":1,"title":"Digest Version"}},"additionalProperties":false,"type":"object","required":["chunks","digest_version"],"title":"DigestRequest"},"DigestResponse":{"properties":{"status":{"type":"string","enum":["ready","running","failed"],"title":"Status"},"digest_key":{"type":"string","title":"Digest Key"},"digest":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Digest"},"cached":{"type":"boolean","title":"Cached","default":false},"usage":{"anyOf":[{"$ref":"#/components/schemas/ArtifactUsage"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/ArtifactError"},{"type":"null"}]},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"}},"type":"object","required":["status","digest_key"],"title":"DigestResponse"},"DriverFaceMetrics":{"properties":{"drowsiness_level":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Drowsiness Level","description":"alert | low_vigilance | drowsy | sleeping | unknown (open string)"},"perclos":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Perclos","description":"PERCLOS, 0–1 fraction preferred"},"on_road_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"On Road Pct"},"off_road_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Off Road Pct"},"unknown_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Unknown Pct","description":"Share of the clip with no usable gaze sample"},"degraded_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Degraded Pct","description":"Share measured at reduced confidence (head-only, etc.)"},"glances_per_min":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Glances Per Min"},"max_glance_ms":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max Glance Ms"},"closures":{"anyOf":[{"items":{"$ref":"#/components/schemas/DriverStateIntervalEvent"},"type":"array"},{"type":"null"}],"title":"Closures"},"glances":{"anyOf":[{"items":{"$ref":"#/components/schemas/DriverStateIntervalEvent"},"type":"array"},{"type":"null"}],"title":"Glances"},"distraction_events":{"anyOf":[{"items":{"$ref":"#/components/schemas/DriverStateIntervalEvent"},"type":"array"},{"type":"null"}],"title":"Distraction Events"},"forward_gaze":{"anyOf":[{"$ref":"#/components/schemas/DriverForwardGazeEstimate"},{"type":"null"}],"description":"The straight-ahead gaze direction attention was measured against for this face. Read it from the driver's face (selection.driver_face_id) and send it unchanged as driver_forward_gaze_prior on a later upload from the same camera; an estimate with confidence 0 is not a usable prior. Null when no straight-ahead estimate is available."}},"type":"object","title":"DriverFaceMetrics","description":"Headline and trust metrics for one tracked face.\n\nunknown_pct and degraded_pct are included so an on_road_pct of 0.0 is not\nmistaken for 'never looked at the road'."},"DriverForwardGaze":{"properties":{"yaw_deg":{"type":"number","maximum":90.0,"minimum":-90.0,"title":"Yaw Deg","description":"Horizontal angle in degrees, camera-relative, in the same axes and sign as the gaze_yaw values returned for faces. Range [-90, 90]."},"pitch_deg":{"type":"number","maximum":60.0,"minimum":-60.0,"title":"Pitch Deg","description":"Vertical angle in degrees, camera-relative, in the same axes and sign as the gaze_pitch values returned for faces. Range [-60, 60]."}},"additionalProperties":false,"type":"object","required":["yaw_deg","pitch_deg"],"title":"DriverForwardGaze","description":"The driver's straight-ahead gaze direction in camera-relative degrees,\nin the same axes and sign as the gaze_yaw / gaze_pitch values returned for faces."},"DriverForwardGazeEstimate":{"properties":{"yaw_deg":{"type":"number","title":"Yaw Deg","description":"Horizontal angle in degrees, in the same axes and sign as the gaze_yaw values returned for faces."},"pitch_deg":{"type":"number","title":"Pitch Deg","description":"Vertical angle in degrees, in the same axes and sign as the gaze_pitch values returned for faces."},"confidence":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Confidence","description":"Confidence in the estimate, 0–1."},"method":{"type":"string","minLength":1,"title":"Method","description":"How the direction was obtained, as a label (open string; new values may appear). For example, 'prior' when the driver_forward_gaze_prior supplied at upload was applied, or 'override' when an explicit driver_forward_gaze or a steering-column camera_mount was used; other values mean it was estimated from the footage."},"convention":{"type":"string","const":"public","title":"Convention","description":"Sign convention of yaw_deg and pitch_deg. Always 'public': the axes and sign of the gaze values returned for faces, and of driver_forward_gaze_prior.","default":"public"}},"type":"object","required":["yaw_deg","pitch_deg","confidence","method"],"title":"DriverForwardGazeEstimate","description":"The estimated straight-ahead gaze direction for one face, camera-relative degrees."},"DriverForwardGazePrior":{"properties":{"yaw_deg":{"type":"number","maximum":90.0,"minimum":-90.0,"title":"Yaw Deg","description":"Horizontal angle in degrees, camera-relative, in the same axes and sign as the gaze_yaw values returned for faces. Range [-90, 90]."},"pitch_deg":{"type":"number","maximum":90.0,"minimum":-90.0,"title":"Pitch Deg","description":"Vertical angle in degrees, camera-relative, in the same axes and sign as the gaze_pitch values returned for faces. Range [-90, 90]."},"confidence":{"type":"number","maximum":1.0,"exclusiveMinimum":0.0,"title":"Confidence","description":"How confident you are in this prior, greater than 0 and at most 1. A low-confidence prior may not be applied."}},"additionalProperties":false,"type":"object","required":["yaw_deg","pitch_deg","confidence"],"title":"DriverForwardGazePrior","description":"A prior for the driver's straight-ahead gaze direction in camera-relative degrees,\nin the same axes and sign as the gaze_yaw / gaze_pitch values returned for faces,\nwith how confident you are in it."},"DriverSelection":{"properties":{"driver_face_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Driver Face Id","description":"Chosen driver face id, or null when none was chosen"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"clear_lead | sole_candidate | no_lead | no_candidates | no_geometry | region_empty (open string)"},"scores":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Scores","description":"Candidate id → score. Keys are strings."}},"type":"object","title":"DriverSelection","description":"Which face is the driver, and why. Present whenever driver_state ran."},"DriverState":{"properties":{"selection":{"anyOf":[{"$ref":"#/components/schemas/DriverSelection"},{"type":"null"}]},"faces":{"anyOf":[{"additionalProperties":{"$ref":"#/components/schemas/DriverFaceMetrics"},"type":"object"},{"type":"null"}],"title":"Faces","description":"Metrics keyed by face id as a string. Values may exist for losing candidates."}},"type":"object","title":"DriverState","description":"Driver drowsiness and distraction results on GET /v2/videos/{id}.\n\nNull when driver-state analysis never ran (every video whose video_type is\nnot in_vehicle)."},"DriverStateIntervalEvent":{"properties":{"start_ms":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Start Ms","description":"Interval start, milliseconds"},"end_ms":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"End Ms","description":"Interval end, milliseconds"},"duration_ms":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Duration Ms","description":"Interval duration, milliseconds"},"start_millis":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Start Millis","description":"Alias of start_ms, populated on some results"},"end_millis":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"End Millis","description":"Alias of end_ms, populated on some results"},"duration_millis":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Duration Millis","description":"Alias of duration_ms, populated on some results"},"zone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Zone","description":"Gaze zone, when one was labelled"},"kind":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Kind","description":"Event kind, when one was labelled"}},"type":"object","title":"DriverStateIntervalEvent","description":"One eye-closure, glance or distraction interval in the driver-state results.\n\nzone and kind are open strings: a value not listed in this version of the\nAPI is still returned as-is."},"EmotionAnalysis":{"properties":{"overall":{"$ref":"#/components/schemas/OverallEmotions","examples":[{"audio":{"angry":0.047,"happy":0.185,"neutral":0.638},"text":{"admiration":0.015,"joy":0.018,"neutral":0.75},"video":{"angry":0.059,"happy":0.175,"neutral":0.408,"sad":0.049}}]},"faces":{"anyOf":[{"additionalProperties":{"additionalProperties":{"type":"number"},"type":"object"},"type":"object"},{"type":"null"}],"title":"Faces","examples":[{"John Smith":{"happy":0.175,"neutral":0.408,"sad":0.049},"face_2":{"angry":0.087,"happy":0.184,"neutral":0.314}}]},"speakers":{"additionalProperties":{"$ref":"#/components/schemas/SpeakerEmotions"},"type":"object","title":"Speakers","examples":[{"Interviewer":{"audio":{"angry":0.05,"happy":0.18,"neutral":0.65},"text":{"curiosity":0.08,"neutral":0.85}},"John Smith":{"audio":{"angry":0.045,"happy":0.19,"neutral":0.625},"text":{"admiration":0.02,"joy":0.025,"neutral":0.72}}}]}},"type":"object","required":["overall"],"title":"EmotionAnalysis","description":"Hierarchical emotion analysis structure."},"EmotionScores":{"properties":{"angry":{"type":"number","minimum":0.0,"title":"Angry","description":"Score for angry","default":0},"contempt":{"type":"number","minimum":0.0,"title":"Contempt","description":"Score for contempt","default":0},"disgust":{"type":"number","minimum":0.0,"title":"Disgust","description":"Score for disgust","default":0},"fear":{"type":"number","minimum":0.0,"title":"Fear","description":"Score for fear","default":0},"happy":{"type":"number","minimum":0.0,"title":"Happy","description":"Score for happy","default":0},"neutral":{"type":"number","minimum":0.0,"title":"Neutral","description":"Score for neutral","default":0},"sad":{"type":"number","minimum":0.0,"title":"Sad","description":"Score for sad","default":0},"surprise":{"type":"number","minimum":0.0,"title":"Surprise","description":"Score for surprise","default":0}},"type":"object","title":"EmotionScores","description":"Emotion scores representing the intensity of each emotion.\n\nAll scores are non-negative floats (``ge=0``) — model intensities, not counts."},"Emotions":{"properties":{"emotions":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Emotions"},"sentence":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sentence"}},"type":"object","title":"Emotions","description":"Model representing detected emotions in a sentence.\n\nAttributes:\n    emotions (Optional[Dict[str, float]]): Emotion label -> score.\n    sentence (Optional[str]): The sentence where emotions were detected."},"EndSessionResponse":{"properties":{"session_id":{"type":"string","title":"Session Id","description":"Session identifier that was ended"},"status":{"type":"string","title":"Status","description":"Final session status"},"message":{"type":"string","title":"Message","description":"Status message"},"summary":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Summary","description":"Session summary with stats (frames processed, emotions detected, etc.)"},"credits":{"anyOf":[{"$ref":"#/components/schemas/SessionCredits"},{"type":"null"}],"description":"What this session cost, and the balance left. Absent if the figures are temporarily unavailable — the session still ended."}},"type":"object","required":["session_id","status","message"],"title":"EndSessionResponse","description":"Response model for ending a session."},"EnrichedAudioLinkResponse":{"properties":{"audio_id":{"type":"string","title":"Audio Id"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"linked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Linked At"},"audio_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Audio Title"},"audio_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Audio Status"},"audio_duration":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Audio Duration"}},"type":"object","required":["audio_id"],"title":"EnrichedAudioLinkResponse","description":"A deal's ``audios[]`` row plus fields from the audio document.\n\nStored shape is ``{audio_id, created_at}``. ``linked_at`` is the same\ninstant as ``created_at`` — the FE Calls tab sorts on it. Title /\nstatus / duration come from the audio doc and are None when it is\nmissing (deleted out of band)."},"EnrichedProjectResponse":{"properties":{"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"project_type":{"type":"string","title":"Project Type"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"videos":{"items":{"$ref":"#/components/schemas/EnrichedVideoLinkResponse"},"type":"array","title":"Videos"},"total_video_count":{"type":"integer","title":"Total Video Count","default":0},"failed_video_count":{"type":"integer","title":"Failed Video Count","default":0},"ready_video_count":{"type":"integer","title":"Ready Video Count","default":0},"context":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Context"},"manual_calls":{"items":{"$ref":"#/components/schemas/ManualCall"},"type":"array","title":"Manual Calls","description":"Record-only calls (notes, no media) — sales deals only."},"audios":{"items":{"$ref":"#/components/schemas/EnrichedAudioLinkResponse"},"type":"array","title":"Audios","description":"Standalone audio-only calls linked to this deal (imentiv-frontend#72). Empty for other project types."},"quality_tier":{"type":"string","title":"Quality Tier","default":"standard"},"link_equal_person_names":{"type":"boolean","title":"Link Equal Person Names","description":"When enabled, adding a video or a standalone audio links a person whose name matches exactly one person already in the project. Matching is Unicode NFC, surrounding spaces stripped, then casefold. The link is made only when that person's face and speaker are not already linked to someone else, and only when every face and speaker of the media person is part of the link. People with a different name are never linked automatically. The project person's spelling is what appears on the linked face and speaker.","default":false},"is_owner":{"type":"boolean","title":"Is Owner","default":false},"is_sample":{"type":"boolean","title":"Is Sample","default":false},"user_id":{"type":"string","title":"User Id"},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By","description":"The user who created the project (the same value as `user_id`). Never changes."},"owner":{"anyOf":[{"$ref":"#/components/schemas/MediaOwnerOut"},{"type":"null"}],"description":"Who owns the project: the user, or the team workspace that holds it. A project and the items in it have the same owner. `null` for a viewer who is not the project's owner (a share link, a sample)."},"can_edit":{"type":"boolean","title":"Can Edit","description":"Whether the caller may change this project: update it, link or unlink items, run analyses in it. The creator and, for a workspace-owned project, the workspace's owners and admins. Other members of the workspace can read it.","default":false},"can_delete":{"type":"boolean","title":"Can Delete","description":"Whether the caller may delete this project: the caller may change it and may delete every workspace-owned item in it. A response that does not read the project's items (creating or updating it) reports only the first condition, so a delete can still be refused with `403` there.","default":false},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"share_status":{"type":"boolean","title":"Share Status","default":false},"prospect":{"anyOf":[{"$ref":"#/components/schemas/ProspectInfo"},{"type":"null"}]},"stage":{"anyOf":[{"type":"string","enum":["discovery","demo","evaluation","negotiation","closed_won","closed_lost"]},{"type":"null"}],"title":"Stage"},"crm":{"anyOf":[{"$ref":"#/components/schemas/DealCrmInfo"},{"type":"null"}]},"template_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Template Id"},"company_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company Name"},"ticker":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ticker"},"exchange":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exchange"},"fiscal_year_end_month":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Fiscal Year End Month"}},"type":"object","required":["id","title","project_type","user_id","created_at","updated_at"],"title":"EnrichedProjectResponse","description":"Project response with enriched video details.\n\n`videos` is the post-filter list when `filter[tags][...]` query params\nare present. `total_video_count` is the unfiltered total — lets the FE\nrender \"Showing 8 of 50\" without a separate round-trip.\n`failed_video_count` and `ready_video_count` are likewise unfiltered, so\n`total_video_count - ready_video_count - failed_video_count` is a stable\n\"still processing\" figure regardless of the active tag filter — deriving\n`ready` from the (possibly filtered) `videos` list instead would silently\nundercount whenever a filter is active (imentiv_web#2135 review)."},"EnrichedVideoLinkResponse":{"properties":{"video_id":{"type":"string","title":"Video Id"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"available":{"type":"boolean","title":"Available","description":"False when the caller cannot read this item in this context, for example an item that is owned by someone else than the project. The link is kept and shown as not available here, and none of the item's details are included.","default":true},"linked_persons":{"items":{"$ref":"#/components/schemas/LinkedPersonRef"},"type":"array","title":"Linked Persons","description":"Live Persons in this project whose `videos` list includes this video. More than one means the video is SHARED — deleting it removes it for every one of them, so clients must confirm destructively rather than silently. Empty for project types that have no Persons. Soft-deleted Persons are excluded."},"recorded_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Recorded At","description":"When the underlying call/recording actually happened (imported videos: the provider's meeting time from external_ref.happened_at). None for uploads and older imports — display layers fall back to created_at (the link time)."},"video_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Video Status"},"video_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Video Title"},"video_duration":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Video Duration"},"video_thumbnail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Video Thumbnail"},"video_percentage":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Video Percentage"},"face_count":{"type":"integer","title":"Face Count","default":0},"face_matched_count":{"type":"integer","title":"Face Matched Count","default":0},"face_personality_count":{"type":"integer","title":"Face Personality Count","default":0},"emotions":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Emotions","description":"Face-averaged emotion distribution, keys sum to 1.0. None when overall_emotions is unavailable."},"audio_emotions":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Audio Emotions","description":"Audio-tone aggregate emotion distribution from the linked audio doc's overall emotions. None when no audio is linked or analysis is incomplete."},"text_emotions":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Text Emotions","description":"Text/transcript aggregate emotion distribution from the linked text doc. None when no transcript is linked or analysis is incomplete."},"valence":{"anyOf":[{"type":"number","maximum":1.0,"minimum":-1.0},{"type":"null"}],"title":"Valence","description":"Aggregate valence in [-1, 1]. Negative = unpleasant, positive = pleasant. None when overall_emotions is unavailable."},"arousal":{"anyOf":[{"type":"number","maximum":1.0,"minimum":-1.0},{"type":"null"}],"title":"Arousal","description":"Aggregate arousal in [-1, 1]. Negative = calm, positive = energetic. None when overall_emotions is unavailable."},"intensity":{"anyOf":[{"type":"number","maximum":1.4142135623730951,"minimum":0.0},{"type":"null"}],"title":"Intensity","description":"Magnitude of (valence, arousal). Theoretical range is [0, sqrt(2)] but achievable values for a normalized 8-emotion distribution are well below that ceiling — the bound is for schema validation, not as a typical scale endpoint. Conflates classifier confidence with expression strength — see `expressiveness` for a signal that doesn't. None when overall_emotions is unavailable."},"expressiveness":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Expressiveness","description":"1 - p(neutral): how expressive the face-averaged distribution is, independent of which emotion dominates. Unlike `intensity`, doesn't conflate classifier confidence with expression strength (a confidently-detected mild smile and an intense one both score high). None when overall_emotions is unavailable."},"emotion_entropy":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Emotion Entropy","description":"Normalized Shannon entropy of the face-averaged emotion distribution: 0 = certain/peaked, 1 = uniform/uncertain. Use to filter out low-confidence frames rather than reading a small `intensity` as 'confidently neutral' — the two are conflated otherwise. None when overall_emotions is unavailable."},"circumplex_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Circumplex Version","description":"Which coordinate table produced valence/arousal/intensity/expressiveness/emotion_entropy for this video. Currently always \"v2\" when set. None when overall_emotions is unavailable."},"tags":{"items":{"oneOf":[{"$ref":"#/components/schemas/StringTag"},{"$ref":"#/components/schemas/EnumTag"},{"$ref":"#/components/schemas/IntegerTag"},{"$ref":"#/components/schemas/BoolTag"},{"$ref":"#/components/schemas/DateTag"},{"$ref":"#/components/schemas/GeoTag"},{"$ref":"#/components/schemas/SetTag"},{"$ref":"#/components/schemas/NoneTag"}],"discriminator":{"propertyName":"value_type","mapping":{"boolean":"#/components/schemas/BoolTag","date":"#/components/schemas/DateTag","enum":"#/components/schemas/EnumTag","integer":"#/components/schemas/IntegerTag","location_admin":"#/components/schemas/GeoTag","none":"#/components/schemas/NoneTag","set":"#/components/schemas/SetTag","string":"#/components/schemas/StringTag"}}},"type":"array","title":"Tags","description":"Generalized media tags (demographic, survey, label, system). Stored as a map field `tags` on the video doc; flattened to this list on the way out via flatten_tags(). Discriminated union over value_type — see /v2/tag-keys for dimension metadata."}},"type":"object","required":["video_id"],"title":"EnrichedVideoLinkResponse","description":"Extended response with video entity details (for project detail view)."},"EnumTag":{"properties":{"id":{"type":"string","title":"Id"},"key":{"type":"string","title":"Key"},"source":{"type":"string","title":"Source"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By"},"value_type":{"type":"string","const":"enum","title":"Value Type"},"value_text":{"type":"string","title":"Value Text"}},"additionalProperties":false,"type":"object","required":["id","key","source","created_at","value_type","value_text"],"title":"EnumTag"},"ErrorDetail":{"properties":{"detail":{"type":"string","title":"Detail","description":"Human-readable error message. Branch on the HTTP status, not on this text.","examples":["Resource not found"]}},"type":"object","required":["detail"],"title":"ErrorDetail","description":"The error envelope every handler in ``core/exception_handling.py`` emits.\n\nAttributes:\n    detail (str): Human-readable message. Either ``str(exc)`` or, when the\n        exception carries no message, the default from ``EXCEPTION_MAPPINGS``\n        (e.g. ``\"Unauthorized\"``, ``\"Resource not found\"``). Never a list, and\n        never machine-parseable — clients must branch on the HTTP status code,\n        not on this string."},"EvaluateRequest":{"properties":{"turn_key":{"type":"string","maxLength":256,"minLength":1,"title":"Turn Key","description":"Caller key of this turn. A key is charged at most once and normally answered once: see the route description."},"window":{"anyOf":[{"$ref":"#/components/schemas/WindowIn"},{"type":"null"}]},"modalities":{"items":{"type":"string","enum":["face","voice","text"]},"type":"array","maxItems":3,"title":"Modalities"},"input":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Input","description":"The turn's input object."},"sections":{"items":{"$ref":"#/components/schemas/SectionIn"},"type":"array","maxItems":16,"title":"Sections","description":"Per-turn sections, e.g. text the model may quote verbatim. A type must keep the trust class it has in the conversation."},"decoding":{"$ref":"#/components/schemas/DecodingIn"},"media_ids":{"anyOf":[{"items":{"type":"string","maxLength":128,"minLength":1},"type":"array","maxItems":200,"minItems":1},{"type":"null"}],"title":"Media Ids","description":"For a conversation bound with `media_bindings`: which of the bound recordings this turn reads, by `media_id` (distinct, each one bound). Absent means all of them. `window` applies to each selected recording's own clock, and `modalities_used` reports what was read across them."}},"additionalProperties":false,"type":"object","required":["turn_key"],"title":"EvaluateRequest"},"EvaluateResponse":{"properties":{"conversation_id":{"type":"string","title":"Conversation Id"},"turn_key":{"type":"string","title":"Turn Key"},"output":{"additionalProperties":true,"type":"object","title":"Output"},"modalities_used":{"items":{"type":"string"},"type":"array","title":"Modalities Used"},"evidence_pending":{"type":"boolean","title":"Evidence Pending","description":"The live window was still filling when evaluated; a later turn may see more data.","default":false},"cache_hit":{"type":"boolean","title":"Cache Hit","default":false},"repaired":{"type":"boolean","title":"Repaired","default":false},"replayed":{"type":"boolean","title":"Replayed","default":false},"usage":{"$ref":"#/components/schemas/EvaluateUsage"},"latency_ms":{"type":"integer","title":"Latency Ms"}},"type":"object","required":["conversation_id","turn_key","output","usage","latency_ms"],"title":"EvaluateResponse"},"EvaluateUsage":{"properties":{"tokens_by_model":{"additionalProperties":{"additionalProperties":{"type":"integer"},"type":"object"},"type":"object","title":"Tokens By Model"},"credits":{"type":"integer","title":"Credits","description":"Price of this turn in credits, charged once per `turn_key` after the response is sent; 0 on a replay."}},"type":"object","required":["credits"],"title":"EvaluateUsage"},"ExpertArcStep":{"properties":{"window":{"$ref":"#/components/schemas/ExpertWindow"},"reading":{"$ref":"#/components/schemas/ExpertReading"}},"additionalProperties":false,"type":"object","required":["window","reading"],"title":"ExpertArcStep"},"ExpertBatchEntry":{"properties":{"index":{"type":"integer","title":"Index","description":"The item's position in the request."},"media_binding":{"$ref":"#/components/schemas/ExpertMediaBindingEcho","description":"The recording this item read."},"result":{"anyOf":[{"$ref":"#/components/schemas/ExpertResult"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/ExpertError"},{"type":"null"}]},"credits_charged":{"type":"integer","title":"Credits Charged","default":0},"replayed":{"type":"boolean","title":"Replayed","default":false},"model_ids":{"anyOf":[{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":"object"},{"type":"null"}],"title":"Model Ids","description":"Per modality, the emotion model version the readings were calibrated against; null when the recording does not name one, and that modality is then reported as untested. Null when the item's recording could not be read."}},"type":"object","required":["index","media_binding"],"title":"ExpertBatchEntry"},"ExpertBatchItem":{"properties":{"window":{"$ref":"#/components/schemas/WindowIn","description":"The part of the recording to read, in milliseconds."},"modalities":{"items":{"type":"string","enum":["face","voice","text"]},"type":"array","maxItems":3,"minItems":1,"title":"Modalities","description":"The modalities to read, each at most once."},"situation":{"items":{"$ref":"#/components/schemas/SituationIn"},"type":"array","maxItems":8,"title":"Situation"},"question":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Question","description":"`interpret_window` only: what to interpret the window for."},"stimulus":{"anyOf":[{"$ref":"#/components/schemas/StimulusIn"},{"type":"null"}],"description":"`assess_response` only, and required there."},"baseline":{"anyOf":[{"$ref":"#/components/schemas/WindowIn"},{"type":"null"}],"description":"`assess_response` only: the reference period, ending at or before the stimulus onset. Defaults to the same length before the onset; required when the onset is at 0."},"moment_id":{"anyOf":[{"type":"string","maxLength":128,"minLength":1},{"type":"null"}],"title":"Moment Id","description":"`explain_moment` only, and required there: a moment of the chosen face in `window`."},"people":{"anyOf":[{"patternProperties":{"^[a-z][a-z0-9_]{0,31}$":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"integer"}]},"propertyNames":{"enum":["face","voice","text"]},"type":"object"}},"type":"object","maxProperties":4,"minProperties":2},{"type":"null"}],"title":"People","description":"`compare` only, and required there: two to four people, each a label mapped to one subject id per requested modality. Replaces the binding's `subjects`."},"focus":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Focus","description":"`narrate` only: what to narrate for."},"media_binding":{"$ref":"#/components/schemas/ExpertMediaBindingIn"}},"additionalProperties":false,"type":"object","required":["window","media_binding"],"title":"ExpertBatchItem","description":"One call of a batch: what it looks at, and which of the caller's recordings it reads."},"ExpertBatchRequest":{"properties":{"product":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,63}$","title":"Product","description":"The calling application's tag, recorded for usage reporting only."},"language":{"anyOf":[{"type":"string","pattern":"^[a-z]{2,3}(-[A-Za-z0-9]{2,8})?$"},{"type":"null"}],"title":"Language","description":"The spoken language, when known. It can only lower a reading's confidence."},"condition":{"type":"string","title":"Condition","description":"The recording condition: `default`, `in_cabin_ir`, `off_axis_gt30` or `road_noise`. A condition other than `default` can only lower a reading's confidence.","default":"default"},"items":{"items":{"$ref":"#/components/schemas/ExpertBatchItem"},"type":"array","maxItems":10,"minItems":1,"title":"Items"}},"additionalProperties":false,"type":"object","required":["product","items"],"title":"ExpertBatchRequest","description":"Up to ten calls of one skill, each on one of the caller's own recordings.\n\nThe calls may read several recordings (at most four distinct ones in a batch); `product`,\n`language` and `condition` are shared, so recordings in different languages belong in\nseparate batches."},"ExpertBatchResponse":{"properties":{"skill":{"type":"string","enum":["interpret_window","assess_response","explain_moment","compare","narrate"],"title":"Skill"},"credits_charged":{"type":"integer","title":"Credits Charged","description":"The sum over the entries."},"entries":{"items":{"$ref":"#/components/schemas/ExpertBatchEntry"},"type":"array","title":"Entries","description":"One per item, in request order."}},"type":"object","required":["skill","credits_charged","entries"],"title":"ExpertBatchResponse"},"ExpertCitation":{"properties":{"id":{"type":"string","minLength":1,"title":"Id","description":"A window of evidence (`w:...`) or a moment (`m:...`)."},"modality":{"type":"string","enum":["face","voice","text"],"title":"Modality"},"subject":{"type":"string","minLength":1,"title":"Subject","description":"The person the evidence belongs to."},"start_ms":{"type":"integer","minimum":0.0,"title":"Start Ms"},"end_ms":{"type":"integer","exclusiveMinimum":0.0,"title":"End Ms"}},"additionalProperties":false,"type":"object","required":["id","modality","subject","start_ms","end_ms"],"title":"ExpertCitation","description":"Evidence on the bound recording that a finding rests on."},"ExpertClaim":{"properties":{"text":{"type":"string","maxLength":400,"minLength":1,"title":"Text"},"citations":{"items":{"$ref":"#/components/schemas/ExpertCitation"},"type":"array","maxItems":24,"title":"Citations"}},"additionalProperties":false,"type":"object","required":["text"],"title":"ExpertClaim","description":"A cited statement. Statements without a citation are removed before the response."},"ExpertError":{"properties":{"code":{"type":"string","title":"Code"},"message":{"type":"string","title":"Message"}},"additionalProperties":true,"type":"object","required":["code","message"],"title":"ExpertError","description":"One item's error: the same object a single call returns as its error `detail`,\nincluding that code's fields (e.g. `retry_after_s` of `expert_in_progress`, `balance`\nand `required` of `insufficient_credits`, `field` of `invalid_request`)."},"ExpertMediaBindingEcho":{"properties":{"media_id":{"type":"string","title":"Media Id"},"media_type":{"type":"string","enum":["video","audio"],"title":"Media Type"}},"type":"object","required":["media_id","media_type"],"title":"ExpertMediaBindingEcho","description":"The recording an entry read."},"ExpertMediaBindingIn":{"properties":{"media_id":{"type":"string","maxLength":128,"minLength":1,"title":"Media Id"},"media_type":{"type":"string","enum":["video","audio"],"title":"Media Type"},"subjects":{"anyOf":[{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"integer"}]},"propertyNames":{"enum":["face","voice","text"]},"type":"object"},{"type":"null"}],"title":"Subjects","description":"Per modality, the one subject id to read. Required for a requested modality with more than one subject in the recording; a modality with exactly one is chosen automatically."}},"additionalProperties":false,"type":"object","required":["media_id","media_type"],"title":"ExpertMediaBindingIn","description":"One of the caller's own processed recordings, with at most one subject per modality."},"ExpertReading":{"properties":{"state":{"type":"string","minLength":1,"title":"State"},"confidence":{"type":"string","enum":["low","medium","high"],"title":"Confidence","description":"Capped by how reliable the emotion models are for this state."},"supporting_modalities":{"items":{"type":"string","enum":["face","voice","text"]},"type":"array","maxItems":3,"minItems":1,"title":"Supporting Modalities"},"citations":{"items":{"$ref":"#/components/schemas/ExpertCitation"},"type":"array","maxItems":24,"minItems":1,"title":"Citations"},"note":{"type":"string","maxLength":400,"title":"Note"}},"additionalProperties":false,"type":"object","required":["state","confidence","supporting_modalities","citations","note"],"title":"ExpertReading","description":"One observed emotional state, with its confidence after calibration."},"ExpertResponse":{"properties":{"skill":{"type":"string","enum":["interpret_window","assess_response","explain_moment","compare","narrate"],"title":"Skill"},"expert_version":{"type":"string","title":"Expert Version","description":"Identifies everything that shaped the result; part of its cache key."},"result":{"$ref":"#/components/schemas/ExpertResult"},"credits_charged":{"type":"integer","title":"Credits Charged","description":"Credits for this call, charged after the response; 0 on a replay."},"replayed":{"type":"boolean","title":"Replayed","description":"An identical earlier request's stored result, returned free of charge."},"model_ids":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":"object","title":"Model Ids","description":"Per modality, the emotion model version the readings were calibrated against; null when the recording does not name one, and that modality is then reported as untested."}},"type":"object","required":["skill","expert_version","result","credits_charged","replayed","model_ids"],"title":"ExpertResponse"},"ExpertResult":{"properties":{"skill":{"type":"string","enum":["interpret_window","assess_response","explain_moment","compare","narrate"],"title":"Skill"},"status":{"type":"string","enum":["ok","insufficient_evidence","out_of_scope"],"title":"Status","description":"`insufficient_evidence` when coverage, grounding or calibration cannot support a finding; `out_of_scope` when the question or focus is not about observed emotion."},"expert_version":{"type":"string","minLength":1,"title":"Expert Version"},"reason":{"type":"string","maxLength":400,"title":"Reason"},"evidence_quality":{"items":{"type":"string","maxLength":400},"type":"array","maxItems":24,"title":"Evidence Quality","description":"Stable codes describing the evidence, e.g. `untested_modality:face` or `low_confidence_language:de`."},"quality_notes":{"items":{"$ref":"#/components/schemas/ExpertClaim"},"type":"array","maxItems":24,"title":"Quality Notes"},"confounds":{"items":{"$ref":"#/components/schemas/ExpertClaim"},"type":"array","maxItems":24,"title":"Confounds"},"alternatives":{"items":{"$ref":"#/components/schemas/ExpertClaim"},"type":"array","maxItems":24,"title":"Alternatives"},"citations":{"items":{"$ref":"#/components/schemas/ExpertCitation"},"type":"array","maxItems":48,"title":"Citations"},"readings":{"items":{"$ref":"#/components/schemas/ExpertReading"},"type":"array","maxItems":24,"title":"Readings"},"verdict":{"anyOf":[{"type":"string","enum":["response","no_response"]},{"type":"null"}],"title":"Verdict"},"direction":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Direction"},"magnitude":{"anyOf":[{"type":"string","enum":["low","medium","high"]},{"type":"null"}],"title":"Magnitude"},"onset_ms":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Onset Ms"},"below_engine_threshold":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Below Engine Threshold"},"baseline_used":{"anyOf":[{"$ref":"#/components/schemas/ExpertWindow"},{"type":"null"}]},"threshold_reference":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Threshold Reference"},"drivers":{"items":{"type":"string","maxLength":400},"type":"array","maxItems":24,"title":"Drivers"},"robustness":{"items":{"$ref":"#/components/schemas/ExpertClaim"},"type":"array","maxItems":24,"title":"Robustness"},"counterevidence":{"items":{"type":"string","maxLength":400},"type":"array","maxItems":24,"title":"Counterevidence"},"contrasts":{"items":{"$ref":"#/components/schemas/ExpertReading"},"type":"array","maxItems":24,"title":"Contrasts"},"arc":{"items":{"$ref":"#/components/schemas/ExpertArcStep"},"type":"array","maxItems":12,"title":"Arc"}},"additionalProperties":false,"type":"object","required":["skill","status","expert_version","reason"],"title":"ExpertResult","description":"One skill result. Anything but `ok` carries no findings."},"ExpertSkillRequest":{"properties":{"product":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,63}$","title":"Product","description":"The calling application's tag, recorded for usage reporting only."},"language":{"anyOf":[{"type":"string","pattern":"^[a-z]{2,3}(-[A-Za-z0-9]{2,8})?$"},{"type":"null"}],"title":"Language","description":"The spoken language, when known. It can only lower a reading's confidence."},"condition":{"type":"string","title":"Condition","description":"The recording condition: `default`, `in_cabin_ir`, `off_axis_gt30` or `road_noise`. A condition other than `default` can only lower a reading's confidence.","default":"default"},"media_binding":{"$ref":"#/components/schemas/ExpertMediaBindingIn"},"window":{"$ref":"#/components/schemas/WindowIn","description":"The part of the recording to read, in milliseconds."},"modalities":{"items":{"type":"string","enum":["face","voice","text"]},"type":"array","maxItems":3,"minItems":1,"title":"Modalities","description":"The modalities to read, each at most once."},"situation":{"items":{"$ref":"#/components/schemas/SituationIn"},"type":"array","maxItems":8,"title":"Situation"},"question":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Question","description":"`interpret_window` only: what to interpret the window for."},"stimulus":{"anyOf":[{"$ref":"#/components/schemas/StimulusIn"},{"type":"null"}],"description":"`assess_response` only, and required there."},"baseline":{"anyOf":[{"$ref":"#/components/schemas/WindowIn"},{"type":"null"}],"description":"`assess_response` only: the reference period, ending at or before the stimulus onset. Defaults to the same length before the onset; required when the onset is at 0."},"moment_id":{"anyOf":[{"type":"string","maxLength":128,"minLength":1},{"type":"null"}],"title":"Moment Id","description":"`explain_moment` only, and required there: a moment of the chosen face in `window`."},"people":{"anyOf":[{"patternProperties":{"^[a-z][a-z0-9_]{0,31}$":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"integer"}]},"propertyNames":{"enum":["face","voice","text"]},"type":"object"}},"type":"object","maxProperties":4,"minProperties":2},{"type":"null"}],"title":"People","description":"`compare` only, and required there: two to four people, each a label mapped to one subject id per requested modality. Replaces the binding's `subjects`."},"focus":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Focus","description":"`narrate` only: what to narrate for."}},"additionalProperties":false,"type":"object","required":["product","media_binding","window"],"title":"ExpertSkillRequest","description":"One skill call on one recording."},"ExpertWindow":{"properties":{"start_ms":{"type":"integer","minimum":0.0,"title":"Start Ms"},"end_ms":{"type":"integer","exclusiveMinimum":0.0,"title":"End Ms"}},"additionalProperties":false,"type":"object","required":["start_ms","end_ms"],"title":"ExpertWindow"},"FaceAnalysis":{"properties":{"gaze_yaw":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Gaze Yaw","description":"Horizontal gaze in degrees. 0 = camera; negative = left; positive = right."},"gaze_pitch":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Gaze Pitch","description":"Vertical gaze in degrees. 0 = level; negative = down; positive = up."},"eye_openness":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Eye Openness","description":"Eye openness in [0, 1]."},"blink":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Blink","description":"True when the eyes are closed on this frame."},"fixation_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Fixation Id","description":"Fixation identifier; the same id spans consecutive frames of one fixation."},"liveness":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Liveness","description":"Liveness label, e.g. 'live'."},"lip_openness":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Lip Openness","description":"Mouth aspect ratio / lip openness in [0, 1]."},"lip_state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lip State","description":"Lip state: idle, speaking, or yawning."},"head_yaw":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Head Yaw","description":"Head yaw in degrees. 0 = facing the camera; negative = turned left."},"head_pitch":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Head Pitch","description":"Head pitch in degrees. Negative = head down. What 0 means depends on where the camera sits, so judge it against the same person's usual, not an absolute angle."},"head_roll":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Head Roll","description":"Head roll (tilt toward a shoulder) in degrees."},"brow_eye_ratio":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Brow Eye Ratio","description":"Brow-to-eye distance over the inter-ocular distance (a proxy for brow lowering). Lower = a lowered brow. Absent when the face is too small or turned too far."},"brow_furrow_ratio":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Brow Furrow Ratio","description":"Distance between the inner brow points over the inter-ocular distance. Lower = more furrowed. Absent under the same conditions as brow_eye_ratio."},"brow_source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brow Source","description":"Landmark model the brow ratios were measured with. Ratios from different sources are not comparable."}},"additionalProperties":true,"type":"object","title":"FaceAnalysis","description":"Per-face gaze / eye / lip / head-pose / brow / liveness signals from temporal analysis.\n\nEvery named field is optional: the producer omits what it could not compute,\nand a face that never went through temporal analysis carries no\n``face_analysis`` key at all. Blink rate and the eye-tracking heatmap are\nnot stored — derive rate from ``blink`` onsets over time, and the heatmap\nfrom ``gaze_yaw`` × ``gaze_pitch``."},"FaceAnalysisRow":{"properties":{"gaze_yaw":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Gaze Yaw","description":"Horizontal gaze in degrees. 0 = camera; negative = left; positive = right."},"gaze_pitch":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Gaze Pitch","description":"Vertical gaze in degrees. 0 = level; negative = down; positive = up."},"eye_openness":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Eye Openness","description":"Eye openness in [0, 1]."},"blink":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Blink","description":"True when the eyes are closed on this frame."},"fixation_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Fixation Id","description":"Fixation identifier; the same id spans consecutive frames of one fixation."},"liveness":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Liveness","description":"Liveness label, e.g. 'live'."},"lip_openness":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Lip Openness","description":"Mouth aspect ratio / lip openness in [0, 1]."},"lip_state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lip State","description":"Lip state: idle, speaking, or yawning."},"head_yaw":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Head Yaw","description":"Head yaw in degrees. 0 = facing the camera; negative = turned left."},"head_pitch":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Head Pitch","description":"Head pitch in degrees. Negative = head down. What 0 means depends on where the camera sits, so judge it against the same person's usual, not an absolute angle."},"head_roll":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Head Roll","description":"Head roll (tilt toward a shoulder) in degrees."},"brow_eye_ratio":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Brow Eye Ratio","description":"Brow-to-eye distance over the inter-ocular distance (a proxy for brow lowering). Lower = a lowered brow. Absent when the face is too small or turned too far."},"brow_furrow_ratio":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Brow Furrow Ratio","description":"Distance between the inner brow points over the inter-ocular distance. Lower = more furrowed. Absent under the same conditions as brow_eye_ratio."},"brow_source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brow Source","description":"Landmark model the brow ratios were measured with. Ratios from different sources are not comparable."},"frame_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Frame Id","description":"Frame number."},"timestamp":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Timestamp","description":"Frame timestamp in milliseconds."},"face_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Face Id","description":"Detected face identifier."}},"additionalProperties":true,"type":"object","title":"FaceAnalysisRow","description":"One face on one frame, flattened for chart / dashboard ingestion."},"FaceImageUrlResponse":{"properties":{"url":{"type":"string","title":"Url","description":"Signed URL of the face image, for use as an image source. Short-lived: request a new one when `expires_at` passes or a request with it is refused. Do not store or share it."},"expires_at":{"type":"string","title":"Expires At","description":"RFC 3339 UTC instant at which `url` stops working, e.g. `2026-09-28T12:30:00Z`."},"content_type":{"type":"string","title":"Content Type","description":"The image's content type, e.g. `image/jpeg`."}},"type":"object","required":["url","expires_at","content_type"],"title":"FaceImageUrlResponse","description":"A short-lived, read-only URL for one face's image."},"FaceNameItem":{"properties":{"face_id":{"type":"integer","title":"Face Id","default":0},"name":{"type":"string","title":"Name"}},"type":"object","required":["name"],"title":"FaceNameItem","description":"Represents a mapping between a face ID and its assigned name.\n\nUsed for labeling or identifying detected faces."},"FacePersonalityStatus":{"properties":{"face_id":{"type":"integer","title":"Face Id","description":"The face ID"},"by_segment_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"By Segment Status","description":"Status of by-segment personality analysis","examples":["completed","processing","pending"]},"by_segment_percentage":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"By Segment Percentage","description":"Progress percentage of by-segment analysis","examples":[0,50,100]},"overall_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Overall Status","description":"Status of overall personality analysis","examples":["completed","processing","pending"]},"overall_percentage":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Overall Percentage","description":"Progress percentage of overall analysis","examples":[0,50,100]}},"type":"object","required":["face_id"],"title":"FacePersonalityStatus","description":"Personality analysis status for a single face."},"FaceRef":{"properties":{"video_id":{"type":"string","maxLength":128,"minLength":1,"title":"Video Id","description":"A video you own."},"face_id":{"type":"integer","minimum":0.0,"title":"Face Id","description":"A face on that video, as listed in the video's faces."}},"additionalProperties":false,"type":"object","required":["video_id","face_id"],"title":"FaceRef","description":"One face on one video."},"FaceSimilarityPair":{"properties":{"probe":{"$ref":"#/components/schemas/FaceRef"},"gallery":{"$ref":"#/components/schemas/FaceRef"},"similarity":{"type":"number","maximum":1.0,"minimum":-1.0,"title":"Similarity","description":"Cosine similarity, rounded to 4 decimal places. Higher is more alike. It is a score, not a decision: choose your own threshold."}},"additionalProperties":false,"type":"object","required":["probe","gallery","similarity"],"title":"FaceSimilarityPair","description":"One probe scored against one gallery face."},"FaceSimilarityRequest":{"properties":{"probes":{"items":{"$ref":"#/components/schemas/FaceRef"},"type":"array","maxItems":16,"minItems":1,"title":"Probes","description":"Faces to look up, 1 to 16."},"gallery":{"items":{"$ref":"#/components/schemas/FaceRef"},"type":"array","maxItems":512,"minItems":1,"title":"Gallery","description":"Faces to compare each probe against, 1 to 512."}},"additionalProperties":false,"type":"object","required":["probes","gallery"],"title":"FaceSimilarityRequest","description":"Faces to score, and the faces to score them against. A face repeated within a list\nis compared once."},"FaceSimilarityResponse":{"properties":{"pairs":{"items":{"$ref":"#/components/schemas/FaceSimilarityPair"},"type":"array","title":"Pairs","description":"Every comparable probe x every comparable gallery face, except a face with itself. Ordered by probe (request order), then similarity descending, then gallery request order."},"incomparable":{"items":{"$ref":"#/components/schemas/IncomparableFace"},"type":"array","title":"Incomparable","description":"One entry per distinct requested face that could not be scored, probes first."},"model_id":{"type":"string","title":"Model Id","description":"Identifier of the face model every score is computed with."},"embedding_version":{"type":"integer","title":"Embedding Version","description":"Version of the face representation every score is computed with. Scores are only comparable across responses with the same model_id and embedding_version."}},"additionalProperties":false,"type":"object","required":["pairs","incomparable","model_id","embedding_version"],"title":"FaceSimilarityResponse","description":"Scores for every comparable probe against every comparable gallery face."},"Faces":{"properties":{"image":{"type":"string","title":"Image","default":""},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"face_id":{"type":"integer","title":"Face Id"},"confidence":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Confidence","description":"Name-assignment confidence. For source 'ocr', the winning share of votes; for 'driver_selection', the strength of that selection. Not comparable across sources. Distinct from speaker-match accuracy."},"votes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Votes","description":"For source 'ocr', the number of votes that produced the winning name. 0 for 'driver_selection'."},"samples":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Samples","description":"For source 'ocr', the vote opportunities where this face was visible. Needed to tell confidence=1.0 from one vote apart from confidence=1.0 from thirty. 0 for 'driver_selection'."},"alternatives":{"anyOf":[{"additionalProperties":{"type":"integer"},"type":"object"},{"type":"null"}],"title":"Alternatives","description":"For source 'ocr', other names read on screen and their vote counts. Empty for 'driver_selection'."},"is_local":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Local","description":"For source 'ocr', true when the on-screen label read You/Me — that tile is the account holder. False for 'driver_selection'."},"source":{"anyOf":[{"type":"string","enum":["ocr","driver_selection"]},{"type":"null"}],"title":"Source","description":"How the name was assigned: 'ocr' (read from an on-screen label) or 'driver_selection' (the in-vehicle role label \"Driver\" on the selected driver's face). A role label is not a person's name."},"inferred_from_prefix":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Inferred From Prefix","description":"For source 'ocr', true when every winning vote came from a truncated label whose prefix uniquely matched a full name read on some other face. The precision-first UI should treat this as a low-trust name. False for 'driver_selection'."},"reason":{"anyOf":[{"type":"string","enum":["clear_lead","sole_candidate"]},{"type":"null"}],"title":"Reason","description":"Why the role label was assigned: 'clear_lead' (one candidate clearly led) or 'sole_candidate' (only one candidate). Set only with source 'driver_selection'."}},"type":"object","required":["face_id"],"title":"Faces","description":"Model representing a detected face.\n\nAttributes:\n    image (str): Image data (base64 or URL). Empty string for placeholder\n        faces created via POST /v1/videos/{id}/faces — see #437. Stays\n        `str` (not `Optional[str]`) so an `image=None` value still raises\n        a validation error: silently letting `None` through would degrade\n        FE thumbnails to `url(null)` whereas a 500 surfaces the corruption.\n    name (Optional[str]): Optional name of the person.\n    face_id (int): Unique identifier for the face.\n    confidence (Optional[float]): Name-assignment confidence. For 'ocr',\n        the winning share of votes; for 'driver_selection', the strength\n        of that selection. Not comparable across sources. Distinct from\n        speaker-match accuracy. Absent on generic videos.\n    votes (Optional[int]): OCR votes that produced the winning name.\n        0 for 'driver_selection'.\n    samples (Optional[int]): Vote opportunities where this face was visible.\n        0 for 'driver_selection'.\n    alternatives (Optional[Dict[str, int]]): Other OCR-read names and their\n        vote counts. Empty for 'driver_selection'.\n    is_local (Optional[bool]): True when the on-screen label read You/Me.\n        False for 'driver_selection'.\n    source (Optional[Literal[\"ocr\", \"driver_selection\"]]): How the name\n        was assigned. 'driver_selection' marks the in-vehicle role label\n        \"Driver\". Widening this is a visible contract edit.\n    inferred_from_prefix (Optional[bool]): True when every winning vote\n        came from a truncated label whose prefix uniquely matched a full\n        name read on some other face. False for 'driver_selection'.\n    reason (Optional[Literal[\"clear_lead\", \"sole_candidate\"]]): Why the\n        role label was assigned. Set only with 'driver_selection'."},"FetchTextResponse":{"properties":{"id":{"type":"string","title":"Id"},"status":{"type":"string","title":"Status"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"model_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model Id"},"document_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Document Type"},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"},"word_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Word Count"},"segments":{"anyOf":[{"items":{"anyOf":[{"$ref":"#/components/schemas/Emotions"},{"$ref":"#/components/schemas/TranscriptEmotions"}]},"type":"array"},{"type":"null"}],"title":"Segments"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text"},"emotions":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Emotions"},"parent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Id"}},"type":"object","required":["id","status"],"title":"FetchTextResponse","description":"Detailed response for a text emotion analysis request.\n\nAttributes:\n    title (Optional[str]): Title of the text (available for v2 texts).\n    model_id (str): Model used to process the text.\n    created_at (Any): Timestamp of creation.\n    text (Optional[str]): The input text.\n    user_id (Optional[str]): ID of the user who submitted the text.\n    word_count (Optional[int]): Number of words in the input.\n    segments (Optional[List[Emotions]]): List of emotions per sentence.\n    emotions (Optional[dict]): Overall emotions detected in the text.\n    parent_id (Optional[str]): ID associated with parent audio id if any."},"FieldState":{"properties":{"state":{"type":"string","title":"State","description":"One of `pending`, `processing`, `completed`, `failed`, `skipped` or `unknown`. More values may be added later: treat any state you do not recognise as not finished.","examples":["processing"]},"terminal":{"type":"boolean","title":"Terminal","description":"True when the field will not change on its own (`completed`, `failed` or `skipped`).","examples":[false]},"progress":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Progress","description":"Fraction done between 0 and 1, or null where progress is not reported.","examples":[0.45]}},"type":"object","required":["state","terminal","progress"],"title":"FieldState","description":"Normalised state of one status field."},"FocusIn":{"properties":{"subjects":{"items":{"type":"string"},"type":"array","maxItems":50,"title":"Subjects"},"objective":{"type":"string","maxLength":2000,"title":"Objective","default":""}},"additionalProperties":false,"type":"object","title":"FocusIn"},"FrameEmotionResponseData":{"properties":{"frame_id":{"type":"string","title":"Frame Id"},"face_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Face Id"},"dominant_emotion":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dominant Emotion"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message"}},"type":"object","required":["frame_id"],"title":"FrameEmotionResponseData","description":"Model for dominant emotion data of a frame.\n\nAttributes:\n    frame_id (str): Identifier for the video frame.\n    face_id (Optional[int]): Face ID detected in the frame.\n    dominant_emotion (Optional[str]): Most prominent emotion detected.\n    message (Optional[str]): Optional message or status info."},"FrameNoteResponse":{"properties":{"id":{"type":"string","title":"Id"},"status":{"type":"string","title":"Status"}},"type":"object","required":["id","status"],"title":"FrameNoteResponse","description":"Model for response after adding a note to a frame.\n\nAttributes:\n    id (str): Frame ID or reference.\n    status (str): Status of the note operation."},"FramesResponse":{"properties":{"original_frame_image":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Original Frame Image"},"frame_image":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Frame Image","description":"Deprecated: always null. Per-frame thumbnails are no longer produced."},"found_faces":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Found Faces"},"timestamp":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Timestamp"},"faces":{"anyOf":[{"items":{"$ref":"#/components/schemas/BoundingBox"},"type":"array"},{"type":"null"}],"title":"Faces"},"frame_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Frame Id"},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"}},"type":"object","title":"FramesResponse","description":"One stored video frame: images, timestamp, and per-face boxes with emotion maps."},"FullAudioAnalyticsResponse":{"properties":{"title":{"type":"string","title":"Title","default":"","examples":["Podcast Episode 42"]},"status":{"type":"string","title":"Status","default":"","examples":["completed"]},"description":{"type":"string","title":"Description","default":"","examples":["Discussion about AI ethics"]},"created_at":{"type":"string","title":"Created At","default":"","examples":["2025-12-01T10:15:00Z"]},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At","description":"ISO-8601 UTC timestamp of the latest change to any of the records this response is built from (the audio and its transcript). Compare it for inequality with the value you hold; it carries no ordering guarantee.","examples":["2025-12-01T14:30:00.000000+00:00"]},"media_type":{"type":"string","title":"Media Type","default":"audio","examples":["audio"]},"audio_id":{"type":"string","title":"Audio Id","default":"","examples":["1154940c-473f-46cb-a32d-923e6f23bedc"]},"text_id":{"type":"string","title":"Text Id","default":"","examples":["c74a3fe1-e8a3-4c7a-98c9-a496ee4a3fb8"]},"duration":{"type":"number","title":"Duration","default":0.0,"examples":[3245.5]},"speaker_count":{"type":"integer","title":"Speaker Count","default":0,"examples":[2]},"media_source_url":{"type":"string","title":"Media Source Url","default":"","examples":["https://storage.googleapis.com/.../audio.mp3"]},"youtube_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube Url","examples":["https://youtube.com/watch?v=..."]},"detected_speakers":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Detected Speakers","examples":[[{"name":"Alice Johnson","speaker_id":1},{"name":"Bob Williams","speaker_id":2}]]},"emotion_analysis":{"$ref":"#/components/schemas/EmotionAnalysis","examples":[{"overall":{"audio":{"angry":0.025,"happy":0.28,"neutral":0.55},"text":{"admiration":0.045,"joy":0.04,"neutral":0.45}},"speakers":{"Alice Johnson":{"audio":{"angry":0.02,"happy":0.32,"neutral":0.52},"text":{"admiration":0.06,"joy":0.055,"neutral":0.4}},"Bob Williams":{"audio":{"angry":0.03,"happy":0.24,"neutral":0.58},"text":{"amusement":0.07,"neutral":0.5}}}}]}},"type":"object","required":["emotion_analysis"],"title":"FullAudioAnalyticsResponse","description":"Complete multimodal analytics response for audio content."},"FullVideoAnalyticsResponse":{"properties":{"title":{"type":"string","title":"Title","default":"","examples":["Customer Interview - Q4 2025"]},"status":{"type":"string","title":"Status","default":"","examples":["completed"]},"description":{"type":"string","title":"Description","default":"","examples":["Quarterly customer satisfaction interview"]},"created_at":{"type":"string","title":"Created At","default":"","examples":["2025-12-01T14:30:00Z"]},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At","description":"ISO-8601 UTC timestamp of the latest change to any of the records this response is built from (the video, its audio and its transcript). Compare it for inequality with the value you hold; it carries no ordering guarantee. It does not cover the emotion analysis computed from frames and faces, which can change without any of those records changing.","examples":["2025-12-01T14:30:00.000000+00:00"]},"media_type":{"type":"string","title":"Media Type","default":"video","examples":["video"]},"video_id":{"type":"string","title":"Video Id","default":"","examples":["01a5aceb-8411-4174-b80f-af6789dbd925"]},"audio_id":{"type":"string","title":"Audio Id","default":"","examples":["1154940c-473f-46cb-a32d-923e6f23bedc"]},"text_id":{"type":"string","title":"Text Id","default":"","examples":["c74a3fe1-e8a3-4c7a-98c9-a496ee4a3fb8"]},"duration":{"type":"number","title":"Duration","default":0.0,"examples":[20.56]},"total_frames_count":{"type":"integer","title":"Total Frames Count","default":0,"examples":[87480]},"frame_rate":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Frame Rate","examples":[30.0]},"frame_height":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Frame Height","examples":[1080.0]},"frame_width":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Frame Width","examples":[1920.0]},"face_count":{"type":"integer","title":"Face Count","default":0,"examples":[2]},"speaker_count":{"type":"integer","title":"Speaker Count","default":0,"examples":[2]},"media_source_url":{"type":"string","title":"Media Source Url","default":"","examples":["https://storage.googleapis.com/.../video.mp4"]},"annotated_video_url":{"type":"string","title":"Annotated Video Url","default":"","examples":["https://storage.googleapis.com/.../annotated.mp4"]},"youtube_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube Url","examples":[null]},"detected_faces":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Detected Faces","examples":[[{"face_id":1,"image":"https://storage.googleapis.com/.../faces/1.jpg","name":"John Smith"},{"face_id":2,"image":"https://storage.googleapis.com/.../faces/2.jpg","name":""}]]},"detected_speakers":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Detected Speakers","examples":[[{"name":"John Smith","speaker_id":1},{"name":"Interviewer","speaker_id":2}]]},"emotion_analysis":{"$ref":"#/components/schemas/EmotionAnalysis","examples":[{"faces":{"John Smith":{"happy":0.175,"neutral":0.408,"sad":0.049},"face_2":{"angry":0.087,"happy":0.184,"neutral":0.314}},"overall":{"audio":{"angry":0.047,"happy":0.185,"neutral":0.638},"text":{"admiration":0.015,"joy":0.018,"neutral":0.75},"video":{"angry":0.059,"happy":0.175,"neutral":0.408,"sad":0.049}},"speakers":{"Interviewer":{"audio":{"angry":0.05,"happy":0.18,"neutral":0.65},"text":{"curiosity":0.08,"neutral":0.85}},"John Smith":{"audio":{"angry":0.045,"happy":0.19,"neutral":0.625},"text":{"admiration":0.02,"joy":0.025,"neutral":0.72}}}}]}},"type":"object","required":["emotion_analysis"],"title":"FullVideoAnalyticsResponse","description":"Complete multimodal analytics response for video content."},"GenerateCSVResponse":{"properties":{"id":{"type":"string","title":"Id"},"message":{"type":"string","title":"Message"},"status":{"type":"string","title":"Status"}},"type":"object","required":["id","message","status"],"title":"GenerateCSVResponse","description":"Response after generating a CSV report.\n\nAttributes:\n    id (str): ID of the resource associated with the CSV.\n    message (str): Confirmation or status message.\n    status (str): Current status of the CSV generation."},"GenerateReportResponse":{"properties":{"id":{"type":"string","title":"Id"},"message":{"type":"string","title":"Message"},"status":{"type":"string","title":"Status"}},"type":"object","required":["id","message","status"],"title":"GenerateReportResponse","description":"Response model for PDF report generation\n\nInherits from\n    GenerateCSVResponse"},"GeoTag":{"properties":{"id":{"type":"string","title":"Id"},"key":{"type":"string","title":"Key"},"source":{"type":"string","title":"Source"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By"},"value_type":{"type":"string","const":"location_admin","title":"Value Type"},"value_geo":{"$ref":"#/components/schemas/GeoValue"}},"additionalProperties":false,"type":"object","required":["id","key","source","created_at","value_type","value_geo"],"title":"GeoTag"},"GeoValue":{"properties":{"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"},"state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City"}},"additionalProperties":false,"type":"object","title":"GeoValue","description":"Administrative-level location. lat/lng intentionally omitted in v1."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HealthCheckResponse":{"properties":{"status":{"type":"string","title":"Status","description":"Health status"},"service":{"type":"string","title":"Service","description":"Service name","default":"imentiv-realtime"}},"type":"object","required":["status"],"title":"HealthCheckResponse","description":"Response model for realtime service health check."},"HighlightGenerateResponse":{"properties":{"video_id":{"type":"string","title":"Video Id"},"message":{"type":"string","title":"Message"},"status":{"type":"string","title":"Status"}},"type":"object","required":["video_id","message","status"],"title":"HighlightGenerateResponse","description":"Model for highlight video generation trigger response.\n\nAttributes:\n    video_id (str): The video ID for which highlights are being generated.\n    message (str): Status message about the highlight generation process.\n    status (str): Current status of highlight generation (queue, processing, completed, failed)."},"HighlightResponse":{"properties":{"highlight_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Highlight Status"},"highlight_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Highlight Url"}},"type":"object","title":"HighlightResponse","description":"Model for highlight video response.\n\nAttributes:\n    message (str): Status or success message.\n    highlight_url (str): URL to the generated highlight video."},"HighlightSegment":{"properties":{"index":{"type":"integer","title":"Index","description":"Index of the segment in the highlight reel"},"start_time":{"type":"integer","title":"Start Time","description":"Start time of the segment in milliseconds"},"end_time":{"type":"integer","title":"End Time","description":"End time of the segment in milliseconds"}},"type":"object","required":["index","start_time","end_time"],"title":"HighlightSegment","description":"Represents a single highlight segment with timing information."},"ICECandidateRequest":{"properties":{"session_id":{"type":"string","title":"Session Id","description":"Active session identifier"},"candidate":{"type":"string","title":"Candidate","description":"ICE candidate string"},"sdp_mid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sdp Mid","description":"SDP media ID"},"sdp_mline_index":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sdp Mline Index","description":"SDP media line index"}},"type":"object","required":["session_id","candidate"],"title":"ICECandidateRequest","description":"Request model for forwarding ICE candidate."},"ICECandidateResponse":{"properties":{"session_id":{"type":"string","title":"Session Id","description":"Session identifier"},"status":{"type":"string","title":"Status","description":"Processing status"}},"type":"object","required":["session_id","status"],"title":"ICECandidateResponse","description":"Response model for ICE candidate acknowledgement."},"IceServer":{"properties":{"urls":{"anyOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"}],"title":"Urls","description":"STUN/TURN server URL(s). Can be single URL or list for multiple transports."},"username":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Username","description":"Username for TURN authentication (not needed for STUN)"},"credential":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Credential","description":"Credential/password for TURN authentication (not needed for STUN)"}},"type":"object","required":["urls"],"title":"IceServer","description":"ICE server configuration for WebRTC connections.\n\nUsed to provide STUN/TURN server details to clients for NAT traversal.\nTURN servers include ephemeral credentials generated per-session."},"ImageDBResponse":{"properties":{"id":{"type":"string","title":"Id"},"status":{"type":"string","title":"Status"},"title":{"type":"string","title":"Title"},"description":{"type":"string","title":"Description"},"created_at":{"title":"Created At"},"original_frame_image":{"title":"Original Frame Image"},"height":{"type":"number","title":"Height"},"width":{"type":"number","title":"Width"},"found_faces":{"items":{"type":"integer"},"type":"array","title":"Found Faces"},"faces":{"title":"Faces"},"annotated_image":{"title":"Annotated Image"}},"type":"object","required":["id","status","title","description","created_at","original_frame_image","height","width","found_faces","faces","annotated_image"],"title":"ImageDBResponse","description":"Detailed response model for image emotion stored in DB.\n\nAttributes:\n    title (str): Title of the image.\n    description (str): Description provided by the user.\n    created_at (Any): Timestamp when image was added.\n    original_frame_image (Any): Original image file.\n    height (float): Height of the image.\n    width (float): Width of the image.\n    found_faces (int): Number of faces detected.\n    faces (Any): Detailed face data.\n    annotated_image (Any): Image with annotations (e.g., bounding boxes)."},"ImageDeleteResponse":{"properties":{"id":{"type":"string","title":"Id"},"status":{"type":"string","title":"Status"}},"type":"object","required":["id","status"],"title":"ImageDeleteResponse","description":"Response returned after deleting an image analysis result."},"ImageDetailResponseV2":{"properties":{"actors":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Actors","description":"List of actors/known faces with image, name, and user_id"},"annotated_image":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Annotated Image","description":"Annotated image URL"},"bulk_upload_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bulk Upload Id","description":"Bulk upload batch ID"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At","description":"Creation timestamp"},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At","description":"ISO-8601 UTC timestamp of the last change to this image. Compare it for inequality with the value you hold to tell whether your copy is stale; it carries no ordering guarantee. It covers this image's own fields only, not data derived from other records such as frames or analytics.","examples":["2025-12-01T14:30:00.000000+00:00"]},"csv_analysis_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Csv Analysis Url","description":"CSV analysis file URL"},"csv_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Csv Status","description":"CSV generation status"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Image description"},"face_detection_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Face Detection Status","description":"Face detection status message"},"faces":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Faces","description":"Detected faces with emotions"},"found_faces":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Found Faces","description":"List of found face IDs"},"height":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Height","description":"Image height in pixels"},"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id","description":"Unique image identifier"},"is_user":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is User","description":"Whether this is a user-uploaded image"},"model_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model Id","description":"Model used for processing"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes","description":"General notes for the image"},"original_frame_image":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Original Frame Image","description":"Original image URL"},"predefined_qa":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Predefined Qa","description":"Predefined Q&A about the image"},"psychologist_review":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Psychologist Review","description":"Psychologist review content"},"psychologist_review_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Psychologist Review Status","description":"Psychologist review status"},"share_status":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Share Status","description":"Whether image is shared"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","description":"Processing status"},"tags":{"anyOf":[{"items":{"oneOf":[{"$ref":"#/components/schemas/StringTag"},{"$ref":"#/components/schemas/EnumTag"},{"$ref":"#/components/schemas/IntegerTag"},{"$ref":"#/components/schemas/BoolTag"},{"$ref":"#/components/schemas/DateTag"},{"$ref":"#/components/schemas/GeoTag"},{"$ref":"#/components/schemas/SetTag"},{"$ref":"#/components/schemas/NoneTag"}],"discriminator":{"propertyName":"value_type","mapping":{"boolean":"#/components/schemas/BoolTag","date":"#/components/schemas/DateTag","enum":"#/components/schemas/EnumTag","integer":"#/components/schemas/IntegerTag","location_admin":"#/components/schemas/GeoTag","none":"#/components/schemas/NoneTag","set":"#/components/schemas/SetTag","string":"#/components/schemas/StringTag"}}},"type":"array"},{"type":"null"}],"title":"Tags","description":"Generalized media tags (demographic, survey, label, system). Discriminated union over value_type — see /v2/tag-keys for dimension metadata."},"tags_map":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Tags Map","description":"Tags map with general and sample flags"},"thumbnail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Thumbnail","description":"Thumbnail image URL"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"Image title"},"ui_request":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Ui Request","description":"Whether request came from UI"},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id","description":"The user who created the item (the same value as `created_by`)."},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By","description":"The user who created the item. Never changes, including when the item is moved to a workspace."},"owner":{"anyOf":[{"$ref":"#/components/schemas/MediaOwnerOut"},{"type":"null"}],"description":"Who owns the item: the user, or the team workspace that holds it."},"width":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Width","description":"Image width in pixels"}},"type":"object","title":"ImageDetailResponseV2","description":"Complete V2 response model for image details with all possible fields.\nReturns null for fields that don't exist in the database.\nFields are ordered alphabetically."},"ImageEditResponse":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"}},"type":"object","title":"ImageEditResponse","description":"Model for editing image metadata.\n\nAttributes:\n    title (str): Updated title.\n    description (str): Updated description."},"ImageEmotionResponseV2":{"properties":{"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"status":{"type":"string","title":"Status"},"media_source":{"type":"string","title":"Media Source"},"media_source_url":{"type":"string","title":"Media Source Url"},"media_source_type":{"type":"string","title":"Media Source Type"},"user_id":{"type":"string","title":"User Id"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"}},"type":"object","required":["id","title","status","media_source","media_source_url","media_source_type","user_id"],"title":"ImageEmotionResponseV2","description":"V2 response model for image emotion detection with URL source tracking.\n\nSupports:\n- File uploads (JPG, JPEG, PNG, WEBP)\n- Cloud storage URLs (Dropbox, direct URLs)\n- URL source tracking\n\nAttributes:\n    id (str): Unique ID of the image.\n    title (str): Title of the image.\n    status (str): Processing status (\"queue\").\n    media_source (str): Source type (\"dropbox\", \"public_url\", \"file_upload\").\n    media_source_url (str): Full URL as provided by user or uploaded file URL.\n    media_source_type (str): Type of source media (always \"image\" for this endpoint).\n    user_id (str): ID of the user who uploaded the image.\n    created_at (Optional[str]): Timestamp when image was created."},"ImageNoteResponse":{"properties":{"id":{"type":"string","title":"Id"},"status":{"type":"string","title":"Status"}},"type":"object","required":["id","status"],"title":"ImageNoteResponse","description":"Response after updating or retrieving notes for an image."},"ImageReprocessResponse":{"properties":{"id":{"type":"string","title":"Id"},"status":{"type":"string","title":"Status"},"message":{"type":"string","title":"Message"}},"type":"object","required":["id","status","message"],"title":"ImageReprocessResponse","description":"Response model returned after reprocessing an image emotion analysis."},"IncomparableFace":{"properties":{"video_id":{"type":"string","title":"Video Id"},"face_id":{"type":"integer","title":"Face Id"},"reason":{"type":"string","enum":["not_found","stale","no_embedding","unverified_model","model_mismatch"],"title":"Reason","description":"`not_found`: no such video or face among videos you own. `stale`: the video's faces were merged after analysis; reprocess the video to compare it. `no_embedding`: no usable face data for this face, for example because the video is not completed, predates this feature, or the face was never seen clearly. `unverified_model`: the face data was produced by an unidentified model version. `model_mismatch`: the face data was produced by a different model version from the one this response is scored on."}},"additionalProperties":false,"type":"object","required":["video_id","face_id","reason"],"title":"IncomparableFace","description":"A requested face that could not be scored, and why."},"IntegerTag":{"properties":{"id":{"type":"string","title":"Id"},"key":{"type":"string","title":"Key"},"source":{"type":"string","title":"Source"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By"},"value_type":{"type":"string","const":"integer","title":"Value Type"},"value_int":{"type":"integer","title":"Value Int"}},"additionalProperties":false,"type":"object","required":["id","key","source","created_at","value_type","value_int"],"title":"IntegerTag"},"InterpretationReliability":{"properties":{"narrowband":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Narrowband","description":"True when this speaker (else the file) is narrowband. Null means unknown, not false."},"limited":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Limited","description":"True when this speaker (else the file) is a limited capture. Null means unknown, not false."},"arousal_version":{"anyOf":[{"type":"string","enum":["acoustic_v1","acoustic_v2"]},{"type":"null"}],"title":"Arousal Version","description":"Dimensions version from this speaker's ungated parents. acoustic_v2 means arousal is live even when limited is true — do not treat limited as 'arousal is dead'."},"unreadable_share":{"type":"number","title":"Unreadable Share","description":"register unknown + other, or 0 when register is null.","default":0.0}},"type":"object","title":"InterpretationReliability","description":"Per-speaker capture / model reliability for a contact-centre interpretation."},"InterpretationSegment":{"properties":{"relative":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Relative","description":"emotions minus this speaker's register, per emotion2vec label, values in [-1, 1]. A difference, not a distribution — do not feed it to computeEmotionVectors. Null when that speaker's register is null."},"moment":{"type":"boolean","title":"Moment","description":"True when the max placeable non-neutral relative is >= 0.25. Sink rows are never moments.","default":false},"moment_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Moment Label","description":"The placeable non-neutral label that cleared the moment floor, else null."},"tone_label":{"anyOf":[{"type":"string","enum":["animated","pleased","flat"]},{"type":"null"}],"title":"Tone Label","description":"Agent-only, request-time role. animated = bright voice + non-positive text valence; pleased = bright + positive text valence; flat = not bright. Null for non-agent, unset role, and unreadable (unknown: 1.0) sink rows. A role edit flips this on the next GET."},"stance":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stance","description":"Process-time C1 label from stance.segments['<idx>'].label. Null when C1 is missing or the stance map length does not match segment_emotions (e.g. after a speaker delete before C2 restamp). A role edit does not flip this."}},"type":"object","title":"InterpretationSegment","description":"One parent row, index-aligned with segment_emotions[] (CSV Index = i+1)."},"InterpretationSpeaker":{"properties":{"register":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Register","description":"Duration-weighted mean of ungated parent emotion2vec vectors. Null below the floor."},"register_null_reason":{"anyOf":[{"type":"string","enum":["too_few_segments","too_short"]},{"type":"null"}],"title":"Register Null Reason","description":"Why register is null. too_few_segments wins if both floors fail. Null when register is present."},"n_segments":{"type":"integer","title":"N Segments","description":"Ungated parent count for this speaker.","default":0},"duration_ms":{"type":"integer","title":"Duration Ms","description":"Ungated parent duration in milliseconds.","default":0},"reliability":{"$ref":"#/components/schemas/InterpretationReliability"},"stance_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stance Summary","description":"Process-time C1 speaker summary. A role edit does not flip this."},"top_stances":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Top Stances","description":"Process-time C1 top stances for this speaker. A role edit does not flip this."}},"type":"object","title":"InterpretationSpeaker","description":"Per-speaker register and reliability. Keys on AudioInterpretation.speakers are string ids."},"LinkVideoRequest":{"properties":{"video_id":{"type":"string","minLength":1,"title":"Video Id","description":"ID of an existing Imentiv video"},"person_id":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}],"title":"Person Id","description":"Optional Person ID for person-scoped linking — uniform across all project types."},"task":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Task","description":"Task or scenario label"}},"type":"object","required":["video_id"],"title":"LinkVideoRequest"},"LinkedPersonRef":{"properties":{"person_id":{"type":"string","title":"Person Id"},"name":{"type":"string","title":"Name","default":""}},"type":"object","required":["person_id"],"title":"LinkedPersonRef","description":"A Person in this project that links the video (imentiv-backend#891).\n\nShared video linking (#713) means one video can back several Persons'\ntiles. Deleting the video destroys the analysis for all of them, so the\ndelete confirmation has to be able to name them."},"ListOfFaces":{"properties":{"faces":{"items":{"$ref":"#/components/schemas/Faces"},"type":"array","title":"Faces"}},"type":"object","required":["faces"],"title":"ListOfFaces","description":"Model representing a collection of faces.\n\nAttributes:\n    faces (List[Faces]): List of detected or named faces."},"ManualCall":{"properties":{"call_id":{"type":"string","title":"Call Id"},"title":{"type":"string","maxLength":150,"minLength":1,"title":"Title"},"happened_at":{"type":"string","format":"date-time","title":"Happened At"},"participants":{"items":{"$ref":"#/components/schemas/ManualCallParticipant"},"type":"array","title":"Participants"},"notes":{"type":"string","maxLength":20000,"title":"Notes","default":""},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"}},"type":"object","required":["call_id","title","happened_at"],"title":"ManualCall","description":"A record-only call on a deal (web#2030 Phase A): no media, just when\nit happened, who was there, and the notes. Timeline position comes from\n``happened_at`` — required, unlike videos where it can be absent."},"ManualCallParticipant":{"properties":{"name":{"type":"string","maxLength":200,"minLength":1,"title":"Name"},"side":{"anyOf":[{"type":"string","enum":["buyer","seller","unclassified"]},{"type":"null"}],"title":"Side"}},"type":"object","required":["name"],"title":"ManualCallParticipant"},"MediaAppearance":{"properties":{"video_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Video Id","description":"Video this appearance is on."},"audio_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Audio Id","description":"Standalone audio this appearance is on."},"face_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Face Id","description":"Face on the video."},"speaker_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Speaker Id","description":"Speaker attached to this person."},"linked_at":{"type":"string","format":"date-time","title":"Linked At"},"linked_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Linked By","description":"Account that attached this appearance. Absent when the viewer is not signed in."}},"type":"object","required":["linked_at"],"title":"MediaAppearance","description":"One face or speaker attached to this person."},"MediaAppearanceInput":{"properties":{"face_id":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Face Id","description":"Face on the video."},"speaker_id":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Speaker Id","description":"Speaker on this video's audio, or on a standalone audio."}},"type":"object","title":"MediaAppearanceInput","description":"A face or a speaker to attach. At least one is required."},"MediaBindingIn":{"properties":{"media_id":{"type":"string","maxLength":128,"minLength":1,"title":"Media Id"},"media_type":{"type":"string","enum":["video","audio"],"title":"Media Type"},"subjects":{"anyOf":[{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"integer"},{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array"}]},"propertyNames":{"enum":["face","voice","text"]},"type":"object"},{"type":"null"}],"title":"Subjects","description":"Per modality, the subject id (or ids of one person) to report. Required for a modality with more than one subject in the recording."},"people":{"anyOf":[{"items":{"$ref":"#/components/schemas/ConversationPersonIn"},"type":"array","maxItems":4,"minItems":1},{"type":"null"}],"title":"People","description":"Explicitly selected, separately labelled people in caller order. Mutually exclusive with subjects. Omitted modalities stay unbound, even with one speaker. Unlike expert item people (a map with restricted labels), this is a list allowing role labels and fragmented IDs."}},"additionalProperties":false,"type":"object","required":["media_id","media_type"],"title":"MediaBindingIn","description":"Evidence from one of the caller's own processed recordings."},"MediaOwnerOut":{"properties":{"kind":{"type":"string","enum":["user","workspace"],"title":"Kind","description":"`user`: an item owned by one person, its creator. `workspace`: an item owned by a team workspace, which its members can read. This says who owns the item only; a share link or other access to it is separate."},"id":{"type":"string","title":"Id","description":"The owner's id: the user's id, or the workspace's id."},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"The workspace's name, or the creator's display name. Absent when it is not known."}},"type":"object","required":["kind","id"],"title":"MediaOwnerOut","description":"Who owns a media item. Never carries an email address."},"MediaPerson":{"properties":{"person_id":{"type":"string","title":"Person Id"},"scope":{"type":"string","const":"media","title":"Scope","default":"media"},"media_type":{"type":"string","enum":["video","audio"],"title":"Media Type","description":"Home this person is stored on. Use this in the update path."},"media_id":{"type":"string","title":"Media Id","description":"Home id. Use this id, not the page you listed from, when you update."},"name":{"type":"string","title":"Name"},"appearances":{"items":{"$ref":"#/components/schemas/MediaAppearance"},"type":"array","title":"Appearances"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["person_id","media_type","media_id","name","created_at","updated_at"],"title":"MediaPerson","description":"A person named on one video or one standalone audio.\n\n``media_type`` and ``media_id`` are the home. Updates, including from a\nlinked audio page, use that home. A linked audio is not a home of its own."},"MediaPersonDeleteResponse":{"properties":{"person_id":{"type":"string","title":"Person Id"},"deleted":{"type":"boolean","title":"Deleted","default":true}},"type":"object","required":["person_id"],"title":"MediaPersonDeleteResponse","description":"The person was removed from this video or audio."},"MediaPersonListResponse":{"properties":{"persons":{"items":{"$ref":"#/components/schemas/MediaPerson"},"type":"array","title":"Persons"}},"type":"object","required":["persons"],"title":"MediaPersonListResponse","description":"People named on this video or audio."},"MediaStatusResponse":{"properties":{"media_id":{"type":"string","title":"Media Id","description":"The media ID","examples":["abc123-def456"]},"media_type":{"type":"string","title":"Media Type","description":"The media type","examples":["video"]},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","description":"Main processing status","examples":["completed","processing","failed","pending"]},"csv_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Csv Status","description":"CSV export status","examples":["completed","processing","failed","pending"]},"highlight_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Highlight Status","description":"Highlight reel status (video only)","examples":["completed","processing","failed","none"]},"highlight_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Highlight Url","description":"Highlight video URL (video only, available when highlight_status is completed)","examples":["https://storage.example.com/highlights/video123.mp4"]},"highlight_segments":{"anyOf":[{"items":{"$ref":"#/components/schemas/HighlightSegment"},"type":"array"},{"type":"null"}],"title":"Highlight Segments","description":"Highlight segments with timing info (video only, available when highlight_status is completed)"},"pdf_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pdf Status","description":"PDF report status","examples":["completed","processing","failed","pending"]},"annotated_video_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Annotated Video Status","description":"Annotated video status (video only)","examples":["completed","processing","pending"]},"annotated_video_percentage":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Annotated Video Percentage","description":"Annotated video processing percentage (video only)","examples":["0","45","100"]},"annotated_video_stage":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Annotated Video Stage","description":"Where the annotated video job is (video only): queued, starting, fetching_frames, downloading_source, extracting_audio, rendering, muxing_audio, uploading, finalizing, done, failed","examples":["queued","rendering","uploading"]},"annotated_video_error":{"anyOf":[{"$ref":"#/components/schemas/AnnotatedVideoError"},{"type":"null"}],"description":"Why the annotated video job failed (video only, present when annotated_video_status is failed)"},"personality_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Personality Status","description":"Personality analysis status (video only)","examples":["completed","processing","failed","pending","no audio"]},"personality_processing":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Personality Processing","description":"Personality processing percentage (video only)","examples":["0","45","100"]},"personality_statuses":{"anyOf":[{"items":{"$ref":"#/components/schemas/FacePersonalityStatus"},"type":"array"},{"type":"null"}],"title":"Personality Statuses","description":"Per-face personality analysis statuses (video only)"},"speech_status":{"anyOf":[{"type":"string"},{"type":"boolean"},{"type":"null"}],"title":"Speech Status","description":"Speech transcription status (audio/video)","examples":[true,false,"completed","processing"]},"frame_upload_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Frame Upload Status","description":"Frame upload status (video only)","examples":["completed","processing","pending"]},"frame_upload_progress":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Frame Upload Progress","description":"Frame upload progress percentage (video only)","examples":[0,50,100]},"process_status":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Process Status","description":"Processing percentage (audio/video)","examples":[75.5,100.0]},"summary_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary Status","description":"Summary generation status","examples":["completed","processing","pending"]},"psychologist_review_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Psychologist Review Status","description":"Psychologist review status","examples":["processing","completed","pending"]},"psychologist_review":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Psychologist Review","description":"Psychologist review content (available when psychologist_review_status is completed)","examples":["The analysis shows positive emotional patterns..."]},"face_deletion_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Face Deletion Status","description":"Face deletion status (video only)","examples":["completed"]},"speaker_deletion_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Speaker Deletion Status","description":"Speaker deletion status (audio only)","examples":["completed"]},"share_status":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Share Status","description":"Whether media is shared","examples":[true,false]},"speaker_face_match_reason":{"anyOf":[{"type":"string","enum":["no_speakers","timed_out"]},{"type":"null"}],"title":"Speaker Face Match Reason","description":"Why matching completed empty (no_speakers) or a suggestions run failed (timed_out)."},"speaker_face_match_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Speaker Face Match Status","description":"Speaker-face match status (video only). In-flight auto-match stores ``queue``; the trigger response uses ``processing``.","examples":["completed","processing","failed","pending","queue"]},"states":{"additionalProperties":{"$ref":"#/components/schemas/FieldState"},"type":"object","title":"States","description":"Normalised state of each requested status field, keyed by field name. Wait on this rather than on the raw values. Only fields that have a lifecycle appear; an empty map means none was requested."}},"type":"object","required":["media_id","media_type","states"],"title":"MediaStatusResponse","description":"Response model for media status API.\nReturns all status fields for a specific media item."},"MediaType":{"type":"string","enum":["videos","audios"],"title":"MediaType"},"MergeResponse":{"properties":{"message":{"type":"string","title":"Message"}},"type":"object","required":["message"],"title":"MergeResponse","description":"Response model for merging video/audio content.\n\nAttributes:\n    message (str): Status message about the merge result."},"MergeSpeakersResponse":{"properties":{"message":{"type":"string","title":"Message"}},"type":"object","required":["message"],"title":"MergeSpeakersResponse","description":"Response model after merging speakers within an audio.\n\nAttributes:\n    message (str): Status message about the merge result."},"ModalityCoverage":{"properties":{"dual_modality_share":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Dual Modality Share","description":"Fraction of weight contributed by appearances with both face and speaker linked"},"primary_modality":{"type":"string","enum":["face","voice","balanced"],"title":"Primary Modality"}},"type":"object","required":["dual_modality_share","primary_modality"],"title":"ModalityCoverage","description":"Per-trait modality-coverage hint.\n\nSome traits read less reliably from a face alone than others, so each trait\nsays how much of its weight came from appearances with both face and voice.\nThe narrative uses more cautious language for low-coverage traits."},"Moment":{"properties":{"id":{"type":"string","title":"Id","description":"Id of this moment. A later computation may assign a new id. `GET /v2/videos/{video_id}/moments/resolve` follows an id from an earlier computation to the current one, or reports that it is gone or unknown. Ids from older computations may be reported as unknown."},"kind":{"type":"string","title":"Kind","description":"What happened, for example `eye_closure.microsleep` or `sentiment_flip`."},"subject":{"type":"string","title":"Subject","description":"The subject it belongs to; join with `subjects[].id`."},"subject_resolution":{"type":"string","title":"Subject Resolution","description":"How the subject was identified: `face`, `speaker` or `person`."},"baseline_source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Baseline Source","description":"Which baseline the deviation was measured against, when one was used."},"start_ms":{"type":"integer","title":"Start Ms"},"end_ms":{"type":"integer","title":"End Ms"},"duration_ms":{"type":"integer","title":"Duration Ms","description":"Observed duration, in milliseconds."},"resolution_ms":{"type":"integer","title":"Resolution Ms"},"modalities":{"items":{"$ref":"#/components/schemas/MomentSpan"},"type":"array","title":"Modalities"},"anchor":{"anyOf":[{"$ref":"#/components/schemas/MomentAnchor"},{"type":"null"}]},"confidence":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Confidence","description":"0-1, when calibrated."},"score":{"type":"number","title":"Score","description":"Relative importance used to rank moments."},"evidence":{"additionalProperties":true,"type":"object","title":"Evidence","description":"Kind-specific measurements. `contaminated` is `true` when some of the voice or words evidence overlapped known non-human audio; it is absent otherwise."},"evidence_quality":{"items":{"type":"string"},"type":"array","title":"Evidence Quality","description":"Which evidence overlapped known non-human audio, such as a voice assistant or music: `audio_overlap:voice` and/or `audio_overlap:words`, in that order. Empty when none did, and on moments computed before this field existed."},"source_events":{"items":{"type":"string"},"type":"array","title":"Source Events","description":"Ids of the signal events it was built from."}},"type":"object","required":["id","kind","subject","subject_resolution","start_ms","end_ms","duration_ms","resolution_ms","score"],"title":"Moment","description":"One notable moment for one subject."},"MomentAnchor":{"properties":{"kind":{"type":"string","title":"Kind","description":"`sentence` or `stimulus`."},"id":{"type":"string","title":"Id","description":"The sentence or stimulus id."},"start_ms":{"type":"integer","title":"Start Ms"},"end_ms":{"type":"integer","title":"End Ms"}},"type":"object","required":["kind","id","start_ms","end_ms"],"title":"MomentAnchor","description":"What a moment responds to: a transcript sentence or a stimulus."},"MomentCoverage":{"properties":{"subject":{"type":"string","title":"Subject"},"kind":{"type":"string","title":"Kind"},"status":{"type":"string","title":"Status","description":"`measured`, `partial` or `unavailable`."},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Why coverage is partial or unavailable."},"observed_s":{"type":"number","title":"Observed S","description":"Seconds in which the kind could be observed.","default":0},"detail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Detail"},"detected_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Detected Count","description":"Set when not every detected moment of this kind is listed (reason `cap_reached`): how many were detected for this subject."},"retained_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Retained Count","description":"Set together with `detected_count`: how many of those moments are listed in `moments`."}},"type":"object","required":["subject","kind","status"],"title":"MomentCoverage","description":"Whether a kind of moment could be looked for on a subject. An empty list of\nmoments of a kind means \"none\" only where coverage for that kind is `measured`."},"MomentResolveResponse":{"properties":{"moments_status":{"type":"string","title":"Moments Status","description":"`ready`, `computing`, `failed` or `unavailable`."},"stale":{"type":"boolean","title":"Stale","description":"True when the stored moments are not the current computation. Answers are still about those stored moments.","default":false},"generation":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Generation","description":"Which computation the stored moments belong to. Null when nothing is stored. Compare it together with `profile_version` and `computed_at`."},"profile_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile Version","description":"Analysis profile version of the stored moments. Null when nothing is stored."},"computed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Computed At","description":"When the stored moments were computed. Changes on every successful computation, including when `generation` and `profile_version` stay the same. Null when nothing is stored."},"results":{"items":{"$ref":"#/components/schemas/MomentResolveRow"},"type":"array","title":"Results"}},"type":"object","required":["moments_status"],"title":"MomentResolveResponse","description":"Where moment ids from an earlier computation are in the latest stored moments.\n\nDoes not start a computation. `moments_status` and `stale` mean the same as on\n`GET /v2/videos/{video_id}/moments`."},"MomentResolveRow":{"properties":{"id":{"type":"string","title":"Id","description":"The id that was asked about."},"status":{"type":"string","title":"Status","description":"`current`, `superseded`, `gone` or `unknown`."},"current_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current Id","description":"The id in the latest stored moments. Equal to `id` when `current`, the followed id when `superseded`, and null when `gone` or `unknown`."}},"type":"object","required":["id","status"],"title":"MomentResolveRow","description":"One asked id, followed to the latest stored moments."},"MomentSpan":{"properties":{"modality":{"type":"string","title":"Modality","description":"`face`, `voice` or `words`."},"start_ms":{"type":"integer","title":"Start Ms","description":"Start, in milliseconds of video time."},"end_ms":{"type":"integer","title":"End Ms","description":"End, in milliseconds of video time."},"resolution_ms":{"type":"integer","title":"Resolution Ms","description":"Time resolution of this modality, in milliseconds."}},"type":"object","required":["modality","start_ms","end_ms","resolution_ms"],"title":"MomentSpan","description":"Where a moment was observed in one modality."},"MomentSubject":{"properties":{"id":{"type":"string","title":"Id","description":"`face:{face_id}` or `speaker:{speaker_id}`."},"face_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Face Id"},"speaker_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Speaker Id"},"person_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Person Id","description":"The linked project person, if any."},"is_driver":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Driver","description":"Set on in-vehicle videos for the driver."},"role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Role","description":"The speaker's role, for example `agent` or `customer`."}},"type":"object","required":["id"],"title":"MomentSubject","description":"Who a moment is about: a face, a speaker, or both, and a linked person."},"MomentsProfile":{"properties":{"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id","description":"For example `conversation`, `sales` or `in_vehicle`."},"version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version"},"schema_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Schema Version","description":"Version of the moments result format."},"safety_kinds":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Safety Kinds","description":"Kinds of moment this profile treats as safety-relevant and ranks ahead of the others; empty when it has none. Null for moments computed before this was reported."}},"type":"object","title":"MomentsProfile","description":"The analysis profile the moments were computed with."},"MultiFaceResponse":{"properties":{"message":{"type":"string","title":"Message"},"updated_frames":{"items":{"type":"integer"},"type":"array","title":"Updated Frames"}},"type":"object","required":["message","updated_frames"],"title":"MultiFaceResponse","description":"Response model after updating multiple faces.\n\nAttributes:\n    message (str): Operation status or result.\n    updated_frames (List[int]): IDs of the frames whose faces were updated."},"MultipleFramesResponse":{"properties":{"processed_frames":{"items":{"$ref":"#/components/schemas/FramesResponse"},"type":"array","title":"Processed Frames","examples":[[{"faces":[{"arousal":0.1082,"box":[10,20,100,120],"emotions":{"angry":0.0003,"contempt":0.0383,"disgust":0.0019,"fear":4e-05,"happy":0.9505,"neutral":0.008,"sad":0.0002,"surprise":0.0008},"face_id":1,"intensity":0.6334,"valence":0.6241}],"found_faces":[1],"frame_id":1,"timestamp":0}]]},"circumplex_version":{"type":"string","title":"Circumplex Version","description":"Name of the valence/arousal coordinate table the numbers were computed with: each emotion label sits at a fixed (valence, arousal) point and a face's values are the score-weighted sum of those points. It reflects the table in use now, not the one in use when the video was analysed, and it changes only when the table does. Applies to every face's `valence`, `arousal` and `intensity`.","default":"v2","examples":["v2"]}},"type":"object","required":["processed_frames"],"title":"MultipleFramesResponse","description":"Frames list. Per-face scores: processed_frames[].faces[].emotions."},"NameUpdateResponse":{"properties":{"face_id":{"type":"integer","title":"Face Id"},"name":{"type":"string","title":"Name"}},"type":"object","required":["face_id","name"],"title":"NameUpdateResponse","description":"Model for updating the name associated with a face.\n\nAttributes:\n    face_id (int): Identifier of the face being updated.\n    name (str): New name to assign to the face."},"NonHumanSpan":{"properties":{"start_ms":{"type":"integer","maximum":86400000.0,"minimum":0.0,"title":"Start Ms","description":"When it starts, in milliseconds."},"end_ms":{"type":"integer","maximum":86400000.0,"exclusiveMinimum":0.0,"title":"End Ms","description":"When it ends, in milliseconds. After start_ms."},"role":{"type":"string","enum":["system","media"],"title":"Role","description":"Who is speaking: \"system\" or \"media\"."},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label","description":"Optional name, 1-80 characters after surrounding spaces are removed."}},"additionalProperties":false,"type":"object","required":["start_ms","end_ms","role"],"title":"NonHumanSpan","description":"One known non-human interval. Customer-facing: this text reaches OpenAPI."},"NonHumanSpansRequest":{"properties":{"non_human_spans":{"items":{"$ref":"#/components/schemas/NonHumanSpan"},"type":"array","maxItems":1000,"title":"Non Human Spans"}},"additionalProperties":false,"type":"object","required":["non_human_spans"],"title":"NonHumanSpansRequest","description":"Body of ``PUT /v2/videos/{video_id}/non-human-spans``. ``[]`` clears."},"NonHumanSpansResponse":{"properties":{"video_id":{"type":"string","title":"Video Id"},"non_human_spans":{"items":{"$ref":"#/components/schemas/NonHumanSpan"},"type":"array","title":"Non Human Spans"}},"type":"object","required":["video_id","non_human_spans"],"title":"NonHumanSpansResponse","description":"The list as stored, in the order sent."},"NoneTag":{"properties":{"id":{"type":"string","title":"Id"},"key":{"type":"string","title":"Key"},"source":{"type":"string","title":"Source"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By"},"value_type":{"type":"string","const":"none","title":"Value Type"}},"additionalProperties":false,"type":"object","required":["id","key","source","created_at","value_type"],"title":"NoneTag"},"OceanScores":{"properties":{"agreeableness":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Agreeableness"},"conscientiousness":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Conscientiousness"},"extraversion":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Extraversion"},"neuroticism":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Neuroticism"},"openness":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Openness"}},"type":"object","required":["agreeableness","conscientiousness","extraversion","neuroticism","openness"],"title":"OceanScores","description":"Big Five OCEAN trait scores in [0, 1].\n\nField names match ``PersonalityOceanMetrics`` exactly so JSON serialization\nis interchangeable across the two surfaces."},"OutputSchemaIn":{"properties":{"version":{"type":"string","maxLength":128,"minLength":1,"title":"Version"},"json_schema":{"additionalProperties":true,"type":"object","title":"Json Schema","description":"JSON Schema (draft 2020-12) of the output object. The root must have `\"type\": \"object\"`."},"native":{"type":"boolean","title":"Native","description":"Send the schema to the model as a structured-output constraint. Set false for a schema the model rejects as too complex; the output is validated against it either way.","default":true}},"additionalProperties":false,"type":"object","required":["version","json_schema"],"title":"OutputSchemaIn"},"OverallEmotions":{"properties":{"video":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Video","examples":[{"angry":0.059,"happy":0.175,"neutral":0.408,"sad":0.049}]},"audio":{"additionalProperties":{"type":"number"},"type":"object","title":"Audio","examples":[{"angry":0.047,"happy":0.185,"neutral":0.638}]},"text":{"additionalProperties":{"type":"number"},"type":"object","title":"Text","examples":[{"admiration":0.015,"joy":0.018,"neutral":0.75}]}},"type":"object","title":"OverallEmotions","description":"Overall emotions across modalities."},"PaginatedResponse":{"properties":{"documents":{"title":"Documents"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["documents","count"],"title":"PaginatedResponse","description":"Model for paginated audio emotion responses.\n\nAttributes:\n    documents (List[AudioEmotionResponse]): List of audio responses.\n    count (int): Total count of documents."},"PersonalityAnalysisResponse":{"properties":{"video_id":{"type":"string","title":"Video Id"},"face_id":{"type":"integer","minimum":1.0,"title":"Face Id"},"analysis_status":{"anyOf":[{"additionalProperties":{"$ref":"#/components/schemas/PersonalityAnalysisStatus"},"type":"object"},{"type":"null"}],"title":"Analysis Status"},"overall":{"anyOf":[{"$ref":"#/components/schemas/PersonalityOverallMetrics"},{"type":"null"}]},"by_segment":{"anyOf":[{"$ref":"#/components/schemas/PersonalitySegmentMetrics"},{"type":"null"}]},"project_personality":{"anyOf":[{"$ref":"#/components/schemas/ProjectPersonalityBlock"},{"type":"null"}]}},"type":"object","required":["video_id","face_id"],"title":"PersonalityAnalysisResponse","description":"Complete response for personality analysis retrieval (public API).\n\nAttributes:\n    video_id (str): Video identifier\n    face_id (int): Face identifier (must be >= 1)\n    analysis_status (Optional[Dict[str, PersonalityAnalysisStatus]]): Status of overall and/or by_segment analysis (only includes keys that exist)\n    overall (Optional[PersonalityOverallMetrics]): Overall analysis (only if completed)\n    by_segment (Optional[PersonalitySegmentMetrics]): Segment analysis (only if completed)\n    project_personality (Optional[ProjectPersonalityBlock]): Additive block populated when this face is linked to a project Person with an aggregated personality. Existing clients ignore it; new clients offer a Project/Video toggle that switches headline OCEAN between this block and ``overall``. See `module/project_persons/README.md` \"Read-time substitution\"."},"PersonalityAnalysisStatus":{"properties":{"status":{"type":"string","pattern":"^(pending|processing|completed|failed)$","title":"Status"},"percentage":{"anyOf":[{"type":"integer","maximum":100.0,"minimum":0.0},{"type":"null"}],"title":"Percentage"}},"type":"object","required":["status"],"title":"PersonalityAnalysisStatus","description":"Status detail for personality analysis processing.\n\nAttributes:\n    status (str): One of: pending, processing, completed, failed\n    percentage (Optional[int]): Progress percentage (0-100), only present during processing"},"PersonalityOceanMetrics":{"properties":{"agreeableness":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Agreeableness"},"conscientiousness":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Conscientiousness"},"extraversion":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Extraversion"},"neuroticism":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Neuroticism"},"openness":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Openness"}},"type":"object","required":["agreeableness","conscientiousness","extraversion","neuroticism","openness"],"title":"PersonalityOceanMetrics","description":"OCEAN personality trait metrics (Big Five personality traits).\n\nAttributes:\n    agreeableness (float): Agreeableness level (0-1 range)\n    conscientiousness (float): Conscientiousness level (0-1 range)\n    extraversion (float): Extraversion level (0-1 range)\n    neuroticism (float): Neuroticism level (0-1 range)\n    openness (float): Openness to experience (0-1 range)"},"PersonalityOverallMetrics":{"properties":{"ocean_metrics":{"$ref":"#/components/schemas/PersonalityOceanMetrics"}},"type":"object","required":["ocean_metrics"],"title":"PersonalityOverallMetrics","description":"Overall personality analysis metrics.\n\nAttributes:\n    ocean_metrics (PersonalityOceanMetrics): OCEAN personality trait scores"},"PersonalitySegmentItem":{"properties":{"segment_id":{"type":"integer","minimum":1.0,"title":"Segment Id"},"start_millis":{"type":"integer","minimum":0.0,"title":"Start Millis"},"end_millis":{"type":"integer","exclusiveMinimum":0.0,"title":"End Millis"},"ocean_metrics":{"$ref":"#/components/schemas/PersonalityOceanMetrics"}},"type":"object","required":["segment_id","start_millis","end_millis","ocean_metrics"],"title":"PersonalitySegmentItem","description":"Individual segment data with OCEAN metrics.\n\nAttributes:\n    segment_id (int): Unique identifier for this segment\n    start_millis (int): Segment start time in milliseconds\n    end_millis (int): Segment end time in milliseconds\n    ocean_metrics (PersonalityOceanMetrics): OCEAN personality trait scores for this segment"},"PersonalitySegmentMetrics":{"properties":{"segment_interval_millis":{"type":"integer","exclusiveMinimum":0.0,"title":"Segment Interval Millis"},"segments":{"items":{"$ref":"#/components/schemas/PersonalitySegmentItem"},"type":"array","title":"Segments"}},"type":"object","required":["segment_interval_millis","segments"],"title":"PersonalitySegmentMetrics","description":"Segment-level personality analysis with segment interval and list of segments.\n\nAttributes:\n    segment_interval_millis (int): Interval duration in milliseconds\n    segments (List[PersonalitySegmentItem]): List of segment data with OCEAN metrics"},"PersonalityTriggerResponse":{"properties":{"video_id":{"type":"string","title":"Video Id"},"face_id":{"type":"integer","title":"Face Id"},"speaker_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Speaker Id"},"analysis_type":{"type":"string","title":"Analysis Type"},"status":{"type":"string","title":"Status"},"message":{"type":"string","title":"Message"}},"type":"object","required":["video_id","face_id","analysis_type","status","message"],"title":"PersonalityTriggerResponse","description":"Response model returned after triggering personality processing\nfor a specific face (and optionally a speaker) within a video.\n\nAttributes:\n    video_id (str): Unique identifier of the video where personality processing was triggered.\n    face_id (int): Identifier of the face entity associated with the personality trigger.\n    speaker_id (Optional[int]): Identifier of the linked speaker entity, if applicable; otherwise None.\n    analysis_type (str): Type of personality analysis - 'overall', 'by_segment', or 'combined'.\n    status (str): Result status of the trigger operation\n    message (str): Status message describing the result of the trigger operation."},"PlaybackUrlResponse":{"properties":{"url":{"type":"string","title":"Url","description":"Signed URL that streams the media inline. Short-lived: request a new one when `expires_at` passes or a request with it is refused. Do not store or share it."},"expires_at":{"type":"string","title":"Expires At","description":"RFC 3339 UTC instant at which `url` stops working, e.g. `2026-09-28T12:30:00Z`."},"content_type":{"type":"string","title":"Content Type","description":"The stored media's content type, e.g. `video/mp4`."}},"type":"object","required":["url","expires_at","content_type"],"title":"PlaybackUrlResponse","description":"A short-lived, read-only URL for playing back stored media."},"ProcessingFpsOverBudgetError":{"properties":{"detail":{"type":"string","title":"Detail","description":"Human-readable error message. Branch on the HTTP status, not on this text.","examples":["Resource not found"]},"code":{"anyOf":[{"type":"string","const":"processing_fps_over_budget"},{"type":"null"}],"title":"Code","description":"`processing_fps_over_budget` when the video is too long for its frame rate; absent otherwise."},"recommended_processing_fps":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Recommended Processing Fps","description":"The highest frame rate tier that fits, to pre-select and confirm with the person. `null` when no tier fits, in which case the video has to be trimmed."},"fits_processing_fps":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Fits Processing Fps","description":"Every frame rate tier that fits, highest first. Empty when none fits."},"duration_s":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration S","description":"The video's length in whole seconds, rounded up. `null` when the length could not be read."},"max_duration_by_fps":{"anyOf":[{"items":{"$ref":"#/components/schemas/ProcessingFpsTierLimit"},"type":"array"},{"type":"null"}],"title":"Max Duration By Fps","description":"Every frame rate tier's longest video, highest first. A tier is in `fits_processing_fps` exactly when `duration_s <= max_duration_s`."},"native_fps_assumed":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Native Fps Assumed","description":"True when the video's own frame rate was unknown, so the limits assume 30 fps."}},"type":"object","required":["detail"],"title":"ProcessingFpsOverBudgetError","description":"The 422 body of an upload or reprocess whose video is too long for its frame rate (#1972).\n\nA superset of :class:`ErrorDetail`: the same status also answers request validation, whose body is the\nplain ``{\"detail\": str}``. The other fields are present only when ``code`` is\n``processing_fps_over_budget``."},"ProcessingFpsTierLimit":{"properties":{"processing_fps":{"type":"integer","title":"Processing Fps","description":"The frame rate tier.","examples":[10]},"max_duration_s":{"type":"integer","title":"Max Duration S","description":"The longest video, in whole seconds, that this tier can process.","examples":[5400]}},"type":"object","required":["processing_fps","max_duration_s"],"title":"ProcessingFpsTierLimit","description":"One frame-rate tier's longest video in a too-long 422 (#1972)."},"ProjectListResponse":{"properties":{"projects":{"items":{"$ref":"#/components/schemas/ProjectResponse"},"type":"array","title":"Projects"},"has_next":{"type":"boolean","title":"Has Next","default":false},"next_start_after":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Start After","description":"Pass as `start_after` to get the next page; set when `has_next` is true. It is the last project of this page unless the list had to skip a long run of projects that are not in this view, in which case the page may be short, or empty, and this names where to resume."}},"type":"object","required":["projects"],"title":"ProjectListResponse"},"ProjectPersonalityBlock":{"properties":{"project_id":{"type":"string","title":"Project Id"},"person_id":{"type":"string","title":"Person Id"},"person_name":{"type":"string","title":"Person Name"},"ocean":{"$ref":"#/components/schemas/OceanScores"},"modality_confidence":{"additionalProperties":{"$ref":"#/components/schemas/ModalityCoverage"},"type":"object","title":"Modality Confidence"},"aggregated_from_count":{"type":"integer","minimum":1.0,"title":"Aggregated From Count","description":"Number of eligible appearances that contributed to this aggregate."},"input_versions":{"additionalProperties":{"type":"string"},"type":"object","title":"Input Versions","description":"Per-video personality stamps (``{video_id: analysis_version}``) the aggregate was computed from. Use as a precise cache key for narrative synthesis and downstream cross-endpoint dedupe; ``computed_at`` alone is a coarser proxy that bumps on any recompute."},"computed_at":{"type":"string","format":"date-time","title":"Computed At"}},"type":"object","required":["project_id","person_id","person_name","ocean","modality_confidence","aggregated_from_count","computed_at"],"title":"ProjectPersonalityBlock","description":"Additive block on the per-video personality response when a face is linked.\n\nCarried alongside the existing per-video fields (``overall`` / ``by_segment``)\nso existing clients can ignore it and new clients can offer a Project/Video\ntoggle without a separate fetch. The shape is intentionally lightweight —\nweights / bounds / ineligible appearances list stay on the Person detail\nendpoint to keep this payload small."},"ProjectResponse":{"properties":{"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"project_type":{"type":"string","title":"Project Type"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"videos":{"items":{"$ref":"#/components/schemas/VideoLinkResponse"},"type":"array","title":"Videos"},"user_id":{"type":"string","title":"User Id"},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By","description":"The user who created the project (the same value as `user_id`). Never changes."},"owner":{"anyOf":[{"$ref":"#/components/schemas/MediaOwnerOut"},{"type":"null"}],"description":"Who owns the project: the user, or the team workspace that holds it. A project and the items in it have the same owner. `null` for a viewer who is not the project's owner (a share link, a sample)."},"can_edit":{"type":"boolean","title":"Can Edit","description":"Whether the caller may change this project: update it, link or unlink items, run analyses in it. The creator and, for a workspace-owned project, the workspace's owners and admins. Other members of the workspace can read it.","default":false},"can_delete":{"type":"boolean","title":"Can Delete","description":"Whether the caller may delete this project: the caller may change it and may delete every workspace-owned item in it. A response that does not read the project's items (creating or updating it) reports only the first condition, so a delete can still be refused with `403` there.","default":false},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"quality_tier":{"type":"string","title":"Quality Tier","default":"standard"},"video_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Video Count"},"ready_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Ready Count"},"failed_video_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Failed Video Count"},"latest_video_thumbnail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Latest Video Thumbnail"},"share_status":{"type":"boolean","title":"Share Status","default":false},"link_equal_person_names":{"type":"boolean","title":"Link Equal Person Names","description":"When enabled, adding a video or a standalone audio links a person whose name matches exactly one person already in the project. Matching is Unicode NFC, surrounding spaces stripped, then casefold. The link is made only when that person's face and speaker are not already linked to someone else, and only when every face and speaker of the media person is part of the link. People with a different name are never linked automatically. The project person's spelling is what appears on the linked face and speaker.","default":false},"prospect":{"anyOf":[{"$ref":"#/components/schemas/ProspectInfo"},{"type":"null"}]},"stage":{"anyOf":[{"type":"string","enum":["discovery","demo","evaluation","negotiation","closed_won","closed_lost"]},{"type":"null"}],"title":"Stage"},"crm":{"anyOf":[{"$ref":"#/components/schemas/DealCrmInfo"},{"type":"null"}]},"template_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Template Id"},"company_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company Name"},"ticker":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ticker"},"exchange":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exchange"},"fiscal_year_end_month":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Fiscal Year End Month"}},"type":"object","required":["id","title","project_type","user_id","created_at","updated_at"],"title":"ProjectResponse"},"ProspectInfo":{"properties":{"company":{"type":"string","maxLength":200,"minLength":1,"title":"Company"},"domains":{"items":{"type":"string"},"type":"array","maxItems":20,"title":"Domains","description":"Prospect email domains (bare hostnames, e.g. acme.com) — drive buyer/seller classification of call participants."}},"type":"object","required":["company"],"title":"ProspectInfo","description":"The single customer/prospect account a sales-conversation deal tracks."},"ProviderTagImportRequest":{"properties":{"source":{"type":"string","maxLength":120,"minLength":1,"title":"Source","default":"importer.voxpopme"},"tags":{"items":{"$ref":"#/components/schemas/TagWriteRequest"},"type":"array","minItems":1,"title":"Tags"}},"additionalProperties":false,"type":"object","required":["tags"],"title":"ProviderTagImportRequest","description":"POST /v2/videos/{id}/provider-tags body.\n\nImports importer-sourced external data (provider `vpm.*`, cohort\n`demo.*` / `survey.*`) that the user-facing `POST /v2/videos/{id}/tags`\nendpoint deliberately forbids. `source` must be an `importer.*` value;\nevery `tags[].key` must resolve in the registry AND carry an import-safe\ncategory (see `registry.IMPORTABLE_CATEGORIES`) — `system.*` and\nuser-editable keys (`label.*` / `user.*`) are rejected."},"ProviderTranscriptPutResponse":{"properties":{"media_id":{"type":"string","title":"Media Id"},"source":{"type":"string","enum":["tldv","zoom","meet","other"],"title":"Source"},"segment_count":{"type":"integer","title":"Segment Count"},"speakers":{"additionalProperties":{"type":"string"},"type":"object","title":"Speakers"},"attached_at":{"type":"string","title":"Attached At","description":"When the transcript was first attached in its current form (ISO 8601, UTC)."},"replaced":{"type":"boolean","title":"Replaced","description":"True when this request replaced a different provider transcript that was already attached; false for a first attach and for a repeat of identical content."}},"type":"object","required":["media_id","source","segment_count","speakers","attached_at","replaced"],"title":"ProviderTranscriptPutResponse","description":"The attached transcript's summary."},"ProviderTranscriptRequest":{"properties":{"source":{"type":"string","enum":["tldv","zoom","meet","other"],"title":"Source","description":"Where the transcript came from. Use `other` for any other source."},"segments":{"items":{"$ref":"#/components/schemas/ProviderTranscriptSegment"},"type":"array","minItems":1,"title":"Segments","description":"The whole transcript. Stored and returned in start_ms order; turns with the same start keep the order sent."},"speakers":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Speakers","description":"Optional display names, keyed by a speaker label used in segments. Each name is 1-60 characters after surrounding spaces are removed."}},"additionalProperties":false,"type":"object","required":["source","segments"],"title":"ProviderTranscriptRequest","description":"Body of ``PUT /v2/{videos|audios}/{id}/transcript``."},"ProviderTranscriptSegment":{"properties":{"speaker":{"type":"string","title":"Speaker","description":"The speaker's label as the source named it, 1-120 characters after surrounding spaces are removed."},"start_ms":{"type":"integer","maximum":86400000.0,"minimum":0.0,"title":"Start Ms","description":"When the turn starts, in milliseconds on the media's own timeline. No offset is applied: shift the source's clock before sending."},"end_ms":{"type":"integer","maximum":86400000.0,"minimum":0.0,"title":"End Ms","description":"When the turn ends, in milliseconds. Not before start_ms."},"text":{"type":"string","title":"Text","description":"What was said, 1-20000 characters after surrounding spaces are removed."}},"additionalProperties":false,"type":"object","required":["speaker","start_ms","end_ms","text"],"title":"ProviderTranscriptSegment","description":"One turn of a transcript."},"RealtimeBindingIn":{"properties":{"realtime_session_id":{"type":"string","maxLength":128,"minLength":1,"title":"Realtime Session Id"}},"additionalProperties":false,"type":"object","required":["realtime_session_id"],"title":"RealtimeBindingIn","description":"Evidence from a live realtime session the caller created."},"RealtimeWindow":{"properties":{"session_id":{"type":"string","title":"Session Id"},"origin_epoch_ms":{"type":"integer","minimum":0.0,"title":"Origin Epoch Ms","description":"The Unix time, in milliseconds, of the session clock's zero."},"now_ms":{"type":"integer","minimum":0.0,"title":"Now Ms","description":"The session clock when this was answered. It stops moving once the session ends."},"start_ms":{"type":"integer","minimum":0.0,"title":"Start Ms","description":"The start of the span asked for, echoed."},"end_ms":{"type":"integer","minimum":0.0,"title":"End Ms","description":"The end of the span asked for, echoed."},"modalities":{"items":{"type":"string","enum":["face","voice","text"]},"type":"array","title":"Modalities","description":"The streams asked for, echoed without repeats."},"modalities_used":{"items":{"type":"string","enum":["face","voice","text"]},"type":"array","title":"Modalities Used","description":"The streams that had at least one reading in the window."},"per_modality":{"$ref":"#/components/schemas/RealtimeWindowStreams","description":"Statistics for each stream asked for."},"words":{"items":{"$ref":"#/components/schemas/RealtimeWindowWord"},"type":"array","title":"Words","description":"The final transcript overlapping the window, by start time."},"words_granularity":{"type":"string","enum":["word","segment"],"title":"Words Granularity","description":"Whether each entry of `words` is one word or a whole segment."},"words_processed_through_ms":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Words Processed Through Ms","description":"The session time up to which the transcript is final."},"words_flags":{"items":{"type":"string","enum":["pending","retention_truncated","unavailable"]},"type":"array","title":"Words Flags","description":"Why the transcript may be incomplete. `pending` means ask again."}},"type":"object","required":["session_id","origin_epoch_ms","now_ms","start_ms","end_ms","words_granularity"],"title":"RealtimeWindow","description":"What a live session produced over one span: the transcript and emotion statistics."},"RealtimeWindowAxis":{"properties":{"mean":{"type":"number","title":"Mean","description":"The average over the window."},"peak":{"type":"number","title":"Peak","description":"The reading furthest from zero, signed."},"peak_t_ms":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Peak T Ms","description":"When the peak happened, inside the window. Null if not tracked."}},"type":"object","required":["mean","peak"],"title":"RealtimeWindowAxis","description":"Valence or arousal over the window, from -1 to 1."},"RealtimeWindowEmotion":{"properties":{"label":{"type":"string","title":"Label","description":"The emotion label."},"share":{"type":"number","title":"Share","description":"The fraction of usable data this label led, from 0 to 1."}},"type":"object","required":["label","share"],"title":"RealtimeWindowEmotion","description":"How much of the window one emotion label led."},"RealtimeWindowShift":{"properties":{"t_ms":{"type":"integer","minimum":0.0,"title":"T Ms","description":"When it happened, inside the window."},"kind":{"type":"string","enum":["onset","peak","offset"],"title":"Kind","description":"Whether the departure begins, is largest, or ends."},"magnitude":{"type":"number","title":"Magnitude","description":"How far from the baseline, in baseline standard deviations."},"metric":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Metric","description":"What departed: null for the overall state, else valence, arousal or an emotion label."}},"type":"object","required":["t_ms","kind","magnitude"],"title":"RealtimeWindowShift","description":"A departure from the person's own baseline inside the window."},"RealtimeWindowSignal":{"properties":{"coverage":{"type":"number","title":"Coverage","description":"The fraction of the window covered by usable data, from 0 to 1."},"samples":{"type":"integer","minimum":0.0,"title":"Samples","description":"How many readings fell in the window."},"processed_through_ms":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Processed Through Ms","description":"The session time up to which this stream is final. Null when nothing is processed yet."},"flags":{"items":{"type":"string","enum":["low_coverage","baseline_unavailable","pending","retention_truncated","unavailable"]},"type":"array","title":"Flags","description":"Why the numbers may be thin or incomplete. `pending` means ask again."}},"type":"object","required":["coverage","samples"],"title":"RealtimeWindowSignal","description":"How much usable data one stream had in the window."},"RealtimeWindowStream":{"properties":{"valence":{"anyOf":[{"$ref":"#/components/schemas/RealtimeWindowAxis"},{"type":"null"}],"description":"Null when the stream has no valence."},"arousal":{"anyOf":[{"$ref":"#/components/schemas/RealtimeWindowAxis"},{"type":"null"}],"description":"Null when the stream has no arousal."},"circumplex_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Circumplex Version","description":"The version of the scale valence and arousal are on. Windows compare only on the same version."},"top_emotions":{"items":{"$ref":"#/components/schemas/RealtimeWindowEmotion"},"type":"array","title":"Top Emotions","description":"Up to three labels, the most frequent first."},"shifts":{"items":{"$ref":"#/components/schemas/RealtimeWindowShift"},"type":"array","title":"Shifts","description":"Departures from baseline, by time."},"signal_quality":{"$ref":"#/components/schemas/RealtimeWindowSignal"}},"type":"object","required":["signal_quality"],"title":"RealtimeWindowStream","description":"One emotion stream's statistics over the window."},"RealtimeWindowStreams":{"properties":{"face":{"anyOf":[{"$ref":"#/components/schemas/RealtimeWindowStream"},{"type":"null"}]},"voice":{"anyOf":[{"$ref":"#/components/schemas/RealtimeWindowStream"},{"type":"null"}]},"text":{"anyOf":[{"$ref":"#/components/schemas/RealtimeWindowStream"},{"type":"null"}]}},"type":"object","title":"RealtimeWindowStreams","description":"The streams asked for, one entry each, including those with no data."},"RealtimeWindowWord":{"properties":{"text":{"type":"string","title":"Text","description":"What was said."},"start_ms":{"type":"integer","minimum":0.0,"title":"Start Ms"},"end_ms":{"type":"integer","minimum":0.0,"title":"End Ms"},"speaker":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Speaker","description":"Who said it. Null unless speakers were told apart."}},"type":"object","required":["text","start_ms","end_ms"],"title":"RealtimeWindowWord","description":"One piece of the transcript in the window."},"RenameMediaPersonRequest":{"properties":{"name":{"type":"string","maxLength":120,"minLength":1,"title":"Name","description":"Display name."}},"type":"object","required":["name"],"title":"RenameMediaPersonRequest","description":"A new display name."},"ReplaceTranscriptRequest":{"properties":{"transcript":{"type":"string","minLength":1,"title":"Transcript"},"format":{"type":"string","const":"text","title":"Format","default":"text"}},"type":"object","required":["transcript"],"title":"ReplaceTranscriptRequest"},"ReplaceTranscriptResponse":{"properties":{"id":{"type":"string","title":"Id"},"status":{"type":"string","title":"Status"},"segments_count":{"type":"integer","title":"Segments Count"},"speakers":{"items":{"$ref":"#/components/schemas/SpeakerUpdate"},"type":"array","title":"Speakers"}},"type":"object","required":["id","status","segments_count","speakers"],"title":"ReplaceTranscriptResponse"},"ReprocessVideoResponse":{"properties":{"id":{"type":"string","title":"Id"},"message":{"type":"string","title":"Message"},"status":{"type":"string","title":"Status"}},"type":"object","required":["id","message","status"],"title":"ReprocessVideoResponse","description":"Response returned after requesting reprocessing of a video.\n\nInherits from:\n    GenerateCSVResponse"},"ResolvedStimulus":{"properties":{"id":{"type":"string","title":"Id","description":"Stable stimulus id."},"label":{"type":"string","title":"Label","description":"What appears."},"kind":{"type":"string","title":"Kind","description":"The kind of stimulus."},"start_ms":{"type":"integer","title":"Start Ms","description":"When it appears in this video, in milliseconds."},"end_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"End Ms","description":"When it ends in this video, in milliseconds."},"category":{"anyOf":[{"type":"string","maxLength":32,"minLength":1,"pattern":"^[A-Za-z0-9_.-]+$"},{"type":"null"}],"title":"Category","description":"Optional grouping label of your own. 1-32 characters of A-Z, a-z, 0-9, '_', '.' or '-'; surrounding spaces are removed and case is kept."},"variant":{"anyOf":[{"type":"string","maxLength":64,"minLength":1},{"type":"null"}],"title":"Variant","description":"Optional label for comparing versions of the same stimulus. 1-64 characters; surrounding spaces are removed and case is kept."},"response_window_ms":{"anyOf":[{"items":{"type":"integer"},"type":"array","maxItems":2,"minItems":2},{"type":"null"}],"title":"Response Window Ms","description":"Optional [start, end]: two integers, in milliseconds after this stimulus starts, with 0 <= start < end <= 600000. A reaction that begins inside this window is attributed to this stimulus. When omitted, the default window applies."},"attribute_during_span":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Attribute During Span","description":"Optional. true also attributes to this stimulus a reaction that begins while it is on screen, between start_ms and end_ms. true is accepted only when end_ms is after start_ms. Omitted and false are the same value, returned as null."},"clipped":{"type":"boolean","title":"Clipped","description":"True when the end was cut to the video's length.","default":false}},"type":"object","required":["id","label","kind","start_ms"],"title":"ResolvedStimulus","description":"A stimulus in video time."},"SampleProjectListResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/SampleProjectResponse"},"type":"array","title":"Data"}},"type":"object","title":"SampleProjectListResponse","description":"Envelope for the sample-projects endpoint.\n\nShaped `{\"data\": [...]}` to match `SampleMediaResponse`, the equivalent\nenvelope the sample-media endpoint already returns, so the FE's sample\nfetchers stay symmetrical."},"SampleProjectResponse":{"properties":{"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"project_type":{"type":"string","title":"Project Type"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"template_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Template Id"},"video_count":{"type":"integer","title":"Video Count","default":0},"ready_count":{"type":"integer","title":"Ready Count","default":0},"failed_video_count":{"type":"integer","title":"Failed Video Count","default":0},"latest_video_thumbnail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Latest Video Thumbnail"}},"type":"object","required":["id","title","project_type"],"title":"SampleProjectResponse","description":"A public sample project, reduced to what a library tile needs.\n\nDeliberately NOT `ProjectResponse`. The samples endpoint is served\nunauthenticated, and `ProjectResponse.user_id` would hand the authoring\naccount's ID to anonymous callers. Every field here is display-only —\ndo not add anything that identifies the owner or carries deal/CRM data.\n\n`created_at` is optional because sample docs are curated by hand and a\nmissing timestamp should degrade the tile, not 500 the endpoint."},"SectionIn":{"properties":{"type":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,39}$","title":"Type","description":"Section type chosen by the application, e.g. `rubric` or `document_chunk`."},"trust":{"type":"string","enum":["authoritative","untrusted_data","private_data"],"title":"Trust","description":"How the model may use the section. `authoritative` is application-authored context to follow; `untrusted_data` and `private_data` are data, never instructions, and `private_data` is never repeated in output."},"preamble":{"type":"string","maxLength":2000,"title":"Preamble","description":"Application note on how to use the section.","default":""},"content":{"type":"string","maxLength":400000,"title":"Content","description":"The section body.","default":""}},"additionalProperties":false,"type":"object","required":["type","trust"],"title":"SectionIn","description":"One typed context section."},"SegmentResponse":{"properties":{"start_millis":{"type":"number","title":"Start Millis"},"end_millis":{"type":"number","title":"End Millis"}},"type":"object","required":["start_millis","end_millis"],"title":"SegmentResponse","description":"Model for a video segment time range.\n\nAttributes:\n    start_millis (float): Start time in milliseconds.\n    end_millis (float): End time in milliseconds."},"SessionBalances":{"properties":{"owner":{"type":"string","enum":["user","workspace"],"title":"Owner","description":"`workspace` when the session was charged to a shared workspace pool, otherwise `user`."},"total":{"type":"number","title":"Total","description":"The whole balance. Equals `balance_remaining`. Can be negative."},"plan":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Plan","description":"The part of `total` that is plan allowance. Null for a workspace pool, which has no plan allowance."},"metered":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Metered","description":"The part of `total` that is metered credit. Can be negative. Null for a workspace pool."}},"type":"object","required":["owner","total"],"title":"SessionBalances","description":"The balance the session was charged against, and whose it is."},"SessionCredits":{"properties":{"total_credits":{"type":"number","title":"Total Credits","description":"Total credits charged for this session across all streams. Refunds are not deducted from this figure."},"by_type":{"$ref":"#/components/schemas/SessionCreditsByType","description":"Breakdown of total_credits per billed stream"},"balance_remaining":{"type":"number","title":"Balance Remaining","description":"Caller's credit balance after this session. Reflects the balance the session was metered against: the workspace pool for enterprise workspaces, otherwise the user's own balance."},"by_source":{"$ref":"#/components/schemas/SessionCreditsBySource","description":"Breakdown of total_credits by which credits paid for it"},"balances":{"$ref":"#/components/schemas/SessionBalances","description":"balance_remaining, with whose balance it is and how it divides"},"settled":{"type":"boolean","title":"Settled","description":"True once the session has ended and every charge for it is included. While false the figures may still rise; fetch `GET /v2/realtime/session/{session_id}/credits` again for the final ones.","default":false},"pending":{"items":{"type":"string","enum":["live_analysis","chat","summarization","unknown"]},"type":"array","title":"Pending","description":"What may still add charges: `live_analysis`, `chat`, `summarization`, or `unknown` for work that could not be identified. Empty does not by itself mean final: check `settled`, which is also false until the session ends."}},"type":"object","required":["total_credits","by_type","balance_remaining","by_source","balances"],"title":"SessionCredits","description":"What this realtime session cost, and what is left.\n\nCovers every part of the session that is charged for, and matches the amount\nyour balance was reduced by."},"SessionCreditsBySource":{"properties":{"plan":{"type":"number","title":"Plan","description":"Credits drawn from your plan's included allowance.","default":0.0},"metered":{"type":"number","title":"Metered","description":"Credits drawn from your metered balance: purchased, earned and free credits.","default":0.0},"workspace":{"type":"number","title":"Workspace","description":"Credits drawn from your workspace's shared credit pool.","default":0.0},"unknown":{"type":"number","title":"Unknown","description":"Credits whose source was not recorded. Present only for charges made before the source was recorded.","default":0.0}},"type":"object","title":"SessionCreditsBySource","description":"Which credits paid for the session. Always sums to `total_credits`."},"SessionCreditsByType":{"properties":{"session":{"type":"number","title":"Session","description":"Credits for the live video/audio stream, metered per second","default":0.0},"chat":{"type":"number","title":"Chat","description":"Credits for questions asked during the session. 0 if you asked none.","default":0.0},"summarization":{"type":"number","title":"Summarization","description":"Credits for preparing supporting material supplied with the session. 0 if none was supplied.","default":0.0}},"type":"object","title":"SessionCreditsByType","description":"Per-stream split of a session's credit spend."},"SessionCreditsResponse":{"properties":{"session_id":{"type":"string","title":"Session Id","description":"Session identifier"},"credits":{"$ref":"#/components/schemas/SessionCredits","description":"What this session cost, and the balance left"}},"type":"object","required":["session_id","credits"],"title":"SessionCreditsResponse","description":"A realtime session's credit summary."},"SessionQueuedResponse":{"properties":{"session_id":{"type":"string","title":"Session Id","description":"Identifier for the queued request. Claim it to get in, or release it to leave the queue; it becomes the session's id."},"status":{"type":"string","const":"queued","title":"Status","description":"Always 'queued' — the discriminator against CreateSessionResponse.","default":"queued"},"message":{"type":"string","title":"Message","description":"Human-readable status message"},"position":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Position","description":"1-based place in line, or null when the realtime service does not report one (contract < 0.7.0). Per realtime instance, not global — meaningful only while a single instance serves sessions."},"poll_after_ms":{"type":"integer","title":"Poll After Ms","description":"How long to wait before claiming again. Claiming faster does not get you in sooner; claiming slower risks losing your place."}},"type":"object","required":["session_id","message","poll_after_ms"],"title":"SessionQueuedResponse","description":"Answer to a create/claim that found every GPU slot taken (202).\n\nDistinct from ``CreateSessionResponse`` on purpose: there is no session\nyet, so none of its connection fields (``webrtc_url``, ``ice_servers``)\ncould be filled. ``status`` is a fixed literal so a client can branch on\nthe field alone rather than on the HTTP code.\n\nThe client's job on receiving this is to keep claiming the same\n``session_id`` every ``poll_after_ms`` until it gets a\n``CreateSessionResponse`` back. That claim is not only how it learns it\ngot in — it is also the liveness signal that holds its place in line, so\na client that stops claiming is dropped from the queue within seconds\n(imentiv-realtime ``pending_heartbeat_ttl_seconds``).\n\n``position`` is None until the shared contract carries it: the realtime\nservice computes it already (``_waiters_ahead_of``) but\n``SessionAdmissionResponse`` at 0.6.0 has no field to return it in.\nDeclared now so adding it later is not a breaking response change."},"SetTag":{"properties":{"id":{"type":"string","title":"Id"},"key":{"type":"string","title":"Key"},"source":{"type":"string","title":"Source"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By"},"value_type":{"type":"string","const":"set","title":"Value Type"},"value_text":{"type":"string","title":"Value Text"}},"additionalProperties":false,"type":"object","required":["id","key","source","created_at","value_type","value_text"],"title":"SetTag"},"SignedDownloadUrlResponse":{"properties":{"video_id":{"type":"string","title":"Video Id"},"download_url":{"type":"string","title":"Download Url"},"filename":{"type":"string","title":"Filename"},"expires_in_minutes":{"type":"integer","title":"Expires In Minutes"},"expires_at":{"type":"string","title":"Expires At"},"note":{"type":"string","title":"Note","default":"Download URLs are short-lived. Request a new URL if a download fails."},"disposition":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Disposition"}},"type":"object","required":["video_id","download_url","filename","expires_in_minutes","expires_at"],"title":"SignedDownloadUrlResponse","description":"Response containing an authorized URL for downloading an annotated video.\n\nAttributes:\n    video_id (str): ID of the video.\n    download_url (str): Authorized URL for downloading the video file. Treat it as\n        short-lived and fetch it promptly; request a new one rather than storing it.\n    filename (str): Suggested filename for the download.\n    expires_in_minutes (int): How long to treat `download_url` as usable before\n        requesting a new one.\n    expires_at (str): ISO 8601 timestamp corresponding to `expires_in_minutes`.\n    note (str): Additional information about the download.\n    disposition (Optional[str]): ``attachment`` when `download_url` itself makes a browser save the file\n        (the header is signed into the URL), so a client can hand it to the browser without reading\n        the bytes. Absent from older servers."},"SituationIn":{"properties":{"type":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,39}$","title":"Type","description":"Section type chosen by the application."},"text":{"type":"string","maxLength":1900,"minLength":1,"title":"Text"}},"additionalProperties":false,"type":"object","required":["type","text"],"title":"SituationIn","description":"Context about the situation, e.g. what was happening at the time. Always treated as data."},"SpeakerEmotions":{"properties":{"audio":{"additionalProperties":{"type":"number"},"type":"object","title":"Audio","examples":[{"angry":0.045,"happy":0.19,"neutral":0.625}]},"text":{"additionalProperties":{"type":"number"},"type":"object","title":"Text","examples":[{"admiration":0.02,"joy":0.025,"neutral":0.72}]}},"type":"object","title":"SpeakerEmotions","description":"Speaker-specific emotions across audio and text modalities."},"SpeakerFaceMatchRequest":{"properties":{"speaker_id":{"type":"integer","title":"Speaker Id"},"face_id":{"type":"integer","title":"Face Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}},"type":"object","required":["speaker_id","face_id","name"],"title":"SpeakerFaceMatchRequest","description":"Request model for mapping a detected face to a speaker with an optional name.\n\nAttributes:\n    speaker_id (int): Unique identifier for the speaker from audio analysis.\n    face_id (int): Unique identifier for the detected face in the video.\n    name (Optional[str]): Optional name for the person. If empty/null/whitespace,\n                          adopts the speaker's name only when the face still has a\n                          placeholder label; a real face name is kept. If provided,\n                          updates both face and speaker names."},"SpeakerFaceMatchResponse":{"properties":{"status":{"type":"string","title":"Status"},"message":{"type":"string","title":"Message"}},"type":"object","required":["status","message"],"title":"SpeakerFaceMatchResponse","description":"Response model for the result of speaker-face matching operations.\n\nAttributes:\n    status (str): Status of the operation (e.g., \"completed\").\n    message (str): Informational message about the operation result."},"SpeakerFaceMatchSuggestionItem":{"properties":{"speaker_id":{"type":"integer","title":"Speaker Id"},"face_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Face Id"},"accuracy":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Accuracy"},"reasoning":{"type":"string","maxLength":280,"title":"Reasoning"}},"type":"object","required":["speaker_id","reasoning"],"title":"SpeakerFaceMatchSuggestionItem","description":"One suggested face for an in-scope speaker. Unresolved speakers have\n``face_id`` / ``accuracy`` null and a ``reasoning`` explaining why."},"SpeakerFaceMatchSuggestionsResponse":{"properties":{"reason":{"anyOf":[{"type":"string","const":"no_speakers"},{"type":"null"}],"title":"Reason","description":"Completed audio has no eligible speakers; items is empty."},"generated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Generated At"},"target_speaker_ids":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Target Speaker Ids"},"items":{"items":{"$ref":"#/components/schemas/SpeakerFaceMatchSuggestionItem"},"type":"array","title":"Items"}},"type":"object","required":["items"],"title":"SpeakerFaceMatchSuggestionsResponse","description":"Stored suggestions block for ``GET .../speaker-face-match/suggestions``."},"SpeakerNameInferenceRequest":{"properties":{"target_speaker_ids":{"items":{"type":"integer"},"type":"array","title":"Target Speaker Ids"},"infer_roles":{"type":"boolean","title":"Infer Roles","description":"When true, the same model pass also proposes agent or customer per targeted speaker, grounded in a quote from that speaker's own words. Independent of the name verdict. Roles are never written; commit them via PUT /v1/audios/{audio_id}/speakers/roles. Default false; omitted and false are the same request.","default":false}},"type":"object","required":["target_speaker_ids"],"title":"SpeakerNameInferenceRequest","description":"Manual, speaker-scoped re-run of transcript-based speaker-name inference.\n\n``target_speaker_ids`` is required and non-empty. ``infer_roles`` defaults\nto false; omitted and false are the same request."},"SpeakerNameInferenceResponse":{"properties":{"video_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Video Id"},"audio_id":{"type":"string","title":"Audio Id"},"results":{"items":{"$ref":"#/components/schemas/SpeakerNameInferenceResult"},"type":"array","title":"Results"}},"type":"object","required":["audio_id","results"],"title":"SpeakerNameInferenceResponse","description":"Response for a manual speaker-name-inference trigger.\n\n``results`` has at least one row per requested target. Duplicate model\nrows for the same id, if any, are not collapsed.\n\n``video_id`` is the parent video when this audio has one; ``None`` for\naudio-only media (standalone uploads store ``video_id: \"\"``, which is\nnormalized to ``None`` rather than published as an empty string)."},"SpeakerNameInferenceResult":{"properties":{"speaker_id":{"type":"integer","title":"Speaker Id"},"accepted":{"type":"boolean","title":"Accepted"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"quote":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Quote"},"pattern":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pattern"},"role":{"anyOf":[{"type":"string","enum":["agent","customer"]},{"type":"null"}],"title":"Role","description":"Proposed role. Present only when a role was accepted."},"role_accepted":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Role Accepted","description":"True when the cited quote was found verbatim in a line this speaker spoke. The role itself is the model's judgement and is not verified."},"role_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Role Reason","description":"Why the role was rejected. Omitted on accept. One of, checked in this order: role_already_set, insufficient_evidence, invalid_role, quote_too_short, quote_mismatch, wrong_speaker."},"role_quote":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Role Quote","description":"A quote from that speaker's own words that the model cited for the role. Present only when a role was accepted."}},"type":"object","required":["speaker_id","accepted"],"title":"SpeakerNameInferenceResult","description":"Name verdict for one speaker, and when roles were requested an\nindependent role verdict on the same speaker.\n\nRoles are not mutually exclusive across speakers (a transfer can yield\ntwo agents). A role is never written by this endpoint; commit via\n``PUT /v1/audios/{audio_id}/speakers/roles``."},"SpeakerNameInferenceStoredSuggestionsResponse":{"properties":{"generated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Generated At"},"audio_id":{"type":"string","title":"Audio Id"},"video_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Video Id"},"results":{"items":{"$ref":"#/components/schemas/SpeakerNameInferenceResult"},"type":"array","title":"Results"}},"type":"object","required":["audio_id","results"],"title":"SpeakerNameInferenceStoredSuggestionsResponse","description":"Stored auto-run suggestions for ``GET .../speaker-name-inference/suggestions``.\n\nRow shape matches the suggestions POST (``results``). ``generated_at`` is\nthe stored timestamp, or ``null`` when missing — never fabricated as now.\n``video_id`` is ``null`` for audio-only media (empty-string storage\nnormalizes to ``null``; the field is not omitted)."},"SpeakerRoleUpdate":{"properties":{"speaker_id":{"type":"integer","title":"Speaker Id"},"role":{"anyOf":[{"type":"string","enum":["agent","customer","system","media"]},{"type":"null"}],"title":"Role"}},"type":"object","required":["speaker_id","role"],"title":"SpeakerRoleUpdate","description":"One speaker's declared role.\n\n``role`` is required (no default) so a malformed entry 422s instead of\nsilently clearing. Send ``null`` to clear. Do not put ``role`` on\n``SpeakerUpdate`` — that would force the names PUT to send it."},"SpeakerRoleUpdateResponse":{"properties":{"speakers":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Speakers"}},"type":"object","required":["speakers"],"title":"SpeakerRoleUpdateResponse","description":"Full speaker list after a role merge (id, name, role).\n\nThe names PUT response stays ``List[SpeakerUpdate]`` ({speaker_id, name}\nonly). If D1 refreshes local state from the rename response, role chips\nvanish until reload — re-GET or use this role PUT's response."},"SpeakerUpdate":{"properties":{"speaker_id":{"type":"integer","title":"Speaker Id"},"name":{"type":"string","title":"Name"}},"type":"object","required":["speaker_id","name"],"title":"SpeakerUpdate","description":"Model for updating a speaker's name.\n\nAttributes:\n    speaker_id (int): Identifier of the speaker.\n    name (str): New name for the speaker."},"SpeakerUpdateResponse":{"properties":{"speakers":{"items":{"$ref":"#/components/schemas/SpeakerUpdate"},"type":"array","title":"Speakers"}},"type":"object","required":["speakers"],"title":"SpeakerUpdateResponse","description":"Response model after updating speakers.\n\nAttributes:\n    speakers (List[SpeakerUpdate]): List of updated speakers."},"StimulusCoverage":{"properties":{"subject":{"type":"string","title":"Subject"},"stimulus_id":{"type":"string","title":"Stimulus Id"},"status":{"type":"string","title":"Status"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"observed_s":{"type":"number","title":"Observed S","description":"Seconds of the stimulus's response window in which the subject could be observed. For a subject listed in `visibility`, this can instead be the seconds of that window in which its face was visible.","default":0}},"type":"object","required":["subject","stimulus_id","status"],"title":"StimulusCoverage","description":"Coverage of one stimulus for one subject."},"StimulusIn":{"properties":{"onset_ms":{"type":"integer","minimum":0.0,"title":"Onset Ms","description":"When the stimulus began. Must be inside `window`."},"end_ms":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"End Ms","description":"When it ended, if known."},"label":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Label","description":"A short name for the stimulus."}},"additionalProperties":false,"type":"object","required":["onset_ms"],"title":"StimulusIn","description":"What the subject may have responded to, on the recording's clock."},"StimulusLinkView":{"properties":{"source":{"type":"string","enum":["track","inline"],"title":"Source","description":"A linked stimulus track, or stimuli set on the video."},"track_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Track Id","description":"The linked track (source 'track')."},"track_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Track Name","description":"The linked track's name."},"offset_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Offset Ms","description":"Where track time 0 falls in the video."},"status":{"type":"string","enum":["resolved","track_missing","unavailable"],"title":"Status","description":"'resolved': stimuli is current. 'track_missing': the linked track no longer exists, so there are no stimuli. 'unavailable': the stimuli could not be read right now; retry."},"excluded_count":{"type":"integer","title":"Excluded Count","description":"Stimuli left out because they start outside the video, for example after the track was edited or the video trimmed.","default":0}},"type":"object","required":["source","status"],"title":"StimulusLinkView","description":"Where the video's stimuli come from."},"StringTag":{"properties":{"id":{"type":"string","title":"Id"},"key":{"type":"string","title":"Key"},"source":{"type":"string","title":"Source"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By"},"value_type":{"type":"string","const":"string","title":"Value Type"},"value_text":{"type":"string","title":"Value Text"}},"additionalProperties":false,"type":"object","required":["id","key","source","created_at","value_type","value_text"],"title":"StringTag"},"SubjectVisibility":{"properties":{"subject":{"type":"string","title":"Subject","description":"The subject; join with `subjects[].id`."},"visible_spans":{"items":{"$ref":"#/components/schemas/VisibleSpan"},"type":"array","title":"Visible Spans","description":"When the face was visible, in order and never overlapping. Time outside every span is time the face was not visible. Empty when the face was never visible."},"visible_ms":{"type":"integer","title":"Visible Ms","description":"Total visible time, in milliseconds: the sum of `visible_spans`."},"uninterrupted":{"type":"boolean","title":"Uninterrupted","description":"True when each span is continuous visibility. False when brief absences inside a span were joined over, so a span alone does not prove the face was visible throughout it."}},"type":"object","required":["subject","visible_ms","uninterrupted"],"title":"SubjectVisibility","description":"When one subject's face was visible in the video."},"TagWriteRequest":{"properties":{"key":{"type":"string","maxLength":200,"minLength":1,"title":"Key"},"value":{"anyOf":[{},{"type":"null"}],"title":"Value"}},"additionalProperties":false,"type":"object","required":["key"],"title":"TagWriteRequest","description":"POST /v2/videos/{id}/tags body.\n\n`key` is user-editable: a registered typed entry under a\nUSER_EDITABLE_PREFIXES namespace (`user.*`, `fin.*` — see\nmodule.tags.registry) or any `label.*` suffix (lax — normalized\nlowercase + slugify server-side).\n`value` shape must match the registry's value_type: None for\nlabel/none, int for integer, str for string/enum/set, dict for\nlocation_admin, an ISO-8601 date/datetime string for date (JSON has\nno datetime type — a Python `datetime` is only ever accepted from\ninternal/importer callers that invoke the write path directly, not\nover this HTTP endpoint)."},"TextDetailResponseV2":{"properties":{"bulk_upload_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bulk Upload Id","description":"Bulk upload batch ID"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At","description":"Creation timestamp"},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At","description":"ISO-8601 UTC timestamp of the last change to this text. Compare it for inequality with the value you hold to tell whether your copy is stale; it carries no ordering guarantee. It covers this text's own fields only, not data derived from other records such as frames or analytics.","examples":["2025-12-01T14:30:00.000000+00:00"]},"csv_analysis_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Csv Analysis Url","description":"CSV analysis file URL"},"csv_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Csv Status","description":"CSV generation status"},"document_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Document Type","description":"Document type: standalone, transcript"},"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id","description":"Unique text identifier"},"is_user":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is User","description":"Whether user submitted text"},"media_source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Media Source","description":"Cloud platform source: dropbox, google_drive, aws_s3, public_url"},"media_source_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Media Source Url","description":"Original cloud storage URL"},"model_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model Id","description":"Model used for processing"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url","description":"GCS bucket URL of uploaded extracted text"},"parent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Id","description":"Parent audio/video ID if transcript"},"predefined_qa":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Predefined Qa","description":"Predefined Q&A about the text"},"psychologist_review":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Psychologist Review","description":"Psychologist review content"},"psychologist_review_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Psychologist Review Status","description":"Psychologist review status"},"sentences":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Sentences","description":"Sentences with emotions"},"share_status":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Share Status","description":"Whether text is shared"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","description":"Processing status"},"summary":{"anyOf":[{"type":"string"},{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Summary","description":"Text summary or summary object"},"tags":{"anyOf":[{"items":{"oneOf":[{"$ref":"#/components/schemas/StringTag"},{"$ref":"#/components/schemas/EnumTag"},{"$ref":"#/components/schemas/IntegerTag"},{"$ref":"#/components/schemas/BoolTag"},{"$ref":"#/components/schemas/DateTag"},{"$ref":"#/components/schemas/GeoTag"},{"$ref":"#/components/schemas/SetTag"},{"$ref":"#/components/schemas/NoneTag"}],"discriminator":{"propertyName":"value_type","mapping":{"boolean":"#/components/schemas/BoolTag","date":"#/components/schemas/DateTag","enum":"#/components/schemas/EnumTag","integer":"#/components/schemas/IntegerTag","location_admin":"#/components/schemas/GeoTag","none":"#/components/schemas/NoneTag","set":"#/components/schemas/SetTag","string":"#/components/schemas/StringTag"}}},"type":"array"},{"type":"null"}],"title":"Tags","description":"Generalized media tags (demographic, survey, label, system). Discriminated union over value_type — see /v2/tag-keys for dimension metadata."},"tags_map":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Tags Map","description":"Tags map with general and sample flags"},"text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text","description":"The actual text content (may contain HTML)"},"text_emotions":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Text Emotions","description":"Overall text emotions"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"Text title"},"ui_request":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Ui Request","description":"Whether request came from UI"},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id","description":"The user who created the item (the same value as `created_by`)."},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By","description":"The user who created the item. Never changes, including when the item is moved to a workspace."},"owner":{"anyOf":[{"$ref":"#/components/schemas/MediaOwnerOut"},{"type":"null"}],"description":"Who owns the item: the user, or the team workspace that holds it."},"word_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Word Count","description":"Word count"},"youtube_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube Url","description":"YouTube URL if applicable"}},"type":"object","title":"TextDetailResponseV2","description":"Complete V2 response model for text details with all possible fields.\nReturns null for fields that don't exist in the database.\nFields are ordered alphabetically."},"TextEmotionResponseV2":{"properties":{"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"status":{"type":"string","title":"Status"},"word_count":{"type":"integer","title":"Word Count"},"user_id":{"type":"string","title":"User Id"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"media_source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Media Source"},"media_source_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Media Source Url"}},"type":"object","required":["id","title","status","word_count","user_id"],"title":"TextEmotionResponseV2","description":"V2 response model for text emotion detection.\n\nSupports direct text submission and cloud URL file uploads.\nDirect text submission with mandatory title field.\n\nAttributes:\n    id (str): Unique ID of the text.\n    title (str): Title of the text.\n    status (str): Processing status (queue, processing, done, etc.).\n    word_count (int): Number of words in the text.\n    user_id (str): ID of the user who submitted the text.\n    created_at (Optional[str]): Timestamp when text was created.\n    media_source (Optional[str]): Cloud platform source (dropbox, google_drive, aws_s3, public_url).\n    media_source_url (Optional[str]): Original cloud storage URL provided by user."},"TextReprocessResponse":{"properties":{"id":{"type":"string","title":"Id"},"status":{"type":"string","title":"Status"},"message":{"type":"string","title":"Message"}},"type":"object","required":["id","status","message"],"title":"TextReprocessResponse","description":"Response model for reprocessing a text analysis request.\n\nAttributes:\n    message (str): Additional information about the reprocessing result."},"TranscriptEmotions":{"properties":{"emotions":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Emotions"},"start_time":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Start Time"},"end_time":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"End Time"},"speaker_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Speaker Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"max_emotion":{"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"}]},"type":"object","title":"Max Emotion"},"segment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Segment"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language","description":"Line language; omitted for the audio's main language."},"language_source":{"anyOf":[{"type":"string","enum":["user","detected"]},{"type":"null"}],"title":"Language Source","description":"Whether a user range or automatic detection chose the line language."},"range_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Range Id","description":"User range provenance; absent on detected lines."}},"type":"object","title":"TranscriptEmotions","description":"Represents emotional data for a transcript segment.\n\nAttributes:\n    emotions (Optional[Dict[str, float]]): Emotion label -> score, summing to 1.0.\n    start_time (Optional[int]): Start time of the segment in milliseconds.\n    end_time (Optional[int]): End time of the segment in milliseconds.\n    speaker_id (Optional[int]): ID of the speaker for this segment.\n    name (Optional[str]): Name of the speaker, if available.\n    max_emotion (Dict[str, Union[str, float]]): ``{\"label\": str, \"score\": float}``.\n    segment (Optional[str]): The actual text of the transcript segment."},"TranscriptResponse":{"properties":{"media_id":{"type":"string","title":"Media Id"},"transcript":{"type":"string","enum":["machine","provider"],"title":"Transcript","description":"Which transcript this is: `machine` or `provider`."},"source":{"anyOf":[{"type":"string","enum":["tldv","zoom","meet","other"]},{"type":"null"}],"title":"Source","description":"For a provider transcript, where it came from. Null for `machine`."},"segments":{"items":{"$ref":"#/components/schemas/TranscriptSegmentOut"},"type":"array","title":"Segments"},"speakers":{"additionalProperties":{"type":"string"},"type":"object","title":"Speakers","description":"Display names by speaker label. Empty for `machine`."},"attached_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Attached At","description":"Null for `machine`."}},"type":"object","required":["media_id","transcript","segments","speakers"],"title":"TranscriptResponse","description":"One of a media's two transcripts."},"TranscriptSegment":{"properties":{"start_millis":{"type":"number","minimum":0.0,"title":"Start Millis","description":"Start time in milliseconds (>= 0)"},"end_millis":{"type":"number","minimum":0.0,"title":"End Millis","description":"End time in milliseconds (>= 0)"},"segment":{"type":"string","title":"Segment"},"speaker_id":{"type":"integer","title":"Speaker Id"}},"type":"object","required":["start_millis","end_millis","segment","speaker_id"],"title":"TranscriptSegment","description":"Represents a single segment of transcribed audio with timing and speaker ID.\n\nAttributes:\n    start_millis (float): Start time of the segment in milliseconds (must be >= 0).\n    end_millis (float): End time of the segment in milliseconds (must be >= 0).\n    segment (str): Transcribed text for the segment.\n    speaker_id (int): Identifier of the speaker in this segment."},"TranscriptSegmentOut":{"properties":{"speaker":{"type":"string","title":"Speaker"},"start_ms":{"type":"integer","title":"Start Ms"},"end_ms":{"type":"integer","title":"End Ms"},"text":{"type":"string","title":"Text"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language","description":"The language this segment was transcribed in, when it differs from the audio's: it was detected automatically or transcribed again for a user language range. Absent or null means the audio's language."},"language_source":{"anyOf":[{"type":"string","enum":["user","detected"]},{"type":"null"}],"title":"Language Source","description":"Who chose `language`: `user` or `detected`. Null with `language`."},"range_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Range Id","description":"The user language range that wrote this segment; absent on automatically detected lines."}},"type":"object","required":["speaker","start_ms","end_ms","text"],"title":"TranscriptSegmentOut"},"TranslationIndexOut":{"properties":{"audio_id":{"type":"string","title":"Audio Id"},"source_revision":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Revision","description":"The transcript revision a new translation would accept (see `source_revision` on a request), or null when a new translation could not start: the audio has no transcript that can be translated, is sample media, is not yet `completed`, the transcript is all blank, over the size limits, or prices at a non-positive estimate."},"estimated_credits":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Estimated Credits","description":"The hold a new translation of this transcript would take, in credits. The charge is the actual usage and can differ. Null when `source_revision` is null."},"translations":{"items":{"$ref":"#/components/schemas/TranslationOut"},"type":"array","title":"Translations"}},"type":"object","required":["audio_id","translations"],"title":"TranslationIndexOut","description":"The translations of one audio."},"TranslationOut":{"properties":{"language":{"type":"string","title":"Language","description":"The target language, as a canonical BCP-47 tag."},"status":{"type":"string","enum":["queued","running","ready","failed"],"title":"Status","description":"`queued`, `running`, `ready` or `failed`."},"lines":{"anyOf":[{"items":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":"array"},{"type":"null"}],"title":"Lines","description":"One entry per transcript segment, in the transcript's order. Present only when `status` is `ready`. A segment with no text comes back as an empty string. A segment that was already in the target language was not translated and comes back as null: show its own text."},"source_revision":{"type":"string","title":"Source Revision","description":"The transcript revision this translation was requested for."},"source_line_hashes":{"anyOf":[{"items":{"anyOf":[{"type":"string"},{"type":"null"}]},"type":"array"},{"type":"null"}],"title":"Source Line Hashes","description":"For each segment, the SHA-256 (64 lowercase hexadecimal characters) of the segment's text (trimmed with ECMAScript `trim`, then NFC-normalised) that the translation was made from; null for a segment that was not translated. Compare with the current segment to tell which line is out of date. Null for a translation made before these were recorded: use `stale`."},"stale_lines":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Stale Lines","description":"The indexes of the segments whose text changed since this translation was made, when it can be said line by line: the number of segments and each segment's time and speaker are unchanged. Empty while `stale` is false. Null when `stale` is true and it cannot be said line by line: treat the whole translation as out of date."},"stale":{"type":"boolean","title":"Stale","description":"True once the transcript's text or timing has changed since this translation was made. Renaming, merging or deleting speakers does not change it."},"error_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Code","description":"Why a `failed` translation failed. Present only for the owner."},"attempt_charge_state":{"anyOf":[{"type":"string","enum":["reserved","captured","released"]},{"type":"null"}],"title":"Attempt Charge State","description":"`reserved` while an attempt can still be charged, then `captured` if it was charged or `released` if it was not. Present only for the owner."},"captured_credits":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Captured Credits","description":"Credits charged for the attempt. Present only for the owner."},"reserved_credits":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reserved Credits","description":"Credits held for the attempt that have not been settled yet; 0 once settled. Present only for the owner."},"estimated_credits":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Estimated Credits","description":"The estimate this attempt reserved. The charge is the actual usage and can differ. Present only for the owner."},"retry_kind":{"anyOf":[{"type":"string","enum":["resend","new_attempt"]},{"type":"null"}],"title":"Retry Kind","description":"For a `failed` translation, what `POST .../retry` must send: `resend` repeats the original request, `new_attempt` needs the current transcript revision and a new `idempotency_key`. Present only for the owner."}},"type":"object","required":["language","status","source_revision","stale"],"title":"TranslationOut","description":"One stored translation of an audio's transcript."},"TrimVideoResponse":{"properties":{"id":{"type":"string","title":"Id"},"status":{"type":"string","title":"Status"},"message":{"type":"string","title":"Message"}},"type":"object","required":["id","status","message"],"title":"TrimVideoResponse","description":"Result of a trim request — the video re-enters processing."},"UpdateEmotionResponse":{"properties":{"frame_id":{"type":"string","title":"Frame Id"},"face_id":{"type":"integer","title":"Face Id"},"emotions":{"additionalProperties":{"type":"number"},"type":"object","title":"Emotions"}},"type":"object","required":["frame_id","face_id","emotions"],"title":"UpdateEmotionResponse","description":"Model representing updated emotion values for a face.\n\nAttributes:\n    frame_id (str): Frame identifier.\n    face_id (int): ID of the face.\n    emotions (Dict[str, float]): Dictionary of emotion scores."},"UpdateProjectRequest":{"properties":{"title":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Title"},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Description"},"quality_tier":{"anyOf":[{"type":"string","enum":["standard","pro","ultra"]},{"type":"null"}],"title":"Quality Tier","description":"Project-level generation quality. Drives the Compare delta's model tier and the chatbot's default reasoning level."},"prospect":{"anyOf":[{"$ref":"#/components/schemas/ProspectInfo"},{"type":"null"}],"description":"sales_deal only: prospect company + email domains"},"stage":{"anyOf":[{"type":"string","enum":["discovery","demo","evaluation","negotiation","closed_won","closed_lost"]},{"type":"null"}],"title":"Stage","description":"sales_deal only: deal stage"},"crm":{"anyOf":[{"$ref":"#/components/schemas/DealCrmInfo"},{"type":"null"}],"description":"sales_deal only: CRM position. Merged over the stored map like `prospect` — omitted sub-fields keep their stored value."},"template_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Template Id","description":"product_testing only: template assignment."},"company_name":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Company Name","description":"financial_research only: the tracked company's name."},"ticker":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ticker","description":"financial_research only: stock ticker, canonicalized server-side."},"exchange":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Exchange","description":"financial_research only: display-only exchange hint."},"fiscal_year_end_month":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Fiscal Year End Month","description":"financial_research only: 1-12."},"link_equal_person_names":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Link Equal Person Names","description":"When enabled, adding a video or a standalone audio links a person whose name matches exactly one person already in the project. Matching is Unicode NFC, surrounding spaces stripped, then casefold. The link is made only when that person's face and speaker are not already linked to someone else, and only when every face and speaker of the media person is part of the link. People with a different name are never linked automatically. The project person's spelling is what appears on the linked face and speaker."}},"type":"object","title":"UpdateProjectRequest"},"UploadUrlResponse":{"properties":{"media_id":{"type":"string","title":"Media Id","description":"Unique identifier for the media file. Use this as the reference ID when submitting for processing."},"upload_url":{"type":"string","title":"Upload Url","description":"Signed URL for uploading the file via HTTP PUT"},"public_url":{"type":"string","title":"Public Url","description":"Reference to the uploaded file. Submit it as `video_url` / `media_url` once the upload completes. It is not guaranteed to be readable: media may be stored privately. Use the media's `playback-url` endpoint to play it back."},"required_headers":{"additionalProperties":{"type":"string"},"type":"object","title":"Required Headers","description":"Headers the client must include in the PUT request"},"expires_in":{"type":"integer","title":"Expires In","description":"URL expiration time in seconds"}},"type":"object","required":["media_id","upload_url","public_url","required_headers","expires_in"],"title":"UploadUrlResponse","description":"Response containing a presigned URL for direct-to-storage upload."},"ValenceArousalIntensity":{"properties":{"valence":{"type":"number","title":"Valence"},"arousal":{"type":"number","title":"Arousal"},"intensity":{"type":"number","title":"Intensity"},"circumplex_version":{"type":"string","title":"Circumplex Version","description":"Name of the valence/arousal coordinate table the numbers were computed with: each emotion label sits at a fixed (valence, arousal) point and a face's values are the score-weighted sum of those points. It reflects the table in use now, not the one in use when the video was analysed, and it changes only when the table does.","default":"v2","examples":["v2"]}},"type":"object","required":["valence","arousal","intensity"],"title":"ValenceArousalIntensity","description":"Model representing emotional intensity values.\n\nAttributes:\n    valence (float): Positive or negative emotion scale.\n    arousal (float): Energy or excitement level.\n    intensity (float): Overall emotion strength.\n    circumplex_version (str): Which valence/arousal coordinate table the numbers come from."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VersionedTextIn":{"properties":{"version":{"type":"string","maxLength":128,"minLength":1,"title":"Version"},"text":{"type":"string","minLength":1,"title":"Text"}},"additionalProperties":false,"type":"object","required":["version","text"],"title":"VersionedTextIn"},"VideoCompletedData":{"properties":{"id":{"type":"string","title":"Id","description":"The video id."},"status":{"type":"string","const":"completed","title":"Status"},"video_status":{"type":"string","title":"Video Status","description":"The video analysis outcome, lowercase."},"audio_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Audio Status","description":"The outcome of the video's audio track. Omitted when it has none."},"processed_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Processed Seconds","description":"How long the analysis took, in seconds."}},"type":"object","required":["id","status","video_status"],"title":"VideoCompletedData"},"VideoCompletedEvent":{"properties":{"event_id":{"type":"string","title":"Event Id","description":"Unique per event and identical on every delivery attempt. Use it to ignore duplicates."},"timestamp":{"type":"integer","title":"Timestamp","description":"When the event was created, in Unix seconds."},"payload_version":{"type":"string","const":"v1","title":"Payload Version","default":"v1"},"event_type":{"type":"string","const":"video.completed","title":"Event Type"},"resource_type":{"type":"string","const":"video","title":"Resource Type"},"data":{"$ref":"#/components/schemas/VideoCompletedData"}},"type":"object","required":["event_id","timestamp","event_type","resource_type","data"],"title":"VideoCompletedEvent"},"VideoDeleteResponse":{"properties":{"id":{"type":"string","title":"Id"},"status":{"type":"string","title":"Status"}},"type":"object","required":["id","status"],"title":"VideoDeleteResponse","description":"Response after deleting a video entry.\n\nInherits:\n    ResponseStatus: Generic response with status and message."},"VideoDetailResponseV2":{"properties":{"languages":{"items":{"type":"string"},"type":"array","title":"Languages","description":"Detected languages in stored dominant-first order; empty when unavailable. Short stretches may have line language marks without appearing in this list. This is an output summary, independent of the expected_languages input hint.","default":[],"readOnly":true,"examples":[["en","ja"]]},"annotated_video":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Annotated Video","description":"Annotated video URL"},"annotated_video_mp4":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Annotated Video Mp4","description":"Annotated video MP4 URL"},"annotated_video_percentage":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Annotated Video Percentage","description":"Annotated video progress"},"annotated_video_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Annotated Video Status","description":"Annotated video status"},"audio_csv_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Audio Csv Url","description":"Audio-specific CSV analysis URL"},"audio_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Audio Id","description":"Associated audio analysis ID"},"audio_transcription":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Audio Transcription","description":"Audio transcription text"},"auto_translation":{"anyOf":[{"$ref":"#/components/schemas/AutoTranslation"},{"type":"null"}],"description":"What happened when this finished run was asked to translate the lines that are not in the audio's main language into it, which is done on its own for an audio uploaded with `multilingual` on (billed as a translation, on top of transcription). `{language, status}` where `status` is `requested` (a translation was asked for: its own state, `queued` to `ready` or `failed`, is on `GET /v2/audios/{audio_id}/translations`) or `skipped` (none was asked for: the audio still completed, and the translation can be requested from the translations endpoint). `reason`, for the owner only, says why a skipped one was skipped: `insufficient_credits` or `unavailable`. Null when this run asked for nothing: the audio is not `multilingual`, has no line outside its main language, or has no recorded main language; or a translation of the current transcript already exists that someone else asked for, in which case it stands for this one and its state is on the translations endpoint. A reprocess overwrites it."},"bulk_upload_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bulk Upload Id","description":"Bulk upload batch ID"},"check_audio":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Check Audio","description":"Whether audio should be checked"},"completed_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Completed At","description":"Completion timestamp"},"compressed_frames_data_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Compressed Frames Data Url","description":"Deprecated: always null. The frames archive is an internal format; read frames through the frames routes."},"compressed_frames_last_updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Compressed Frames Last Updated At","description":"Compressed frames last update timestamp"},"compressed_frames_schema_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Compressed Frames Schema Version","description":"Schema version for compressed frames"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At","description":"Creation timestamp"},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At","description":"ISO-8601 UTC timestamp of the last change to this video. Compare it for inequality with the value you hold to tell whether your copy is stale; it carries no ordering guarantee. It covers this video's own fields only, not data derived from other records such as frames or analytics.","examples":["2025-12-01T14:30:00.000000+00:00"]},"csv_analysis_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Csv Analysis Url","description":"CSV analysis file URL"},"csv_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Csv Status","description":"CSV generation status"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Video description"},"dropbox_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dropbox Url","description":"Dropbox URL if applicable"},"duration":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Duration","description":"Video duration in seconds"},"emotions_list":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Emotions List","description":"List of emotion labels"},"end_millis":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"End Millis","description":"End time in milliseconds"},"expected_languages":{"items":{"type":"string"},"type":"array","title":"Expected Languages","description":"The languages you said the video's audio track may contain, as sent on upload or reprocess (Whisper codes, main language included). This is the INPUT you gave, not the languages detected in the speech. `[]` when none was given.","default":[],"examples":[["en","ja"]]},"face_deletion_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Face Deletion Status","description":"Face deletion status"},"face_duplicate_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Face Duplicate Count","description":"Count of duplicate faces detected"},"face_emotions":{"anyOf":[{"additionalProperties":{"additionalProperties":{"type":"number"},"type":"object"},"type":"object"},{"type":"null"}],"title":"Face Emotions","description":"Face emotions per face ID"},"faces":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Faces","description":"Detected faces. Each entry includes face_id, name, and image. Online-meeting analyses may also include OCR name-assignment provenance (confidence, votes, samples, alternatives, is_local, source, inferred_from_prefix). In-vehicle analyses may name the selected driver's face \"Driver\", with source 'driver_selection' and reason ('clear_lead' or 'sole_candidate'); that is a role label, not a person's name. Untyped passthrough — extra keys are not filtered."},"fps":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Fps","description":"Frames per second"},"frame_upload_progress":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Frame Upload Progress","description":"Frame upload progress percentage"},"frame_upload_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Frame Upload Status","description":"Frame upload status"},"frames_with_faces_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Frames With Faces Count","description":"Frames with detected faces"},"height":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Height","description":"Video height in pixels"},"highlight_segments":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Highlight Segments","description":"Highlight video segments"},"highlight_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Highlight Status","description":"Highlight video status"},"highlight_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Highlight Url","description":"Highlight video URL"},"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id","description":"Unique video identifier"},"is_user":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is User","description":"Whether uploaded by user"},"media_source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Media Source","description":"Source type: youtube, dropbox, public_url, file_upload"},"media_source_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Media Source Url","description":"The URL the media was submitted with. For an upload this is a reference to the stored media, not guaranteed to be readable; use the media's `playback-url` endpoint to play it back."},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Status message"},"model_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model Id","description":"Model used for processing"},"multilingual":{"type":"boolean","title":"Multilingual","description":"Whether mixed-language transcription was requested for this video's audio track. This is the INPUT you gave, not a detection result. `false` when it was not requested. When on, the lines that are not in the audio's main language are also translated into it once the audio completes (see `auto_translation`), billed as a translation on top of transcription.","default":false,"examples":[true]},"orientation":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Orientation","description":"Video orientation (PORTRAIT/LANDSCAPE)"},"overall_emotions":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Overall Emotions","description":"Overall emotion scores for video"},"pdf_generation_timestamp":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pdf Generation Timestamp","description":"PDF generation timestamp"},"pdf_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pdf Status","description":"PDF report status"},"pdf_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pdf Url","description":"PDF report URL"},"predefined_qa":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Predefined Qa","description":"Predefined Q&A data"},"personality_data":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Personality Data","description":"Personality analysis data"},"personality_processing":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Personality Processing","description":"Personality processing progress"},"personality_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Personality Status","description":"Personality analysis status"},"process_status":{"anyOf":[{"type":"number"},{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Process Status","description":"Processing progress"},"processed_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Processed Seconds","description":"Processing time in seconds"},"processing_fps":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Processing Fps","description":"Reduced processing frame-rate tier (15/10/5/1, or 30 when picked automatically), or null for full/native fps"},"processing_fps_auto":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Processing Fps Auto","description":"True when processing_fps was picked so the video fits the processing time limit, because the upload set none: full frame rate when it fits, else 30, else 15. False when the caller set it. Null on documents that predate the field."},"processing_fps_estimate_s":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Processing Fps Estimate S","description":"Estimated processing time in seconds at processing_fps, or null when unknown"},"video_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Video Type","description":"Processing mode: 'generic', 'online_meeting', or 'auto' as submitted. Once the worker has run, an 'auto' submission is replaced by the mode it resolved to, so this reports what actually ran. Null on documents that predate the field. Open string (not the submit enum) so a stored value this backend version does not know still serializes."},"meeting_platform":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Meeting Platform","description":"Optional meeting-client hint (zoom, google_meet, teams, webex). Null when unknown or unset. Open string for the same reason as video_type."},"driver_position":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Driver Position","description":"Seat hint for video_type='in_vehicle': which half of the VIDEO FRAME the driver appears in ('left'/'right') — not the vehicle's drive side. Null when unset or on a non-in_vehicle video. Open string for the same reason as video_type."},"driver_forward_gaze":{"anyOf":[{"$ref":"#/components/schemas/DriverForwardGaze"},{"type":"null"}],"description":"The driver's straight-ahead gaze direction supplied at upload for video_type='in_vehicle' (degrees, camera-relative, same axes and sign as the gaze_yaw / gaze_pitch values returned for faces). Null when not supplied."},"camera_mount":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Camera Mount","description":"Camera-mount hint supplied at upload for video_type='in_vehicle' ('steering_column', 'dashboard_center', 'windshield' or 'unknown'). Null when not supplied. Open string for the same reason as video_type."},"driver_forward_gaze_prior":{"anyOf":[{"$ref":"#/components/schemas/DriverForwardGazePrior"},{"type":"null"}],"description":"The straight-ahead gaze prior supplied at upload for video_type='in_vehicle' (degrees, camera-relative, same axes and sign as the gaze_yaw / gaze_pitch values returned for faces, with its confidence). Null when not supplied. Whether it was applied is reported by driver_state.faces[<driver face id>].forward_gaze.method."},"driver_state":{"anyOf":[{"$ref":"#/components/schemas/DriverState"},{"type":"null"}],"description":"In-vehicle drowsiness/distraction results. Null when driver-state analysis never ran (every non-in_vehicle video, and in_vehicle videos still processing). Headline and trust fields only. Visible to anyone who can GET this video; upload requires the driver_state feature."},"project_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Id","description":"Associated project ID"},"project_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Project Type","description":"Type of the associated project (recruitment/product_testing/...), null for library videos"},"psychologist_review":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Psychologist Review","description":"Psychologist review content"},"psychologist_review_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Psychologist Review Status","description":"Psychologist review status"},"share_status":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Share Status","description":"Whether video is shared"},"diarization":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Diarization","description":"Whether diarization was enabled at upload time (defaults to true if absent from the stored document)"},"has_transcript":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Transcript","description":"Whether a transcript exists (or is being produced): our ASR ran, OR one was supplied and applied. Drive the transcript UI off THIS, not `transcription` — an imported call has transcription=false and a real transcript, so that flag hides one that is right there."},"speech_status":{"anyOf":[{"type":"string"},{"type":"boolean"},{"type":"null"}],"title":"Speech Status","description":"Speech transcription status"},"start_millis":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Start Millis","description":"Start time in milliseconds"},"text_emotion":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Text Emotion","description":"Whether text emotion analysis was enabled at upload time (defaults to true if absent from the stored document)"},"transcription":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Transcription","description":"Whether transcription/ASR was enabled at upload time (defaults to true if absent from the stored document)"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","description":"Processing status"},"stimuli":{"anyOf":[{"items":{"$ref":"#/components/schemas/ResolvedStimulus"},"type":"array"},{"type":"null"}],"title":"Stimuli","description":"Stimuli (what the viewer reacts to) in this video's time, sorted by start. null when the video has none, or when stimulus_link.status is not 'resolved'. An empty list means the linked stimuli all fall outside the video."},"stimulus_link":{"anyOf":[{"$ref":"#/components/schemas/StimulusLinkView"},{"type":"null"}],"description":"Where the stimuli come from. null when the video has none."},"non_human_spans":{"items":{"$ref":"#/components/schemas/NonHumanSpan"},"type":"array","title":"Non Human Spans","description":"Known non-human audio, such as a voice assistant or music, in milliseconds of this video. Empty when none has been set. A span does not by itself remove a speaker from analysis results."},"summary":{"anyOf":[{"type":"string"},{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Summary","description":"Video summary or summary object"},"tags":{"anyOf":[{"items":{"oneOf":[{"$ref":"#/components/schemas/StringTag"},{"$ref":"#/components/schemas/EnumTag"},{"$ref":"#/components/schemas/IntegerTag"},{"$ref":"#/components/schemas/BoolTag"},{"$ref":"#/components/schemas/DateTag"},{"$ref":"#/components/schemas/GeoTag"},{"$ref":"#/components/schemas/SetTag"},{"$ref":"#/components/schemas/NoneTag"}],"discriminator":{"propertyName":"value_type","mapping":{"boolean":"#/components/schemas/BoolTag","date":"#/components/schemas/DateTag","enum":"#/components/schemas/EnumTag","integer":"#/components/schemas/IntegerTag","location_admin":"#/components/schemas/GeoTag","none":"#/components/schemas/NoneTag","set":"#/components/schemas/SetTag","string":"#/components/schemas/StringTag"}}},"type":"array"},{"type":"null"}],"title":"Tags","description":"Generalized media tags (demographic, survey, label, system). Discriminated union over value_type — see /v2/tag-keys for dimension metadata."},"tags_map":{"anyOf":[{"additionalProperties":{"type":"boolean"},"type":"object"},{"type":"null"}],"title":"Tags Map","description":"Legacy tags_map (sample flag only). No new keys here — use the `tags` field instead."},"text_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text Id","description":"Associated text analysis ID"},"thumbnail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Thumbnail","description":"Thumbnail image URL"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title","description":"Video title"},"total_frames_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Frames Count","description":"Total frames count"},"ui_request":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Ui Request","description":"Whether request came from UI"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url","description":"Stored video URL. A reference to the stored media, not guaranteed to be readable: use `GET /v2/videos/{video_id}/playback-url` to play it back."},"use_case":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Use Case","description":"What the recording is, as declared on upload ('interview'), or null when none was declared. Open string for the same reason as video_type."},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id","description":"The user who created the item (the same value as `created_by`)."},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By","description":"The user who created the item. Never changes, including when the item is moved to a workspace."},"owner":{"anyOf":[{"$ref":"#/components/schemas/MediaOwnerOut"},{"type":"null"}],"description":"Who owns the item: the user, or the team workspace that holds it."},"video_csv_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Video Csv Url","description":"Video-specific CSV analysis URL"},"width":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Width","description":"Video width in pixels"},"youtube_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube Url","description":"YouTube URL if applicable"}},"type":"object","title":"VideoDetailResponseV2","description":"Complete V2 response model for video details with all possible fields.\nReturns null for fields that don't exist in the database, except\ndiarization/text_emotion/transcription, which default to true when\nabsent (enabled-by-default speech stages, matching the upload contract).\nFields are ordered alphabetically."},"VideoEditResponse":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"}},"type":"object","title":"VideoEditResponse","description":"Response model after editing a video.\n\nInherits:\n    ImageEditResponse: Includes status and edit metadata."},"VideoEmotionResponseV2":{"properties":{"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"status":{"type":"string","title":"Status"},"duration":{"type":"integer","title":"Duration"},"media_source":{"type":"string","title":"Media Source"},"media_source_url":{"type":"string","title":"Media Source Url"},"user_id":{"type":"string","title":"User Id"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"processing_fps":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Processing Fps"},"processing_fps_auto":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Processing Fps Auto"},"processing_fps_estimate_s":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Processing Fps Estimate S"},"video_type":{"anyOf":[{"type":"string","enum":["generic","online_meeting","auto","in_vehicle"]},{"type":"null"}],"title":"Video Type"},"meeting_platform":{"anyOf":[{"type":"string","enum":["zoom","google_meet","teams","webex"]},{"type":"null"}],"title":"Meeting Platform"},"use_case":{"anyOf":[{"type":"string","enum":["interview","product_testing","sales_call"]},{"type":"null"}],"title":"Use Case"},"driver_position":{"anyOf":[{"type":"string","enum":["left","right"]},{"type":"null"}],"title":"Driver Position"},"driver_forward_gaze":{"anyOf":[{"$ref":"#/components/schemas/DriverForwardGaze"},{"type":"null"}]},"camera_mount":{"anyOf":[{"type":"string","enum":["steering_column","dashboard_center","windshield","unknown"]},{"type":"null"}],"title":"Camera Mount"},"driver_forward_gaze_prior":{"anyOf":[{"$ref":"#/components/schemas/DriverForwardGazePrior"},{"type":"null"}]},"diarization":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Diarization"},"text_emotion":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Text Emotion"},"transcription":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Transcription"},"speaker_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Speaker Count"}},"type":"object","required":["id","title","status","duration","media_source","media_source_url","user_id"],"title":"VideoEmotionResponseV2","description":"V2 response model for video emotion detection with URL source tracking.\n\nSupports:\n- YouTube URLs\n- Cloud storage URLs (Dropbox, direct URLs, etc.)\n- Social Media URLs (Instagram Reels, Facebook videos/reels, Twitter/X status videos)\n- Time parameters for all URL types\n- URL source tracking\n\nAttributes:\n    id (str): Unique ID of the video.\n    title (str): Title of the video.\n    status (str): Processing status (queue, processing, done, etc.).\n    duration (int): Duration in seconds (trimmed duration if time params provided).\n    media_source (str): Source type (\"youtube\", \"dropbox\", \"instagram\", \"facebook\", \"twitter\", \"public_url\", \"file_upload\").\n    media_source_url (str): Full URL as provided by user.\n    user_id (str): ID of the user who uploaded the video.\n    created_at (Optional[str]): Timestamp when video was created."},"VideoFaceAnalysisResponse":{"properties":{"video_id":{"type":"string","title":"Video Id"},"frames":{"items":{"$ref":"#/components/schemas/FaceAnalysisRow"},"type":"array","title":"Frames"}},"type":"object","required":["video_id","frames"],"title":"VideoFaceAnalysisResponse","description":"Image-free gaze / eye / lip time-series for a processed video."},"VideoFailedData":{"properties":{"id":{"type":"string","title":"Id","description":"The video id."},"status":{"type":"string","const":"failed","title":"Status"},"video_status":{"type":"string","title":"Video Status","description":"The video analysis outcome, lowercase."},"audio_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Audio Status","description":"The outcome of the video's audio track. Omitted when it has none."},"processed_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Processed Seconds","description":"How long the analysis took, in seconds."},"error":{"type":"string","title":"Error","description":"A short, fixed description of the failure."},"error_code":{"type":"string","enum":["processing_failed","timeout"],"title":"Error Code","description":"A stable code to branch on."}},"type":"object","required":["id","status","video_status","error","error_code"],"title":"VideoFailedData"},"VideoFailedEvent":{"properties":{"event_id":{"type":"string","title":"Event Id","description":"Unique per event and identical on every delivery attempt. Use it to ignore duplicates."},"timestamp":{"type":"integer","title":"Timestamp","description":"When the event was created, in Unix seconds."},"payload_version":{"type":"string","const":"v1","title":"Payload Version","default":"v1"},"event_type":{"type":"string","const":"video.failed","title":"Event Type"},"resource_type":{"type":"string","const":"video","title":"Resource Type"},"data":{"$ref":"#/components/schemas/VideoFailedData"}},"type":"object","required":["event_id","timestamp","event_type","resource_type","data"],"title":"VideoFailedEvent"},"VideoLinkResponse":{"properties":{"video_id":{"type":"string","title":"Video Id"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"}},"type":"object","required":["video_id"],"title":"VideoLinkResponse"},"VideoMomentsResponse":{"properties":{"video_id":{"type":"string","title":"Video Id"},"status":{"type":"string","title":"Status","description":"`ready`, `computing`, `failed` or `unavailable`."},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Why `status` is `unavailable` when the video is complete: `not_requested` means Moments has not been requested for this video (see `POST /v2/videos/{video_id}/moments/request`). Null otherwise, including while the video is still processing."},"stale":{"type":"boolean","title":"Stale","description":"True when a recomputation is pending for newer inputs.","default":false},"generation":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Generation","description":"Which computation these moments belong to. Null when no moments are returned. Compare it together with `profile_version` and `computed_at`; it does not change on every recomputation."},"profile_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile Version","description":"Analysis profile version these moments were computed with, the same value as `profile.version` when a profile is present. Null when no moments are returned."},"moments":{"items":{"$ref":"#/components/schemas/Moment"},"type":"array","title":"Moments"},"coverage":{"items":{"$ref":"#/components/schemas/MomentCoverage"},"type":"array","title":"Coverage"},"stimulus_coverage":{"items":{"$ref":"#/components/schemas/StimulusCoverage"},"type":"array","title":"Stimulus Coverage"},"counts":{"additionalProperties":{"type":"integer"},"type":"object","title":"Counts","description":"Number of moments per kind."},"visibility":{"anyOf":[{"items":{"$ref":"#/components/schemas/SubjectVisibility"},"type":"array"},{"type":"null"}],"title":"Visibility","description":"When each face was visible, one entry per subject. A subject not listed has unknown visibility. Null when visibility is not included with these moments."},"profile":{"anyOf":[{"$ref":"#/components/schemas/MomentsProfile"},{"type":"null"}]},"subjects":{"items":{"$ref":"#/components/schemas/MomentSubject"},"type":"array","title":"Subjects"},"computed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Computed At","description":"When these moments were computed."}},"type":"object","required":["video_id","status"],"title":"VideoMomentsResponse","description":"Moments for a video.\n\n`status` is `ready` (moments below), `computing` (first computation in progress;\npoll again), `failed` (the last computation failed for the current inputs) or\n`unavailable` (the video has not finished processing, or Moments was not requested for it).\n`stale` is true when the video changed since the moments were computed; the\nprevious moments are returned while they are recomputed."},"VideoResponse":{"properties":{"title":{"type":"string","title":"Title","default":""},"description":{"type":"string","title":"Description","default":""},"id":{"type":"string","title":"Id"},"audio_id":{"type":"string","title":"Audio Id","default":""},"model_id":{"type":"string","title":"Model Id","default":""},"emotions_list":{"items":{"type":"string"},"type":"array","title":"Emotions List","default":["angry","contempt","disgust","fear","happy","neutral","sad","surprise"]},"url":{"type":"string","title":"Url","default":""},"faces":{"anyOf":[{"$ref":"#/components/schemas/ListOfFaces"},{"type":"null"}]},"annotated_video_mp4":{"title":"Annotated Video Mp4"},"summary":{"type":"string","title":"Summary","default":""},"total_frames_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Frames Count"},"fps":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Fps"},"height":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Height"},"width":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Width"},"status":{"type":"string","title":"Status"},"created_at":{"title":"Created At","default":""},"thumbnail_image":{"type":"string","title":"Thumbnail Image","default":""},"text_id":{"type":"string","title":"Text Id","default":""}},"type":"object","required":["id","status"],"title":"VideoResponse","description":"Model representing detailed video metadata and analysis.\n\nAttributes:\n    title (Optional[str]): Title of the video.\n    description (str): Description of the video.\n    id (str): Unique video identifier.\n    audio_id (str): Related audio ID if extracted.\n    model_id (str): Model used to process video.\n    emotions_list (List[str]): List of possible emotions.\n    url (str): Original video file url\n    faces (Optional[ListOfFaces]): Detected faces in the video.\n    annotated_video_mp4 (Any): Processed video with annotations.\n    summary (str): Summary text of the video.\n    total_frames_count (Optional[int]): Total number of frames.\n    fps (Optional[float]): Frames per second.\n    height (Optional[float]): Height of the video.\n    width (Optional[float]): Width of the video.\n    status (str): Current processing status.\n    created_at (str): Creation timestamp.\n    thumbnail_image (str): URL or data of the thumbnail image.\n    text_id (str): Linked text analysis ID."},"VisibleSpan":{"properties":{"start_ms":{"type":"integer","title":"Start Ms","description":"Start, in milliseconds of video time (inclusive)."},"end_ms":{"type":"integer","title":"End Ms","description":"End, in milliseconds of video time (exclusive)."}},"type":"object","required":["start_ms","end_ms"],"title":"VisibleSpan","description":"A stretch of video time in which a face was visible."},"WebRTCOfferRequest":{"properties":{"session_id":{"type":"string","title":"Session Id","description":"Active session identifier"},"sdp":{"type":"string","title":"Sdp","description":"SDP offer string"},"type":{"type":"string","title":"Type","description":"SDP type (usually 'offer')","default":"offer"}},"type":"object","required":["session_id","sdp"],"title":"WebRTCOfferRequest","description":"Request model for forwarding WebRTC SDP offer."},"WebRTCOfferResponse":{"properties":{"session_id":{"type":"string","title":"Session Id","description":"Session identifier"},"sdp":{"type":"string","title":"Sdp","description":"SDP answer string"},"type":{"type":"string","title":"Type","description":"SDP type (usually 'answer')"}},"type":"object","required":["session_id","sdp","type"],"title":"WebRTCOfferResponse","description":"Response model containing SDP answer."},"WebhookDeliveries":{"properties":{"deliveries":{"items":{"$ref":"#/components/schemas/WebhookDelivery"},"type":"array","title":"Deliveries"}},"type":"object","required":["deliveries"],"title":"WebhookDeliveries"},"WebhookDelivery":{"properties":{"event_id":{"type":"string","title":"Event Id","description":"The `event_id` in the body and the `Imentiv-Event-Id` header."},"event_type":{"type":"string","title":"Event Type"},"resource_type":{"type":"string","title":"Resource Type"},"resource_id":{"type":"string","title":"Resource Id"},"status":{"type":"string","enum":["pending","delivered","failed","exhausted","rejected"],"title":"Status","description":"`pending` will be retried; `delivered` got a 2xx; `failed` got an answer that is not retried; `exhausted` used every attempt; `rejected` was never sent because the URL is not allowed."},"url":{"type":"string","title":"Url","description":"Your endpoint, reduced to its scheme and host."},"attempts":{"items":{"$ref":"#/components/schemas/WebhookDeliveryAttempt"},"type":"array","title":"Attempts"},"next_attempt_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Next Attempt At"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["event_id","event_type","resource_type","resource_id","status","url","attempts","created_at"],"title":"WebhookDelivery"},"WebhookDeliveryAttempt":{"properties":{"at":{"type":"string","format":"date-time","title":"At"},"status_code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Status Code","description":"The HTTP status your endpoint answered with, if it answered."},"error":{"anyOf":[{"type":"string","enum":["timeout","connection_error","dns_error","url_rejected","internal_error"]},{"type":"null"}],"title":"Error","description":"Why the attempt got no answer."},"duration_ms":{"type":"integer","title":"Duration Ms"}},"type":"object","required":["at","duration_ms"],"title":"WebhookDeliveryAttempt"},"WebhookSecret":{"properties":{"secret":{"type":"string","title":"Secret","description":"The signing secret, `whsec_...`. Store it now; it is not shown again."},"secret_last4":{"type":"string","title":"Secret Last4"},"rotated":{"type":"boolean","title":"Rotated","description":"True if this call replaced a secret you had already read. The previous secret keeps signing deliveries alongside the new one for 24 hours. False if it returned the secret created for your account, which is not rotated so that you can verify deliveries already sent."},"previous_secret_expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Previous Secret Expires At"}},"type":"object","required":["secret","secret_last4","rotated"],"title":"WebhookSecret","description":"A signing secret. This is the only response that contains it."},"WebhookSettings":{"properties":{"has_secret":{"type":"boolean","title":"Has Secret","description":"Whether a signing secret exists for your account."},"secret_last4":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Secret Last4","description":"The last four characters of the current secret, to tell secrets apart."},"secret_revealed":{"type":"boolean","title":"Secret Revealed","description":"False until you have read your secret with POST /v2/webhooks/secret. Deliveries are signed from your first event, so read it before you start verifying."},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At","description":"When the current secret was created."},"previous_secret_expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Previous Secret Expires At","description":"After a rotation, until when the previous secret still signs deliveries. While this is in the future each delivery carries two v1 signatures."},"payload_version":{"type":"string","enum":["legacy","v1"],"title":"Payload Version","description":"The payload format your webhooks use. `legacy` is the format sent before payload versions existed (UPPERCASE status values, free-text error). `v1` is the documented format."}},"type":"object","required":["has_secret","secret_revealed","payload_version"],"title":"WebhookSettings","description":"Your webhook signing secret and payload format. The secret itself is never included."},"WebhookSettingsUpdate":{"properties":{"payload_version":{"type":"string","enum":["legacy","v1"],"title":"Payload Version","description":"The payload format to use for events created from now on. `legacy` can only be kept, not chosen: it is available to accounts that already receive it."}},"type":"object","required":["payload_version"],"title":"WebhookSettingsUpdate"},"WindowIn":{"properties":{"start_ms":{"type":"integer","minimum":0.0,"title":"Start Ms"},"end_ms":{"type":"integer","minimum":0.0,"title":"End Ms"}},"additionalProperties":false,"type":"object","required":["start_ms","end_ms"],"title":"WindowIn","description":"A half-open window `[start_ms, end_ms)` on the bound media's clock."},"PublicCreateSessionRequest":{"description":"Options for a new realtime session.\n\nEvery field is optional — send ``{}``, or omit the body entirely, for a full\nvideo + audio session.","properties":{"audio_only":{"default":false,"description":"Set true for a voice-only session: send no camera track on the WebRTC offer and expect no facial-emotion results. Any video track sent anyway is ignored. Defaults to false, a full video + audio session.","title":"Audio Only","type":"boolean"},"emotion_analysis_enabled":{"default":true,"description":"Set false to run the session with no emotion analysis: no facial, vocal or text emotion model runs for it, and its results and windows carry no emotion values. Transcription is unaffected. It holds for the whole session, and a queued place keeps it however a later claim is sent. Defaults to true.","title":"Emotion Analysis Enabled","type":"boolean"}},"title":"PublicCreateSessionRequest","type":"object"}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Imentiv API key from the 'My Profile' tab. Accepts a raw key, a bare JWT, or `IMT <jwt>` (get_user_info's resolution order)."},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"},"OAuth2Delegated":{"type":"oauth2","flows":{"authorizationCode":{"authorizationUrl":"https://api.imentiv.ai/oauth/authorize","tokenUrl":"https://api.imentiv.ai/oauth/token","refreshUrl":"https://api.imentiv.ai/oauth/token","scopes":{"account.read":"See your workspace, plan and credit balance","account.workspace.read":"See your workspace's name and apps, your role in it and who pays for it","usage.read":"See your credit usage history","projects.read":"See your projects and the people in them","projects.write":"Create projects and manage the people in them","projects.delete":"Delete projects and every analysis in them","media.read":"See your analyses and their results","media.write":"Upload media and request analyses, which spend your credits","media.delete":"Delete your videos and audios, including their analyses","realtime.write":"Run live analysis sessions, which spend your credits","credits.charge":"Charge your Imentiv credits for your use of this app","media.move":"Move your media into a workspace you belong to; this cannot be undone","insights.write":"Get insights from your analyses, which spend your credits"}}}}}},"security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"tags":[{"name":"Video Emotion API","description":"The Video Emotion Analysis API by Imentiv analyzes the emotional states of individuals in a video. It performs multi-modal emotion detection using facial expressions, audio, and text transcript, and has the following features:\n\n- Overall emotion summary\n- Frame-by-frame emotion data\n- Per-face emotional analysis\n- Valence-arousal-intensity data\n- Audio and transcript emotion data\n- Personality analysis\n- Video Highlights\n- Optional psychological insights\n            "},{"name":"Image Emotion API","description":"The Image Emotion Recognition API by Imentiv analyzes human facial expressions in an image and returns the detected emotions. It can detect multiple faces and evaluate each face's emotional state, providing detailed emotional labels such as happy, sad, angry, neutral, surprise, disgust, fear, and contempt.\n            "},{"name":"Audio Emotion API","description":"The Audio Emotion Analysis API processes audio files or YouTube URLs to analyze emotions and transcripts. The API provides:\n\n**Speaker Diarization:** Identifies and segments audio by speakers.\n\n**Audio Emotion Analysis:** Detects one of 8 emotions per audio segment.\n\n**Text Emotion Analysis:** Generates a transcript for each segment and detects one of 28 emotions from the text.\n            "},{"name":"Text Emotion API","description":"The Text Emotion Analysis API by Imentiv analyzes a given piece of text and returns the detected emotional tone(s).\n\n**Features:**\n- The API processes the text, detects emotions for each paragraph, and returns results.\n- A callback mechanism is supported for asynchronous analysis.\n- **Supported emotions (28):** Admiration, Amusement, Approval, Caring, Desire, Excitement, Gratitude, Joy/Happy, Love, Optimism, Pride, Relief, Anger, Disappointment, Annoyance, Disapproval, Disgust, Embarrassment, Fear, Grief, Nervousness, Remorse, Sadness, Confusion, Curiosity, Realization, Surprise, Neutral\n            "},{"name":"Realtime Emotion API","description":"Analyse emotion from a live camera and microphone feed as it happens.\n\nMedia is streamed to Imentiv over **WebRTC**; facial emotion, voice-tone emotion, transcription and text emotion come back over a **WebSocket**. These REST endpoints set the session up and tear it down — one `session_id` ties all of it together.\n\n**The flow:** create a session, open the results WebSocket, exchange the WebRTC offer/answer and ICE candidates, read results, then end the session.\n\nRealtime analysis has finite concurrent capacity: when none is free, session creation returns `503` with `code: at_capacity`, which is a designed \"try again shortly\" rather than a fault.\n\nA session lives on one server for its whole life. If that server restarts or goes away, the session ends with it: calls for it return `410` with `code: session_node_gone` (and its WebSockets close with reason `Session node gone`), so start a new session. Ending such a session still succeeds.\n            "},{"name":"Media Persons","description":"Name the people who appear in a video or a standalone audio, and attach the faces and speakers that belong to them.\n\nA video and its audio share one set of people, stored on the video. A standalone audio stores its own. Each person record says which one is its home; update it there.\n            "},{"name":"Faces","description":"Compare faces across the videos you own.\n\nPick the faces to look up and the faces to compare them against, by video and face id. Each pair comes back with a similarity score; a face that cannot be scored comes back with a reason instead.\n            "},{"name":"Report API","description":"The Report API provides comprehensive emotion analysis reports in PDF format for analyzed media content.\n\n**Features:**\n- Generate detailed PDF reports for video emotion analysis\n- Includes visual charts, emotion timelines, and insights\n- Supports asynchronous report generation with callback notifications\n- Retrieve existing reports or trigger new generation\n\n**Use Cases:** Ideal for creating shareable analysis reports, documentation of emotional insights, and presentation-ready materials.\n            "},{"name":"Media API","description":"Cross-media utilities that work across videos, audios, images, and texts.\n\n**Endpoints:**\n- `POST /v2/{media_type}/upload-url` — get a presigned URL to upload large files directly to cloud storage (step 1 of the upload flow; then PUT the file to the URL and call `POST /v2/videos` or `POST /v2/audios` with the returned `public_url`)\n- `GET /v2/{media_type}/{media_id}/statuses` — poll fine-grained processing statuses (transcription, personality, report, annotated video, …) without fetching the full analysis payload\n            "},{"name":"Insights API","description":"The Insights API provides conversational AI-powered analysis of emotional data from your media content.\n\n**Features:**\n- Ask natural language questions about detected emotions, patterns, and trends\n- Supports both real-time streaming (SSE) and standard JSON responses\n- Conversation continuity across multiple messages\n- Works with all media types: video, audio, image, and text\n\n**Endpoints:**\n- `POST /v2/{media_type}/{media_id}/insights` - Send a message and receive AI response; supports streaming SSE or JSON (streaming recommended)\n- `GET /v2/{media_type}/{media_id}/insights` - Retrieve conversation history\n- `DELETE /v2/{media_type}/{media_id}/insights` - Clear conversation history\n\n**Use Cases:** Ideal for gaining deeper insights into emotional patterns, generating summaries, comparing emotional states, and understanding sentiment trends in your media content.\n            "}]}