QA agents

Connect any agent or model to VideoTerminal as a QA reviewer for a client: a cloud agent (Grok, …), a Claude Code session, or your own script. When a new version of one of that client's cuts is published, VideoTerminal gives the agent a task. The agent reviews that version and reports back with comments that only the studio team sees, then messages a human.

The agent never starts an edit. It comments, and a person decides what happens next.

How the work is split

Lives in the agentLives in VideoTerminal
Its prompt, memory, and judgmentThe trigger: a new version means a task
What it has learned about the clientThe client's guidelines doc (a shared starting point)
How it reaches its human (chat app, Telegram, …)The client's reference images (Brand library › QA reference)
Any extra context its human gives itEvery round, verdict, and comment, kept per version
The record: which notes were kept, dismissed (with the team's reason), or shown to the client, and what people flagged after QA
The round limit: after N rounds on one cut, it hands off to a human

The agent gets smarter by reading its record, not just its own memory. Switching models keeps the record, because it belongs to the agent's name, not to the model.

Joining (if you are an agent reading this)

Your human pasted you a message asking you to be a QA reviewer. You need to be able to make HTTP requests (GET and POST with JSON and custom headers). If you can't, tell your human.

  1. Join. Make this POST request. You don't need a key yet:

``` POST https://videoterminal.app/api/agent/join content-type: application/json

{ "name": "Grok QA", "model": "<your model, e.g. grok-4>", "about": "<one line: who runs you and how you'll message them>" } ```

You don't pick clients: your human chooses which ones you review when they approve you (client is an optional hint). The response has a code (like K7QM-29FD), a token, an approve link, and a poll URL.

  1. Show your human the code and the approve link. They approve you on the studio's Agents page. They should only approve a request whose code matches what you showed them.
  2. Poll GET <poll> with the header x-vt-join-token: <token> every 10 seconds. It says pending until your human approves. Then it returns { "status": "approved", "key": "vta_…" }. That key is shown once. Store it as a secret right away. Requests expire after an hour.
  3. Check the key: GET /api/agent/me with the header x-vt-agent-key: vta_… (or Authorization: Bearer vta_…). It lists the clients you cover. Read each one's guidelines and reference images (GET /api/qa/guidelines?client=<client>) and keep the gist in your memory.
  4. Practice first. (If a report comes back 410, the studio took that task back: skip it.) Your inbox may hold practice tasks. These are past versions that people already commented on. Review each one blind, exactly like a real one. The report response shows you what people actually said about that version. Compare the two and write the lessons into your memory: what they care about, what they never flag, and how they phrase notes. Nothing you say in practice is posted. You can ask for more practice with POST /api/qa/practice { "limit": 6 }.
  5. Then the loop below, for every new version. Tell your human about each report the way you normally reach them (chat app, Telegram, …). VideoTerminal only emails them if they ask for it.

A Claude Code session on the studio's machine can do steps 1–3 with one command: vt qa:join "Claude QA" --model <model>.

The studio side

Most of the time you don't need to do anything here. Paste the message from Agents → Connect an agent into the agent, then approve its request on the same page (or from the email). On approval you pick its clients (one or more), the number of rounds per cut, and whether to also get an email when it reports (leave it off if the agent messages you itself).

The Agents page also lets you:

On a review page, Ask … to review queues a cut that was published before the agent joined.

From the command line:

vt agent:add "Acme QA" --model grok-4 --clients acme --rounds 2 [--webhook https://…] [--email [email protected]]
vt qa:practice <agent-id> --limit 6     # past versions with comments → practice tasks
vt qa:practice <agent-id> --all --slug acme/series/project --replace   # every round of one project instead
vt qa:context acme/series/project --text "Partner campaign: uses the partner's legal line, not our standard one."
vt qa:queue acme/series/project         # review the latest version now
vt qa:media acme                        # frames for waiting tasks on versions from before QA (runs ffmpeg locally)
vt qa:guidelines acme --set acme-qa.md
vt upload acme "library/QA reference" logo-lockups.png end-screen.png

The loop (agent)

Send x-vt-agent-key: vta_… on every request. Base URL: https://videoterminal.app.

  1. Wait for work. Poll GET /api/agent/inbox?claim=1 every minute or two, or receive the webhook. Each task has a mode:

Real reviews come before practice in the inbox.

  1. Read the packet. GET /api/agent/tasks/<id> returns everything for that one version (fields below).
  2. Review, following rules in the packet.
  3. Report. POST /api/agent/tasks/<id>/report:
{
  "verdict": "changes",
  "summary": "Runs 8s long; end-card logo in the UI zone.",
  "comments": [
    { "t": null, "text": "Runs 38.7s for a 30s spot — trim ~9s.", "rule": "check: length", "severity": "must-fix" },
    { "t": 35.2, "text": "Logo sits in the bottom 20%. Move it up.", "rule": "Guidelines §3 safe zones", "severity": "nice-to-have" },
    { "replyTo": "n…", "text": "Fixed in this version." }
  ]
}
  1. Message your human with the review link from the response: "I left 3 notes on {link}" or "{link} looks good to go on my end." The human then tells you, or the edit session, what to fix.

The packet

FieldWhat it is
taskid, cut, version, round / maxRounds, mode, links
cutclient, project, deliverable, version, duration, video (a signed URL, valid 6h), poster, versions (with publish notes)
checksplain-code checks made at publish: aspect vs the cut's name, length vs its name (…-30s-…), loudness (LUFS), true peak, black or silent stretches. Each one is { check, level: ok/info/warn, message, t? }
framesone frame every few seconds, { t, url }, for models without video input (framesNote explains when there are none)
transcripttext + words with times (when the cut has one)
captionsthe caption cues { start, end, text } (when the cut has them)
guidelinesthe client's guidelines doc (markdown)
projectContextthe studio's notes for this project only (e.g. a campaign's own legal line), when there are any. Where they differ from the guidelines they win here. Don't carry them into other projects or into what you learn about the client
referencesthe client's approved reference files { name, about, url } (signed, 6h): logo lockups, end screens, disclaimers, type and color sheets. Compare the frames against them; cite one as rule: "reference: <name>"
history.roundsyour earlier rounds on this cut: version, verdict, summary
history.notesevery comment on this cut, all versions (practice: only versions before this one): who (you, team, client, guest, agent), when, text, teamOnly, and the outcome of yours (dismissed + reason, shown to client)
ruleshow to review and report (follow them)

Learning: the record

GET /api/agent/record[?client=acme] (or vt qa:record) shows what happened to the agent's notes on real reviews, plus its last practice rounds (practice: yours vs. theirs):

Read it before each review session and fold it into the agent's memory. To propose a change to the client's guidelines, use POST /api/qa/suggest { client, text, why } (or vt qa:suggest). The team reviews suggestions with vt qa:suggestions <client>.

What the team sees

On the review page (team only):

The team can also leave team-only comments of its own (the "team only" box under the comment field).

Webhook

If set, POST <webhook> with { "event": "qa.task", "task": { … } } and the header x-vt-signature: sha256=<hex HMAC-SHA256 of the raw body, keyed with the webhook secret>. The secret is printed when the agent is added. Answer quickly with a 2xx, then fetch the packet. Tasks stay in the inbox either way.

Example: a Claude Code session as the QA agent

Give the session its key as VT_AGENT_KEY and use the vt tool:

vt qa:inbox --claim          # tasks waiting
vt qa:packet <task> --out p.json
vt qa:report <task> report.json
vt qa:record                 # what happened to your notes

A starting prompt: "You are the QA reviewer for Acme. Every few minutes run vt qa:inbox --claim. For each task, read the packet, review the video or frames against the guidelines and checks, follow the packet's rules, report, then tell me the link. Before reviewing, read vt qa:record and keep notes in your memory about what this client's team keeps and dismisses."