openapi: 3.0.1
info:
  title: Intercom Manager API
  description: API documentation for Eyevinn Open Intercom Manager
  version: 1.0.0
servers:
  - url: http://localhost:8080
    description: Local development server
tags:
  - name: Productions
    description: Endpoints for managing productions and lines
  - name: Sessions
    description: Endpoints for session lifecycle and SDP handling
  - name: Participants
    description: Long polling and participant tracking
  - name: Reauthentication
    description: Generate new OSC access tokens
  - name: Sharing
    description: Generate shareable links for a given application path
  - name: WHIP (WebRTC-HTTP Ingest Protocol)
    description: Endpoints for WHIP media session management
paths:
  /api/v1/production:
    post:
      tags:
        - Productions
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NewProduction'
      summary: Create a production
      description: |
        Creates a new production, including initial lines.

        Request must include a name and an array of lines.
        Each line must have a unique name.

        Returns the production ID on success.
      responses:
        "200":
          description: Production created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductionResponse'
        "400":
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
    get:
      tags:
        - Productions
      summary: Get recent productions (Deprecated)
      description: Retrieves 50 most recently created productions
      deprecated: true
      responses:
        "200":
          description: List of recent productions
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProductionResponse'
        "500":
          description: Server error
          content:
            text/plain:
              schema:
                type: string

  /api/v1/productionlist:
    get:
      tags:
        - Productions
      summary: List all productions (paginated)
      description: |
        Retrieves a paginated list of all productions in the system. Optionally returns
        detailed line information for each production if `extended=true` is set.

        - `limit`: max number of productions per page (default: 50)

        - `offset`: pagination offset (default: 0)

        - `extended`: if true, includes full line details (e.g., participants)

        This endpoint replaces the older `/production` endpoint.
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
        - name: offset
          in: query
          schema:
            type: integer
        - name: extended
          in: query
          schema:
            type: boolean
      responses:
        "200":
          description: Paginated production list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductionListResponse'
        "500":
          description: Server error
          content:
            text/plain:
              schema:
                type: string

  /api/v1/production/{productionId}:
    get:
      tags:
        - Productions
      summary: Get production details
      description: |
        Returns detailed information about a specific production,
        including all of its lines and participant information.
      parameters:
        - name: productionId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Production details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DetailedProductionResponse'
        "500":
          description: Server error
          content:
            text/plain:
              schema:
                type: string
    patch:
      tags:
        - Productions
      summary: Rename a production
      description: |
        Modifies the name of an existing production.

        Returns the updated name and ID.
      requestBody:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PatchProduction'
      parameters:
        - name: productionId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Updated production
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PatchProductionResponse'
        "400":
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "500":
          description: Server error
          content:
            text/plain:
              schema:
                type: string
    delete:
      tags:
        - Productions
      summary: Delete a production
      description: |
        Permanently deletes the specified production and all of its lines.
      parameters:
        - name: productionId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Deleted production
          content:
            text/plain:
              schema:
                type: string
        "500":
          description: Server error
          content:
            text/plain:
              schema:
                type: string
  
  /api/v1/production/{productionId}/line:
    get:
      tags:
        - Productions
      summary: List lines in a production
      description: |
        Retrieves all lines associated with the given production.
      parameters:
        - name: productionId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: List of production lines
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/LineResponse'
        "500":
          description: Server error
          content:
            text/plain:
              schema:
                type: string
    post:
      tags:
        - Productions
      summary: Add line to production
      description: |
        Adds a new line to the specified production.

        Line name must be unique within the production.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NewProductionLine'
      parameters:
      - name: productionId
        in: path
        required: true
        schema:
          type: string
      responses:
        "200":
          description: Line added
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/LineResponse'
        "400":
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "500":
          description: Server error
          content:
            text/plain:
              schema:
                type: string

  /api/v1/production/{productionId}/line/{lineId}:
    get:
      tags:
        - Productions
      summary: Get line details
      description: |
        Retrieves details of a specific line within a production,
        including current participants and configuration.
      parameters:
        - name: productionId
          in: path
          required: true
          schema:
            type: string
        - name: lineId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Line details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LineResponse'
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "500":
          description: Server error
          content:
            text/plain:
              schema:
                type: string
    patch:
      tags:
        - Productions
      summary: Rename a line
      description: |
        Updates the name of a line in the specified production.
        Returns updated metadata.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PatchLine'
      parameters:
        - name: productionId
          in: path
          required: true
          schema:
            type: string
        - name: lineId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Line updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PatchLineResponse'
        "400":
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "500":
          description: Server error
          content:
            text/plain:
              schema:
                type: string
    delete:
      tags:
        - Productions
      summary: Delete a line
      description: |
        Deletes a line from the production.

        Cannot delete if the line has active participants.
      parameters:
        - name: productionId
          in: path
          required: true
          schema:
            type: string
        - name: lineId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Deleted
          content:
            text/plain:
              schema:
                type: string
        "400":
          description: Cannot delete with active participants
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "500":
          description: Server error
          content:
            text/plain:
              schema:
                type: string
  
  /api/v1/session:
    post:
      tags:
        - Sessions
      summary: Create session and generate SDP offer
      description: |
        Initiates the connection protocol.

        This endpoint:
        - Creates a new conference and endpoint via the SMB service.

        - Creates a new user session and returns an SDP offer for WebRTC negotiation.
        
        - Stores session and endpoint info in memory for use in `/session/{sessionId}`.

        Requires a `productionId`, `lineId`, and `username`.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NewSession'
      responses:
        "201":
          description: SDP offer created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionResponse'
        "400":
          description: Could not establish media connection
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "500":
          description: Server error
          content:
            text/plain:
              schema:
                type: string

  /api/v1/session/{sessionId}:
    patch:
      tags:
        - Sessions
      summary: Finalize session with SDP answer
      description: |
        Finalizes the WebRTC connection for a session by submitting the
        client-generated SDP answer.

        Uses the session's stored data to complete ICE and media negotiation
        via the SMB service.

        This endpoint does not return a body — a 200 response indicates success.
      parameters:
        - name: sessionId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Session finalized
          content:
            text/plain:
              schema:
                type: string
        "500":
          description: Server error
          content:
            text/plain:
              schema:
                type: string
    delete:
      tags:
        - Sessions
      summary: Delete a user session
      description: |
        Deletes a user session and removes its reference from memory.

        This is typically called when a client disconnects from a media session.
        Does not terminate the SMB endpoint explicitly.
      parameters:
        - name: sessionId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Deleted session
          content:
            text/plain:
              schema:
                type: string
        "500":
          description: Server error
          content:
            text/plain:
              schema:
                type: string

  /api/v1/production/{productionId}/line/{lineId}/participants:
    post:
      tags:
        - Participants
      summary: Long poll for participants
      description: |
        Long-poll endpoint that waits for changes in the participant
        list for a given line.

        Used for live updating client UIs.
      parameters:
        - name: productionId
          in: path
          required: true
          schema:
            type: string
        - name: lineId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: List of participants
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/UserResponse'
        "500":
          description: Server error
          content:
            text/plain:
              schema:
                type: string
  
  /api/v1/heartbeat/{sessionId}:
    get:
      tags:
        - Sessions
      summary: Update Session Activity
      description: Update user session lastSeen
      parameters:
        - name: sessionId
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Session updated
          content:
            text/plain:
              schema:
                type: string
        "410":
          description: Session not found
          content:
            text/plain:
              schema:
                type: string

  /api/v1/share:
    post:
      tags:
        - Sharing
      summary: Generate shareable link
      description: |
        Generates a shareable link based on a given application-relative path.

        If `OSC_ACCESS_TOKEN` is present, the service attempts to delegate
        token-based access through the OSC delegate service. If successful, the
        delegated share URL replaces the default constructed one.

        Returns the resulting shareable URL (delegated or direct) in the response.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ShareRequest'
      responses:
        "200":
          description: Share link created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShareResponse'
        "400":
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

  /api/v1/reauth:
    get:
      tags:
        - Reauthentication
      summary: Generate new OSC Service Access Token for the OSC Intercom instance
      description: |
        Generates a new OSC Service Access Token (SAT) by making a POST request to
        the OSC ServiceToken endpoint.

        Requires a valid `OSC_ACCESS_TOKEN` to be set in the environment.
        On success, the SAT token is returned in the response and also set
        as an HTTP-only cookie.

        If the `OSC_ACCESS_TOKEN` is not available, a 404 is returned.
        If the ServiceToken request fails, a 500 is returned.
        responses:
          "200":
            description: Token issued
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/ReauthResponse'
          "400":
            description: Missing credentials
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/ErrorResponse'

  /api/v1/whip/{productionId}/{lineId}/{username}:
    post:
      tags:
        - WHIP (WebRTC-HTTP Ingest Protocol)
      summary: Start WHIP session and return SDP answer
      description: |
        Accepts an SDP offer from a WHIP client and initializes a new media session.

        This endpoint performs the following:

        - Validates `Content-Type: application/sdp`

        - Parses the SDP offer using `sdp-transform`

        - Creates a new SMB conference and endpoint

        - Configures the endpoint using parsed SDP

        - Generates an SDP answer

        - Verifies that all `m=` sections were accepted by checking `mid` values

        - Creates a user session for the given `username`

        - Starts a server-side heartbeat

        - Returns WHIP headers:
          - `Location`: Resource URL

          - `ETag`: Session ID

          - `Link`: ICE servers

        - Enforces rate limiting: **10 requests per minute**
      parameters:
        - name: productionId
          in: path
          required: true
          schema: { type: string }
        - name: lineId
          in: path
          required: true
          schema: { type: string }
        - name: username
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/sdp:
            schema:
              $ref: '#/components/schemas/WhipRequest'
      responses:
        "201":
          description: Session created successfully, returns SDP answer
          content:
            application/sdp:
              schema:
                $ref: '#/components/schemas/WhipResponse'
          headers:
            Location:
              description: Resource URL for future DELETE or PATCH
              schema: { type: string }
            ETag:
              description: Session ID
              schema: { type: string }
            Link:
              description: ICE server configuration
              schema: { type: string }
        "400":
          description: Invalid or malformed SDP
        "406":
          description: One or more `m=` sections rejected in SDP negotiation
        "415":
          description: Unsupported media type
        "429":
          description: Rate limit exceeded
        "500":
          description: Internal server error

  /api/v1/whip/{productionId}/{lineId}/{sessionId}:
    delete:
      tags:
        - WHIP (WebRTC-HTTP Ingest Protocol)
      summary: Terminate a WHIP session
      description: |
        Terminates an active WHIP session by:

        - Stopping the heartbeat for the session

        - Removing the session from memory

        - Returning CORS headers for browser compatibility
      parameters:
        - name: productionId
          in: path
          required: true
          schema: { type: string }
        - name: lineId
          in: path
          required: true
          schema: { type: string }
        - name: sessionId
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Session terminated successfully
          content:
            text/plain:
              schema:
                type: string
                example: OK
        "404":
          description: Session not found
        "500":
          description: Failed to terminate session
    patch:
      tags:
        - WHIP (WebRTC-HTTP Ingest Protocol)
      summary: Trickle ICE (Not Supported)
      description: |
        This endpoint currently does not support Trickle ICE.

        Returns a `405 Method Not Allowed` error.
      parameters:
        - name: productionId
          in: path
          required: true
          schema: { type: string }
        - name: lineId
          in: path
          required: true
          schema: { type: string }
        - name: sessionId
          in: path
          required: true
          schema: { type: string }
      responses:
        "405":
          description: Method not allowed
          content:
            text/plain:
              schema:
                type: string
                example: Method not allowed

  /api/v1/whip/{productionId}/{lineId}:
    options:
      tags:
        - WHIP (WebRTC-HTTP Ingest Protocol)
      summary: WHIP discovery and CORS preflight
      description: |
        Preflight endpoint to:

        - Validate that the `productionId` and `lineId` exist

        - Return WHIP-specific CORS headers:
        
          - `Access-Control-Allow-*`

          - `Accept-Post: application/sdp`

        - Support browser-based clients
      parameters:
        - name: productionId
          in: path
          required: true
          schema: { type: string }
        - name: lineId
          in: path
          required: true
          schema: { type: string }
      responses:
        "200":
          description: CORS preflight successful
          content:
            text/plain:
              schema:
                type: string
                example: OK
        "400":
          description: Invalid production ID
        "404":
          description: Production or line not found
        "500":
          description: Internal server error

components:
  schemas:
    ProductionResponse:
      type: object
      properties:
        name:
          type: string
        productionId:
          type: string
        lines:
          type: array
          items:
            $ref: '#/components/schemas/LineResponse'
      required:
        - name
        - productionId
    LineResponse:
      type: object
      properties:
        name:
          type: string
        id:
          type: string
        smbConferenceId:
          type: string
        participants:
          type: array
          items:
            $ref: '#/components/schemas/UserResponse'
        programOutputLine:
          type: boolean
      required:
        - name
        - id
        - smbConferenceId
        - participants
    UserResponse:
      type: object
      properties:
        name:
          type: string
        sessionId:
          type: string
        endpointId:
          type: string
        isActive:
          type: boolean
      required:
        - name
        - sessionId
        - isActive
    ProductionListResponse:
      type: object
      properties:
        productions:
          type: array
          items:
            $ref: '#/components/schemas/ProductionResponse'
        offset:
          type: number
        limit:
          type: number
        totalItems:
          type: number
      required:
        - productions
        - offset
        - limit
        - totalItems
    DetailedProductionResponse:
      type: object
      properties:
        name:
          type: string
        productionId:
          type: string
        lines:
          type: array
          items:
            $ref: '#/components/schemas/LineResponse'
      required:
        - name
        - productionId
        - lines
    PatchLine:
      type: object
      properties:
        name:
          type: string
        programOutputLine:
          type: boolean
      required:
        - name
    PatchLineResponse:
      type: object
      properties:
        name:
          type: string
        id:
          type: string
        programOutputLine:
          type: boolean
      required:
        - name
        - id
    PatchProduction:
      type: object
      properties:
        name:
          type: string
          required:
            - name
    PatchProductionResponse:
      type: object
      properties:
        _id:
          type: number
        name:
          type: string
      required:
        - _id
        - name
    NewSession:
      type: object
      properties:
        productionId:
          type: string
        lineId:
          type: string
        username:
          type: string
      required:
        - productionId
        - lineId
        - username
    SessionResponse:
      type: object
      properties:
        sdp:
          type: string
        sessionId:
          type: string
      required:
        - sdp
        - sessionId
    SdpAnswer:
      type: object
      properties:
        sdpAnswer:
          type: string
    ErrorResponse:
      type: object
      properties:
        message:
          type: string
        stackTrace:
          type: string
    ShareRequest:
      type: object
      properties:
        path:
          type: string
      required:
        - path
    ShareResponse:
      type: object
      properties:
        url:
          type: string
      required:
        - url
    ReauthResponse:
      type: object
      properties:
        token:
          type: string
      required:
        - token
    NewProduction:
      type: object
      properties:
        name:
          type: string
        lines:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
              programOutputLine:
                type: boolean
            required:
              - name
      required:
        - name
        - lines
    NewProductionLine:
      type: object
      properties:
        name:
          type: string
          required: true
        programOutputLine:
          type: boolean




