Skip to content
ChatClipThatAPI
Documentation

Video and audio scanner API

Define reusable questions, run the same six typed methods against authorized video or audio, and query observations with explicit media and evidence kinds. One-off analysis and rendering remain a separate video-only flow.

RESTVideo + audioTyped observationsVersioned scannersAsync runs

Core concepts

A Scanner is a saved, versioned question and typed output contract. A Run applies one scanner version to one completed private video or audio upload. An Observation is a typed answer with relevance, source metadata, and a grounded timestamp range.

Use a video find_moments scanner when you want the same production moment-selection behavior as ChatClipThat from a full recording or selected time window. Use the other scanner types for bounded questions such as monitoring, classification, scoring, summarization, or extraction. Audio-only scanners answer questions that the audio can support; generated speech excerpts are not certified verbatim transcripts.

Scanner runs do not render automatically. A completed video find_moments run returns Moment IDs that you may explicitly submit to /v1/renders. Other scanner observations and all audio scanner results remain JSON-only.

Authentication

Create a revocable API key in the console and send it from your server in the Bearer header. Never ship a secret key in browser or mobile client code.

HTTP
Authorization: Bearer cct_your_secret_key

Resources are account-scoped. Requests for an unknown resource and a resource owned by a different account both return 404.

API reference

POST/v1/scanners

Create a reusable, versioned scanner definition.

GET/v1/scanners

List account-owned scanner definitions.

PATCH/v1/scanners/{scanner_id}

Create the next immutable scanner version.

GET/v1/scanners/{scanner_id}/versions

List definition snapshots used by prior runs.

POST/v1/scanners/{scanner_id}/runs

Run the current scanner version against one recording.

GET/v1/runs/{run_id}

Read run state, source metadata, and provenance.

GET/v1/runs/{run_id}/observations

Read typed findings from one completed run.

GET/v1/observations

Query observations across account-owned runs.

POST/v1/uploads

Issue a private, time-limited direct upload target.

POST/v1/webhook-endpoints

Create a signed event-delivery endpoint when enabled.

GET/v1/account

Read wallet, plan limits, retention, concurrency, and built-in providers.

POST/v1/sources/preflight

Resolve provider metadata and source duration before submission.

POST/v1/analyses/quote

Check exact credits and affordability for an analysis window.

POST/v1/analyses

Submit an authorized video for asynchronous analysis.

GET/v1/analyses

List account-owned analyses.

GET/v1/analyses/{analysis_id}

Read analysis status and terminal errors.

GET/v1/analyses/{analysis_id}/progress

Read queue position and bounded execution progress.

POST/v1/analyses/{analysis_id}/cancel

Cancel and refund work that has not started.

GET/v1/analyses/{analysis_id}/moments

Retrieve ranked Moments after completion.

GET/v1/render-presets

Read supported output and caption controls.

POST/v1/renders

Optionally render selected Moment IDs.

GET/v1/renders/{render_id}

Read render status and completed assets.

POST

Create a saved scanner

All six scanner methods support video and audio-only uploads. Each type fixes the observation value type and semantics; changing a scanner creates a new version.

monitorboolean / match_foundTrue or false answer. A supported full-source no-match is an explicit false observation.
summarizerstring / moment_summaryConcise whole-source summary.
classifierstring / classification_labelConfigured label, or other when no configured label fits.
scorernumber / relevance_scoreFinite 0–1 value under your criterion; not calibrated probability.
extractorobject / extracted_fieldsStructured answer limited to requested output fields.
find_momentsobject / timestamped_momentEditorially selected timestamped moments. Video results include renderable Moment IDs.

Instructions may contain up to 600 characters. For clipping and highlight discovery, use find_moments with a video upload. Ask audio-only sources only questions the recording's audio can answer. Generated speech excerpts are not certified verbatim transcripts.

cURL
curl -X POST https://api.chatclipthat.com/v1/scanners \
  -H "Authorization: Bearer $CCT_API_KEY" \
  -H "Idempotency-Key: scanner-001" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Onboarding friction",
    "instructions": "Find where the user repeats a setup action, receives a visible error, or leaves without advancing.",
    "type": "find_moments",
    "profile": "conversation",
    "max_observations": 20,
    "min_seconds": 10,
    "max_seconds": 60,
    "output": {"fields": ["title", "summary", "start_ms", "end_ms", "relevance_score"]},
    "metadata": {"owner": "product-research"}
  }'

Updating a scanner creates a new version. Runs retain the version and definition they used; old observation provenance is not rewritten.

POST

Run a scanner

Production scanner runs require a completed, account-owned private video or audio upload_id. Direct source_url execution is not available at this boundary. Video find_moments runs may include analysis_start_seconds and analysis_end_seconds; provide both, or omit both to analyze the full upload. Run metadata accepts up to 20 bounded scalar values and is copied onto resulting observations.

cURL
curl -X POST https://api.chatclipthat.com/v1/scanners/scn_YOUR_ID/runs \
  -H "Authorization: Bearer $CCT_API_KEY" \
  -H "Idempotency-Key: recording-1842-scanner-v3" \
  -H "Content-Type: application/json" \
  -d '{
    "upload_id": "upl_YOUR_READY_UPLOAD",
    "analysis_start_seconds": 120,
    "analysis_end_seconds": 900,
    "metadata": {"recording_id": "1842", "cohort": "new-user"}
  }'

The run response reports source_kind and media_kind alongside a real analysis_id for durable lifecycle and billing. queued means accepted for processing, dispatched means assigned to available capacity, and processing means active execution. Start time is not guaranteed; poll every 5–10 seconds with jitter.

GET

Read typed observations

An observation separates its typed value and value_type from explicit semantics. relevance_score ranks support for the scanner instructions and must not be presented as calibrated confidence.

JSON
{
  "observation_id": "obs_...",
  "scanner_id": "scn_...",
  "scanner_version": 3,
  "run_id": "run_...",
  "type": "find_moments",
  "value_type": "object",
  "semantics": "timestamped_moment",
  "value": {"moment_id": "mom_...", "event_type": "setup_blocked"},
  "title": "Repeated connection failure",
  "summary": "The user retries the same connection flow after an error.",
  "relevance_score": 0.91,
  "evidence": {"media_kind": "video", "evidence_kind": "visual", "start_ms": 84210, "end_ms": 109440, "duration_ms": 25230, "transcript_excerpt": null, "speaker": null, "signals": []},
  "source_metadata": {"recording_id": "1842", "cohort": "new-user"}
}

For audio, evidence.media_kind is audio and evidence_kind is transcript, audio, or audio_and_transcript. Speech-backed answers may include a timestamped transcript_excerpt and speaker label. Generated excerpts are not certified verbatim transcripts, and relevance_score is not calibrated confidence.

False is not unanswerable. A supported monitor no-match is an answered result with one boolean false observation whose range covers the full source. If the recording cannot support an answer, the completed run has zero observations. Never convert that zero-result state into false.

An answered result contains one or more grounded observations. Summarizer, classifier, scorer, and extractor answers may use the full recording as their range; find_moments answers use timestamped windows. Poll the run until completed before interpreting an empty observation list.

POST

Create a one-off analysis

Use the lower-level analysis endpoint when you do not need a saved definition or cross-run observation model. YouTube, Twitch, Kick, Vimeo, Loom, Google Drive, Dropbox, Zoom, Rumble, Facebook, LinkedIn, Riverside, Frame.io, StreamYard, TikTok, Instagram, and X/Twitter are built-in sources and do not require Trusted Sources setup. Arbitrary HTTPS media origins still require an exact trusted hostname. Call POST /v1/sources/preflight, choose one explicit window that fits the account's available credits and source-size limit, then call POST /v1/analyses/quote before submission. The exact quote is enforced again and reserved before source acquisition or compute begins. Longer paid windows are automatically processed as bounded chunks and returned as one merged analysis.

cURL
curl -X POST https://api.chatclipthat.com/v1/analyses \
  -H "Authorization: Bearer $CCT_API_KEY" \
  -H "Idempotency-Key: request-001" \
  -H "Content-Type: application/json" \
  -d '{
    "source_url": "https://www.youtube.com/watch?v=YOUR_VIDEO_ID",
    "analysis_start_seconds": 0,
    "analysis_end_seconds": 3600,
    "what_to_find": "Find where the user repeatedly fails to connect an integration",
    "profile": "conversation",
    "chat_moment_types": ["q_and_a", "advice"],
    "speech_cleanup": "tight",
    "max_moments": 10,
    "min_seconds": 10,
    "max_seconds": 60
  }'

Request fields

source_urlstringBuilt-in provider page URL or HTTPS media URL on an exact trusted custom hostname.
analysis_start_seconds / analysis_end_secondsnumberRequired bounded analysis window. Paid windows must fit the account's byte, credit, and concurrency limits; proof runs allow up to 7,200 seconds.
what_to_findstringPlain-language search brief, up to 600 characters.
profileenumconversation or gameplay.
chat_moment_typesarrayFor conversation: auto, q_and_a, story, opinion, reaction, funny, or advice. auto cannot be combined with another value.
speech_cleanupenumFor conversation: natural (default) or tight.
max_momentsintegerMaximum results from 1–50.
min_seconds / max_secondsintegerTarget Moment length, each from 5–600 seconds.
GET

Follow analysis status

Use signed analysis.completed and analysis.failed webhooks for terminal delivery. GET /v1/analyses/{analysis_id}/progress returns queue position and bounded progress for interactive status. Poll GET /v1/analyses/{analysis_id} with jitter only as a fallback. Queued work can be stopped through POST /v1/analyses/{analysis_id}/cancel; work that has already started returns 409 analysis_not_cancelable.

JSON
{
  "analysis_id": "ana_0123456789abcdef0123456789abcdef",
  "status": "completed",
  "profile": "conversation",
  "chat_moment_types": ["q_and_a", "advice"],
  "speech_cleanup": "tight",
  "created_at": "2026-08-14T18:00:00Z",
  "updated_at": "2026-08-14T18:03:24Z",
  "error": null
}
GET

Retrieve Moments

A completed analysis returns ranked timestamps, summaries, transcript evidence, selection signals, and edit instructions. Zero Moments is a valid answer when the recording does not support the brief.

JSON
{
  "analysis_id": "ana_...",
  "count": 1,
  "moments": [{
    "id": "mom_0123456789abcdef0123456789abcdef",
    "start_ms": 84210,
    "end_ms": 109440,
    "duration_ms": 25230,
    "score": 0.91,
    "title": "Repeated setup failure",
    "event_type": "highlight",
    "transcript_excerpt": "Why won't this connect?",
    "edit": {"mode": "continuous", "subclips": []}
  }]
}

Read render capabilities

GET /v1/render-presets returns the supported aspect ratios, output format, caption presets, bundled fonts, effects, and speaker-framing modes. auto is the default and keeps the person who is talking in frame. stacked is an explicit podcast-style choice, not automatic behavior. Read this response instead of hard-coding capability values.

POST

Create an optional render

Choose 1–20 Moment IDs from one completed analysis. ChatClipThat reuses the privately acquired source and creates one MP4 per Moment.

cURL
curl -X POST https://api.chatclipthat.com/v1/renders \
  -H "Authorization: Bearer $CCT_API_KEY" \
  -H "Idempotency-Key: render-001" \
  -H "Content-Type: application/json" \
  -d '{
    "analysis_id": "ana_...",
    "moment_ids": ["mom_..."],
    "render": {
      "output": {"format": "mp4", "aspect_ratio": "9:16"},
      "captions": {"enabled": true, "preset_id": "cct_clean_v1"},
      "framing": {"mode": "auto"}
    }
  }'

Private uploads

POST /v1/uploads accepts a plain filename, supported content type, and declared byte size. Video accepts video/mp4, video/quicktime, video/webm, and video/x-matroska. Audio accepts audio/mpeg, audio/mp3, audio/wav, audio/x-wav, audio/aiff, audio/aac, audio/ogg, audio/flac, audio/x-flac, audio/mp4, audio/x-m4a, audio/webm, and audio/opus. The response includes media_kind.

Send the raw bytes to the time-limited signed target using the returned upload_method and every entry in upload_headers, then call POST /v1/uploads/{upload_id}/complete. Only status: "ready" can start a scanner run. Completion probes the real stream layout. Attached MP3/M4A cover art is accepted but never decoded or sent to the model; malformed audio and any non-attached video stream fail closed.

Upload creation is not idempotent today. POST /v1/uploads does not honor Idempotency-Key. Persist its response immediately. If the outcome is ambiguous after a timeout, list recent uploads and reconcile before creating another ticket. Completion is safe to retry once the object exists because an already-ready upload returns its ready record.

The upload record’s expires_at is the deadline for using the signed PUT target. It is not the retention deadline for a source that already reached ready. Ready-source retention follows the account and deployment policy shown in Console → Usage.

Availability is explicit. A 503 uploads_unavailable response means this deployment cannot run production scanners. Trusted direct HTTPS URLs remain available only for the separate one-off video analysis flow.

Run multiple saved questions against one upload

A ready video or audio upload_id can be reused across scanner runs owned by the same account. The batch surface is machine/API-key only; the authenticated browser console shows resulting runs but does not create or cancel batches. Use POST /v1/scanner-batches to submit up to 100 scanner/upload pairs as one durable orchestration record. Each item still creates an independent scanner run, analysis lifecycle, and charge. Batch items analyze their full upload; use the single-run endpoint when a video find_moments request needs a selected time window.

cURL
curl -X POST https://api.chatclipthat.com/v1/scanner-batches \
  -H "Authorization: Bearer $CCT_API_KEY" \
  -H "Idempotency-Key: research-2026-08-15-01" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      {"client_id":"question-1","scanner_id":"scn_QUESTION_ONE","upload_id":"upl_YOUR_READY_UPLOAD","metadata":{"study":"activation"}},
      {"client_id":"question-2","scanner_id":"scn_QUESTION_TWO","upload_id":"upl_YOUR_READY_UPLOAD","metadata":{"study":"activation"}}
    ]
  }'

Idempotency-Key is required. Item client_id values must be unique within the request; the body is limited to 100 items and 512 KiB. The response is 202 Accepted with a durable batch_id plus per-item status, pinned scanner_version, and eventual run_id/analysis_id.

Fair scheduling means batch items may remain queued; no batch or run has an instant-start guarantee.

Rendering stays explicit. Video find_moments results expose Moment IDs that may be submitted to /v1/renders. Other scanner observations and all audio results are not renderable.

Billing is independent per item. Reusing an upload avoids transferring the bytes again, but does not share analysis work. Absent proof-run or refund effects, N scanner items over a source lasting D minutes consume N × ceil(D) analysis credits.

List, inspect, and cancel scanner batches

POST /v1/scanner-batches202Create 1–100 independently billed items. A stable Idempotency-Key is required.
GET /v1/scanner-batches200List at most 50 summaries per page with limit and opaque cursor; summaries do not embed items.
GET /v1/scanner-batches/{batch_id}200Read counters and at most 100 items per page with item_limit and numeric item_cursor.
POST /v1/scanner-batches/{batch_id}/cancel202Stop queued work from starting; already-spawned work drains to a terminal state.

Batch states are queued, processing, canceling, completed, partial, failed, or canceled. Item states are queued, processing, completed, failed, or canceled. Continue following items_next_cursor until it is null before treating the detail response as the complete item set.

Availability is deployment-gated. The public contract is stable, but a deployment can return 503 scanner_batch_service_unavailable until durable batch processing is enabled. Do not fall back by resubmitting the same work under new keys.

Signed completion webhooks

When event delivery is enabled, create a target with POST /v1/webhook-endpoints and subscribe to scanner-run, analysis, render, or upload terminal events. The signing secret is returned once. Verify the HMAC against the raw request body, reject stale timestamps, and deduplicate by event ID.

List delivery state at GET /v1/webhook-deliveries. If webhook service is unavailable for the account, poll run status every 5–10 seconds instead.

Errors

invalid_api_key401The bearer key is missing, invalid, or revoked.
private_upload_required400A production scanner run did not reference an owner-verified ready upload.
source_host_not_allowed422The URL is neither a built-in provider nor an exact trusted custom hostname.
scanner_not_found404The scanner is unknown or belongs to another account.
run_not_found404The run is unknown or belongs to another account.
analysis_not_ready409One-off Moments or rendering were requested before completion.
analysis_not_cancelable409The analysis has already started or reached a terminal state.
insufficient_credits402The shared account wallet cannot cover the operation.
dispatch_failed503The operation could not be queued.

Idempotency and retries

Idempotency is resource-specific; do not assume every POST supports it. Scanner creation, scanner updates, scanner-run creation, one-off analyses, and render creation accept Idempotency-Key. For those operations, retry the same request with the same key; reusing a key for a different request returns a conflict.

POST /v1/uploads and webhook-endpoint creation do not currently implement idempotency keys. Upload completion is naturally retry-safe after the object exists, but that behavior is based on the upload’s ready state rather than a request header.

Limits, source safety, and retention

Video scanner runs use owner-verified private uploads and default to an 8 GiB, 120 minute boundary. Audio defaults to 2 GiB and 120 minutes. Both allow at most 50 observations; deployments and accounts may set lower limits. Authoritative media probing happens before scanner execution.

One-off analysis uses the same acquisition pipeline as the app for built-in recorded-video providers. Arbitrary direct media remains limited to exact account-approved public DNS hostnames; private and loopback destinations are blocked. Read the authoritative byte, window, concurrency, and retention limits from GET /v1/account.

Audio is generation-pinned private input. Temporary hosted-provider media must be deleted before the run completes, and no public audio artifact is created. Request and response fields are allowlisted; raw source URLs, signed query strings, secrets, private frames, and renderer-internal identifiers are excluded from public control responses.

Use only recordings you are authorized to process. Private upload access, consent, retention, and downstream decisions remain your responsibility.