Bulk recorded-media analysis should behave like durable orchestration, not a loop that fires thousands of requests and hopes for the best. Submit bounded scanner batches for authorized video or audio and keep your own source inventory as the source of truth.
/v1/scanner-batches is a machine/API-key-only surface that durably orchestrates up to 100 explicit scanner/upload pairs. It does not discover third-party recordings or schedule recurring scans. Work is fairly scheduled, and batch availability is deployment-gated until the API reports it enabled.1. Pin the scanner version you intend to operate
Create the scanner once with POST /v1/scanners. Save the returned scanner_id and version beside your batch definition. If you later patch the scanner, treat the new version as a new processing cohort rather than silently mixing outputs.
POST /v1/scanners
Authorization: Bearer $CCT_API_KEY
Idempotency-Key: onboarding-friction-scanner-v1
{
"name": "Onboarding friction",
"type": "find_moments",
"instructions": "Find repeated setup actions, visible errors, or abandonment before the next step.",
"profile": "conversation",
"max_observations": 20,
"min_seconds": 10,
"max_seconds": 60
}
2. Submit a bounded durable batch
Store your own recording ID, ready upload ID, media_kind, scanner ID, intended scanner version, unique item client_id, request idempotency key, and returned batch/run IDs. Never put signed PUT targets or bearer credentials in logs. A request accepts 1โ100 items and at most 512 KiB.
POST /v1/scanner-batches
Authorization: Bearer $CCT_API_KEY
Idempotency-Key: study-activation-2026-08-15-page-1
{
"items": [
{
"client_id": "rec-1842:friction-v3",
"scanner_id": "scn_YOUR_SCANNER",
"upload_id": "upl_YOUR_READY_UPLOAD",
"metadata": {"recording_id": "rec_1842", "cohort": "new-user"}
}
]
}
A retry with the same idempotency key and body returns the original batch. Reusing a key for different items is a conflict. Use a new key only when you truly intend a distinct batch.
3. Follow batch and item state
Read current concurrency and monthly allowances in Console โ Usage. Fetch GET /v1/scanner-batches/{batch_id} with item_limit and item_cursor, following items_next_cursor until null. Batch states include queued, processing, canceling, completed, partial, failed, and canceled. The underlying run history distinguishes queued, dispatched, and processing. None guarantees an instant start.
GET /v1/scanner-batches/bat_YOUR_ID?item_limit=50&item_cursor=0
GET /v1/scanner-batches?limit=20
# Stop queued items from starting. Already-spawned work drains.
POST /v1/scanner-batches/bat_YOUR_ID/cancel
4. Read each terminal run and preserve zero results
Each item eventually reports its own run_id and analysis_id. Poll GET /v1/runs/{run_id} every 5โ10 seconds with jitter until terminal. For a completed run, fetch GET /v1/runs/{run_id}/observations once and store the observation IDs plus scanner, batch item, and run provenance.
A completed run with zero observations is not a processing failure. Available visual or audio evidence could not support an answer. Keep it separate from an answered monitor no-match, which contains one explicit full-source boolean false observation after a completed review.
5. Aggregate only compatible records
Query GET /v1/observations?scanner_id=scn_...&limit=100 for cross-run retrieval, but group by scanner version and semantics before aggregating. Boolean monitor output, a classifier label, a scorer value, and a timestamped moment are different contracts. relevance_score helps rank support for the scanner brief; it is not calibrated confidence.
Failure checklist
- Persist the
batch_id, item IDs, and run IDs before claiming submission succeeded. - Use completed, account-owned private video or audio uploads; production scanners do not run arbitrary source URLs.
- Expect one independently billed analysis per item, even when several items reuse one upload.
- Separate terminal
failed, unanswerable zero-observation, and explicit monitor-false outcomes. - Retain
scanner_version,source_metadata,media_kind,evidence_kind,client_id, andrun_id. - Use webhooks only when the console confirms delivery is enabled; keep reconciliation polling even then.