Skip to content

Analysis

Use stored analysis profiles and report templates to produce typed answers and presentation reports.

This guide shows you how to store an analysis profile and report template, run an analysis, validate the typed answers, read the automatically rendered report, and explicitly re-render a new presentation when needed.

Analysis runs produce typed answers. Reports are presentation outputs created from completed answers. The API keeps these concerns separate, so rendering or re-rendering a report does not inspect the video again.

The task-first path is: store an analysis profile, store a report template, start a kind: analysis run with both resource IDs and optional versions, poll the run, read and validate the named answers, read the automatically rendered artifact, and re-render only when a different presentation is needed. The profile defines what the run produces; the report template only projects the stored answers.

Analysis Profiles

An analysis profile contains instructions and an ordered answer_specs list. Each answer spec has an answer_key, answer_type, fields, and evidence requirements. The analysis profile is the only customer prompt surface. Its instructions steer both what the run observes in the video and how answers are synthesized. Each field's description steers how that field's answer is synthesized. Store a reusable profile:

curl --fail-with-body -X POST "$BASE_URL/api/v1alpha1/profiles" \
  -H "Authorization: Bearer $DEEPFRAME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: profile-create-01" \
  -d '{
    "name": "Inspection timeline",
    "instructions": "Segment the complete video into evidence-backed activity windows.",
    "answer_specs": [
      {
        "answer_key": "inspection_timeline",
        "answer_type": "timeline",
        "temporal_scope": "windowed",
        "temporal_granularity": "semantic_segment",
        "aggregation_level": "video",
        "coverage": "complete",
        "fields": [
          {"key":"activity","label":"Activity","type":"string","description":"The observable activity in this segment.","required":true}
        ],
        "evidence": {"required":true,"per_field":true,"accepted_types":["clip","frame"]}
      }
    ]
  }'

The evidence contract is checked when the profile is created. For a timeline profile, evidence.required must be true and evidence.per_field must be true; this admitted shape is enforced before the profile is stored. A well-formed profile with another evidence shape receives 422 unprocessable-entity with the failed admission_constant and the admitted shape. Use the public evidence values listed below.

Profiles are versioned. POST /profiles/{profile_id}/versions appends an immutable version. GET /profiles/{profile_id}/versions lists versions; omitting profile_version selects latest. DELETE /profiles/{profile_id} archives the profile. It does not delete versions or break past runs.

Why IDs And Versions Matter

POST /api/v1alpha1/runs accepts only stored profile_id and report_template_id references for analysis. Add profile_version and report_template_version when a run must use an exact immutable revision. If a version is omitted, the server resolves latest at submission and records the resolved version in the run snapshot. This keeps a completed run reproducible even after a resource receives a new version.

The API version in /api/v1alpha1 identifies the public contract revision. Resource versions identify the exact analysis instructions, answer specifications, and presentation selected within that contract. Neither version is a model or customer-specific branch.

From A Field To A Named Answer

answer_specs[].answer_key names one answer. answer_specs[].fields[].key names a typed field inside that answer. A report block's answer_key selects the named answer, while a columns[].key selects an existing field for presentation. They are different namespaces:

Contract locationExampleMeaning
answer_specs[].answer_keyinspection_timelineThe named answer produced by the run.
answer_specs[].fields[].keyactivityA typed field inside each timeline event.
Report block answer_keyinspection_timelineThe answer consumed by the block.
Report block columns[].keyactivityAn existing answer field selected for display.

The answers collection exposes the same named answer as its top-level key. A report cannot define a field that the selected answer did not produce.

For timeline answers, answer_specs[].fields[].key is a verbatim identifier. The same key is returned in each timeline attribute and must be used unchanged in report columns and client code. A field label is display text; it does not rename the key. Do not rely on synonyms or automatic case conversion.

Report Templates

Report templates define the presentation blocks and labels for answer keys. Post a named template to /api/v1alpha1/report-templates. Versions are immutable, and DELETE /report-templates/{report_template_id} archives the head.

{
  "name": "Inspection timeline",
  "language": "en",
  "blocks": [
    {"type":"timeline","answer_key":"inspection_timeline","label":"Activity timeline","columns":[{"key":"activity","label":"Activity"}]}
  ]
}

Use POST /report-templates/{report_template_id}/versions to append a version. Save the id and immutable version returned by the create request. A run references that stored template and version. To render a new presentation without reanalysis, use the separate rerender endpoint described in Reports And Templates.

Analysis Admission

The OpenAPI schema lists the public request vocabulary:

FieldValues in the request schema
answer_typeverdict, measurement, enumeration, timeline, comparison, retrieval, synthesis
evidence.accepted_typesclip, frame, ocr_span, transcript_span, scene_description
coverageretrieved_evidence, selected_range, representative, complete
temporal_scopeinstantaneous, windowed, cumulative, terminal
temporal_granularityinstant, window, semantic_segment
aggregation_leveloccurrence, video, run, project

These enums are deliberately wider than the admitted language. The current public examples establish these admitted combinations:

Answer typePublicly admitted example
timelinecoverage: complete, temporal_scope: windowed, temporal_granularity: semantic_segment, aggregation_level: video, and clip plus frame evidence.
synthesiscoverage: complete, aggregation_level: video, with clip plus scene_description evidence. Temporal fields are omitted.
verdict, measurement, enumeration, comparison, retrievalNo additional combination is promised by this alpha revision. Do not infer an admitted Cartesian product from the schema enums.

If a combination is well formed but not admitted, the submit response is 422 unprocessable-entity. The problem body identifies the failed capability in admission_constant, carries the relevant answer_key when available, and may carry admitted_values. For example, ANALYSIS_ADMISSION_CAPABILITIES.timeline.coverage can reject a timeline coverage value and return the accepted value complete. Correct the profile rather than retrying the same request unchanged.

Keep the vocabularies separate: the current run kind values are workflow and analysis; admitted answer types for this tutorial are timeline and synthesis; report block types are timeline, narrative, table, value, and json. audit and the findings-to-report adapter remain planned, so do not submit kind: audit.

Start An Analysis Run

Use completed, searchable source_ids, one stored profile reference, one stored report-template reference, formats, and the admitted execution_profile_ref. premium-analysis@1 is the highest currently served execution tier. Treat it as an opaque public reference and do not depend on internal implementation names. Inline profiles and inline report templates are not accepted on this endpoint. The following request uses the stored resources created above; replace the example IDs with the IDs returned by those create requests:

curl --fail-with-body -X POST "$BASE_URL/api/v1alpha1/runs" \
  -H "Authorization: Bearer $DEEPFRAME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: analysis-run-create-01" \
  -d '{
    "kind": "analysis",
    "source_ids": ["vid_0190b5d4-7e1f-7a2b-9c3d-1234567890ab"],
    "profile_id": "prof_0190b5d4-7e1f-7a2b-9c3d-1234567890ab",
    "profile_version": 1,
    "report_template_id": "tpl_0190b5d4-7e1f-7a2b-9c3d-1234567890ab",
    "report_template_version": 1,
    "formats": ["json", "markdown"],
    "execution_profile_ref": "premium-analysis@1",
    "execute_now": true
  }'

The 202 response confirms acceptance. Poll GET /api/v1alpha1/runs/{run_id} until the top-level status is terminal. Then validate the run and answer result state before using the automatically rendered report. The run pins the selected profile and report-template versions; the analysis detail records the exact IDs and versions used.

Read Answers

curl --fail-with-body --get "$BASE_URL/api/v1alpha1/runs/$RUN_ID/answers" \
  -H "Authorization: Bearer $DEEPFRAME_API_KEY" \
  --data-urlencode "expand[]=events" \
  --data-urlencode "expand[]=events.evidence"

Answers are typed by answer_type and preserve coverage, result_state, limitations, public provenance, and evidence references. The request above asks for both events and events.evidence, so it can return the expanded timeline event and its event-level evidence; without those query values, the page may contain only the summary projection. Analysis answers use VIEW_INSIGHTS. Do not use answer values until their semantic result state and evidence pass the gate in Runs And Status.

For a timeline, the model selects semantic event bounds from the profile instructions and indexed evidence:

{
  "key": "inspection_timeline",
  "answer_payload": {
    "events": [
      {
        "event_id": "event_0190b5d4-01",
        "label": "Activity",
        "start_ms": 1200,
        "end_ms": 4300,
        "attributes": [
          {"key": "activity", "label": "Activity", "value": "observable activity", "evidence": [{"id": "evidence_0190b5d4-7e1f-7a2b-9c3d-1234567890ab"}]}
        ],
        "evidence": [{"id": "evidence_0190b5d4-7e1f-7a2b-9c3d-1234567890ab"}],
        "result_state": "found"
      }
    ],
    "coverage": {"state": "complete", "scope": "full_video"},
    "result_state": "found",
    "limitations": []
  }
}

The start_ms and end_ms values are AI-selected semantic intervals, not internal processing chunks. DeepFrame may process media in implementation chunks, but those windows are not user-facing events. Do not submit timestamp ranges or a fixed event count; describe the event in the profile instructions. If a boundary is unclear, preserve uncertain or a limitation rather than inventing precision. Keep found, not_found, uncertain, and not_checked distinct in the answer and report artifact.

Read And Re-render Reports

The stored report template is the presentation input captured by the run. When the analysis run completes, the API automatically renders the requested formats and persists the artifacts. After the semantic result gate passes, list and read those artifacts. The automatic render does not re-run analysis.

curl --fail-with-body --get "$BASE_URL/api/v1alpha1/reports" \
  -H "Authorization: Bearer $DEEPFRAME_API_KEY" \
  --data-urlencode "run_id=$RUN_ID" \
  --data-urlencode "expand[]=manifest"

curl --fail-with-body --get "$BASE_URL/api/v1alpha1/reports/$REPORT_ID" \
  -H "Authorization: Bearer $DEEPFRAME_API_KEY" \
  --data-urlencode "expand[]=content" \
  --data-urlencode "expand[]=manifest"

GET /api/v1alpha1/reports lists persisted artifacts for the required run_id; it does not create them. Retrieve one with GET /api/v1alpha1/reports/{report_id}. Use expand[]=content for the rendered artifact and expand[]=manifest for its trust bindings. If the list is empty, inspect the run and its limitations; do not create a first-view report as a fallback.

To create a different presentation from the same stored answers, use the explicit re-render endpoint:

curl --fail-with-body -X POST "$BASE_URL/api/v1alpha1/runs/$RUN_ID/reports" \
  -H "Authorization: Bearer $DEEPFRAME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: report-rerender-01" \
  -d '{
    "report_template": {
      "language": "en",
      "blocks": [
        {"type":"timeline","answer_key":"inspection_timeline","label":"Activity timeline","columns":[{"key":"activity","label":"Activity"}]}
      ]
    },
    "formats": ["json", "markdown"]
  }'

The explicit re-render returns 201 Created. It reads stored answers and changes presentation only; it does not re-run analysis. Do not use re-rendering to turn uncertain, not-checked, not-found, or partial answer data into a successful result. Before report use, check the manifest bindings for result_state, limitations, evidence_refs, evidence_urls, coverage_ref, and provenance_refs. Preserve these fields when you store or export the report.

The rendered JSON artifact carries the selected field (other document metadata is omitted here):

{
  "answerKey": "inspection_timeline",
  "columns": [{"key":"activity","label":"Activity"}],
  "events": [{"fields":[{"key":"activity","label":"Activity","value":"observable activity"}]}]
}

Its trust-manifest binding points from /blocks/0/events/0/fields/0/value to events/event_0190b5d4-01/attributes/activity with result_state: "found", empty limitations, and the event evidence reference.

Analysis answers attach evidence refs to each timeline event. When a ref carries both absolute start_ms and end_ms bounds, the answer response also returns the clip-relative offset_start_ms and offset_end_ms values in milliseconds. An offset is the event bound minus the evidence clip start, clamped to the clip duration. Older answers omit these fields, and there is no backfill; rerun the analysis to create them.

Per-event evidence is returned only when the request asks for it. Read the answers with expand[]=events and expand[]=events.evidence. The expand[]=evidence value alone returns the answer-level representative clips, which carry absolute bounds but no offsets and no fragment. The accepted values are events, events.evidence, evidence, and provenance.

The media URL in an answer response can end with a client-side #t=start,end fragment in seconds. The fragment is appended after signing, so it does not alter the signed query or HTTP range requests. Do not replace it with a query parameter.

For a zero-length point claim the fragment is #t=7,7. Do not shorten it to #t=7, which means play from 7 seconds to the end of the clip.

JSON report artifacts keep numeric clip-relative offsets and evidence spans. Markdown report artifacts include evidence links for timeline events: the stored Markdown body uses a deterministic placeholder, and the artifact read response replaces resolvable placeholders with fresh signed URLs. An unresolved evidence ref stays readable without a link.

On this page