API, private preview

Run this from your own setup

Send a video URL, get back the same thing the site shows you: the score, the shots, who is in them, what is wrong and when, and what to change. Ask me for a key and I will turn it on.

Request access

This just opens your mail app. Or email sindri@johannsson.io

Submit a job

POST https://mhfxmaylqosurzzupfyy.supabase.co/functions/v1/api-score
Authorization: Bearer <api_key>
Content-Type: application/json
{
  "video_url": "https://example.com/shot.mp4",
  "references": [
    {
      "name": "ada",
      "image_urls": ["https://example.com/ada-1.png", "https://example.com/ada-2.png"]
    }
  ],
  "webhook_url": "https://your.app/hooks/consistency",
  "client_ref": "scene-014"
}

Response:

{
  "job_id": "1f0b…c7",
  "status_url": "https://mhfxmaylqosurzzupfyy.supabase.co/functions/v1/job-status?id=1f0b…c7"
}

Full example:

curl -X POST https://mhfxmaylqosurzzupfyy.supabase.co/functions/v1/api-score \
  -H "Authorization: Bearer $VC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "video_url": "https://example.com/shot.mp4",
    "references": [{ "name": "ada", "image_urls": ["https://example.com/ada-1.png"] }],
    "webhook_url": "https://your.app/hooks/consistency",
    "client_ref": "scene-014"
  }'

Read the result

The result is POSTed to webhook_url when the run finishes, and is always readable at:

GET https://mhfxmaylqosurzzupfyy.supabase.co/functions/v1/job-status?id=<job_id>
Authorization: Bearer <api_key>
{
  "id": "1f0b…c7",
  "status": "done",
  "result": {
    "overall": 0.74,
    "overall_spread": 0.03,
    "verdict": "Four shots need repair.",
    "shots": [{ "index": 3, "start": 8.4, "end": 12.9 }],
    "entities": [
      { "id": "maya", "name": "Maya", "kind": "person",
        "anchor": { "shot": 3, "t": 9.1 } }
    ],
    "findings": [
      {
        "id": "f_04",
        "category": "physical",
        "type": "wardrobe",
        "slot": "jacket",
        "severity": "high",
        "shot": 12,
        "entity": "maya",
        "t_start": 41.2,
        "t_end": 46.0,
        "description": "The jacket is brown and open; it was dark green and buttoned in shot 3.",
        "confidence": 0.81,
        "votes": { "runs": 3, "agree": 3 }
      }
    ],
    "fixes": [
      {
        "id": "x_02",
        "resolves": ["f_04"],
        "target": { "shot": 12, "start": 41.2, "end": 46.0 },
        "entities": ["maya"],
        "fix_text": "Shot 12, 0:41 to 0:46. …",
        "status": "open"
      }
    ],
    "meta": {
      "models": ["…"],
      "mode_bible": "agentic",
      "runs": 3,
      "tokens_in": 412000,
      "tokens_out": 18400,
      "cost_usd_estimate": 0.42
    }
  },
  "report_url": "https://…/report.html?token=…"
}

Fields

video_url
Required. https URL to the video, up to 5 GB. The analysis downloads it.
references
Optional. Reference images per character, as { name, image_urls[] }. When present they become the anchor instead of the first appearance.
webhook_url
Optional. Receives the finished result payload by POST.
client_ref
Optional string echoed back in the webhook payload.

Webhook payload

When the run finishes we POST this JSON to webhook_url:

{
  "job_id": "1f0b…c7",
  "status": "done",
  "client_ref": "scene-014",
  "result": { "overall": 0.74, "overall_spread": 0.03, "findings": [ … ], "fixes": [ … ] }
}

Rate limits and retention

5 active jobs
Per account on create-job (browser uploads). Exceeding it returns HTTP 429.
30 jobs / hour
Per API key on api-score. Exceeding it returns HTTP 429 with an error message.
5 GB max
Per video, for both uploads and video_url submissions.
7 day retention
Uploaded videos are deleted 7 days after the run.
90 day retention
Reports are deleted 90 days after the run.

Your API keys (preview)

Sign in and you can make keys here.

Sign in