Recording semantic search API
Server integrations call /api/v2/recording/search/* with an existing API Token. Search needs recordings.play; manual file recovery needs recordings.manage. Each request also checks the Recording AI Search license, module switch, participating channels, and source recording state. Keep tokens on the server, outside browser bundles, URLs, and logs.
Search with text
Send JSON to POST /api/v2/recording/search/text:
curl -X POST 'https://example.invalid/api/v2/recording/search/text' \
-H 'Authorization: Bearer ak_pat_<TOKEN>' \
-H 'Content-Type: application/json' \
--data '{"text":"有人穿黄色外套进入大门","channelIds":["channel-1"],"fromUtc":"2026-09-30T00:00:00Z","toUtc":"2026-10-01T00:00:00Z","minSimilarity":0.45,"maxSegments":5}'
text is required and accepts 1–500 characters. Omit channelIds to search all participating channels. Times use ISO 8601 with a timezone. minSimilarity accepts 0–1 and defaults to 0.45; maxSegments accepts 1–50 and defaults to 5. Out-of-range values return HTTP 400. The server filters low-score vectors before ranking and returning the top N segments. The start must not follow the end. A scope with no participating channels returns an empty segments array.
minSimilarity:0.60 means a cosine similarity of at least 0.60, not a calibrated 60% probability that an object is present. Scores vary by model and query; an overly high threshold may return nothing. In one RK3588 RN50 measurement, the leading scores for “男人” were about 0.42 and those for “出风口” about 0.48. The UI starts at 0.45 / top 5 and lets the operator adjust both values.
Search with an image
Send multipart/form-data to POST /api/v2/recording/search/image with exactly one JPEG/PNG image file and optional query JSON filters. The image limit is 5 MiB and the request limit is 6 MiB. Server file paths and external image URLs are not accepted.
curl -X POST 'https://example.invalid/api/v2/recording/search/image' \
-H 'Authorization: Bearer ak_pat_<TOKEN>' \
-F 'image=@./reference.jpg' \
-F 'query={"channelIds":["channel-1"],"minSimilarity":0.45,"maxSegments":5}'
Interpret a result and play it
Both search modes use the same response shape. The values below illustrate fields only; use IDs, times, and scores returned by your server:
{
"modelSpaceId": "chinese-clip-rn50-official-v1",
"effectiveBackend": "Cpu",
"coverage": "Unverified",
"segments": [{
"recordFileId": "00000000-0000-0000-0000-000000000001",
"channelId": "channel-1",
"channelName": "Entrance camera",
"deviceId": "device-1",
"nodeId": "node-1",
"mediaServerId": "media-1",
"vhost": "__defaultVhost__",
"app": "live",
"streamId": "stream-1",
"fileName": "record.mp4",
"hitAtUtc": "2026-09-30T01:02:03Z",
"hitOffsetMs": 42000,
"segmentStartMs": 40000,
"segmentEndMs": 47000,
"similarity": 0.82,
"mediaUrl": "/api/v2/recording/files/00000000-0000-0000-0000-000000000001/media",
"playableOnCurrentNode": true
}]
}
mediaUrl is a protected relative URL, not a public video link. A server client can attach the same Bearer Token and use HTTP Range. Browser <video> cannot set an Authorization header directly; obtain a short-lived link with POST /api/v2/third-party/recordings/{fileId}/playback-link, then seek to the result offset. Playback still depends on file availability and permission.
Status and manual recovery
GET /api/v2/recording/search/status returns license, model, vector service, and job state. GET /api/v2/recording/search/hardware and /capabilities need system.config.view for operations checks. A visible runtime does not prove the selected model has passed validation.
After an administrator temporarily enables FileRecoveryEnabled, submit explicit file IDs to POST /api/v2/recording/search/index-jobs:
{"recordFileIds":["00000000-0000-0000-0000-000000000001"]}
Submit 1–100 IDs at once. HTTP 202 includes requested, accepted, and skipped; it means queued, not indexed. This endpoint requires recordings.manage and the same module entitlement. Turning off recovery does not delete existing search results.
Handle errors
| HTTP | Code | Action |
|---|---|---|
| 400 | invalid_query, invalid_image, invalid_filter, invalid_file_ids |
Correct text, image, or filters; do not replay unchanged |
| 401 | authentication_required |
Supply a valid Bearer API Token |
| 403 | permission_denied or license rejection |
Check Token grants and the advanced-module entitlement |
| 503 | recording_search_disabled, recording_search_unavailable, file_recovery_disabled |
Check module status, model, Qdrant, and recovery switch; retry with backoff |
coverage: "Unverified" means that the index may be incomplete. Once a recording is deleted, discard cached playback links and old search results.