openapi: 3.1.0
info:
  title: Valossa Core API
  version: "1.0"
  description: |
    The Valossa Core API is a REST API for submitting video analysis jobs and retrieving
    rich AI-generated metadata. It analyzes speech, audio, visual content, and faces to
    produce time-coded detections covering objects, people, sounds, topics, emotions,
    and more.

    **Base URL:** `https://api-eu.valossa.com/core/1.0/`

    All requests must use HTTPS. Responses are JSON.

    **Authentication:** Pass your API key as the `api_key` field in the JSON body
    (POST requests) or as the `api_key` query parameter (GET requests).

    **Get an API key:** Sign up for a free Transcribe Pro Vision MAX trial at
    https://valossa.com/transcribe-video-to-text-ai/ then retrieve your key from
    Valossa Portal under My Account → Subscriptions and API Keys.
  contact:
    name: Valossa Support
    url: https://valossa.com/contact/
  license:
    name: Proprietary
    url: https://valossa.com/terms/
  x-logo:
    url: https://valossa.com/wp-content/themes/valossa/img/valossa-logo.svg

servers:
  - url: https://api-eu.valossa.com/core/1.0/
    description: Valossa EU production API

tags:
  - name: Jobs
    description: Create, monitor, retrieve, and manage video analysis jobs
  - name: Uploads
    description: Chunked upload workflow for large video files

paths:
  /new_job:
    post:
      operationId: newJob
      summary: Create a new video analysis job
      description: |
        Submits a video for AI analysis. Returns a `job_id` UUID that you use
        to poll status and retrieve results. The video can be provided via a
        download URL or via a previously uploaded `valossaupload://` URI.
      tags: [Jobs]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NewJobRequest'
            examples:
              url_download:
                summary: Analyze video from URL
                value:
                  api_key: "YOUR_API_KEY"
                  media:
                    video:
                      url: "https://example.com/my-video.mp4"
              with_title_and_language:
                summary: Analyze with title, description and language
                value:
                  api_key: "YOUR_API_KEY"
                  media:
                    video:
                      url: "https://example.com/my-video.mp4"
                    title: "Product Launch 2026"
                    description: "Annual product launch event for the new widget line"
                    language: "en-US"
              with_transcript:
                summary: Analyze with existing SRT transcript
                value:
                  api_key: "YOUR_API_KEY"
                  media:
                    video:
                      url: "https://example.com/my-video.mp4"
                    transcript:
                      url: "https://example.com/my-transcript.srt"
      responses:
        "200":
          description: Job created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NewJobResponse'
              example:
                job_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
        "400":
          $ref: '#/components/responses/BadRequest'
        "401":
          $ref: '#/components/responses/Unauthorized'
        "429":
          $ref: '#/components/responses/RateLimited'

  /job_status:
    get:
      operationId: jobStatus
      summary: Get the status of a job
      description: |
        Polls the current status of a video analysis job. The response includes
        a `poll_again_after_seconds` field indicating how many seconds to wait before
        polling again.
      tags: [Jobs]
      parameters:
        - $ref: '#/components/parameters/api_key'
        - $ref: '#/components/parameters/job_id'
      responses:
        "200":
          description: Job status retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobStatusResponse'
              examples:
                processing:
                  summary: Job still processing
                  value:
                    job_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                    status: "processing"
                    media_transfer_status: "finished"
                    details: null
                    poll_again_after_seconds: 30
                finished:
                  summary: Job finished
                  value:
                    job_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                    status: "finished"
                    media_transfer_status: "finished"
                    details: null
                    poll_again_after_seconds: 0
        "400":
          $ref: '#/components/responses/BadRequest'
        "401":
          $ref: '#/components/responses/Unauthorized'
        "404":
          $ref: '#/components/responses/NotFound'

  /job_results:
    get:
      operationId: jobResults
      summary: Download results for a completed job
      description: |
        Retrieves the metadata output for a completed analysis job.
        Use the `type` parameter to select which metadata format to download.
        Store the result in your own database — results may not be retained
        indefinitely in the Valossa system.
      tags: [Jobs]
      parameters:
        - $ref: '#/components/parameters/api_key'
        - $ref: '#/components/parameters/job_id'
        - name: type
          in: query
          required: false
          description: |
            Which metadata format to download. Defaults to `core`.
            - `core` — Full Valossa Core Metadata JSON (default)
            - `frames_faces` — Per-frame face bounding boxes JSON
            - `seconds_objects` — Per-second object bounding boxes JSON
            - `frames_objects` — Per-frame object bounding boxes JSON
            - `speech_to_text_srt` — Speech transcript in SRT subtitle format
            - `speech_to_text_vtt` — Speech transcript in WebVTT format
            - `speech_to_text_ttml` — Speech transcript in TTML format
            - `speech_to_text_avid_txt` — Speech transcript in Avid TXT format
            - `visual_captions` — Visual scene descriptions
          schema:
            type: string
            enum:
              - core
              - frames_faces
              - seconds_objects
              - frames_objects
              - speech_to_text_srt
              - speech_to_text_vtt
              - speech_to_text_ttml
              - speech_to_text_avid_txt
              - visual_captions
            default: core
      responses:
        "200":
          description: Results downloaded successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CoreMetadata'
            text/plain:
              schema:
                type: string
                description: SRT subtitle content (when type=speech_to_text_srt)
        "400":
          $ref: '#/components/responses/BadRequest'
        "401":
          $ref: '#/components/responses/Unauthorized'
        "404":
          $ref: '#/components/responses/NotFound'
        "409":
          description: Job not yet completed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /list_jobs:
    get:
      operationId: listJobs
      summary: List all jobs for an API key
      description: Returns a list of all analysis jobs associated with the given API key.
      tags: [Jobs]
      parameters:
        - $ref: '#/components/parameters/api_key'
        - name: show_status
          in: query
          required: false
          schema:
            type: boolean
          description: Include `job_status` object for each job when true
        - name: show_media_info
          in: query
          required: false
          schema:
            type: boolean
          description: Include media information (for example title) when true
        - name: n_jobs
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 10000
            default: 200
          description: Number of jobs to return
      responses:
        "200":
          description: Job list retrieved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListJobsResponse'
        "401":
          $ref: '#/components/responses/Unauthorized'

  /cancel_job:
    post:
      operationId: cancelJob
      summary: Cancel a job
      description: Cancels a job if it is still in a cancellable state (queued or early processing).
      tags: [Jobs]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/JobActionRequest'
            example:
              api_key: "YOUR_API_KEY"
              job_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
      responses:
        "200":
          description: Job cancelled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobActionResponse'
        "400":
          $ref: '#/components/responses/BadRequest'
        "401":
          $ref: '#/components/responses/Unauthorized'
        "404":
          $ref: '#/components/responses/NotFound'
        "409":
          description: Job cannot be cancelled in its current state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /delete_job:
    post:
      operationId: deleteJob
      summary: Delete a job and its assets
      description: Permanently deletes a job and all associated assets (video file, metadata).
      tags: [Jobs]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/JobActionRequest'
            example:
              api_key: "YOUR_API_KEY"
              job_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
      responses:
        "200":
          description: Job deleted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobActionResponse'
        "400":
          $ref: '#/components/responses/BadRequest'
        "401":
          $ref: '#/components/responses/Unauthorized'
        "404":
          $ref: '#/components/responses/NotFound'

  /initialize_file_upload:
    post:
      operationId: initializeFileUpload
      summary: Initialize a chunked video upload
      description: Initializes upload and returns `upload_id` and required chunk size.
      tags: [Uploads]
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [api_key, file_size_bytes]
              properties:
                api_key:
                  type: string
                file_size_bytes:
                  type: integer
      responses:
        "200":
          description: Upload initialized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InitializeFileUploadResponse'
        "400":
          $ref: '#/components/responses/BadRequest'
        "401":
          $ref: '#/components/responses/Unauthorized'

  /send_file_chunk:
    post:
      operationId: sendFileChunk
      summary: Send one upload chunk
      description: Sends one chunk for an upload initialized by `initialize_file_upload`.
      tags: [Uploads]
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [api_key, upload_id, chunk_index, file_data]
              properties:
                api_key:
                  type: string
                upload_id:
                  type: string
                  format: uuid
                chunk_index:
                  type: integer
                file_data:
                  type: string
                  format: binary
      responses:
        "200":
          description: Chunk accepted
          content:
            application/json:
              schema:
                type: object
        "400":
          $ref: '#/components/responses/BadRequest'
        "401":
          $ref: '#/components/responses/Unauthorized'

  /finalize_file_upload:
    post:
      operationId: finalizeFileUpload
      summary: Finalize chunked upload
      description: Finalizes upload and returns `uploaded_file_url` for `new_job`.
      tags: [Uploads]
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [api_key, upload_id]
              properties:
                api_key:
                  type: string
                upload_id:
                  type: string
                  format: uuid
      responses:
        "200":
          description: Upload finalized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FinalizeFileUploadResponse'
        "400":
          $ref: '#/components/responses/BadRequest'
        "401":
          $ref: '#/components/responses/Unauthorized'

components:
  parameters:
    api_key:
      name: api_key
      in: query
      required: true
      description: Your Valossa API key
      schema:
        type: string
        example: "YOUR_API_KEY"
    job_id:
      name: job_id
      in: query
      required: true
      description: The UUID of the job
      schema:
        type: string
        format: uuid
        example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"

  schemas:
    NewJobRequest:
      type: object
      required: [api_key, media]
      properties:
        api_key:
          type: string
          description: Your Valossa API key
          example: "YOUR_API_KEY"
        media:
          type: object
          required: [video]
          properties:
            video:
              type: object
              required: [url]
              properties:
                url:
                  type: string
                  format: uri
                  description: |
                    Direct URL to the video file. Supports HTTP/HTTPS, Google Drive,
                    Dropbox, AWS S3 (if configured), and `valossaupload://` URIs.
                  example: "https://example.com/my-video.mp4"
            transcript:
              type: object
              description: Optional pre-existing SRT transcript. Disables automatic speech-to-text.
              properties:
                url:
                  type: string
                  format: uri
                  description: Direct URL to an SRT subtitle file (max 5MB)
                  example: "https://example.com/my-transcript.srt"
            title:
              type: string
              description: Optional title for the video. Analyzed as external keyword source.
              example: "Product Launch 2026"
            description:
              type: string
              description: |
                Optional freetext description in English. Analyzed as external keyword source.
                Detected concepts appear in `external.keyword.*` detections.
              example: "Annual product launch event covering the new widget line."
            language:
              type: string
              description: |
                BCP-47 language code for speech analysis. Defaults to `en-US`.
                See supported languages documentation.
              example: "en-US"
              default: "en-US"
        analysis_parameters:
          type: object
          description: Optional fine-tuning parameters for analysis
          properties:
            face_min_relative_height:
              type: number
              format: float
              minimum: 0
              maximum: 1
              description: Minimum face height as a fraction of frame height to be detected
              example: 0.1

    NewJobResponse:
      type: object
      properties:
        job_id:
          type: string
          format: uuid
          description: UUID of the created job. Store this for subsequent status and results calls.
          example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"

    JobStatusResponse:
      type: object
      properties:
        job_id:
          type: string
          format: uuid
        status:
          type: string
          enum: [queued, on_hold, preparing_analysis, processing, finished, error]
          description: |
            - `queued` — Job is waiting to be processed
            - `on_hold` — Job is temporarily paused before processing
            - `preparing_analysis` — Processing setup in progress
            - `processing` — Video is being analyzed by AI models
            - `finished` — Analysis is finished; results are available
            - `error` — An error occurred during processing
        media_transfer_status:
          type: string
          enum: [queued, downloading, finished, error]
          description: Transfer status of source media into Valossa processing pipeline
        details:
          oneOf:
            - type: string
            - type: "null"
          description: Additional details, often present when status is `error`
        poll_again_after_seconds:
          type: integer
          description: Suggested number of seconds to wait before polling again
          example: 30

    JobSummary:
      type: object
      description: A single job entry as returned in `jobs` array.
      properties:
        job_id:
          type: string
          format: uuid
        job_status:
          $ref: '#/components/schemas/JobStatusResponse'
        media:
          type: object
          description: Present when `show_media_info=true`

    ListJobsResponse:
      type: object
      properties:
        jobs:
          type: array
          items:
            $ref: '#/components/schemas/JobSummary'

    JobActionRequest:
      type: object
      required: [api_key, job_id]
      properties:
        api_key:
          type: string
          example: "YOUR_API_KEY"
        job_id:
          type: string
          format: uuid
          example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"

    JobActionResponse:
      type: object
      properties:
        job_id:
          type: string
          format: uuid
        status:
          type: string

    CoreMetadata:
      type: object
      description: |
        Valossa Core Metadata JSON (format version 1.8.1). Contains all AI detections,
        temporal groupings, and media information for an analyzed video.
        See full metadata documentation at https://docs.valossa.com/metadata/overview
      properties:
        version_info:
          type: object
          properties:
            metadata_format:
              type: string
              example: "1.8.1"
            backend:
              type: string
              example: "3.1.1"
            metadata_type:
              type: string
              enum: [core, frames_faces, seconds_objects, frames_objects, visual_captions]
              example: "core"

    InitializeFileUploadResponse:
      type: object
      properties:
        upload_id:
          type: string
          format: uuid
        file_chunk_size_bytes:
          type: integer

    FinalizeFileUploadResponse:
      type: object
      properties:
        uploaded_file_url:
          type: string
          example: "valossaupload://7c62bc7b-e143-4a81-aa83-a7eb0ec37077"
        job_info:
          type: object
          properties:
            job_id:
              type: string
              format: uuid
            request:
              type: object
        media_info:
          type: object
          properties:
            technical:
              type: object
              properties:
                duration_s:
                  type: number
                  example: 1234.56
                fps:
                  type: number
                  example: 24
            from_customer:
              type: object
              properties:
                title:
                  type: string
        detections:
          type: object
          description: |
            Associative array keyed by detection ID (string). Each value is a detection
            with at minimum `t` (type) and `label` fields.
          additionalProperties:
            $ref: '#/components/schemas/Detection'
        detection_groupings:
          type: object
          properties:
            by_detection_type:
              type: object
              description: Detection IDs grouped by detection type, sorted by relevance
              additionalProperties:
                type: array
                items:
                  type: string
            by_second:
              type: array
              description: Per-second index of detections present at each second of video
              items:
                type: array
                items:
                  type: object
                  properties:
                    d:
                      type: string
                      description: Detection ID
                    c:
                      type: number
                      description: Confidence at this second
                    o:
                      type: array
                      items:
                        type: string
                      description: Occurrence IDs active at this second
        segmentations:
          type: object
          description: Shot boundary detection data

    Detection:
      type: object
      required: [t, label]
      properties:
        t:
          type: string
          description: Detection type identifier (e.g. visual.context, human.face)
          example: "visual.context"
        label:
          type: string
          description: Human-readable label for the detected concept
          example: "sunglasses"
        cid:
          type: string
          description: Valossa Concept ID (present on visual.context and audio.context)
          example: "02g28TYU3dMt"
        ext_refs:
          type: object
          description: References to external ontologies (Wikidata, GKG, IAB)
          properties:
            wikidata:
              type: object
              properties:
                id:
                  type: string
                  example: "Q111"
            gkg:
              type: object
              properties:
                id:
                  type: string
                  example: "/m/01yrx"
            iab:
              type: object
              properties:
                id:
                  type: string
                  example: "226"
                labels_hierarchy:
                  type: array
                  items:
                    type: string
        categ:
          type: object
          description: Category tags for this detection (e.g. content_compliance, violence)
        a:
          type: object
          description: |
            Additional attributes, detection-type specific.
            For human.face: gender, similar_to, quality, s_visible.
            For audio.speech: sen (sentiment/valence).
        occs:
          type: array
          description: Time ranges where this detection appears in the video
          items:
            $ref: '#/components/schemas/Occurrence'

    Occurrence:
      type: object
      properties:
        id:
          type: string
          description: Occurrence ID (string)
        ss:
          type: number
          description: Start second (relative to video start)
          example: 12.34
        se:
          type: number
          description: End second
          example: 23.45
        c_max:
          type: number
          description: Maximum confidence during this occurrence (visual.context and audio.context only)
          example: 0.92
        c:
          type: number
          description: Confidence for this occurrence (topic.iab.section only)
        shs:
          type: integer
          description: Shot index where occurrence starts
        she:
          type: integer
          description: Shot index where occurrence ends

    ErrorResponse:
      type: object
      properties:
        errors:
          type: array
          items:
            type: object
            properties:
              message:
                type: string
                example: "Invalid API key"

  responses:
    BadRequest:
      description: Malformed request or missing required fields
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            errors:
              - message: "Missing required field: media.video.url"
    Unauthorized:
      description: Invalid or missing API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            errors:
              - message: "Invalid API key"
    NotFound:
      description: Job not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            errors:
              - message: "Job not found"
    RateLimited:
      description: Too many requests
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            errors:
              - message: "Rate limit exceeded. Please reduce request frequency."
