> ## 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.

# Run a failed evaluation again

> Re-files a failed run's own request and returns the NEW evaluation, in
`queued`. The original is left alone, with `retried_by` pointing at the
successor — the failure stays on the record, because `error.code` is the
only thing that can answer whether retrying was worth it.

**This is the one submission in this API that is idempotent**, and it is
idempotent on the run being retried rather than on your request. A burst
of presses picks exactly one winner; every other press is handed that same
winner. `POST /v1/evaluations` has no such guarantee — see its
description.

**It costs the ordinary price, and that is not paying twice.** A run that
failed was refunded when it settled: in full on `failed`, and on `partial`
exactly the detectors that did not land. The detectors that DID land were
cached at the same moment, so the retry pays for the missing legs and gets
the rest from cache. It consumes a `detect` rate-limit slot like any
submission.

Refused with `400` when there is nothing to retry, and the message says
which case it is: the run has not finished, it did not fail, it delivered
every detector it was asked for, it analyzed no stored clip (submit it
fresh instead), it has already been attempted too many times, or it was
already retried and that retry has since been deleted.




## OpenAPI

````yaml /openapi/galileo-v1.yaml post /v1/evaluations/{evaluation_id}/retry
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/{evaluation_id}/retry:
    parameters:
      - $ref: '#/components/parameters/EvaluationId'
    post:
      tags:
        - Evaluations
      summary: Run a failed evaluation again
      description: >
        Re-files a failed run's own request and returns the NEW evaluation, in

        `queued`. The original is left alone, with `retried_by` pointing at the

        successor — the failure stays on the record, because `error.code` is the

        only thing that can answer whether retrying was worth it.


        **This is the one submission in this API that is idempotent**, and it is

        idempotent on the run being retried rather than on your request. A burst

        of presses picks exactly one winner; every other press is handed that
        same

        winner. `POST /v1/evaluations` has no such guarantee — see its

        description.


        **It costs the ordinary price, and that is not paying twice.** A run
        that

        failed was refunded when it settled: in full on `failed`, and on
        `partial`

        exactly the detectors that did not land. The detectors that DID land
        were

        cached at the same moment, so the retry pays for the missing legs and
        gets

        the rest from cache. It consumes a `detect` rate-limit slot like any

        submission.


        Refused with `400` when there is nothing to retry, and the message says

        which case it is: the run has not finished, it did not fail, it
        delivered

        every detector it was asked for, it analyzed no stored clip (submit it

        fresh instead), it has already been attempted too many times, or it was

        already retried and that retry has since been deleted.
      operationId: retryEvaluation
      responses:
        '201':
          description: A new evaluation, queued.
          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'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '501':
          $ref: '#/components/responses/NotImplemented'
components:
  parameters:
    EvaluationId:
      name: evaluation_id
      in: path
      required: true
      description: Evaluation identifier.
      schema:
        type: string
        format: uuid
  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
  schemas:
    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
    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'
    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
    GlitchType:
      type: string
      enum:
        - visual_glitch
        - prompt_misalignment
    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
  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'
    NotFound:
      description: The resource does not exist or is unavailable to this account.
      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'
    NotImplemented:
      description: This deployment does not implement the requested operation.
      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_`.

````