{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/hookdetector-com/json-schema/hookdetector-com-run-out-schema.json",
  "title": "RunOut",
  "x-generated": "2026-11-11",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/hookdetector-com-openapi.json#/components/schemas/RunOut",
  "properties": {
    "run_id": {
      "type": "string",
      "title": "Run Id"
    },
    "kind": {
      "type": "string",
      "title": "Kind",
      "description": "what the run researches: \"short\" (hooks from TikTok, Instagram and YouTube Shorts, POST /v1/research), \"long\" (how long-form YouTube outliers open, POST /v1/longform) or \"packaging\" (their titles and thumbnails, POST /v1/packaging). Every run from before 2026-10-07 is short",
      "default": "short"
    },
    "topic": {
      "type": "string",
      "title": "Topic"
    },
    "requested": {
      "type": "integer",
      "title": "Requested"
    },
    "language": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Language",
      "description": "the language or dialect asked for, as sent, or null for any language"
    },
    "country": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Country",
      "description": "the country asked for, as an ISO 3166-1 alpha-2 code (upper-cased, UK read as GB), or null for any country"
    },
    "status": {
      "anyOf": [
        {
          "type": "string",
          "enum": [
            "queued",
            "running",
            "done",
            "failed"
          ]
        },
        {
          "type": "string"
        }
      ],
      "title": "Status"
    },
    "error": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Error",
      "description": "on a failed run: why, ending with the request id"
    },
    "reserved": {
      "type": "integer",
      "title": "Reserved",
      "description": "credits held for this run when it started; what it does not use is refunded when it finishes"
    },
    "charged": {
      "type": "integer",
      "title": "Charged",
      "description": "credits deducted from your balance for this run: what it used, never more than `reserved`; 0 while it runs, on a failed run, and on a run that found no hooks within the account's allowance of free empty runs (three a day, 500 credits in all; `outcome` says which)"
    },
    "credits_ig": {
      "type": "integer",
      "title": "Credits Ig",
      "description": "Instagram provider credits used so far, including on a refunded run; what you pay is `charged`"
    },
    "credits_tt": {
      "type": "integer",
      "title": "Credits Tt",
      "description": "TikTok provider credits used so far, including on a refunded run; what you pay is `charged`"
    },
    "credits_yt": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Credits Yt",
      "description": "of credits_tt, the provider credits that went to YouTube Shorts (since 2026-10-01; null on older runs); already inside credits_tt, never added again; what you pay is `charged`"
    },
    "posted_within_days": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Posted Within Days",
      "description": "the recency window asked for (7, 30, 90 or 365 days), or null"
    },
    "seed_hook_ids": {
      "anyOf": [
        {
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "title": "Seed Hook Ids",
      "description": "the seed hooks asked for, or null"
    },
    "platforms": {
      "anyOf": [
        {
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "title": "Platforms",
      "description": "the platforms this Short form run searches and delivers from (\"tiktok\", \"instagram\", \"youtube\"): the ones asked for, or all three; null on a long or packaging run"
    },
    "stats": {
      "$ref": "#/$defs/RunStats",
      "description": "how a finished run went; {} until it finishes. Keys can be added; read the ones you know"
    },
    "progress": {
      "$ref": "#/$defs/ProgressOut"
    },
    "created_at": {
      "type": "string",
      "title": "Created At"
    },
    "finished_at": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Finished At"
    },
    "request_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Request Id",
      "description": "the X-Request-Id of the call that started it"
    },
    "conversation_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Conversation Id",
      "description": "the chat conversation this run belongs to, or null for a run started outside one"
    },
    "hooks": {
      "items": {
        "$ref": "#/$defs/HookOut"
      },
      "type": "array",
      "title": "Hooks",
      "description": "the main deck: clips where a person on camera is talking, ranked. Since 2026-09-29 it never holds a clip without talking; a run that found fewer talking heads than asked says so in `outcome`"
    },
    "bonus": {
      "items": {
        "$ref": "#/$defs/HookOut"
      },
      "type": "array",
      "title": "Bonus",
      "description": "always empty. Bonus (clips without a person talking) was removed on 2026-10-08; the key stays so existing clients keep working.",
      "default": []
    },
    "outcome": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Outcome",
      "description": "on a finished run that delivered fewer hooks than asked for (or none): one plain paragraph on why, what to try next, and whether an empty run was free. On a full run whose hooks under the view floor are there because the country filter set most clips aside: that one sentence, with no advice. On a run that asked for a kind of opening, one more sentence: how many hooks open that way. null on any other run"
    },
    "patterns": {
      "anyOf": [
        {
          "items": {
            "$ref": "#/$defs/PatternOut"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "title": "Patterns",
      "description": "since 2026-10-01: up to 3 opening structures SHARED by at least two of this deck's hooks (a device, never a topic), from the delivered deck (`hooks`), grouped by `hook_type` in code since 2026-10-02 and computed before the run is marked done, in the deck's language, the most shared first and then by the summed virality of their hooks: each `name`, `template` (the structure with {placeholders}), `why` and `hook_ids` (the delivered hooks using it) with `count` (always 2 or more). One cheap model call names the groups; null on older runs, when the call failed on a deck without hook types, and when no structure is shared (then `stats.patterns_note` says so in one line)."
    },
    "understood": {
      "anyOf": [
        {
          "$ref": "#/$defs/UnderstoodOut"
        },
        {
          "type": "null"
        }
      ],
      "description": "since 2026-10-04: what the request was understood to ask, read before the first search: the subject searched and judged (hook words such as \"controversial hooks about\" taken out), its language and country, its core and audience, the neighbours searched beside it, the kinds of opening asked (styles, style_other) and the content asked (wants). Null on older runs and until it is read."
    },
    "options": {
      "anyOf": [
        {
          "additionalProperties": true,
          "type": "object"
        },
        {
          "type": "null"
        }
      ],
      "title": "Options",
      "description": "long and packaging runs only: the options the request set (min_outlier, intro_seconds, openers, ideas); {} when it set none"
    },
    "finds": {
      "anyOf": [
        {
          "items": {
            "$ref": "#/$defs/FindOut"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "title": "Finds",
      "description": "long and packaging runs only (since 2026-10-07): the run's results, ranked, each a real long-form YouTube video that beat its own channel's usual views (`outlier`, never under the run's min_outlier) and was posted in 2023 or later. On a long run each carries the video's opening as it was said (`hook`, `intro`) and why it holds; on a packaging run, what its title and thumbnail do together. When the topic was a video's address the one find is that video, whatever its outlier. [] until the run is done: while it runs, the finds written so far are in `progress.items`, as cards"
    },
    "report": {
      "anyOf": [
        {
          "additionalProperties": true,
          "type": "object"
        },
        {
          "type": "null"
        }
      ],
      "title": "Report",
      "description": "long and packaging runs only, once done; null until then. Long: `patterns`, the opening types at least two of the finds share (hook_type, count, video_ids, template, example); `shape`, how the openings are built, counted from the finds (videos, mapped, hook_seconds, how long the hook runs, and hook_words as medians, beats: each beat with the count of openings using it and the median second it starts_at, and sequence: the order of beats most of them share); and `openers` (hook, then, hook_type, based_on: openings WRITTEN for the subject from those structures, always labelled by `openers_label`, \"Written by Hook Detector from the structure of these real openings\", and never counted as a find). Packaging: `title_patterns` and `thumbnail_patterns` (each name, template or description, why, video_ids, count: what at least two of the packages share), `ideas` (title, thumbnail, thumbnail_text, pattern, based_on: packages WRITTEN for the subject from those patterns, always labelled by `ideas_label`, \"Written by Hook Detector from the patterns in these real packages\", and never counted as a find; an idea over 70 characters, with more than 5 words on its thumbnail, or one that repeats a real title is dropped), `numbers` (the packages counted: title_chars and title_words as medians, titles_with_number, titles_with_question, titles_cut_on_phone (with phone_title_chars, the approximate length a phone cuts a title at), thumbnails_with_text and thumbnail_words over the `analysed` ones, and the title_devices, thumbnail_devices and emotions that repeat, each name and count; and age_cohorts: the same counts for packages posted in the last split_days days (recent) and for older ones, with the undated counted apart) and `playbook_note` (one line when the patterns could not be written). Both kinds: `landscape`, the subject at a glance, counted from the videos the run held against their own channel (videos, channels, bar, outliers and their share, strong, breakouts, small_channel_outliers, recent_outliers, median_views, and beat_2x, beat_5x and beat_10x: how many beat their channel by that much whatever bar the run asked for); null when none was measured"
    }
  },
  "additionalProperties": true,
  "type": "object",
  "required": [
    "run_id",
    "topic",
    "requested",
    "status",
    "error",
    "reserved",
    "charged",
    "credits_ig",
    "credits_tt",
    "stats",
    "progress",
    "created_at",
    "finished_at",
    "request_id",
    "conversation_id",
    "hooks"
  ],
  "$defs": {
    "FindOut": {
      "properties": {
        "find_id": {
          "type": "string",
          "format": "uuid",
          "title": "Find Id",
          "description": "the id to keep, reject or read it by; absent on an item of `progress.items`"
        },
        "run_id": {
          "type": "string",
          "format": "uuid",
          "title": "Run Id",
          "description": "the run that found it"
        },
        "kind": {
          "type": "string",
          "enum": [
            "long",
            "packaging"
          ],
          "title": "Kind",
          "description": "long or packaging"
        },
        "rank": {
          "type": "integer",
          "title": "Rank",
          "description": "its place in the run, from 1: the strongest outlier first"
        },
        "verdict": {
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "keep",
            "reject",
            null
          ],
          "title": "Verdict",
          "description": "keep, reject or null"
        },
        "video_id": {
          "type": "string",
          "title": "Video Id",
          "description": "the YouTube video id"
        },
        "platform": {
          "type": "string",
          "title": "Platform",
          "description": "always \"youtube\""
        },
        "title": {
          "type": "string",
          "title": "Title",
          "description": "the video's title as YouTube shows it, word for word (runs of spaces read as one)"
        },
        "channel": {
          "type": [
            "string",
            "null"
          ],
          "title": "Channel",
          "description": "the channel's name"
        },
        "channel_handle": {
          "type": [
            "string",
            "null"
          ],
          "title": "Channel Handle",
          "description": "the channel's @handle, without the @"
        },
        "watch_url": {
          "type": "string",
          "title": "Watch Url",
          "description": "where to watch it"
        },
        "player_url": {
          "type": "string",
          "title": "Player Url",
          "description": "an iframe src that plays it (16:9)"
        },
        "thumbnail_url": {
          "type": "string",
          "title": "Thumbnail Url",
          "description": "the video's thumbnail on YouTube's image host; `thumbnail_urls` lists every size, best first, and a size a video lacks answers 404"
        },
        "views": {
          "type": "integer",
          "title": "Views",
          "description": "views when the run read it"
        },
        "likes": {
          "type": [
            "integer",
            "null"
          ],
          "title": "Likes",
          "description": "likes, when YouTube gave them"
        },
        "comments": {
          "type": [
            "integer",
            "null"
          ],
          "title": "Comments",
          "description": "comments, when YouTube gave them"
        },
        "age_days": {
          "type": [
            "number",
            "null"
          ],
          "title": "Age Days",
          "description": "days since it was posted, when the run read it"
        },
        "views_per_day": {
          "type": [
            "number",
            "null"
          ],
          "title": "Views Per Day",
          "description": "views divided by age_days"
        },
        "baseline_videos": {
          "type": [
            "integer",
            "null"
          ],
          "title": "Baseline Videos",
          "description": "how many recent uploads the channel's usual was measured on"
        },
        "found_by": {
          "type": [
            "string",
            "null"
          ],
          "title": "Found By",
          "description": "the search that surfaced it"
        },
        "duration_s": {
          "type": [
            "integer",
            "null"
          ],
          "title": "Duration S",
          "description": "length in seconds; always over 180"
        },
        "posted_at": {
          "type": [
            "string",
            "null"
          ],
          "title": "Posted At",
          "description": "when it was posted (ISO 8601). Exact when `posted_exact` is true; otherwise the latest it can be, from YouTube's \"3 months ago\", and never before 2023 either way"
        },
        "posted_exact": {
          "type": "boolean",
          "title": "Posted Exact",
          "description": "whether posted_at is the real timestamp"
        },
        "channel_median_views": {
          "type": [
            "integer",
            "null"
          ],
          "title": "Channel Median Views",
          "description": "the channel's usual: the median views of its recent long-form uploads"
        },
        "outlier": {
          "type": [
            "number",
            "null"
          ],
          "title": "Outlier",
          "description": "views divided by channel_median_views: 28.2 means 28 times what this channel normally gets"
        },
        "outlier_score": {
          "type": "integer",
          "title": "Outlier Score",
          "description": "the video on one fixed scale, 0 to 100: the outlier, its reach, how fresh it is and how hard its viewers reacted, placed between two fixed anchors. Nothing else a run found moves it, so finds from different runs rank together; it moves only with the video's own numbers and age"
        },
        "channel_size": {
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "small",
            "medium",
            "large",
            null
          ],
          "title": "Channel Size",
          "description": "the channel by what its uploads usually get (channel_median_views): small, medium or large"
        },
        "seen_before": {
          "type": "boolean",
          "title": "Seen Before",
          "description": "true when an earlier run of yours on this tab already found this video; a keep or reject made then is already on it"
        },
        "analysis_from_earlier_read": {
          "type": "boolean",
          "title": "Analysis From Earlier Read",
          "description": "true when the opening or the package analysis is the one an earlier run of yours wrote for this video; the numbers are this run's"
        },
        "channel_id": {
          "type": [
            "string",
            "null"
          ],
          "title": "Channel Id",
          "description": "the channel's YouTube id (UC...)"
        },
        "thumbnail_urls": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Thumbnail Urls",
          "description": "every thumbnail size, best first; a size a video lacks answers 404"
        },
        "score": {
          "type": [
            "number",
            "null"
          ],
          "title": "Score",
          "description": "the ranking score outlier_score is scaled from"
        },
        "hook_lines": {
          "type": "integer",
          "title": "Hook Lines",
          "description": "long: how many lines of `intro` the hook reaches, counted from line 1; the last may be only partly the hook, `hook_words` counts exactly"
        },
        "hook_end_s": {
          "type": "number",
          "title": "Hook End S",
          "description": "long: the second the hook ends, to a tenth, never null. Inside a line it is estimated at the opening's own pace"
        },
        "hook_start_s": {
          "type": "number",
          "title": "Hook Start S",
          "description": "long: the second the hook starts: 0 or close to it, later when a sponsor read or a greeting came first"
        },
        "hook_words": {
          "type": "integer",
          "title": "Hook Words",
          "description": "long: the hook's length in words"
        },
        "auto_captions": {
          "type": "boolean",
          "title": "Auto Captions",
          "description": "long: true when the opening carries almost no punctuation (auto captions): its sentences are not marked and names may be misheard"
        },
        "about_subject": {
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "yes",
            "partly",
            null
          ],
          "title": "About Subject",
          "description": "long: whether the video is about the run's subject, read from its title and opening in the call that analyses it: yes, or partly (the subject is one part of it, or it covers a neighbouring subject). Null when the question was not asked (a channel or video link) or not answered. A video the model answers \"no\" for is never delivered"
        },
        "hook_type_other": {
          "type": [
            "string",
            "null"
          ],
          "title": "Hook Type Other",
          "description": "long: the device in words when hook_type is \"other\""
        },
        "title_device_other": {
          "type": [
            "string",
            "null"
          ],
          "title": "Title Device Other",
          "description": "packaging: the device in words when title_device is \"other\""
        },
        "multiple": {
          "type": [
            "number",
            "null"
          ],
          "title": "Multiple",
          "description": "views over channel_median_views, one decimal: 12.4 means 12.4 times what this channel normally gets (the number 1of10 and vidIQ show). Null when the channel was not measured"
        },
        "multiple_band": {
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "10x",
            "5x",
            "2x",
            "under 2x",
            null
          ],
          "title": "Multiple Band",
          "description": "the multiple's band: 10x (10 or more), 5x (5 to under 10), 2x (2 to under 5), under 2x (a video asked for by its address, or a bar set under 2); null with no multiple"
        },
        "content_type": {
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "long",
            "short",
            null
          ],
          "title": "Content Type",
          "description": "the kind of video: long (long-form) or short (a YouTube Short)"
        },
        "outlier_tier": {
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "outlier",
            "strong",
            "breakout",
            null
          ],
          "title": "Outlier Tier",
          "description": "outlier (under 5 times the channel's usual), strong (5 to 10) or breakout (10 or more)"
        },
        "likes_per_1k": {
          "type": [
            "number",
            "null"
          ],
          "title": "Likes Per 1K",
          "description": "likes for every 1,000 views"
        },
        "comments_per_1k": {
          "type": [
            "number",
            "null"
          ],
          "title": "Comments Per 1K",
          "description": "comments for every 1,000 views"
        },
        "style_match": {
          "type": [
            "boolean",
            "null"
          ],
          "title": "Style Match",
          "description": "when the request asked for a kind of opening or title: whether this one is that kind. Null when it asked for none"
        },
        "angle": {
          "type": [
            "string",
            "null"
          ],
          "title": "Angle",
          "description": "the specific angle the video takes on the subject"
        },
        "format": {
          "type": [
            "string",
            "null"
          ],
          "title": "Format",
          "description": "long: the kind of video, one of talking_head, voiceover, vlog, tutorial, interview, documentary, challenge, review, story, other"
        },
        "opens_with": {
          "type": [
            "string",
            "null"
          ],
          "title": "Opens With",
          "description": "long: how the first seconds begin, one of cold_open, greeting, teaser, question, sponsor, other"
        },
        "thumbnail_checked": {
          "type": "boolean",
          "title": "Thumbnail Checked",
          "description": "packaging: true when thumbnail_url is the size YouTube really served and the analysis looked at"
        },
        "thumbnail_sha256": {
          "type": "string",
          "title": "Thumbnail Sha256",
          "description": "packaging: a digest of the picture the analysis looked at; a later run reuses the analysis only while YouTube serves the same picture"
        },
        "analysed_at": {
          "type": "string",
          "title": "Analysed At",
          "description": "packaging: when the thumbnail was looked at (ISO 8601); a reused analysis keeps its own date"
        },
        "title_chars": {
          "type": "integer",
          "title": "Title Chars",
          "description": "packaging: the title's length in characters"
        },
        "cut_on_phone": {
          "type": "boolean",
          "title": "Cut On Phone",
          "description": "packaging: true when the title is longer than 60 characters, where a phone roughly cuts a title. Approximate: YouTube publishes no limit, and the cut moves with the screen, the font and the letters (about 58 to 62)"
        },
        "thumbnail_text_role": {
          "type": [
            "string",
            "null"
          ],
          "title": "Thumbnail Text Role",
          "description": "packaging: how the words on the thumbnail stand to the title, one of repeats_title, adds_to_title, contrasts_title, no_text. Counted in code from `title_overlap` where that can be (repeats_title at 0.7 or more, adds_to_title under 0.3); between, the model's reading"
        },
        "title_overlap": {
          "type": [
            "number",
            "null"
          ],
          "title": "Title Overlap",
          "description": "packaging: the share of the thumbnail's words that are also in the title, 0 to 1, counted in code. null when the thumbnail has no words, or its words are in a script the title does not use"
        },
        "on_subject": {
          "type": [
            "string",
            "null"
          ],
          "enum": [
            "yes",
            "partly",
            "no",
            null
          ],
          "title": "On Subject",
          "description": "packaging: whether the look, told the run's subject, judged the package to be about it: yes, or partly (it covers the subject as one part of something broader). A package judged no is never delivered. null when the run was on a channel's or a video's address, when the look gave no usable verdict, or when the analysis is one an earlier run wrote (its subject may have been another)"
        },
        "thumbnail_faces": {
          "type": [
            "integer",
            "null"
          ],
          "title": "Thumbnail Faces",
          "description": "packaging: how many faces the thumbnail shows"
        },
        "thumbnail_colors": {
          "type": [
            "string",
            "null"
          ],
          "title": "Thumbnail Colors",
          "description": "packaging: the colours that carry the frame"
        },
        "why": {
          "type": [
            "string",
            "null"
          ],
          "title": "Why",
          "description": "one line naming the outlier in words"
        },
        "language": {
          "type": [
            "string",
            "null"
          ],
          "title": "Language",
          "description": "the language spoken, as YouTube names the caption track"
        },
        "analysed": {
          "type": "boolean",
          "title": "Analysed",
          "description": "false when the model could not analyse it: the find is still real, its analysis fields are null"
        },
        "hook": {
          "type": "string",
          "title": "Hook",
          "description": "long: the hook, verbatim: the lines of `intro` the analysis marks as the hook, cut at the last sentence end that keeps it inside 25 seconds and 60 words, a leading sponsor read or greeting left out. The whole opening is `intro_text`"
        },
        "hook_seconds": {
          "type": [
            "number",
            "null"
          ],
          "title": "Hook Seconds",
          "description": "long: the second the hook ends; the same number as `hook_end_s`, kept for older readers"
        },
        "intro": {
          "items": {
            "type": "object"
          },
          "type": "array",
          "title": "Intro",
          "description": "long: the opening as it was said, cut by the transcript's own clock: lines of n (from 1), t (start second, null when the transcript had no clock) and text"
        },
        "intro_text": {
          "type": "string",
          "title": "Intro Text",
          "description": "long: the same words as one string"
        },
        "intro_seconds": {
          "type": "integer",
          "title": "Intro Seconds",
          "description": "long: how much of the opening was read"
        },
        "hook_type": {
          "type": [
            "string",
            "null"
          ],
          "title": "Hook Type",
          "description": "long: since 2026-10-02: the device the opening uses, one of curiosity_gap, contrarian_claim, specific_number, identity_callout, stakes_threat, promise_payoff, pattern_interrupt, story_open, question, direct_command, other (then `hook_type_other` names it in two to five words). Null on older hooks and when the write-up gave none. A client should treat an unknown value as other."
        },
        "mechanism": {
          "type": [
            "string",
            "null"
          ],
          "title": "Mechanism",
          "description": "long: why the opening holds a viewer, quoting it word for word"
        },
        "promise": {
          "type": [
            "string",
            "null"
          ],
          "title": "Promise",
          "description": "what the viewer is told they will get"
        },
        "open_loop": {
          "type": [
            "string",
            "null"
          ],
          "title": "Open Loop",
          "description": "long: the question the opening leaves unanswered"
        },
        "title_link": {
          "type": [
            "string",
            "null"
          ],
          "title": "Title Link",
          "description": "long: how the first lines pay off the title"
        },
        "beats": {
          "items": {
            "type": "object"
          },
          "type": "array",
          "title": "Beats",
          "description": "long: the opening in consecutive parts: from and to (line numbers of `intro`), beat (one of hook, stakes, proof, credibility, context, promise, preview, story, payoff, cta, other) and does"
        },
        "template": {
          "type": [
            "string",
            "null"
          ],
          "title": "Template",
          "description": "long: the hook as a fill-in structure with {placeholders}"
        },
        "steal_it": {
          "type": [
            "string",
            "null"
          ],
          "title": "Steal It",
          "description": "how another niche reuses the move"
        },
        "audience": {
          "type": [
            "string",
            "null"
          ],
          "title": "Audience",
          "description": "the viewer it is made for"
        },
        "title_device": {
          "type": [
            "string",
            "null"
          ],
          "title": "Title Device",
          "description": "packaging: the title's device, one of curiosity_gap, contrarian_claim, specific_number, how_to, listicle, challenge, story, question, warning, comparison, authority, timeliness, identity_callout, other"
        },
        "title_template": {
          "type": [
            "string",
            "null"
          ],
          "title": "Title Template",
          "description": "packaging: the title as a fill-in structure with {placeholders}"
        },
        "thumbnail_text": {
          "type": [
            "string",
            "null"
          ],
          "title": "Thumbnail Text",
          "description": "packaging: the words printed on the thumbnail, as a vision model read them; null when none"
        },
        "thumbnail_subject": {
          "type": [
            "string",
            "null"
          ],
          "title": "Thumbnail Subject",
          "description": "packaging: what the picture shows"
        },
        "thumbnail_emotion": {
          "type": [
            "string",
            "null"
          ],
          "title": "Thumbnail Emotion",
          "description": "packaging: the feeling it signals"
        },
        "thumbnail_composition": {
          "type": [
            "string",
            "null"
          ],
          "title": "Thumbnail Composition",
          "description": "packaging: how the frame is built"
        },
        "thumbnail_devices": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Thumbnail Devices",
          "description": "packaging: up to four of face_closeup, big_text, arrow_or_circle, before_after, number, contrast_color, object_hero, screenshot, split_screen, action_shot, collage, minimal, other"
        },
        "gap": {
          "type": [
            "string",
            "null"
          ],
          "title": "Gap",
          "description": "packaging: what the thumbnail shows that the title does not say, and the question that leaves"
        },
        "visible_action": {
          "type": [
            "string",
            "null"
          ],
          "title": "Visible Action",
          "description": "packaging: the single thing happening in the picture, one sentence with a verb (a model's reading)"
        },
        "title_carries": {
          "type": [
            "string",
            "null"
          ],
          "title": "Title Carries",
          "description": "packaging: what only the title tells the viewer"
        },
        "picture_adds": {
          "type": [
            "string",
            "null"
          ],
          "title": "Picture Adds",
          "description": "packaging: what only the picture adds that the title does not say"
        },
        "text_box": {
          "items": {
            "type": "integer"
          },
          "type": [
            "array",
            "null"
          ],
          "title": "Text Box",
          "description": "packaging: the box around the biggest line of printed words, [left, top, right, bottom] in thousandths of the width and height; null with no words"
        },
        "thumb_metrics": {
          "type": [
            "object",
            "null"
          ],
          "title": "Thumb Metrics",
          "description": "packaging: the thumbnail measured in code from its pixels: palette (three colours with their share %), brightness, contrast, saturation and busyness (0 to 100), text_px_210 (the biggest words' height when the thumbnail is 210 px wide, from text_box), text_reads_at_210 (6.2 px or more), badge_corner_clear (no words where YouTube prints the length) and basis. Null when the picture could not be read"
        }
      },
      "additionalProperties": true,
      "type": "object",
      "title": "FindOut",
      "description": "One result of a long or packaging run. Sent back exactly as stored: a long find has\nthe opening fields, a packaging find the title and thumbnail fields, and a field one\nkind does not have is absent, never null."
    },
    "HookOut": {
      "properties": {
        "hook_id": {
          "type": "string",
          "title": "Hook Id"
        },
        "rank": {
          "type": "integer",
          "title": "Rank"
        },
        "hook": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Hook",
          "description": "the exact opening line; null when the clip had none"
        },
        "catchy_line": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Catchy Line"
        },
        "main_idea": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Main Idea"
        },
        "why_it_travelled": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Why It Travelled"
        },
        "topic": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Topic"
        },
        "onscreen_text": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Onscreen Text",
          "description": "text burned into the video; on hooks found before 2026-09-26, every slide of a photo post in order"
        },
        "transcript": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Transcript"
        },
        "transcript_kind": {
          "anyOf": [
            {
              "type": "string",
              "enum": [
                "speech",
                "music",
                "none",
                "other"
              ]
            },
            {
              "type": "null"
            }
          ],
          "title": "Transcript Kind"
        },
        "heard": {
          "type": "boolean",
          "title": "Heard",
          "description": "false when our speech service could not hear the clip right now: it shows no spoken words and transcript is null; true otherwise",
          "default": true
        },
        "language": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Language",
          "description": "the language or dialect the clip was judged to be in, named in English (\"Moroccan Darija\", not \"Arabic\"); null for hooks found before this field"
        },
        "platform": {
          "anyOf": [
            {
              "type": "string",
              "enum": [
                "tiktok",
                "instagram",
                "youtube"
              ]
            },
            {
              "type": "string"
            }
          ],
          "title": "Platform",
          "description": "tiktok, instagram or youtube (YouTube Shorts only, never long-form, since 2026-10-01; its watch_url is the Shorts form)"
        },
        "creator": {
          "type": "string",
          "title": "Creator"
        },
        "views": {
          "type": "integer",
          "title": "Views"
        },
        "likes": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "title": "Likes"
        },
        "above_floor": {
          "type": "boolean",
          "title": "Above Floor"
        },
        "watch_url": {
          "type": "string",
          "title": "Watch Url"
        },
        "cover_url": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Cover Url"
        },
        "slide_count": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "title": "Slide Count",
          "description": "always null on hooks found since 2026-09-26 (no carousels are delivered); the slide count of a TikTok photo post on older hooks"
        },
        "found_by": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Found By"
        },
        "caption": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Caption"
        },
        "verdict": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Verdict"
        },
        "format": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Format",
          "description": "what kind of clip it is: talking_head, podcast, interview, stage (a person on camera talking; the main deck), or voiceover, text_on_screen, broll, skit, lipsync, trending_sound, slideshow, call_recording (a recorded call or voicemail), live_interaction (the creator with real people in the scene, not talking to the viewer), other (never delivered since 2026-10-08). Null on hooks found before 2026-09-29. A client should treat an unknown value as other."
        },
        "section": {
          "type": "string",
          "const": "main",
          "title": "Section",
          "description": "always \"main\": the deck of a person on camera talking. Bonus was removed on 2026-10-08, and bonus hooks older runs stored are no longer returned.",
          "default": "main"
        },
        "creator_country": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Creator Country",
          "description": "the country of the creator's TikTok account as an ISO 3166-1 alpha-2 code (\"MA\"), as TikTok states it on the clip's search row. It is the account's country, not GPS. Always null on Instagram, which states none, and on hooks found before 2026-09-30. Never guessed."
        },
        "place": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Place",
          "description": "the name of the place the creator tagged on the post (at most 80 characters), null when untagged, as most posts are. Where it was posted from, not where the creator lives."
        },
        "place_country": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Place Country",
          "description": "the tagged place's country as an ISO 3166-1 alpha-2 code, only when the platform states it (TikTok usually does, Instagram rarely). Null otherwise; never guessed from the name or the map position."
        },
        "banner": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Banner",
          "description": "since 2026-09-30: the banner hook, the title-style text the creator placed on the video to hook the viewer (a headline, a \"POV:\", a question, a list title), verbatim as one line of at most 200 characters. Null when the clip has none: running subtitles of the speech, usernames, watermarks and a lower third naming the speaker are never a banner. Null on hooks found before 2026-09-30. `onscreen_text` still holds all the text read on screen."
        },
        "match": {
          "anyOf": [
            {
              "type": "string",
              "enum": [
                "exact",
                "close"
              ]
            },
            {
              "type": "null"
            }
          ],
          "title": "Match",
          "description": "since 2026-09-30, on runs with a language or a country: \"exact\" when the clip is about the subject itself, \"close\" when it is about a neighbouring subject with the same intent (general wealth-building advice for \"getting wealthy in your 20s\"): a close match, delivered after every exact match and to be labelled as one. Null on hooks found before 2026-09-30 and on runs with no language or country, which are judged as before."
        },
        "style_match": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "title": "Style Match",
          "description": "since 2026-10-04, on a run whose request asked for a kind of opening (\"controversial hooks\", \"question hooks\", \"storytime\"): true when this hook opens that way (its hook_type is one asked), false when it is a strong hook on the subject that opens differently, delivered after the ones that match. Null when no kind was asked."
        },
        "signals": {
          "properties": {
            "followers": {
              "type": "integer",
              "description": "the creator's followers"
            },
            "shares": {
              "type": "integer"
            },
            "saves": {
              "type": "integer"
            },
            "comments": {
              "type": "integer"
            },
            "posted": {
              "type": "string",
              "description": "the post date, YYYY-MM-DD, UTC"
            },
            "reach": {
              "type": "number",
              "description": "views over the creator's followers: 12.5 means it reached 12.5 times the creator's own audience"
            },
            "shares_per_1k": {
              "type": "number",
              "description": "shares per 1,000 views"
            },
            "saves_per_1k": {
              "type": "number",
              "description": "saves per 1,000 views"
            },
            "opening": {
              "type": "string",
              "enum": [
                "banger",
                "ok",
                "weak",
                "other"
              ],
              "description": "how hard the clip's first words stop a scrolling viewer, as the write-up rated them; absent on hooks found before 2026-10-13"
            },
            "opening_why": {
              "type": "string",
              "description": "at most 15 words on why the opening rates as it does"
            }
          },
          "type": [
            "object",
            "null"
          ],
          "title": "Signals",
          "description": "how far the clip travelled beyond its creator, from the platform's own numbers (TikTok carries all of them, Instagram at most comments and the post date); each key only when the platform stated it; null for a hook with none. Hooks are ranked by views, reach and shares and saves per view together since 2026-09-28"
        },
        "posted_at": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Posted At",
          "description": "when the clip was posted, exact when the platform gives it; null when unknown"
        },
        "age_days": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "title": "Age Days",
          "description": "days since posted_at, at delivery"
        },
        "views_per_day": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "title": "Views Per Day",
          "description": "views divided by age_days"
        },
        "creator_median_views": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "title": "Creator Median Views",
          "description": "the creator's usual views per video: the median of their recent videos, pinned excluded (TikTok may use likes per video as a proxy, said so in outlier_basis)"
        },
        "outlier": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "title": "Outlier",
          "description": "views divided by the creator's usual views per video (`creator_median_views`): 31.2 means 31 times what this creator normally gets. Null when the creator's baseline was not priced for this run."
        },
        "outlier_basis": {
          "anyOf": [
            {
              "type": "string",
              "enum": [
                "median_views",
                "likes_per_video"
              ]
            },
            {
              "type": "null"
            }
          ],
          "title": "Outlier Basis",
          "description": "what creator_median_views was measured from, or null"
        },
        "duration_s": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "title": "Duration S",
          "description": "the clip's length in seconds"
        },
        "paid": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "title": "Paid",
          "description": "an ad, a paid partnership or branded content, as the platform labels it; null when unknown"
        },
        "sound_reuse": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "title": "Sound Reuse",
          "description": "videos reusing the clip's sound"
        },
        "own_sound": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "title": "Own Sound",
          "description": "the sound is the creator's own"
        },
        "hashtags": {
          "anyOf": [
            {
              "items": {
                "type": "string"
              },
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "title": "Hashtags",
          "description": "the post's hashtags, at most 20, without the #"
        },
        "virality": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "title": "Virality",
          "description": "since 2026-10-01: how viral the clip is, 0 to 1 (0.58 is 58 out of 100), since 2026-10-02 on one fixed scale: the same clip scores the same in every run. Built from the outlier (views over the creator's usual, when known), freshness, travel (shares and saves per 1,000 views), own-sound reuse and a paid penalty; null on hooks found before this field."
        },
        "virality_why": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Virality Why",
          "description": "one short line naming the strongest signal behind `virality` (\"31x this creator's usual\", \"1.2M views in 4 days\"); never empty when virality is set; null when unknown"
        },
        "hook_type": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Hook Type",
          "description": "since 2026-10-02: the device the opening uses, one of curiosity_gap, contrarian_claim, specific_number, identity_callout, stakes_threat, promise_payoff, pattern_interrupt, story_open, question, direct_command, other (then `hook_type_other` names it in two to five words). Null on older hooks and when the write-up gave none. A client should treat an unknown value as other."
        },
        "hook_type_other": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Hook Type Other",
          "description": "two to five words naming the device when hook_type is other, else null"
        },
        "mechanism": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Mechanism",
          "description": "since 2026-10-02: why the opening stops the scroll, in at most two short sentences: what the first three seconds make the viewer think or feel, quoting the clip's own words (every quote is checked word for word against what was said or shown), and the open question the clip then pays off. Never a view or follower count. `why_it_travelled` carries the same text. Null on older hooks."
        },
        "visual_hook": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Visual Hook",
          "description": "since 2026-10-02: one sentence on what the banner or other on-screen text adds to the spoken opening; null when no on-screen text hooks"
        },
        "audience": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Audience",
          "description": "since 2026-10-02: the specific viewer this opening stops, a few words"
        },
        "template": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Template",
          "description": "since 2026-10-02: the hook as a fill-in structure of at most 20 words, its subject words replaced by {placeholders}"
        },
        "steal_it": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Steal It",
          "description": "since 2026-10-02: one sentence on how a creator in another niche reuses this exact move"
        },
        "video_id": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Video Id"
        },
        "player_url": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Player Url",
          "description": "a 9:16 iframe src (TikTok's player, Instagram's reel embed, YouTube's embed); null for an unusable id or a photo post found before 2026-09-26"
        },
        "aspect": {
          "type": "string",
          "title": "Aspect"
        },
        "still_url": {
          "type": "string",
          "title": "Still Url",
          "description": "the card image as a JPEG, served by this API; no key needed"
        },
        "media_url": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Media Url",
          "description": "our own player's source: put it in a <video src>; it redirects to the clip's original file on TikTok's or Instagram's CDN (no key, nothing stored). null for YouTube (use player_url) and photo posts; a 404 or 503 from it means show player_url instead"
        }
      },
      "additionalProperties": true,
      "type": "object",
      "required": [
        "hook_id",
        "rank",
        "hook",
        "catchy_line",
        "main_idea",
        "why_it_travelled",
        "topic",
        "onscreen_text",
        "transcript",
        "platform",
        "creator",
        "views",
        "likes",
        "above_floor",
        "watch_url",
        "cover_url",
        "slide_count",
        "found_by",
        "caption",
        "verdict",
        "video_id",
        "player_url",
        "aspect",
        "still_url"
      ],
      "title": "HookOut"
    },
    "LocaleOut": {
      "properties": {
        "subject": {
          "type": [
            "string",
            "null"
          ],
          "title": "Subject",
          "description": "what was searched, read out of the topic"
        },
        "language": {
          "type": [
            "string",
            "null"
          ],
          "title": "Language",
          "description": "the language or dialect enforced, or null"
        },
        "country": {
          "type": [
            "string",
            "null"
          ],
          "title": "Country",
          "description": "the creators' country enforced, or null"
        },
        "exclude": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Exclude",
          "description": "languages, dialects and countries the judges ruled out"
        }
      },
      "additionalProperties": true,
      "type": "object",
      "title": "LocaleOut"
    },
    "PatternOut": {
      "properties": {
        "name": {
          "type": "string",
          "title": "Name",
          "description": "the structure, named in a few words"
        },
        "template": {
          "type": "string",
          "title": "Template",
          "description": "the structure with {placeholders} for the subject-specific words, to fill for another niche"
        },
        "why": {
          "type": "string",
          "title": "Why",
          "description": "what it does to the viewer"
        },
        "hook_ids": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Hook Ids",
          "description": "the delivered hooks using it"
        },
        "count": {
          "type": "integer",
          "title": "Count",
          "description": "how many: len(hook_ids)"
        }
      },
      "additionalProperties": true,
      "type": "object",
      "required": [
        "name",
        "template",
        "why",
        "hook_ids",
        "count"
      ],
      "title": "PatternOut"
    },
    "ProgressCounts": {
      "properties": {
        "found": {
          "type": "integer",
          "title": "Found"
        },
        "above_floor": {
          "type": "integer",
          "title": "Above Floor"
        },
        "on_topic": {
          "type": "integer",
          "title": "On Topic"
        },
        "read": {
          "type": "integer",
          "title": "Read"
        },
        "held": {
          "type": "integer",
          "title": "Held"
        },
        "written": {
          "type": "integer",
          "title": "Written"
        },
        "wanted": {
          "type": "integer",
          "title": "Wanted"
        },
        "talking": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "title": "Talking",
          "description": "clips checked so far that show a person talking (the main deck's kind); since 2026-09-29"
        },
        "channels": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "title": "Channels",
          "description": "Long form and Packaging: channels measured so far, counting up while they are"
        },
        "outliers": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "title": "Outliers",
          "description": "Long form and Packaging: candidates that beat their channel by the bar once ranked, counting each channel's other uploads that did; once channels are measured"
        },
        "measured": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "title": "Measured",
          "description": "Long form and Packaging: videos held against their own channel so far, the set the finished run's report.landscape.videos counts; once a channel is measured (since 2026-10-08)"
        },
        "beat": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "title": "Beat",
          "description": "Long form and Packaging: how many of those beat the bar, the finished run's report.landscape.outliers; use it, not outliers, for a 'beat their channel' number (since 2026-10-08)"
        }
      },
      "additionalProperties": true,
      "type": "object",
      "required": [
        "found",
        "above_floor",
        "on_topic",
        "read",
        "held",
        "written",
        "wanted"
      ],
      "title": "ProgressCounts"
    },
    "ProgressFeedEntry": {
      "properties": {
        "key": {
          "type": "string",
          "title": "Key",
          "description": "opaque and stable per clip within the run; never the provider's id"
        },
        "platform": {
          "type": "string",
          "enum": [
            "tiktok",
            "instagram",
            "youtube"
          ],
          "title": "Platform"
        },
        "creator": {
          "type": "string",
          "title": "Creator"
        },
        "views": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "title": "Views"
        },
        "state": {
          "type": "string",
          "enum": [
            "found",
            "reading",
            "talking",
            "bonus",
            "passed"
          ],
          "title": "State",
          "description": "found: in the pool; reading: being read; talking: a person talking, held for the deck; passed: dropped (off subject, other language, not talking, not needed). bonus appears only on runs from before 2026-10-08, when Bonus was removed"
        },
        "thumb_url": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Thumb Url",
          "description": "absolute URL of the clip's cover as a JPEG, no key needed (GET /v1/runs/{run_id}/thumbs/{key}); null when there is none"
        },
        "line": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Line",
          "description": "the banner or the opening words, only once the judges passed the clip and it is on its way to delivery"
        }
      },
      "additionalProperties": true,
      "type": "object",
      "required": [
        "key",
        "platform",
        "creator",
        "views",
        "state",
        "thumb_url",
        "line"
      ],
      "title": "ProgressFeedEntry"
    },
    "ProgressOut": {
      "properties": {
        "stage": {
          "type": "string",
          "title": "Stage",
          "description": "one of: queued, starting, reusing, searching_instagram, searching_tiktok, judging, transcribing, writing, searching_youtube, saving, done, failed, other"
        },
        "message": {
          "type": "string",
          "title": "Message"
        },
        "updated_at": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Updated At"
        },
        "counts": {
          "anyOf": [
            {
              "$ref": "#/$defs/ProgressCounts"
            },
            {
              "type": "null"
            }
          ]
        },
        "preview": {
          "anyOf": [
            {
              "items": {
                "$ref": "#/$defs/ProgressPreview"
              },
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "title": "Preview"
        },
        "feed": {
          "anyOf": [
            {
              "items": {
                "$ref": "#/$defs/ProgressFeedEntry"
              },
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "title": "Feed",
          "description": "the live research wall: the clips the run is working on, newest first, at most 24; only while a run shows one (since 2026-09-30)"
        },
        "items": {
          "anyOf": [
            {
              "items": {
                "additionalProperties": true,
                "type": "object"
              },
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "title": "Items",
          "description": "on a running long or packaging run: the finds written so far in rank order, each as a card (video_id, kind, rank, title, channel, watch_url, thumbnail_url, views, duration_s, posted_at, posted_exact, age_days, outlier, outlier_score, outlier_tier, style_match, analysed, and hook on Long form, thumbnail_text and thumbnail_faces on Packaging); the whole find, in the shape `finds` carries, arrives with the finished run, which is when this is gone (since 2026-10-07; cards only since 2026-10-08)"
        },
        "scan": {
          "anyOf": [
            {
              "$ref": "#/$defs/ProgressScan"
            },
            {
              "type": "null"
            }
          ],
          "description": "on a running long or packaging run once it has seen a video: the videos it is looking at as tiles, and counts; gone once the run is done or failed (since 2026-10-08)"
        }
      },
      "additionalProperties": true,
      "type": "object",
      "required": [
        "stage",
        "message",
        "updated_at"
      ],
      "title": "ProgressOut"
    },
    "ProgressPreview": {
      "properties": {
        "hook": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Hook"
        },
        "platform": {
          "type": "string",
          "enum": [
            "tiktok",
            "instagram",
            "youtube"
          ],
          "title": "Platform"
        },
        "creator": {
          "type": "string",
          "title": "Creator"
        },
        "views": {
          "type": "integer",
          "title": "Views"
        },
        "topic": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Topic"
        },
        "cover_url": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Cover Url"
        },
        "onscreen_hook": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Onscreen Hook",
          "description": "the text on screen that opens the clip (its banner, else its first line on screen), null when none was read"
        },
        "why": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Why",
          "description": "why the opening holds a viewer, as the finished hook's mechanism says it; null when none"
        },
        "hook_type": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Hook Type",
          "description": "since 2026-10-02: the device the opening uses, one of curiosity_gap, contrarian_claim, specific_number, identity_callout, stakes_threat, promise_payoff, pattern_interrupt, story_open, question, direct_command, other (then `hook_type_other` names it in two to five words). Null on older hooks and when the write-up gave none. A client should treat an unknown value as other."
        },
        "watch_url": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Watch Url",
          "description": "the clip on its platform"
        },
        "player_url": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Player Url",
          "description": "a 9:16 iframe src, the player the finished hook gets; null when it has none"
        },
        "thumb_url": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Thumb Url",
          "description": "the clip's cover as a JPEG, served by this API with no key (the wall's route); null when there is none. Use it rather than cover_url, which is the platform's own address and may not load on your page"
        }
      },
      "additionalProperties": true,
      "type": "object",
      "required": [
        "hook",
        "platform",
        "creator",
        "views",
        "topic",
        "cover_url"
      ],
      "title": "ProgressPreview"
    },
    "ProgressScan": {
      "properties": {
        "tiles": {
          "items": {
            "$ref": "#/$defs/ProgressScanTile"
          },
          "type": "array",
          "title": "Tiles",
          "description": "at most 60, in the order the run first saw each video (oldest first); a tile never changes place. When more exist, the oldest not beat, reading or ready go first"
        },
        "lanes": {
          "additionalProperties": {
            "type": "integer"
          },
          "type": "object",
          "title": "Lanes",
          "description": "counts: found (videos the run holds), on_topic (judged to be about the subject), measured (held against their own channel, the set report.landscape counts), beat (of those, how many beat the bar), reading (being read right now), ready (written up so far)"
        }
      },
      "additionalProperties": true,
      "type": "object",
      "required": [
        "tiles",
        "lanes"
      ],
      "title": "ProgressScan"
    },
    "ProgressScanTile": {
      "properties": {
        "v": {
          "type": "string",
          "title": "V",
          "description": "the YouTube video id (11 characters of A-Z a-z 0-9 _ -); its thumbnail is https://i.ytimg.com/vi/{v}/mqdefault.jpg"
        },
        "s": {
          "type": "string",
          "title": "S",
          "description": "found: a search returned it; on_topic: the title judge kept it; off_topic: the judge dropped it; measured: its channel's usual is known and it is under the bar; beat: its views are at least the bar times the channel's usual; reading: its transcript (Long form) or thumbnail (Packaging) is being read; ready: written up, it is a result; passed: dropped (no captions, another language, before 2023, unreadable, below the bar once dated, not needed). Read an unknown state as found"
        },
        "x": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "title": "X",
          "description": "how many times its channel's usual views it has, to one decimal, once known; else null"
        }
      },
      "additionalProperties": true,
      "type": "object",
      "required": [
        "v",
        "s",
        "x"
      ],
      "title": "ProgressScanTile"
    },
    "RunStats": {
      "properties": {
        "locale": {
          "anyOf": [
            {
              "$ref": "#/$defs/LocaleOut"
            },
            {
              "type": "null"
            }
          ],
          "description": "what the first step understood: the subject searched and the locale enforced"
        },
        "rejected_after_reading": {
          "type": "integer",
          "title": "Rejected After Reading",
          "description": "clips the judges refused after reading what they say and show: another language or dialect, or off topic"
        },
        "dropped_other_country": {
          "type": "integer",
          "title": "Dropped Other Country",
          "description": "TikTok clips placed in another country, dropped before any credit was spent on them"
        },
        "credits_lent_to_tiktok": {
          "type": "integer",
          "title": "Credits Lent To Tiktok",
          "description": "credits of Instagram's share that TikTok searched with instead; they are counted in credits_tt, not credits_ig"
        },
        "dropped_carousels": {
          "type": "integer",
          "title": "Dropped Carousels",
          "description": "TikTok photo posts (slideshows) and Instagram carousels met at search and dropped unread, free: a run delivers single videos only since 2026-09-26"
        },
        "search_breadth": {
          "properties": {
            "phrases": {
              "additionalProperties": {
                "type": "integer"
              },
              "type": "object",
              "description": "search phrases asked for per round, per platform: 6 each for a request of up to 5 hooks, 30 on Instagram and 12 on TikTok for a larger one"
            },
            "instagram_reels": {
              "additionalProperties": {
                "type": "integer"
              },
              "type": "object",
              "description": "Instagram sweeps that bought the viewless reels route (bought) and that skipped it because the popular route already found enough (skipped)"
            }
          },
          "type": "object",
          "title": "Search Breadth",
          "description": "how widely the run searched, since 2026-09-28"
        },
        "rejections": {
          "properties": {
            "other_country": {
              "type": "integer",
              "description": "TikTok clips placed in another country, dropped unread and free, each clip once"
            },
            "off_subject_or_locale": {
              "type": "integer",
              "description": "read, and the judge refused them: not about the subject or, on a run with a language or country, not in it (one verdict answers both)"
            },
            "other_language": {
              "type": "integer",
              "description": "written up, and their own write-up placed them in another language or dialect"
            },
            "no_words": {
              "type": "integer",
              "description": "read, nothing said or shown, on a run whose language rules such a clip out"
            },
            "other": {
              "type": "integer",
              "description": "not checked because a check or a write-up failed"
            },
            "languages": {
              "additionalProperties": {
                "type": "integer"
              },
              "type": "object",
              "description": "the languages the other_language clips were placed in, with counts, at most five plus other"
            }
          },
          "type": "object",
          "title": "Rejections",
          "description": "why clips were refused, each clip in one bucket once; `outcome` says it in words"
        },
        "held_back": {
          "properties": {
            "proof": {
              "type": "integer",
              "description": "on the subject, but no sign it travelled: a TikTok clip with fewer views than its creator has followers and no outlier of 2x, a clip under the view floor without proof, or one doing less than its creator's usual; each clip once, none that was delivered after all"
            },
            "relevance": {
              "type": "integer",
              "description": "read, and the judge refused them: not about the subject or, on a run with a language or country, not in it"
            },
            "language": {
              "type": "integer",
              "description": "their own write-up placed them in another language or dialect"
            },
            "no_words": {
              "type": "integer",
              "description": "nothing said or shown, on a run whose language rules such a clip out"
            },
            "supply": {
              "type": "integer",
              "description": "on the subject, 10,000 views or more, never read because the search ended first; counted only when the deck came back short"
            },
            "other": {
              "type": "integer",
              "description": "not checked because a step failed"
            }
          },
          "type": "object",
          "title": "Held Back",
          "description": "what kept clips out of the deck, by reason, each clip once, since 2026-10-08; `outcome` says the largest reasons in one sentence on a short deck"
        },
        "credits_waived": {
          "type": "integer",
          "title": "Credits Waived",
          "description": "provider credits a run that found no hooks used and was not charged; they still count against the key's credit_limit"
        },
        "waiver_refused": {
          "properties": {
            "limit": {
              "type": "string",
              "enum": [
                "per_day",
                "lifetime"
              ]
            },
            "allowance": {
              "type": "integer"
            }
          },
          "type": "object",
          "title": "Waiver Refused",
          "description": "on a run that found no hooks and was charged: the allowance of free empty runs that was used up, per_day (runs a UTC day) or lifetime (credits in all)"
        },
        "transcription": {
          "type": "object",
          "title": "Transcription",
          "description": "speech recognition on a dialect request: whether the language asked for is a dialect, TikTok clips whose auto captions (or missing captions) were heard again by OpenAI, the seconds and estimated dollars (ours, never charged to you), failures and clips past the run's ceiling"
        },
        "charged": {
          "type": "integer",
          "title": "Charged",
          "description": "credits deducted from your balance for this run: what it used, never more than `reserved`; 0 while it runs, on a failed run, and on a run that found no hooks within the account's allowance of free empty runs (three a day, 500 credits in all; `outcome` says which)"
        },
        "credits_earlier_attempts": {
          "type": "integer",
          "title": "Credits Earlier Attempts",
          "description": "provider credits spent by attempts before a restart; never charged to you"
        },
        "talking": {
          "properties": {
            "checked": {
              "type": "integer",
              "description": "clips checked for a person talking"
            },
            "talking": {
              "type": "integer",
              "description": "of those, a person talking"
            },
            "not_talking": {
              "type": "integer",
              "description": "clips on the subject dropped because nobody talks on camera (since 2026-10-08)"
            },
            "vision_calls": {
              "type": "integer",
              "description": "frame checks it took"
            },
            "certain_by_text": {
              "type": "integer",
              "description": "decided from the words alone, with no frame check"
            }
          },
          "type": "object",
          "title": "Talking",
          "description": "how the run told talking heads (hooks) from clips where nobody talks, since 2026-09-29"
        },
        "model": {
          "type": "object",
          "title": "Model",
          "description": "language model usage for the run"
        },
        "patterns_model": {
          "type": "object",
          "title": "Patterns Model",
          "description": "language model usage of the one patterns call made after delivery (since 2026-10-01); absent when no call was made"
        },
        "patterns_note": {
          "type": "string",
          "title": "Patterns Note",
          "description": "beside null patterns, one honest line: no opening structure is shared by two or more of the deck's hooks (quality round, 2026-10-01); absent otherwise"
        },
        "posted_within_days": {
          "type": "integer",
          "title": "Posted Within Days",
          "description": "the recency window the engine honoured, in days (since 2026-10-01)"
        },
        "seeds": {
          "type": "integer",
          "title": "Seeds",
          "description": "seed hooks the engine steered by (since 2026-10-01)"
        },
        "pool_size": {
          "type": "integer",
          "title": "Pool Size",
          "description": "distinct clips found"
        },
        "reused_from_earlier_runs": {
          "type": "integer",
          "title": "Reused From Earlier Runs",
          "description": "clips taken from your earlier runs, not bought again"
        },
        "resumed_from_earlier_attempt": {
          "type": "integer",
          "title": "Resumed From Earlier Attempt",
          "description": "clips carried over from before a restart"
        },
        "above_floor": {
          "type": "integer",
          "title": "Above Floor",
          "description": "clips above the view floor"
        },
        "on_topic_above_floor": {
          "type": "integer",
          "title": "On Topic Above Floor",
          "description": "of those, the clips judged on topic"
        },
        "delivered": {
          "type": "integer",
          "title": "Delivered",
          "description": "hooks delivered"
        },
        "delivered_above_floor": {
          "type": "integer",
          "title": "Delivered Above Floor",
          "description": "delivered hooks above the view floor"
        },
        "undescribed_dropped": {
          "type": "integer",
          "title": "Undescribed Dropped",
          "description": "chosen clips dropped because they could not be read"
        },
        "hooks_not_verbatim": {
          "type": "integer",
          "title": "Hooks Not Verbatim",
          "description": "opening lines not found word for word in the clip"
        },
        "catchy_lines_not_verbatim": {
          "type": "integer",
          "title": "Catchy Lines Not Verbatim",
          "description": "catchy lines not found word for word"
        },
        "catchy_lines_anchored": {
          "type": "integer",
          "title": "Catchy Lines Anchored",
          "description": "catchy lines matched to the transcript"
        },
        "catchy_lines_by_number": {
          "type": "integer",
          "title": "Catchy Lines By Number",
          "description": "catchy lines picked by number"
        },
        "cross_posts_merged": {
          "type": "integer",
          "title": "Cross Posts Merged",
          "description": "the same clip on both platforms, counted once"
        },
        "timings": {
          "type": "object",
          "title": "Timings",
          "description": "seconds spent per stage"
        },
        "calls_by_route": {
          "type": "object",
          "title": "Calls By Route",
          "description": "provider calls per route"
        },
        "latency": {
          "type": "object",
          "title": "Latency",
          "description": "per provider route: calls, seconds, p50_s and max_s"
        }
      },
      "additionalProperties": true,
      "type": "object",
      "title": "RunStats",
      "description": "A finished run's stats. Every key is optional: a failed or older run carries fewer,\nand a key added later reaches clients before this model names it."
    },
    "UnderstoodOut": {
      "properties": {
        "subject": {
          "type": "string",
          "title": "Subject",
          "description": "what the clips must be about, the request's own words without instructions or hook words"
        },
        "language": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Language"
        },
        "country": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Country"
        },
        "core": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Core",
          "description": "the subject without a qualifier of who it is for"
        },
        "audience": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Audience",
          "description": "who that qualifier names"
        },
        "neighbours": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Neighbours",
          "description": "narrower parts of the subject also searched, delivered as close matches",
          "default": []
        },
        "styles": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Styles",
          "description": "the kinds of opening the request asked for, each a hook_type (contrarian_claim for \"controversial\" or \"hot takes\", story_open for \"storytime\", question, specific_number ...), or other (then style_other names it); [] when none was asked",
          "default": []
        },
        "style_other": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Style Other",
          "description": "the asked kind of opening when styles holds \"other\""
        },
        "wants": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Wants",
          "description": "what the request asked the content to deliver: advice, story, opinion, explainer, comparison or other; null when it did not say"
        },
        "intent": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Intent",
          "description": "what the person who typed the subject wants a clip to give them, one sentence"
        },
        "look_alikes": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Look Alikes",
          "description": "subjects that share the subject's words but are another subject; never delivered",
          "default": []
        },
        "typed": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Typed",
          "description": "the request as typed, when its spelling was fixed before searching; null otherwise"
        }
      },
      "additionalProperties": true,
      "type": "object",
      "required": [
        "subject"
      ],
      "title": "UnderstoodOut"
    }
  }
}