A useful review queue lets a person inspect the source evidence, record a decision, and trace that decision back to the scanner definition that produced it. The observation—not an AI-generated percentage—is the unit of work.
1. Ingest only completed-run observations
After GET /v1/runs/{run_id} reaches completed, request GET /v1/runs/{run_id}/observations. Deduplicate locally on observation_id. Store the full typed value and provenance fields your interface needs rather than flattening everything into a title and score.
{
"observation_id": "obs_...",
"scanner_id": "scn_...",
"scanner_version": 3,
"run_id": "run_...",
"analysis_id": "ana_...",
"type": "find_moments",
"value_type": "object",
"semantics": "timestamped_moment",
"value": {"category": "setup_error"},
"relevance_score": 0.91,
"evidence": {
"start_ms": 84210,
"end_ms": 109440,
"transcript_excerpt": null,
"signals": []
},
"source_metadata": {"recording_id": "rec_1842"}
}
2. Model queue state separately
Do not mutate the API observation into your review decision. Create a local row keyed by observation_id with fields such as state, assignee, decision, note, and timestamps. This leaves the generated result immutable while your team’s review history evolves.
review_item = {
"observation_id": observation.observation_id,
"state": "unreviewed",
"priority": rank_for_team(observation.relevance_score),
"assignee": null,
"decision": null
}
3. Present evidence, not implied certainty
Show the observation title, typed value, source metadata, scanner version, and exact start/end time. The first visual scanner normally returns a null transcript excerpt and empty evidence signals, so do not promise either in the review UI. Label relevance_score as relevance or ranking support—never “91% confidence.” It is not a calibrated probability.
Different scanner types need different controls. A monitor produces a boolean; a classifier a bounded label; a scorer a 0–1 number; an extractor structured data; and find_moments timestamped evidence. Validate value_type and semantics before choosing a UI. Treat explicit monitor false as a negative answer and zero observations as unanswerable.
4. Support cross-run triage carefully
GET /v1/observations accepts scanner_id, run_id, type, and a limit up to 100. Use those server filters, then maintain your own ingestion watermark or seen-ID set. Do not compare scores from unrelated scanners as though they shared a calibration scale.
GET /v1/observations?scanner_id=scn_...&type=find_moments&limit=100
Authorization: Bearer $CCT_API_KEY
5. Give reviewers a source-safe playback path
The public observation contains timestamps and evidence, not a permanent public media URL. Keep playback inside an authorized environment and resolve access from your account-owned upload record; do not persist the signed PUT target as a viewer URL.
6. Escalate only after a decision
An accepted item can create a ticket, send a privacy-safe alert, or feed an aggregate report. Scanner observations do not render and an observation ID is not a Moment ID. If finished video is required, create a separate one-off analysis and render selected Moment IDs from that flow.