Agent-Ready Video

Draft 1.0, proposed

Quick start: L1 in five minutes

Level L1 means crawlers and agents can read your video moments. A static site gets there with five plain files and no server code. Replace example.com and the example video with your own.

What you need: a site you can add files to, one video page, and one moment on that video (a start and end time in milliseconds).

What you get: a manifest, a catalog, JSON-LD Clips, llms.txt and an RSL license: every L1 surface in spec section 5.1.

1. Write /arv.json #

The catalog lists your videos (assets) and the moments on them. Name each moment by its range, arv:{asset_id}#t={start_ms},{end_ms}, and give it a deep link that opens the video at the start.

/arv.json
{
  "arv": "1.0",
  "origin": "https://example.com",
  "publisher": {
    "name": "Example Garage"
  },
  "assets": [
    {
      "arv": "1.0",
      "id": "vid_8f2c",
      "title": "Tuning a carburettor",
      "duration_ms": 1284000,
      "language": "en",
      "content_hash": "sha256:9b1e...",
      "creator": {
        "id": "cr_12",
        "name": "Example Garage",
        "url": "https://example.com"
      },
      "page_url": "https://example.com/videos/carb",
      "c2pa": {
        "credentials_url": "https://example.com/c2pa/vid_8f2c"
      },
      "extensions": {
        "com.mux": {
          "playback_id": "abc"
        }
      }
    }
  ],
  "moments": [
    {
      "arv": "1.0",
      "id": "mom_rg2e4kdenkmkzilboihacumshn",
      "moment_uri": "arv:vid_8f2c#t=12400,31000",
      "moment_url": "https://example.com/videos/carb#t=12.4,31",
      "asset_id": "vid_8f2c",
      "start_ms": 12400,
      "end_ms": 31000,
      "snap": "sentence",
      "title": "Setting the idle mixture screw",
      "evidence": [
        {
          "type": "transcript",
          "start_ms": 12900,
          "end_ms": 18200,
          "text": "Turn the mixture screw a quarter turn out",
          "confidence": 0.93
        },
        {
          "type": "shot_caption",
          "start_ms": 14000,
          "end_ms": 22000,
          "text": "Close-up of a screwdriver on the idle screw",
          "confidence": 0.81
        }
      ],
      "evidence_grade": "C",
      "confidence": {
        "score": 0.84,
        "band": "high"
      },
      "aliases": [
        "amt_2b7c..."
      ]
    }
  ]
}

The moment id is not random. It is mom_ plus the first 26 characters of the lowercase base32 SHA-256 of {origin}|{asset_id}|{start_ms}|{end_ms}, so anyone can check it. Compute yours:

moment-id.mjs
import { createHash } from "node:crypto";
const momentId = (origin, assetId, startMs, endMs) => {
  const hash = createHash("sha256").update(`${origin}|${assetId}|${startMs}|${endMs}`).digest();
  const abc = "abcdefghijklmnopqrstuvwxyz234567";
  let bits = "", out = "";
  for (const byte of hash) bits += byte.toString(2).padStart(8, "0");
  for (let i = 0; i + 5 <= bits.length; i += 5) out += abc[parseInt(bits.slice(i, i + 5), 2)];
  return "mom_" + out.slice(0, 26);
};
console.log(momentId("https://example.com", "vid_8f2c", 12400, 31000));
// mom_rg2e4kdenkmkzilboihacumshn
moment_id.py
import base64, hashlib

def moment_id(origin, asset_id, start_ms, end_ms):
    raw = f"{origin}|{asset_id}|{start_ms}|{end_ms}".encode()
    b32 = base64.b32encode(hashlib.sha256(raw).digest()).decode().rstrip("=").lower()
    return "mom_" + b32[:26]

print(moment_id("https://example.com", "vid_8f2c", 12400, 31000))
# mom_rg2e4kdenkmkzilboihacumshn

2. Add /.well-known/arv #

The manifest is where validators and agents start. At L1 it names your level, your license and your catalogs. Leave out tool profiles and keys until you offer them.

/.well-known/arv
{
  "arv": "1.0",
  "conformance_level": "L1",
  "publisher": {
    "name": "Example Garage",
    "contact": "agents@example.com"
  },
  "license_url": "https://example.com/license.xml",
  "catalogs": [
    "https://example.com/arv.json"
  ],
  "schema": "https://agentreadyvideo.org/schema/1.0/manifest.schema.json"
}

The file has no extension, so tell your host to serve it as application/json. On Vercel that is a headers entry in vercel.json; on Netlify, a _headers line; on GitHub Pages, keep a .nojekyll file so the .well-known folder is published.

3. Add JSON-LD Clip ids to the video page #

On each video page, describe the video as a schema.org VideoObject and each moment as a Clip. The Clip @id must equal the moment_uri and its url must equal the moment_url, so crawlers and agents name the same moment.

/videos/carb (inside <head>)
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "VideoObject",
  "@id": "https://example.com/videos/carb#video",
  "name": "Tuning a carburettor",
  "description": "How to set the idle mixture screw and check the idle speed.",
  "thumbnailUrl": "https://example.com/poster/vid_8f2c.jpg",
  "uploadDate": "2026-09-01",
  "duration": "PT21M24S",
  "hasPart": [
    {
      "@type": "Clip",
      "@id": "arv:vid_8f2c#t=12400,31000",
      "name": "Setting the idle mixture screw",
      "startOffset": 12.4,
      "endOffset": 31,
      "url": "https://example.com/videos/carb#t=12.4,31"
    }
  ]
}
</script>

Do not put a stream or media file URL in this markup. Discovery finds moments; only a playback call starts them (forbidden-field rule). Emit a Clip only for moments whose rights allow segment display.

4. Add llms.txt and an RSL line #

Point language models at the manifest with llms.txt, then publish your license terms in RSL 1.0 and link them from robots.txt next to your sitemap.

/llms.txt
# Example Garage videos

> How-to videos on engine repair. Agent-Ready Video (ARV) level L1.

## What this is

- Every moment is named arv:{asset_id}#t={start_ms},{end_ms} and listed in the catalog.

## Manifest

- https://example.com/.well-known/arv

## Tools

- None yet. Moments are listed in https://example.com/arv.json

## License

- https://example.com/license.xml (RSL 1.0). Credit Example Garage when you show a moment.

## Example questions

- How do I set the idle mixture screw on a carburettor?
/robots.txt
User-agent: *
Allow: /
License: https://example.com/license.xml
Sitemap: https://example.com/sitemap.xml
/license.xml
<rsl xmlns="https://rslstandard.org/rsl">
  <content url="/">
    <license>
      <permits type="usage">search ai-input ai-index</permits>
      <prohibits type="usage">ai-train</prohibits>
      <payment type="attribution"/>
    </license>
  </content>
</rsl>

Use only RSL 1.0 usage words. Your sitemap must list the video page, and the page must return 200.

5. Run the validator coming #

The validator is not released yet. When it ships, one command runs the eight L1 checks against your live site or a local folder and prints pass or fail per standard.

Planned usage
npx arv validate https://example.com --level L1

Until then, check by hand: /.well-known/arv returns JSON, every moment has start_ms < end_ms inside duration_ms, each id recomputes, and the Clip @id values match the catalog. The full list is in spec section 5.1.

Next steps #

L2

Make it callable

Add an MCP endpoint with the origin profile and short-lived playback tokens.

L2 checks

L3

Add rights and a ledger

Publish keys, sign playback tokens and usage receipts, and pass the negative tests.

L3 checks

List it

Tell us it works

Built something that follows ARV? We list it on the implementations page once it passes.

List yours

Stuck or something unclear? Open an issue on GitHub or email team@agentreadyvideo.org.