openapi: 3.0.3
info:
  title: ScorePlay Media API
  version: 1.0.0
  description: |
    The ScorePlay Media API lets you search, enrich, transform, and deliver
    media assets programmatically — the same library that powers your apps,
    websites, and partner integrations.

    All requests authenticate with an `api_key` query parameter and are scoped
    to your organization. Responses are JSON; media search returns three
    quality tiers (photographer, compressed, thumbnail) plus dynamic image URLs.

    Use the **Try it** panel on any endpoint to call the live API with your key,
    or download the OpenAPI spec and a ready-to-import Postman collection from
    the Downloads menu.
  contact:
    name: ScorePlay API Support
    email: engineering@scoreplay.io

servers:
  - url: https://media.scoreplay.io/v1

security:
  - ApiKeyAuth: []

paths:
  /tag/search:
    get:
      summary: Search for public tags, players, and sponsors
      description: |
        Search for tags, players, and sponsors within a company. This endpoint supports text-based search with optional language filtering and external ID lookups.

        ## Usage
        - Use `query` parameter to search by name/text
        - Use `external_id` parameter to find players by external identifier

      operationId: searchPublicTags
      parameters:
        - name: query
          in: query
          description: Search query string to match against tag names, player names, and
            sponsor labels
          schema:
            type: string
            example: goal
        - name: language
          in: query
          description: Language code for filtering tag results
          schema:
            type: string
            example: en
        - name: external_id
          in: query
          description: External identifier for player lookup.
          schema:
            type: string
            example: player123
      responses:
        '200':
          description: Successfully retrieved search results
          content:
            application/json:
              schema:
                type: object
                properties:
                  tags:
                    type: array
                    description: List of matching tag options
                    items:
                      $ref: '#/components/schemas/TagOption'
                  players:
                    type: array
                    description: List of matching players
                    items:
                      $ref: '#/components/schemas/Player'
                  sponsors:
                    type: array
                    description: List of matching sponsors
                    items:
                      $ref: '#/components/schemas/Sponsor'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
      tags:
        - Tags

  /media/{id}:
    get:
      summary: Get media by ID
      description: |
        Retrieve detailed information about a specific media item by its ID. This endpoint returns comprehensive media data including files, tags, metadata, and related information.
      operationId: getMediaByIdPublic
      parameters:
        - name: id
          in: path
          description: Unique identifier of the media item
          required: true
          schema:
            type: integer
            format: int64
            example: 12345
      responses:
        '200':
          description: Successfully retrieved media information
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Media'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Media not found or access denied
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          $ref: '#/components/responses/InternalServerError'
      tags:
        - Media

  /media/search:
    post:
      summary: Search for media content
      description: |
        Perform advanced search for media content with comprehensive filtering options. This endpoint supports complex queries including date ranges, tag filtering, player/sponsor associations, and more.

        ## Search Capabilities
        - Full-text search by media name or query
        - Date range filtering
        - Tag, player, and sponsor filtering (inclusive/exclusive)
        - Media type filtering (photo/video)
        - Event associations
        - Pagination support
      operationId: searchPublicMedia
      parameters:
        - name: page
          in: query
          description: Page number for pagination (1-based)
          schema:
            type: integer
            minimum: 1
            default: 1
            example: 1
        - name: per_page
          in: query
          description: Number of results per page
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
            example: 20
      requestBody:
        description: Search criteria for media filtering
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SearchMediaPayload'
      responses:
        '200':
          description: Successfully retrieved search results
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaSearchResult'
        '400':
          description: Invalid search criteria
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
      tags:
        - Media

  /event/search:
    post:
      summary: Search for events
      description: |
        Search for events within the company with comprehensive filtering options. Events represent collections or containers for media content.

        ## Search Features
        - Text-based search by event name
        - Date range filtering
        - Team and season associations
        - Tag and player filtering
        - Pagination support
      operationId: searchPublicEvents
      requestBody:
        description: Search criteria for event filtering
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SearchEventPayload'
      responses:
        '200':
          description: Successfully retrieved event search results
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Event'
        '400':
          description: Invalid search criteria
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
      tags:
        - Events

  /upload/event/{id}:
    post:
      summary: Create media upload into event
      description: |
        Initialize media upload process for a specific event. This endpoint creates media records and generates upload URLs for multipart uploads to cloud storage.

        ## Upload Process
        1. Call this endpoint to create media record and get upload parameters
        2. Upload file parts using the returned upload URLs
        3. Call `/upload/complete/{id}` to finalize the upload
      operationId: createPublicMediaIntoEvent
      parameters:
        - name: id
          in: path
          description: Event ID where the media will be uploaded
          required: true
          schema:
            type: integer
            format: int64
            example: 12345
        - name: parts
          in: query
          description: Number of parts for multipart upload
          required: true
          schema:
            type: integer
            minimum: 1
            maximum: 10000
            example: 5
        - name: skip_team_tags
          in: query
          description: Whether to skip automatic team tag assignment
          schema:
            type: string
            example: 'true'
      requestBody:
        description: Media creation payload
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateMediaPayload'
      responses:
        '200':
          description: Successfully created media and returned upload information
          content:
            application/json:
              schema:
                type: object
                properties:
                  upload_id:
                    type: string
                    description: Unique identifier for the multipart upload
                    example: abc123xyz
                  key:
                    type: string
                    description: Storage key/path for the file
                    example: 123/456/media_789_1.jpg
                  urls:
                    type: array
                    description: Pre-signed URLs for uploading file parts
                    items:
                      type: string
                      format: uri
                  media_id:
                    type: integer
                    description: ID of the created media record
                    example: 789
        '400':
          description: Invalid request or event not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
      tags:
        - Upload

  /upload/complete/{id}:
    post:
      summary: Complete multipart upload
      description: |
        Finalize a multipart upload by providing the upload parts information. This endpoint completes the multipart upload process and creates the final file record.

        ## Completion Process
        - Validates all uploaded parts
        - Combines parts into final file
        - Creates file record in database
        - Updates media with file information
        - Triggers post-processing workflows
      operationId: completeS3MultipartUploadUnauth
      parameters:
        - name: id
          in: path
          description: Media ID for which to complete the upload
          required: true
          schema:
            type: integer
            format: int64
            example: 12345
      requestBody:
        description: Upload completion payload with parts information
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CompleteUploadPayload'
      responses:
        '200':
          description: Successfully completed upload
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Media'
        '400':
          description: Invalid upload parts or completion failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
      tags:
        - Upload

  /upload/abort:
    post:
      summary: Abort multipart upload
      description: |
        Cancel an in-progress multipart upload and clean up any uploaded parts. This helps prevent storage costs from incomplete uploads.

        ## Cleanup Process
        - Cancels the multipart upload in cloud storage
        - Removes any uploaded parts
        - No effect on media record (if created)
      operationId: abortS3MultipartUpload
      requestBody:
        description: Upload abort payload
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AbortUploadPayload'
      responses:
        '200':
          description: Successfully aborted upload
          content:
            application/json:
              schema:
                type: boolean
                example: true
        '400':
          description: Invalid abort request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          $ref: '#/components/responses/InternalServerError'
      tags:
        - Upload

  /video/logs:
    post:
      summary: Add video logs
      description: |
        Add action logs to videos for creating highlight clips and action analysis. This endpoint processes video timestamp data and creates action clips for sports analysis.

        ## Video Logs
        - Creates action clips with precise timestamps
        - Supports external ID matching for events
        - Generates searchable action metadata

        ## Timing Modes

        Each video log entry must use one of the following timing modes for the `log` object:

        | Mode | Fields | Description |
        |------|--------|-------------|
        | **Offset single** | `offset` | Single point offset in seconds from the start of the video |
        | **Offset range** | `start_offset` + `end_offset` | Offset range in seconds from the start of the video |
        | **Unix single** | `action_timestamp` | Unix timestamp in seconds for a single action point. Optionally include `start_timestamp`/`end_timestamp` for clip boundaries |
        | **Unix range** | `start_timestamp` + `end_timestamp` (no `action_timestamp`) | Unix timestamps in seconds defining a clip range |

        ## Use Cases
        - Sports highlight generation
        - Video analysis and tagging
        - Action timestamp recording
      operationId: addVideoLogs
      requestBody:
        description: Video logs payload containing action data
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VideoLogPayload'
      responses:
        '200':
          description: Successfully processed video logs
          content:
            application/json:
              schema:
                type: boolean
                example: true
        '400':
          description: Invalid video logs or missing required data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
      tags:
        - Video Logs

  /operations:
    post:
      summary: Create operation
      description: |
        Create a new operation to track async tasks like bulk downloads, clip generation, or watermark requests.
        Operations are used to track the status of long-running tasks.
      operationId: createPublicOperation
      requestBody:
        description: Operation details
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOperationRequest'
      responses:
        '200':
          description: Operation created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Operation'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
      tags:
        - Operations

  /operations/{id}:
    put:
      summary: Update operation
      description: |
        Update an existing operation. Use this to update the status, URL, or other fields of an operation.
        Protected fields (company_id, user_id) cannot be modified.
      operationId: updatePublicOperation
      parameters:
        - name: id
          in: path
          required: true
          description: The operation ID
          schema:
            type: integer
            example: 12345
      requestBody:
        description: Fields to update
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateOperationRequest'
      responses:
        '200':
          description: Operation updated successfully
          content:
            application/json:
              schema:
                type: boolean
                example: true
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Forbidden - operation belongs to another company
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          $ref: '#/components/responses/InternalServerError'
      tags:
        - Operations

  /media/{id}/details:
    put:
      summary: Update media details
      description: Updates the details of a specific media item.
      operationId: updatePublicMediaDetails
      tags:
        - Media
      parameters:
        - name: id
          in: path
          required: true
          description: The unique identifier of the media item
          schema:
            type: integer
            format: uint
            example: 12345
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PublicMediaUpdatePayload'
      responses:
        '204':
          description: Media details updated successfully (No Content)
        '400':
          description: Bad request - Invalid input data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized - Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden - Insufficient permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Media not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /media/{id}/tags:
    post:
      summary: Add tags to media
      description: Adds tags to a specific media item.
      operationId: publicAddTagsToMedia
      tags:
        - Media
      parameters:
        - name: id
          in: path
          required: true
          description: The unique identifier of the media item
          schema:
            type: integer
            format: uint
            example: 12345
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateMediaTagsPayload'
      responses:
        '204':
          description: Tags added successfully (No Content)
        '400':
          description: Bad request - Invalid tag data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized - Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden - Insufficient permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Media not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    delete:
      summary: Remove tags from media
      description: Removes tags from a specific media item.
      operationId: publicDeleteTagsFromMedia
      tags:
        - Media
      parameters:
        - name: id
          in: path
          required: true
          description: The unique identifier of the media item
          schema:
            type: integer
            format: uint
            example: 12345
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateMediaTagsPayload'
      responses:
        '204':
          description: Tags removed successfully (No Content)
        '400':
          description: Bad request - Invalid tag data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized - Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden - Insufficient permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Media not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /media/{id}/generate/dynamic_url:
    post:
      x-beta: true
      summary: Generate dynamic URL for media
      description: Generates a dynamic URL for a specific media item.
      operationId: publicGenerateDynamicUrlForMedia
      tags:
        - Media
      parameters:
        - name: id
          in: path
          required: true
          description: The unique identifier of the media item
          schema:
            type: integer
            format: uint
            example: 12345
      responses:
        '200':
          description: Dynamic URL generated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  dynamic_url:
                    type: string
                    format: uri
                    description: Generated dynamic URL for the media item, that can be used for
                      dynamic cropping and resizing
                    example: https://dynamic-crop-cdn.scoreplay.io/12345.jpg
        '400':
          description: Bad request - Invalid tag data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized - Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden - Insufficient permissions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Media not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /storage/transfer:
    post:
      summary: Transfer assets to BYOS storage
      description: |
        Transfer media assets from ScorePlay to a BYOS (Bring Your Own Storage) destination.
        Supports two modes:
        - **Media IDs mode**: Transfers media by ID, preserving ScorePlay folder structure
        - **URLs mode**: Transfers from raw URLs to specified destination paths

        ## Use Cases
        - Replicate ScorePlay assets to on-premise storage
        - Backup media to client-owned cloud storage (S3, Azure, etc.)
        - Sync assets to network shares (SMB) while preserving folder hierarchy

        ## Folder Structure
        When using `media_ids`, the destination path preserves the ScorePlay event hierarchy.
        For example, media in "UEFA Champions League/Final/Match Day 1" will be transferred to
        the same folder structure in the destination storage.
      operationId: startStorageTransfer
      tags:
        - Storage Transfer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StorageTransferRequest'
      responses:
        '200':
          description: Transfer operation started successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StorageTransferResponse'
        '400':
          description: Invalid request - validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Destination storage not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /storage/transfer/{operation_id}:
    get:
      summary: Get storage transfer status
      description: |
        Get the current status of a storage transfer operation, including per-file progress.
      operationId: getStorageTransferStatus
      tags:
        - Storage Transfer
      parameters:
        - name: operation_id
          in: path
          required: true
          description: The operation ID returned from the transfer request
          schema:
            type: integer
            example: 12345
      responses:
        '200':
          description: Transfer status retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StorageTransferStatusResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Forbidden - operation belongs to another company
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Operation not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /storages:
    get:
      summary: List all storages
      description: |
        Retrieve all BYOS (Bring Your Own Storage) configurations for your company.
        Returns a list of configured storage destinations including S3, Azure, GCS, and SMB shares.
      operationId: listStorages
      tags:
        - Storages
      responses:
        '200':
          description: Successfully retrieved storage list
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Storage'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
    post:
      summary: Create a new storage
      description: |
        Create a new BYOS (Bring Your Own Storage) configuration.
        Supports S3-compatible storage (AWS, Wasabi, etc.), Azure Blob, GCS, and SMB shares.
      operationId: createStorage
      tags:
        - Storages
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateStorageRequest'
      responses:
        '201':
          description: Storage created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Storage'
        '400':
          description: Invalid request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /storages/{storage_id}:
    get:
      summary: Get a storage by ID
      description: |
        Retrieve details of a specific BYOS storage configuration by its UUID.
      operationId: getStorage
      tags:
        - Storages
      parameters:
        - name: storage_id
          in: path
          required: true
          description: UUID of the storage
          schema:
            type: string
            format: uuid
            example: "550e8400-e29b-41d4-a716-446655440000"
      responses:
        '200':
          description: Successfully retrieved storage
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Storage'
        '400':
          description: Invalid storage_id format
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Storage not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          $ref: '#/components/responses/InternalServerError'
    put:
      summary: Update a storage
      description: |
        Update an existing BYOS storage configuration.
      operationId: updateStorage
      tags:
        - Storages
      parameters:
        - name: storage_id
          in: path
          required: true
          description: UUID of the storage to update
          schema:
            type: string
            format: uuid
            example: "550e8400-e29b-41d4-a716-446655440000"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateStorageRequest'
      responses:
        '200':
          description: Storage updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Storage'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
    delete:
      summary: Delete a storage
      description: |
        Delete a BYOS storage configuration. This action cannot be undone.
      operationId: deleteStorage
      tags:
        - Storages
      parameters:
        - name: storage_id
          in: path
          required: true
          description: UUID of the storage to delete
          schema:
            type: string
            format: uuid
            example: "550e8400-e29b-41d4-a716-446655440000"
      responses:
        '204':
          description: Storage deleted successfully
        '400':
          description: Invalid storage_id format
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /agents:
    get:
      summary: List all agents
      description: |
        Retrieve all hybrid agents configured for your company. Agents are on-premise
        components that handle file transfers between ScorePlay and local/cloud storage.
      operationId: listAgents
      tags:
        - Agents
      responses:
        '200':
          description: Successfully retrieved agent list
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Agent'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /agents/generate:
    post:
      summary: Generate a new agent
      description: |
        Create a new hybrid agent and optionally associate it with storage configurations.
        The agent will be assigned a unique site ID used for routing file transfer tasks.
      operationId: generateAgent
      tags:
        - Agents
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GenerateAgentRequest'
      responses:
        '201':
          description: Agent created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenerateAgentResponse'
        '400':
          description: Invalid request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /agents/{id}/storages:
    get:
      summary: Get agent storages
      description: |
        Retrieve all storage UUIDs associated with a specific agent.
        This shows which BYOS storages are linked to this agent for file transfers.
      operationId: getAgentStorages
      tags:
        - Agents
      parameters:
        - name: id
          in: path
          required: true
          description: Agent ID
          schema:
            type: integer
            example: 123
      responses:
        '200':
          description: Successfully retrieved agent storages
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/AgentStorage'
        '400':
          description: Invalid agent_id
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # Tag Options CRUD
  /tag/options:
    post:
      summary: Create a tag option
      description: |
        Create a new tag option for your company. Tag options are used to categorize and filter media content.
      operationId: createTagOptionPublic
      tags:
        - Tag Options
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTagOptionRequest'
      responses:
        '201':
          description: Tag option created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TagOption'
        '400':
          description: Invalid request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
    get:
      summary: List tag options
      description: |
        Retrieve all tag options for your company. Supports filtering by query string and excluding metadata tags.
      operationId: listTagOptionsPublic
      tags:
        - Tag Options
      parameters:
        - name: query
          in: query
          description: Search query to filter tag options by name or label
          schema:
            type: string
            example: "goal"
        - name: skip_metadata
          in: query
          description: Exclude metadata tags from results
          schema:
            type: boolean
            example: true
        - name: limit
          in: query
          description: Maximum number of results to return
          schema:
            type: integer
            example: 100
      responses:
        '200':
          description: Successfully retrieved tag options
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TagOption'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /tag/options/{id}:
    get:
      summary: Get a tag option by ID
      description: |
        Retrieve details of a specific tag option by its ID.
      operationId: getTagOptionPublic
      tags:
        - Tag Options
      parameters:
        - name: id
          in: path
          required: true
          description: Tag option ID
          schema:
            type: integer
            example: 12345
      responses:
        '200':
          description: Successfully retrieved tag option
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TagOption'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Tag option not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          $ref: '#/components/responses/InternalServerError'
    put:
      summary: Update a tag option
      description: |
        Update an existing tag option's name, label, or type.
      operationId: updateTagOptionPublic
      tags:
        - Tag Options
      parameters:
        - name: id
          in: path
          required: true
          description: Tag option ID
          schema:
            type: integer
            example: 12345
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateTagOptionRequest'
      responses:
        '200':
          description: Tag option updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TagOption'
        '400':
          description: Invalid request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Tag option not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          $ref: '#/components/responses/InternalServerError'
    delete:
      summary: Delete a tag option
      description: |
        Delete a tag option by its ID. This action cannot be undone.
      operationId: deleteTagOptionPublic
      tags:
        - Tag Options
      parameters:
        - name: id
          in: path
          required: true
          description: Tag option ID
          schema:
            type: integer
            example: 12345
      responses:
        '204':
          description: Tag option deleted successfully
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Tag option not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /tag/options/merge:
    post:
      summary: Merge tag options
      description: |
        Merge multiple tag options into a destination tag option. All media tagged with the source tag options
        will be re-tagged with the destination tag option. The source tag options will be deleted after merge.

        ## Use Cases
        - Consolidating duplicate or similar tags
        - Cleaning up tag taxonomy
        - Migrating from old tag naming conventions
      operationId: mergeTagOptionsPublic
      tags:
        - Tag Options
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MergeTagOptionsRequest'
      responses:
        '200':
          description: Tag options merged successfully
          content:
            application/json:
              schema:
                type: boolean
                example: true
        '400':
          description: Invalid request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: One or more tag options not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          $ref: '#/components/responses/InternalServerError'

  # Players CRUD
  /players:
    post:
      summary: Create a player
      description: |
        Create a new player for your company. Players can be tagged on media for easy filtering and search.
      operationId: createPlayerPublic
      tags:
        - Players
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePlayerRequest'
      responses:
        '201':
          description: Player created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Player'
        '400':
          description: Invalid request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
    get:
      summary: List players
      description: |
        Retrieve all players for your company. Supports pagination and search by name.
      operationId: listPlayersPublic
      tags:
        - Players
      parameters:
        - name: query
          in: query
          description: Search query to filter players by name
          schema:
            type: string
            example: "Barcola"
        - name: limit
          in: query
          description: Maximum number of results to return (default 100)
          schema:
            type: integer
            default: 100
            example: 50
        - name: page
          in: query
          description: Page number for pagination
          schema:
            type: integer
            default: 1
            example: 1
      responses:
        '200':
          description: Successfully retrieved players
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Player'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /players/{id}:
    get:
      summary: Get a player by ID
      description: |
        Retrieve details of a specific player by their ID.
      operationId: getPlayerPublic
      tags:
        - Players
      parameters:
        - name: id
          in: path
          required: true
          description: Player ID
          schema:
            type: integer
            example: 189111
      responses:
        '200':
          description: Successfully retrieved player
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Player'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Player not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          $ref: '#/components/responses/InternalServerError'
    put:
      summary: Update a player
      description: |
        Update an existing player's first name, last name, or external ID.
      operationId: updatePlayerPublic
      tags:
        - Players
      parameters:
        - name: id
          in: path
          required: true
          description: Player ID
          schema:
            type: integer
            example: 189111
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdatePlayerRequest'
      responses:
        '200':
          description: Player updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Player'
        '400':
          description: Invalid request body or no fields to update
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Player not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          $ref: '#/components/responses/InternalServerError'
    delete:
      summary: Delete a player
      description: |
        Delete a player by their ID. This action cannot be undone.
      operationId: deletePlayerPublic
      tags:
        - Players
      parameters:
        - name: id
          in: path
          required: true
          description: Player ID
          schema:
            type: integer
            example: 189111
      responses:
        '204':
          description: Player deleted successfully
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          description: Player not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          $ref: '#/components/responses/InternalServerError'

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: query
      name: api_key
      description: Paste your API key in the **Value** field below.

  responses:
    Unauthorized:
      description: Authentication required or invalid API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    InternalServerError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

  schemas:

    PublicMediaUpdatePayload:
      type: object
      description: Payload for updating media details
      properties:
        name:
          type: string
          description: Media name/title
          example: Championship Game Highlights
          nullable: true
        description:
          type: string
          description: Media description
          example: Key moments from the championship game
          nullable: true
        date:
          type: integer
          format: int64
          description: Unix timestamp for media date
          example: 1642275000
          nullable: true
        captured_time:
          type: string
          description: When the media was captured (string format)
          example: '2024-01-15T19:30:00Z'
          nullable: true
        event_id:
          type: integer
          format: uint
          description: ID of the event this media belongs to
          example: 456
          nullable: true
        label_id:
          type: integer
          format: uint
          description: ID of the label for this media
          example: 789
          nullable: true
        player_accessible:
          type: boolean
          description: Whether players can access this media
          example: true
          nullable: true
        team_accessible:
          type: boolean
          description: Whether the team can access this media
          example: true
          nullable: true
        accessible_from:
          type: integer
          format: uint64
          description: Unix timestamp from when media becomes accessible
          example: 1642275000
          nullable: true
        accessible_until:
          type: integer
          format: uint64
          description: Unix timestamp until when media is accessible
          example: 1672811000
          nullable: true

    UpdateMediaTagsPayload:
      type: object
      description: Payload for updating media tags
      properties:
        tag_option_ids:
          type: array
          description: List of tag option IDs to add/remove
          items:
            type: integer
            format: uint
          example:
            - 123
            - 456
            - 789
        player_ids:
          type: array
          description: List of player IDs to tag/untag
          items:
            type: integer
            format: uint
          example:
            - 111
            - 222
            - 333
        sponsor_ids:
          type: array
          description: List of sponsor IDs to tag/untag
          items:
            type: integer
            format: uint
          example:
            - 444
            - 555
            - 666

    Error:
      type: object
      properties:
        message:
          type: string
          description: Error name or type
          example: USER_NOT_FOUND

    TagOption:
      type: object
      description: Tag option representing a ScorePlay tag
      properties:
        ID:
          type: number
          description: Unique identifier for the tag option
          example: 1910925
        CreatedAt:
          type: string
          description: Creation timestamp (ISO8601)
          example: 2024-08-28T08:00:43.518554Z
        UpdatedAt:
          type: string
          description: Last update timestamp (ISO8601)
          example: 2024-08-28T08:00:43.518554Z
        DeletedAt:
          type: string
          format: nullable
          description: Deletion timestamp if deleted, otherwise null
        name:
          type: string
          description: Internal name of the tag option
          example: parc des princes
        label:
          type: string
          description: Display label for the tag option
          example: Parc des Princes
        company_id:
          type: number
          description: Associated company ID
          example: 123
        is_metadata:
          type: boolean
          description: Whether this tag is used as metadata
        type:
          type: string
          description: Tag type (e.g., metadata, free, credit, size)
          enum:
            - metadata
            - free
            - credit
            - size
          example: metadata

    Player:
      type: object
      description: Player entity with personal and team information
      properties:
        ID:
          type: number
          description: Unique identifier for the player
          example: 189111
        CreatedAt:
          type: string
          description: Creation timestamp (ISO8601)
          example: 2024-09-05T06:34:38.890566Z
        UpdatedAt:
          type: string
          description: Last update timestamp (ISO8601)
          example: 2025-06-23T17:36:34.978456Z
        DeletedAt:
          type: string
          format: nullable
          description: Deletion timestamp if deleted, otherwise null
        first_name:
          type: string
          description: Player's first name
          example: Bradley
        last_name:
          type: string
          description: Player's last name
          example: Barcola
        full_name:
          type: string
          description: Player's full name
          example: Bradley Barcola
        company_id:
          type: number
          description: Associated company ID
          example: 123
        profile_picture:
          type: string
          description: URL to the player's profile picture
          example: https://img.uefa.com/imgml/TP/players/2014/2025/324x324/250134138.jpg
        external_id:
          type: string
          description: External identifier for the player
          example: psg29

    Sponsor:
      type: object
      description: Sponsor/brand entity
      properties:
        ID:
          type: number
          description: Unique identifier for the sponsor
          example: 36692
        CreatedAt:
          type: string
          description: Creation timestamp (ISO8601)
          example: 2025-03-19T15:01:17.593402Z
        UpdatedAt:
          type: string
          description: Last update timestamp (ISO8601)
          example: 2025-03-19T15:01:17.685156Z
        DeletedAt:
          type: string
          format: nullable
          description: Deletion timestamp if deleted, otherwise null
        name:
          type: string
          description: Internal name of the sponsor
          example: booking.com
        label:
          type: string
          description: Display label for the sponsor
          example: Booking.com
        company_id:
          type: number
          description: Associated company ID
          example: 111
        profile_picture:
          type: string
          description: URL to the sponsor's profile picture
          example: https://scoreplay-sponsors.s3.amazonaws.com/593/sponsor_36692.jpg

    Media:
      type: object
      description: Media item containing photos, videos, or other content with
        additional metadata
      properties:
        ID:
          type: number
          example: 46000204
        CreatedAt:
          type: string
          example: 2025-05-31T22:02:23.011204Z
        UpdatedAt:
          type: string
          example: 2025-05-31T22:04:31.546671Z
        DeletedAt:
          type: string
          format: nullable
        name:
          type: string
          example: 2218005865.jpg
        type:
          type: string
          example: photo
        date:
          type: number
          example: 1748728887
        user_id:
          type: number
          example: 0
        company_id:
          type: number
          example: 123
        session_id:
          type: number
          example: 1400636
        team_id:
          type: number
          example: 0
        event_id:
          type: number
          example: 321801
        description:
          type: string
          example: Players of Paris Saint-Germain celebrate after their team's victory to
            secure Paris Saint-Germain's first ever UEFA Champions League title
            in the club's history and a record UEFA Champions League Final
            winning scoreline of 5-0 following the UEFA Champions League Final
            2025 between Paris Saint-Germain and FC Internazionale Milano at
            Munich Football Arena on May 31 2025 in Munich Germany
        captured_time:
          type: string
          example: 2025:05:31 22:52:39.07+02:00
        usage:
          type: string
          example: "editorial"
        accessible_from:
          type: number
          example: 0
        accessible_until:
          type: number
          example: 0
        external_accounts_drms:
          type: array
          items:
            type: number
          example: []
        available_user_type_ids:
          type: array
          items:
            type: number
          example: []
        files:
          type: array
          items:
            $ref: '#/components/schemas/File'
        tags:
          type: array
          items:
            $ref: '#/components/schemas/Tag'
        event:
          $ref: '#/components/schemas/Event'

        video:
          type: string
          format: nullable
        label:
          type: string
          format: nullable
        accessible_user_ids:
          type: string
          format: nullable
        accessible_player_ids:
          type: string
          format: nullable
        accessible_sponsor_ids:
          type: string
          format: nullable
        accessible_team_ids:
          type: string
          format: nullable

    File:
      type: object
      description: File associated with media
      properties:
        id:
          type: integer
          example: 12345
        url:
          type: string
          format: uri
          description: File access URL
          example: https://cdn.scoreplay.io/media/123/photo.jpg
        media_id:
          type: integer
          description: Parent media ID
          example: 456
        media_version_id:
          type: integer
          description: Associated version ID
          example: 789
        details:
          type: string
          description: File quality/type descriptor
          example: photographer_quality

    Tag:
      type: object
      description: Tag association linking media to metadata
      properties:
        id:
          type: integer
          example: 12345
        media_id:
          type: integer
          description: Associated media
          example: 456
        tag_option_id:
          type: integer
          description: Tag type reference
          example: 789
        player_id:
          type: integer
          description: Associated player (if applicable)
          example: 101
        sponsor_id:
          type: integer
          description: Associated sponsor (if applicable)
          example: 102
        tag_option:
          $ref: '#/components/schemas/TagOption'
        sponsor:
          $ref: '#/components/schemas/Sponsor'
        player:
          $ref: '#/components/schemas/Player'

    Event:
      type: object
      description: Event or collection containing media
      properties:
        ID:
          type: number
          example: 321801
        CreatedAt:
          type: string
          example: 2025-05-09T15:27:44.714925Z
        UpdatedAt:
          type: string
          example: 2025-07-05T06:49:24.37897Z
        DeletedAt:
          type: string
          format: nullable
        name:
          type: string
          example: Paris vs Inter (MD17 - UEFA Champions League)
        company_id:
          type: number
          example: 593
        date:
          type: number
          example: 1748710800
        last_media_added_at:
          type: number
          example: 1751698164
        raw_name:
          type: string
          example: "776319909"
        parent_id:
          type: number
          example: 321800
        child_count:
          type: number
          example: 20
        full_path:
          type: string
          example: /198841:UEFA Champions League 2025/321799:Final/321800:MD17
        external_id:
          type: string
          example: football_match_2044466
        start_datetime:
          type: number
          example: 1748710800
        end_datetime:
          type: number
          example: 1748716200
        tag_options:
          type: array
          items:
            $ref: '#/components/schemas/TagOption'
        players:
          type: array
          items:
            $ref: '#/components/schemas/Player'
        sponsors:
          type: array
          items:
            $ref: '#/components/schemas/Sponsor'
        media_count:
          type: number
          example: 6134

    SearchMediaPayload:
      type: object
      description: Comprehensive search criteria for media filtering
      properties:
        media_type:
          type: string
          description: Filter by media type
          enum:
            - photo
            - video
          example: photo
        event_id:
          type: integer
          description: Filter by specific event
          example: 12345
        event_ids:
          type: array
          description: Filter by multiple events
          items:
            type: integer
          example:
            - 123
            - 456
            - 789
        query:
          type: string
          description: Text search query
          example: goal celebration
        name:
          type: string
          description: Search by media filename
          example: IMG_001.jpg
        start_date:
          type: integer
          format: int64
          description: Filter from date (Unix timestamp)
          example: 1642608000
        end_date:
          type: integer
          format: int64
          description: Filter to date (Unix timestamp)
          example: 1642694400
        tag_options:
          type: array
          description: Required tag option IDs (AND logic)
          items:
            type: integer
          example:
            - 101
            - 102
        any_tag_options:
          type: array
          description: Any of these tag option IDs (OR logic)
          items:
            type: integer
          example:
            - 201
            - 202
        not_tag_options:
          type: array
          description: Exclude these tag option IDs
          items:
            type: integer
          example:
            - 301
            - 302
        players:
          type: array
          description: Required player IDs (AND logic)
          items:
            type: integer
          example:
            - 401
            - 402
        any_players:
          type: array
          description: Any of these player IDs (OR logic)
          items:
            type: integer
          example:
            - 501
            - 502
        sponsors:
          type: array
          description: Required sponsor IDs (AND logic)
          items:
            type: integer
          example:
            - 601
            - 602
        validated_accessible:
          type: boolean
          description: Only validated media
          example: true
        player_accessible:
          type: boolean
          description: Player-accessible media only
          example: true
        is_private:
          type: boolean
          description: Include private media
          example: false
        order_by:
          type: string
          description: Sort field
          example: created_at
        order_type:
          type: string
          description: Sort direction
          enum:
            - asc
            - desc
          example: desc

    SearchEventPayload:
      type: object
      description: Search criteria for events (collections of media)
      properties:
        event_ids:
          type: array
          items:
            type: integer
            format: uint32
        company_id:
          type: integer
          format: uint32
        parent_id:
          type: integer
          format: uint32
        date:
          type: integer
          format: int64
        start_date:
          type: integer
          format: uint64
        end_date:
          type: integer
          format: uint64
        query:
          type: string
        raw_name:
          type: string
        team_ids:
          type: array
          items:
            type: integer
            format: uint32
          description: for clubs, not team season association
        external_id:
          type: string
        transcript_query:
          type: string
          description: search through transcriptions
        tag_options:
          type: array
          items:
            type: integer
            format: uint32
          description: Tag filter search, list of tags options IDs
        players:
          type: array
          items:
            type: integer
            format: uint32
        sponsors:
          type: array
          items:
            type: integer
            format: uint32
        not_tag_options:
          type: array
          items:
            type: integer
            format: uint32
        not_players:
          type: array
          items:
            type: integer
            format: uint32
        not_sponsors:
          type: array
          items:
            type: integer
            format: uint32
        page:
          type: integer
          format: uint32
        per_page:
          type: integer
          format: uint32
        order_by:
          type: string
        order_type:
          type: string

    MediaSearchResult:
      type: object
      description: Media search response with pagination
      properties:
        media:
          type: array
          description: Array of media results
          items:
            $ref: '#/components/schemas/Media'
        count:
          type: integer
          description: Total number of results
          example: 150
        page:
          type: integer
          description: Current page number
          example: 1
        per_page:
          type: integer
          description: Results per page
          example: 20
        total_pages:
          type: integer
          description: Total number of pages
          example: 8

    CreateMediaPayload:
      type: object
      description: Payload for creating new media
      required:
        - name
      properties:
        name:
          type: string
          description: Media filename
          example: goal_highlight.mp4
        description:
          type: string
          description: Media description
          example: Amazing goal in the final minute
        date:
          type: integer
          format: int64
          description: Media date (Unix timestamp)
          example: 1642608000
        player_accessible:
          type: boolean
          description: Whether players can access this media
          example: true
        raw_tags:
          type: array
          description: Initial tags to apply
          items:
            type: string
          example:
            - goal
            - highlight

    CreateMediaVersionPayload:
      type: object
      description: Payload for creating media versions
      required:
        - media_id
        - name
      properties:
        media_id:
          type: integer
          description: Parent media ID
          example: 12345
        name:
          type: string
          description: Version name
          example: Edited Version
        description:
          type: string
          description: Version description
          example: Color corrected and cropped

    CompleteUploadPayload:
      type: object
      description: Multipart upload completion data
      required:
        - key
        - upload_id
        - parts
      properties:
        key:
          type: string
          description: S3 object key
          example: 123/456/media_789_1.jpg
        upload_id:
          type: string
          description: Multipart upload ID
          example: abc123xyz
        parts_number:
          type: integer
          description: Total number of parts
          example: 43
        parts:
          type: array
          description: Uploaded parts information
          items:
            $ref: '#/components/schemas/UploadPart'

    UploadPart:
      type: object
      properties:
        parts_number:
          type: integer
          description: Part sequence number
          example: 1
        etag:
          type: string
          description: Part ETag from S3
          example: '"abc123"'

    AbortUploadPayload:
      type: object
      description: Upload abortion data
      required:
        - key
        - upload_id
      properties:
        key:
          type: string
          description: S3 object key
          example: 123/456/media_789_1.jpg
        upload_id:
          type: string
          description: Multipart upload ID to abort
          example: abc123xyz

    VideoLogPayload:
      type: object
      description: Video logs for action analysis
      required:
        - company_id
        - video_logs
      properties:
        company_id:
          type: integer
          description: Company ID
          example: 123
        video_logs:
          type: array
          description: Array of video log entries
          items:
            $ref: '#/components/schemas/VideoLog'

    VideoLog:
      type: object
      description: Individual video log entry
      properties:
        media_id:
          type: integer
          description: Associated media ID
          example: 12345
        external_id:
          type: string
          description: External event/match ID
          example: match_456
        filename_match:
          type: string
          description: Filename pattern for media matching
          example: goal_*.mp4
        log:
          $ref: '#/components/schemas/ActionClip'

    ActionClip:
      type: object
      description: >-
        Action clip with timing information. Exactly one timing mode must be used:
        offset single (offset), offset range (start_offset + end_offset),
        unix single (action_timestamp), or unix range (start_timestamp + end_timestamp).
      properties:
        action_timestamp:
          type: integer
          format: int64
          description: Unix timestamp of the single action in seconds. Used for unix single mode. Optionally combine with start_timestamp/end_timestamp for clip boundaries.
          example: 1771282711
        start_timestamp:
          type: integer
          format: int64
          description: Unix timestamp for clip start in seconds. Used for unix range mode (without action_timestamp) or optionally with action_timestamp for clip boundaries.
          example: 1771282651
        end_timestamp:
          type: integer
          format: int64
          description: Unix timestamp for clip end in seconds. Used for unix range mode (without action_timestamp) or optionally with action_timestamp for clip boundaries.
          example: 1771282711
        offset:
          type: number
          format: double
          description: Offset in seconds from the start of the video for a single action point. Used for offset single mode.
          example: 65.5
        start_offset:
          type: number
          format: double
          description: Start offset in seconds from the start of the video. Used with end_offset for offset range mode.
          example: 60.0
        end_offset:
          type: number
          format: double
          description: End offset in seconds from the start of the video. Used with start_offset for offset range mode.
          example: 75.0
        raw_tags:
          type: array
          items:
            type: string
          description: Tag names to associate with the action clip (created if not existing)
          example: ["goal", "highlight"]
        raw_player_external_ids:
          type: array
          items:
            type: string
          description: External IDs of players to tag on the action clip
          example: ["player_123", "player_456"]

    CreateOperationRequest:
      type: object
      description: Request to create a new operation
      required:
        - name
      properties:
        name:
          type: string
          description: Name of the operation (e.g., "Bulk download", "Clip generation")
          example: "Bulk download"
        subtitle:
          type: string
          description: Additional description or context (optional)
          example: "Downloading 25 media files"
        status:
          type: string
          description: Initial status of the operation
          enum: ["pending", "done", "error"]
          default: "pending"
          example: "pending"
        url:
          type: string
          description: URL associated with the operation (e.g., download link)
          example: "https://example.com/download/abc123"

    UpdateOperationRequest:
      type: object
      description: Fields to update on an operation
      properties:
        name:
          type: string
          description: Name of the operation
        subtitle:
          type: string
          description: Additional description or context
        status:
          type: string
          description: Status of the operation
          enum: ["pending", "done", "error"]
        url:
          type: string
          description: URL associated with the operation
        error_value:
          type: string
          description: Error message if status is "error"
        processing_time:
          type: integer
          description: Processing time in seconds

    Operation:
      type: object
      description: An operation tracking an async task
      properties:
        id:
          type: integer
          description: Operation ID
        name:
          type: string
          description: Name of the operation
        subtitle:
          type: string
          description: Additional description
        status:
          type: string
          description: Current status
          enum: ["pending", "done", "error"]
        error_value:
          type: string
          description: Error message if status is "error"
        processing_time:
          type: integer
          description: Processing time in seconds
        seen:
          type: boolean
          description: Whether the operation has been seen
        url:
          type: string
          description: URL associated with the operation
        source:
          type: string
          description: Source of the operation
          default: "mam"
        downloaded:
          type: boolean
          description: Whether the result was downloaded

    StorageTransferRequest:
      type: object
      description: Request to transfer assets to BYOS storage
      required:
        - destination_storage_uuid
      properties:
        media_ids:
          type: array
          description: |
            Media IDs to transfer. Folder structure will be derived from the event hierarchy.
            Cannot be used together with `urls`.
          items:
            type: integer
          example: [1234, 5678, 9012]
        urls:
          type: array
          description: |
            Raw URLs with explicit destination paths. Use this when you need full control over
            source and destination. Cannot be used together with `media_ids`.
          items:
            $ref: '#/components/schemas/StorageTransferURLItem'
        destination_storage_uuid:
          type: string
          format: uuid
          description: UUID of the destination BYOS storage (configured in ScorePlay settings)
          example: "550e8400-e29b-41d4-a716-446655440000"

    StorageTransferURLItem:
      type: object
      description: A single URL transfer item with source and destination
      required:
        - source_url
        - destination_path
      properties:
        source_url:
          type: string
          format: uri
          description: Source URL to download the file from
          example: "https://cdn.example.com/video1.mp4"
        destination_path:
          type: string
          description: Destination path within the storage (e.g., "2024/Wimbledon/Day1/video.mp4")
          example: "2024/Wimbledon/Day1/video1.mp4"

    StorageTransferResponse:
      type: object
      description: Response when starting a storage transfer
      properties:
        operation_id:
          type: integer
          description: Operation ID for tracking transfer progress
          example: 45678
        status:
          type: string
          description: Initial status of the transfer
          enum: ["pending", "in_progress", "done", "partial", "error"]
          example: "pending"
        total_files:
          type: integer
          description: Total number of files to transfer
          example: 3
        destination:
          type: object
          description: Destination storage information
          properties:
            type:
              type: string
              description: Storage type (e.g., "AWS S3 Bucket", "SMB")
              example: "SMB"
            name:
              type: string
              description: Storage name
              example: "Bristol Archive"

    StorageTransferStatusResponse:
      type: object
      description: Detailed status of a storage transfer operation
      properties:
        operation_id:
          type: integer
          description: Operation ID
          example: 45678
        status:
          type: string
          description: Current status of the transfer
          enum: ["pending", "in_progress", "done", "partial", "error"]
          example: "in_progress"
        total_files:
          type: integer
          description: Total number of files
          example: 5
        completed_files:
          type: integer
          description: Number of successfully transferred files
          example: 3
        failed_files:
          type: integer
          description: Number of failed transfers
          example: 0
        file_statuses:
          type: array
          description: Per-file status details
          items:
            $ref: '#/components/schemas/TransferFileStatus'
        destination:
          type: object
          properties:
            type:
              type: string
              example: "SMB"
            name:
              type: string
              example: "Bristol Archive"
        error:
          type: string
          description: Error message if the overall transfer failed
          nullable: true
          example: "connection timed out"

    TransferFileStatus:
      type: object
      description: Status of an individual file transfer
      properties:
        media_id:
          type: integer
          description: Media ID (if transferred by media_id)
          example: 1234
        source_url:
          type: string
          description: Source URL of the file
          example: "https://cdn.scoreplay.io/..."
        file_name:
          type: string
          description: Name of the file
          example: "clip1.mp4"
        destination_path:
          type: string
          description: Destination path in storage
          example: "Wimbledon/Day1/clip1.mp4"
        status:
          type: string
          description: Transfer status for this file
          enum: ["pending", "in_progress", "completed", "failed"]
          example: "completed"
        error:
          type: string
          description: Error message if this file failed
          nullable: true
          example: "checksum mismatch"

    # Storage schemas
    Storage:
      type: object
      description: BYOS storage configuration
      properties:
        id:
          type: string
          format: uuid
          description: Unique identifier for the storage
          example: "550e8400-e29b-41d4-a716-446655440000"
        name:
          type: string
          description: Storage bucket/container name
          example: "my-archive-bucket"
        type:
          type: string
          description: Storage type
          enum: ["AWS S3 Bucket", "Wasabi", "SMB", "Azure Blob", "GCS"]
          example: "AWS S3 Bucket"
        is_scoreplay:
          type: boolean
          description: Whether this is a ScorePlay-managed storage
          example: false
        region:
          type: string
          description: AWS region (for S3-compatible storage)
          example: "us-east-1"
        endpoint:
          type: string
          description: Custom endpoint URL (for S3-compatible storage)
          example: "https://s3.wasabisys.com"
        server:
          type: string
          description: Server address (for SMB storage)
          example: "192.168.1.100"
        port:
          type: integer
          description: Port number (for SMB storage)
          example: 445
        share_name:
          type: string
          description: Share name (for SMB storage)
          example: "media-archive"
        path_prefix:
          type: string
          description: Base path prefix within the storage
          example: "scoreplay/uploads"
        username:
          type: string
          description: Username for authentication (SMB)
          example: "admin"

    CreateStorageRequest:
      type: object
      description: Request to create a new BYOS storage
      required:
        - name
        - storage_type
      properties:
        name:
          type: string
          description: Storage bucket/container name
          example: "my-archive-bucket"
        label:
          type: string
          description: Display label for the storage
          example: "Production Archive"
        storage_type:
          type: string
          description: Type of storage
          enum: ["AWS S3 Bucket", "Wasabi", "SMB", "Azure Blob", "GCS"]
          example: "AWS S3 Bucket"
        credentials_id:
          type: string
          format: uuid
          description: UUID of the credentials to use
          example: "550e8400-e29b-41d4-a716-446655440001"
        region:
          type: string
          description: AWS region (for S3-compatible storage)
          example: "us-east-1"
        endpoint:
          type: string
          description: Custom endpoint URL (for S3-compatible storage)
          example: "https://s3.wasabisys.com"

    UpdateStorageRequest:
      type: object
      description: Request to update a BYOS storage
      properties:
        name:
          type: string
          description: Storage bucket/container name
          example: "my-archive-bucket"
        label:
          type: string
          description: Display label for the storage
          example: "Production Archive"
        storage_type:
          type: string
          description: Type of storage
          enum: ["AWS S3 Bucket", "Wasabi", "SMB", "Azure Blob", "GCS"]
          example: "AWS S3 Bucket"
        credentials_id:
          type: string
          format: uuid
          description: UUID of the credentials to use
          example: "550e8400-e29b-41d4-a716-446655440001"

    # Agent schemas
    Agent:
      type: object
      description: Hybrid agent for file transfers
      properties:
        id:
          type: integer
          description: Unique identifier for the agent
          example: 123
        company_id:
          type: integer
          description: Company ID this agent belongs to
          example: 456
        site_id:
          type: string
          format: uuid
          description: Unique site identifier for routing tasks
          example: "550e8400-e29b-41d4-a716-446655440000"
        name:
          type: string
          description: Agent name
          example: "Bristol Office Agent"
        local_address:
          type: string
          description: Local network address
          example: "192.168.1.50"
        created_at:
          type: string
          format: date-time
          description: When the agent was created
          example: "2024-01-15T10:30:00Z"
        updated_at:
          type: string
          format: date-time
          description: When the agent was last updated
          example: "2024-01-15T10:30:00Z"

    GenerateAgentRequest:
      type: object
      description: Request to generate a new agent
      required:
        - name
      properties:
        name:
          type: string
          description: Name for the new agent
          example: "Bristol Office Agent"
        storage_uuids:
          type: array
          description: Storage UUIDs to associate with this agent
          items:
            type: string
            format: uuid
          example: ["550e8400-e29b-41d4-a716-446655440000"]

    GenerateAgentResponse:
      type: object
      description: Response when generating a new agent
      properties:
        agent:
          $ref: '#/components/schemas/Agent'
        agent_storages:
          type: array
          description: Storage associations created
          items:
            $ref: '#/components/schemas/AgentStorage'

    AgentStorage:
      type: object
      description: Association between an agent and a storage
      properties:
        id:
          type: integer
          description: Association ID
          example: 789
        remote_storage_id:
          type: integer
          description: Agent ID
          example: 123
        storage_uuid:
          type: string
          format: uuid
          description: Storage UUID linked to this agent
          example: "550e8400-e29b-41d4-a716-446655440000"
        created_at:
          type: string
          format: date-time
          description: When the association was created
          example: "2024-01-15T10:30:00Z"

    # Tag Options request schemas
    CreateTagOptionRequest:
      type: object
      description: Request to create a new tag option
      required:
        - name
      properties:
        name:
          type: string
          description: Internal name of the tag option (used for matching)
          example: "goal"
        label:
          type: string
          description: Display label for the tag option
          example: "Goal"
        type:
          type: string
          description: Tag type (e.g., metadata, free, credit, size)
          example: "free"

    UpdateTagOptionRequest:
      type: object
      description: Request to update a tag option
      properties:
        name:
          type: string
          description: Internal name of the tag option
          example: "goal_scored"
        label:
          type: string
          description: Display label for the tag option
          example: "Goal Scored"
        type:
          type: string
          description: Tag type
          example: "free"

    MergeTagOptionsRequest:
      type: object
      description: Request to merge multiple tag options into one
      required:
        - tag_option_ids
        - destination_tag_option_id
      properties:
        tag_option_ids:
          type: array
          description: IDs of tag options to merge (source tags that will be deleted)
          items:
            type: integer
          example: [123, 456, 789]
        destination_tag_option_id:
          type: integer
          description: ID of the destination tag option (will receive all media from source tags)
          example: 101

    # Player request schemas
    CreatePlayerRequest:
      type: object
      description: Request to create a new player
      required:
        - first_name
        - last_name
      properties:
        first_name:
          type: string
          description: Player's first name
          example: "Bradley"
        last_name:
          type: string
          description: Player's last name
          example: "Barcola"
        external_id:
          type: string
          description: External identifier for integration with other systems
          example: "psg29"

    UpdatePlayerRequest:
      type: object
      description: Request to update a player
      properties:
        first_name:
          type: string
          description: Player's first name
          example: "Bradley"
        last_name:
          type: string
          description: Player's last name
          example: "Barcola"
        external_id:
          type: string
          description: External identifier
          example: "psg29"

tags:
  - name: Tags
    description: Tag and metadata management operations
  - name: Tag Options
    description: CRUD operations for tag options - create, list, update, delete, and merge tag options used to categorize media
  - name: Players
    description: CRUD operations for players - create, list, update, and delete player profiles that can be tagged on media
  - name: Media
    description: Media content operations and search
  - name: Events
    description: Collection management
  - name: Upload
    description: File upload operations
  - name: Video
    description: Video-specific operations and metadata
  - name: Operations
    description: Track async operations like bulk downloads and clip generation
  - name: Video Logs
    description: Timecoded action logs within videos for highlight clips and sports analysis
  - name: Storage Transfer
    description: Transfer assets to BYOS (Bring Your Own Storage) destinations like S3, Azure, or SMB shares
  - name: Storages
    description: Manage BYOS (Bring Your Own Storage) configurations - S3, Azure, GCS, and SMB shares
  - name: Agents
    description: Manage hybrid agents for on-premise file transfers between ScorePlay and local/cloud storage