> ## Documentation Index
> Fetch the complete documentation index at: https://docs.physionlabs.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Create an evaluation

> Queues a video for analysis and returns immediately. The response starts
in `queued` state. Retrieve it by ID until it reaches a terminal state.

The video may be at most 15 seconds and 50 MB, H.264/AVC in an MP4
container. The request body itself is capped at 2 MB, which only binds on
`b64_json` -- see `VideoRef`.




## OpenAPI

````yaml /openapi/galileo-v1.yaml post /v1/evaluations
openapi: 3.1.0
info:
  title: Galileo API
  version: 0.1.0
  summary: Evaluate generated videos with Galileo.
  description: |
    Submit generated videos for visual-glitch and prompt-misalignment analysis.
    Evaluation jobs run asynchronously. Retrieve a job until its status reaches
    `completed`, `partial`, or `failed`.
  contact:
    name: Physion Labs support
    email: support@physionlabs.ai
  license:
    name: Proprietary
    identifier: LicenseRef-Proprietary
servers:
  - url: https://api.physionlabs.ai
    description: Production
  - url: https://api-dev.physionlabs.ai
    description: Development
security:
  - bearerAuth: []
tags:
  - name: Platform
    description: Service health and account metadata.
  - name: Evaluations
    description: Submit videos and retrieve Galileo results.
  - name: Videos
    description: Upload a video once and reference it from evaluations.
paths:
  /v1/evaluations:
    post:
      tags:
        - Evaluations
      summary: Create an evaluation
      description: >
        Queues a video for analysis and returns immediately. The response starts

        in `queued` state. Retrieve it by ID until it reaches a terminal state.


        The video may be at most 15 seconds and 50 MB, H.264/AVC in an MP4

        container. The request body itself is capped at 2 MB, which only binds
        on

        `b64_json` -- see `VideoRef`.
      operationId: createEvaluation
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EvaluationCreate'
      responses:
        '201':
          description: Evaluation created.
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Evaluation'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCredits'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '502':
          $ref: '#/components/responses/BadGateway'
        '504':
          $ref: '#/components/responses/GatewayTimeout'
components:
  schemas:
    EvaluationCreate:
      type: object
      additionalProperties: false
      required:
        - prompt
        - video
      properties:
        model:
          $ref: '#/components/schemas/ModelId'
        model_version:
          type: string
          minLength: 1
          description: >-
            Concrete model version to run. The deployment default applies when
            omitted.
        prompt:
          type: string
          minLength: 1
          pattern: \S
          maxLength: 2500
          description: >-
            What the video was meant to show. REQUIRED as of 2026-08-27
            (previously optional, defaulting to ""); empty or whitespace-only is
            refused with `missing_prompt`.
        video:
          $ref: '#/components/schemas/VideoRef'
        glitch_types:
          type: array
          minItems: 1
          uniqueItems: true
          default:
            - visual_glitch
            - prompt_misalignment
          items:
            $ref: '#/components/schemas/GlitchType'
        metadata:
          type: object
          description: >-
            JSON metadata echoed on the evaluation. Serialized size must not
            exceed 8192 bytes.
          additionalProperties: true
      examples:
        - model: galileo
          prompt: A red ball rolls off a table and bounces twice.
          video:
            url: https://cdn.example.com/red-ball.mp4
          metadata:
            run_id: render-123
    Evaluation:
      type: object
      required:
        - id
        - object
        - model
        - model_version
        - output_schema_version
        - created
        - status
        - input
        - usage
        - result
      properties:
        id:
          type: string
          format: uuid
        object:
          type: string
          const: evaluation
        model:
          $ref: '#/components/schemas/ModelId'
        model_version:
          type: string
        output_schema_version:
          type: string
        created:
          type: integer
          minimum: 0
          description: Unix timestamp in seconds.
        status:
          $ref: '#/components/schemas/EvaluationStatus'
        input:
          type: object
          required:
            - prompt
            - video
          properties:
            prompt:
              type: string
            video:
              $ref: '#/components/schemas/VideoInfo'
        usage:
          $ref: '#/components/schemas/EvaluationUsage'
        result:
          oneOf:
            - $ref: '#/components/schemas/EvaluationResult'
            - type: 'null'
        error:
          description: >-
            Why the run failed, on a `failed` one. NULL on a run that did not
            fail, and ABSENT on rows written before this field existed.

            Not in `required`, and that is a correction rather than a
            preference: it was, and the older rows in the store do not carry the
            key at all. A client that enforced the contract at runtime — the
            Python one does, since its models validate — raised on any page deep
            enough to reach them, while the TypeScript client passed because its
            types are erased before a response is ever seen. So the contract was
            not merely wrong, it was wrong in a way that only one of the two
            clients could report.

            Test for a VALUE, not for the key.
          oneOf:
            - $ref: '#/components/schemas/EvaluationFailure'
            - type: 'null'
        detectors:
          type: array
          description: Per-detector state. Older evaluations may omit this field.
          items:
            $ref: '#/components/schemas/DetectorState'
        video_id:
          type:
            - string
            - 'null'
          description: Stored video identifier when the evaluation used an uploaded video.
        attempt:
          type: integer
          minimum: 1
          description: >-
            Which try this is. 1 for a run submitted directly; 2 or more for one
            produced by `POST /v1/evaluations/{evaluation_id}/retry`. There is a
            ceiling, so a clip that keeps failing under the same instructions
            stops being retryable rather than being retried forever.
        timing:
          description: >-
            How long this run took. NULL when nothing was measured -- which is
            every run settled before it was recorded, and is not backfillable.

            Null and never 0. A run whose latency nobody recorded and a run that
            took no time are different claims, and only one of them is true.

            NOT in `required`, for the same reason `error` is not: during a
            rolling deploy some tasks are still the older build, and a response
            from one of those carries no such key. A client that enforces the
            contract at runtime -- the Python one does -- would raise on those
            responses, and it would raise only during a deploy, which is the
            worst time to be debugging a client. Test for a VALUE, not for the
            key.
          oneOf:
            - $ref: '#/components/schemas/Timing'
            - type: 'null'
        metadata:
          type:
            - object
            - 'null'
          additionalProperties: true
          description: >-
            Whatever you passed on create, echoed back. NULL when you passed
            nothing -- the key is always present, its value says whether there
            was any.

            Only found by submitting through the API. Every evaluation created
            in the console carries metadata, so a contract checked against
            console traffic alone looked correct here.
    ModelId:
      type: string
      enum:
        - galileo
        - gemini
      default: galileo
    VideoRef:
      description: >-
        How to give us the video. Exactly one of the three, and the choice is
        mostly about size.

        `upload_id` is the general answer: upload the file first (three calls,
        one of them straight to storage -- see `POST /v1/videos`) and reference
        it here. It is the only option that both reaches the 50 MB file limit
        and keeps the video private to your account.

        `url` is the shortcut when the video is already hosted somewhere we can
        GET. Note that it has to be publicly reachable; we send no credentials.

        `b64_json` sends the bytes inline. Convenient for a small local file,
        but bounded by the request body limit rather than the file limit -- see
        `POST /v1/evaluations`.
      oneOf:
        - $ref: '#/components/schemas/VideoUrlRef'
        - $ref: '#/components/schemas/VideoUploadRef'
        - $ref: '#/components/schemas/VideoBase64Ref'
    GlitchType:
      type: string
      enum:
        - visual_glitch
        - prompt_misalignment
    EvaluationStatus:
      type: string
      enum:
        - queued
        - processing
        - completed
        - partial
        - failed
    VideoInfo:
      type: object
      additionalProperties: false
      required:
        - duration_sec
        - width
        - height
        - fps
        - num_frames
      properties:
        duration_sec:
          type: number
          minimum: 0
        width:
          type: integer
          minimum: 1
        height:
          type: integer
          minimum: 1
        fps:
          type: number
          exclusiveMinimum: 0
        num_frames:
          type: integer
          minimum: 1
    EvaluationUsage:
      type: object
      additionalProperties: false
      required:
        - video_seconds
        - billable_units
      properties:
        video_seconds:
          type: number
          minimum: 0
        billable_units:
          type: number
          minimum: 0
    EvaluationResult:
      type: object
      additionalProperties: false
      required:
        - glitches
        - summary
      properties:
        glitches:
          type: array
          items:
            $ref: '#/components/schemas/Glitch'
        summary:
          $ref: '#/components/schemas/EvaluationSummary'
    EvaluationFailure:
      type: object
      additionalProperties: false
      required:
        - type
        - code
        - message
        - request_id
      properties:
        type:
          type: string
        code:
          type: string
        message:
          type: string
        request_id:
          type: string
    DetectorState:
      type: object
      additionalProperties: false
      required:
        - detector
        - status
      properties:
        detector:
          $ref: '#/components/schemas/GlitchType'
        status:
          $ref: '#/components/schemas/DetectorStatus'
        error:
          $ref: '#/components/schemas/DetectorError'
    Timing:
      type: object
      additionalProperties: false
      required:
        - e2e_ms
      properties:
        e2e_ms:
          type: integer
          minimum: 0
          description: >-
            Milliseconds from submission to the terminal answer, measured by the
            service. This is what you waited: it includes our queueing, fetching
            the video, and the model's own time.

            One number rather than a breakdown, deliberately. The finer
            measurements are the model server's own clock in its own units, and
            a number read from the wrong level is wrong by three orders of
            magnitude rather than plausibly close.
    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          $ref: '#/components/schemas/Error'
        quota:
          $ref: '#/components/schemas/RateLimitWindow'
    VideoUrlRef:
      type: object
      additionalProperties: false
      required:
        - url
      properties:
        url:
          type: string
          format: uri
          pattern: ^https?://
          description: Public HTTP or HTTPS URL for an MP4 video.
    VideoUploadRef:
      type: object
      additionalProperties: false
      required:
        - upload_id
      properties:
        upload_id:
          type: string
          description: >-
            The `video_id` of a video you uploaded and whose status has reached
            `ready`. See `POST /v1/videos`.
    VideoBase64Ref:
      type: object
      additionalProperties: false
      required:
        - b64_json
      properties:
        b64_json:
          type: string
          contentEncoding: base64
          description: >-
            Base64-encoded MP4 bytes, sent inline with the request.

            Bounded by the 2 MB request body limit, not by the 50 MB file limit:
            base64 costs a third more than the bytes it carries, so the largest
            clip that fits inline is roughly 1.5 MB. That is well under a
            typical 15-second render. Use `url` for anything larger -- a body
            over the limit is rejected with 413, which is a transport failure
            rather than a validation one and so is not in this endpoint's error
            list.
    Glitch:
      description: >-
        One finding. Which fields it carries depends on `type`, so branch on
        that rather than probing for a field.

        A KEY THAT DOES NOT APPLY IS ABSENT, not null. This is a correction: the
        service used to send `severity: null` on a visual glitch and
        `prompt_segment: null` alongside it, and this schema said so. It no
        longer does — the response is now assembled from an allowlist per
        finding type, so a visual glitch has no `severity` key at all. Code
        written against the old shape kept working, because `x.severity == null`
        is true either way; code that tested `"severity" in x` did not.
      oneOf:
        - $ref: '#/components/schemas/VisualGlitch'
        - $ref: '#/components/schemas/PromptMisalignment'
      discriminator:
        propertyName: type
        mapping:
          visual_glitch:
            $ref: '#/components/schemas/VisualGlitch'
          prompt_misalignment:
            $ref: '#/components/schemas/PromptMisalignment'
    EvaluationSummary:
      type: object
      additionalProperties: false
      required:
        - num_glitches
        - has_visual_glitch
        - has_prompt_misalignment
      properties:
        num_glitches:
          type: integer
          minimum: 0
        has_visual_glitch:
          type: boolean
        has_prompt_misalignment:
          type: boolean
    DetectorStatus:
      type: string
      enum:
        - pending
        - reused
        - completed
        - failed
        - skipped
    DetectorError:
      type: object
      additionalProperties: false
      required:
        - code
        - message
      properties:
        code:
          type: string
        message:
          type: string
    Error:
      type: object
      required:
        - type
        - code
        - message
        - request_id
      properties:
        type:
          $ref: '#/components/schemas/ErrorType'
        code:
          $ref: '#/components/schemas/ErrorCode'
        message:
          type: string
        request_id:
          type: string
    RateLimitWindow:
      type: object
      required:
        - limit
        - used
        - remaining
        - reset_at
        - reset_in_sec
        - window_sec
        - unlimited
      properties:
        limit:
          type: integer
          minimum: 0
        used:
          type: integer
          minimum: 0
        remaining:
          type: integer
          minimum: 0
        reset_at:
          type:
            - integer
            - 'null'
          minimum: 0
        reset_in_sec:
          type:
            - integer
            - 'null'
          minimum: 0
        window_sec:
          type: integer
          minimum: 1
        unlimited:
          type: boolean
    VisualGlitch:
      type: object
      additionalProperties: false
      required:
        - id
        - type
        - description
      properties:
        id:
          type: string
        type:
          type: string
          const: visual_glitch
        description:
          type: string
        source:
          $ref: '#/components/schemas/GlitchSource'
        region:
          $ref: '#/components/schemas/GlitchRegion'
          description: |-
            Where in the video this finding is, with per-frame boxes.
            Absent on a finding the detector localised no further than the clip.
    PromptMisalignment:
      type: object
      additionalProperties: false
      required:
        - id
        - type
        - description
      properties:
        id:
          type: string
        type:
          type: string
          const: prompt_misalignment
        description:
          type: string
        source:
          $ref: '#/components/schemas/GlitchSource'
        prompt_segment:
          $ref: '#/components/schemas/PromptSegment'
          description: The span of your prompt this finding is about.
        severity:
          type: integer
          minimum: 1
          maximum: 5
          description: >-
            How far a prompt requirement was from being realized: `6 - score`,
            so 1 is a minor mismatch and 5 means the requirement is absent
            entirely. It reads in the OPPOSITE direction from a score, which is
            the one thing worth getting right before you threshold on it.

            This is also the number our own reporting threshold reads, so a
            finding you receive is by definition one that cleared it.

            There is deliberately no `confidence` beside it. That is the model's
            certainty about its own answer -- a different axis, not stable
            across releases, and not calibrated for anyone outside to threshold
            on.

            Optional: a finding from an older pipeline may carry no score.
    ErrorType:
      type: string
      enum:
        - invalid_request_error
        - authentication_error
        - rate_limit_error
        - api_error
    ErrorCode:
      type: string
      enum:
        - invalid_body
        - unknown_model
        - missing_video
        - missing_prompt
        - prompt_too_long
        - invalid_glitch_types
        - video_too_long
        - invalid_video
        - not_found
        - not_implemented
        - insufficient_credits
        - missing_api_key
        - invalid_api_key
        - unauthenticated
        - rate_limited
        - concurrency_limit
        - internal_error
        - model_unavailable
        - model_output_invalid
        - model_timeout
        - run_abandoned
    GlitchSource:
      type: string
      enum:
        - model
        - human
      description: >-
        Who produced this finding. Absent means the model, which is the common
        case -- the field is only set explicitly where a human annotated
        something the model missed, so treat its absence as `model` rather than
        as unknown.

        Deliberately NOT declared with a `default`. A default reads to a code
        generator as "the server always sends this", and it does not: the value
        is omitted, not defaulted, and a generated type that made it required
        would be wrong on almost every finding.
    GlitchRegion:
      type: object
      additionalProperties: false
      required:
        - start
        - end
        - boxes
      properties:
        start:
          $ref: '#/components/schemas/TimePoint'
        end:
          $ref: '#/components/schemas/TimePoint'
        boxes:
          type: array
          items:
            $ref: '#/components/schemas/BoxKeyframe'
    PromptSegment:
      type: object
      additionalProperties: false
      required:
        - text
        - char_start
        - char_end
      properties:
        text:
          type: string
        char_start:
          type: integer
          minimum: 0
        char_end:
          type: integer
          minimum: 0
    TimePoint:
      type: object
      additionalProperties: false
      required:
        - frame
        - sec
        - timecode
      properties:
        frame:
          type: integer
          minimum: 0
        sec:
          type: number
          minimum: 0
        timecode:
          type: string
          pattern: ^[0-9]{2}:[0-9]{2}:[0-9]{2}:[0-9]{2}$
    BoxKeyframe:
      type: object
      additionalProperties: false
      required:
        - frame
        - sec
        - box
      properties:
        frame:
          type: integer
          minimum: 0
        sec:
          type: number
          minimum: 0
        box:
          $ref: '#/components/schemas/BoundingBox'
    BoundingBox:
      type: object
      additionalProperties: false
      required:
        - xmin
        - ymin
        - xmax
        - ymax
      properties:
        xmin:
          type: number
          minimum: 0
          maximum: 1
        ymin:
          type: number
          minimum: 0
          maximum: 1
        xmax:
          type: number
          minimum: 0
          maximum: 1
        ymax:
          type: number
          minimum: 0
          maximum: 1
  headers:
    RateLimitLimit:
      description: Maximum submissions in the current sliding window.
      schema:
        type: integer
        minimum: 0
    RateLimitRemaining:
      description: Submissions available before the next refusal.
      schema:
        type: integer
        minimum: 0
    RateLimitReset:
      description: Unix seconds when the next rate-limit slot becomes available.
      schema:
        type: integer
        minimum: 0
    RetryAfter:
      description: Seconds to wait before retrying.
      schema:
        type: integer
        minimum: 1
  responses:
    BadRequest:
      description: The request cannot be processed as sent.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Unauthorized:
      description: The API key is missing, invalid, or revoked.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    InsufficientCredits:
      description: The account balance cannot cover this evaluation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    RateLimited:
      description: The account reached a rate or concurrency limit.
      headers:
        X-RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    InternalServerError:
      description: The API encountered an unexpected error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    BadGateway:
      description: A model service failed or returned an unusable response.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    GatewayTimeout:
      description: A model run exceeded its deadline.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Galileo API key
      description: Use a key created in the Playground. Keys begin with `gk_`.

````