Demodesk Recordings API

Endpoints to discover recordings and fetch related artifacts.

OpenAPI Specification

demodesk-recordings-api-openapi.yml Raw ↑
openapi: 3.0.1
info:
  title: API V1 Externally recorded demos Recordings API
  version: v1
  description: Endpoints to discover recordings and fetch related artifacts.
servers:
- url: https://{defaultHost}
  variables:
    defaultHost:
      default: demodesk.com
tags:
- name: Recordings
  description: Endpoints to discover recordings and fetch related artifacts.
paths:
  /recordings:
    get:
      tags:
      - Recordings
      operationId: listRecordings
      x-mcp:
        enabled: true
        toolName: recordings_list
        title: List recordings
        description: 'Find meeting recordings visible to the authenticated Demodesk user.

          Typical use case: searching, filtering, or browsing calls (for example: "show last week''s meetings", "find calls where I talked to Daniel").

          Prefer dedicated people filters over internal ids: `host_eq`, `host_in`, `attendee_eq`, `attendee_in`.

          Person filter values support an email address or `me` (resolves to authenticated user).

          Examples: `filter[host_eq]=host@example.com`, `filter[host_eq]=me`, `filter[attendee_eq]=attendee@example.com`, `filter[attendee_eq]=me`, `filter[attendee_in][]=me&filter[attendee_in][]=champion@example.com`.

          Returns paginated recording metadata and cursor info; use returned ids/tokens in follow-up tools like getting the transcripts of the found meetings.

          '
        readOnlyHint: true
        idempotentHint: true
        destructiveHint: false
        openWorldHint: true
        timeoutMs: 30000
      summary: List recordings
      description: 'Returns recordings **visible to the owner of the API key**.


        If you want to access "all" recordings, use an admin user who is added to all restricted groups. Recordings with explicit `only_me` permission are only accessible by said user.


        Rate limits:

        - Global: 120 requests per minute per API key.

        - Endpoint-specific: 60 requests per minute per API key.

        '
      parameters:
      - $ref: '#/components/parameters/filter'
      - $ref: '#/components/parameters/cursor'
      - $ref: '#/components/parameters/limit'
      responses:
        '200':
          description: Recordings fetched successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/recording-index-response'
        '400':
          $ref: '#/components/responses/bad-request'
        '401':
          $ref: '#/components/responses/unauthorized'
        '429':
          $ref: '#/components/responses/too-many-requests'
  /recordings/{token}:
    get:
      tags:
      - Recordings
      operationId: getRecording
      x-mcp:
        enabled: true
        toolName: recordings_get
        title: Get recording details
        description: 'Fetch one recording by token with detailed metadata and computed stats.

          Typical use case: opening one call after a token is known or selected from list results (for example: "open this recording", "show details for token X").

          Returns a single recording object including ownership, timing, and processing state.

          '
        readOnlyHint: true
        idempotentHint: true
        destructiveHint: false
        openWorldHint: true
        timeoutMs: 45000
      summary: Get recording details
      description: 'Returns a single recording with detailed metadata and derived stats.


        Rate limits:

        - Global: 120 requests per minute per API key.

        '
      parameters:
      - $ref: '#/components/parameters/recording-token'
      responses:
        '200':
          description: Recording fetched successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/recording-detail-response'
        '401':
          $ref: '#/components/responses/unauthorized'
        '404':
          $ref: '#/components/responses/not-found'
        '429':
          $ref: '#/components/responses/too-many-requests'
  /recordings/{token}/transcript:
    get:
      tags:
      - Recordings
      operationId: getRecordingTranscript
      x-mcp:
        enabled: true
        toolName: recordings_get_transcript
        title: Get recording transcript
        description: 'Retrieve transcript content for a single recording, optionally translated via `lang`.

          Typical use case: reading, quoting, summarizing, or analyzing what was said in one call (for example: "show transcript", "translate transcript to de").

          Returns transcript paragraphs/sentences (or plain text format when requested).

          '
        readOnlyHint: true
        idempotentHint: true
        destructiveHint: false
        openWorldHint: true
        timeoutMs: 20000
      summary: Get recording transcript
      description: 'Returns the full transcript grouped into paragraphs and sentences.


        Use the optional `lang` query parameter to request a translated transcript.


        Rate limits:

        - Global: 120 requests per minute per API key.

        '
      parameters:
      - $ref: '#/components/parameters/recording-token'
      - $ref: '#/components/parameters/transcript-language'
      - $ref: '#/components/parameters/transcript-format'
      responses:
        '200':
          description: Transcript fetched successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/recording-transcript-response'
            text/plain:
              schema:
                type: string
              examples:
                meetingLines:
                  summary: speaker-prefixed transcript lines
                  value: 'Host User:

                    Thanks for joining.

                    Let''s review the agenda.

                    Customer Contact:

                    Sounds good, let''s start.

                    '
        '202':
          $ref: '#/components/responses/transcript-processing'
        '204':
          description: Transcript exists but is empty or too short.
        '400':
          $ref: '#/components/responses/bad-request'
        '401':
          $ref: '#/components/responses/unauthorized'
        '404':
          $ref: '#/components/responses/not-found'
        '429':
          $ref: '#/components/responses/too-many-requests'
  /transcripts/batch:
    post:
      tags:
      - Recordings
      operationId: batchGetRecordingsTranscripts
      x-mcp:
        enabled: true
        toolName: recordings_batch_get_transcripts
        title: Get transcripts in batch
        description: 'Retrieve transcripts for up to 100 recordings in one request.

          Typical use case: cross-call analysis over a known set of recording tokens (for example: "get transcripts for these 20 calls").

          This endpoint performs retrieval only and does not create, update, or delete data.

          Returns per-recording transcript payloads with per-item status so partial failures remain inspectable.

          '
        readOnlyHint: true
        idempotentHint: true
        destructiveHint: false
        openWorldHint: true
        timeoutMs: 60000
      summary: Get transcripts in batch
      description: 'Returns transcripts for `n` recordings (`n` <= 100).

        Results are returned with a per-recording status.


        Rate limits:

        - Global: 120 requests per minute per API key.

        '
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/transcript-batch-request'
      responses:
        '200':
          description: Transcript batch fetched successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/transcript-batch-response'
        '400':
          $ref: '#/components/responses/bad-request'
        '401':
          $ref: '#/components/responses/unauthorized'
        '429':
          $ref: '#/components/responses/too-many-requests'
  /recordings/{token}/summaries:
    get:
      tags:
      - Recordings
      operationId: listRecordingSummaries
      x-mcp:
        enabled: true
        toolName: recordings_list_summaries
        title: List recording summaries
        description: 'List AI-generated summaries attached to a recording.

          Typical use case: recap notes, highlights, or summary variants for a specific call (for example: "show the summary", "what are the key takeaways?").

          Returns summary records and metadata for the requested recording token.

          '
        readOnlyHint: true
        idempotentHint: true
        destructiveHint: false
        openWorldHint: true
        timeoutMs: 30000
      summary: List recording summaries
      description: 'Returns generated summaries associated with the recording.


        Rate limits:

        - Global: 120 requests per minute per API key.

        '
      parameters:
      - $ref: '#/components/parameters/recording-token'
      responses:
        '200':
          description: Summaries fetched successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/recording-summaries-response'
        '401':
          $ref: '#/components/responses/unauthorized'
        '404':
          $ref: '#/components/responses/not-found'
        '429':
          $ref: '#/components/responses/too-many-requests'
  /recordings/{token}/scorecards:
    get:
      tags:
      - Recordings
      operationId: listRecordingScorecards
      x-mcp:
        enabled: true
        toolName: recordings_list_scorecards
        title: List recording scorecards
        description: 'List non-deleted scorecards linked to a recording.

          Typical use case: coaching evaluation, rubric results, or scoring details for a call (for example: "show scorecard", "how did this call score?").

          Returns scorecard entries and associated scoring metadata for the recording token.

          '
        readOnlyHint: true
        idempotentHint: true
        destructiveHint: false
        openWorldHint: true
        timeoutMs: 15000
      summary: List recording scorecards
      description: 'Returns non-deleted scorecards associated with the recording.


        Rate limits:

        - Global: 120 requests per minute per API key.

        '
      parameters:
      - $ref: '#/components/parameters/recording-token'
      responses:
        '200':
          description: Scorecards fetched successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/recording-scorecards-response'
        '401':
          $ref: '#/components/responses/unauthorized'
        '404':
          $ref: '#/components/responses/not-found'
        '429':
          $ref: '#/components/responses/too-many-requests'
components:
  schemas:
    recording-summaries-response:
      type: object
      required:
      - data
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/summary-list-item'
    error-response:
      type: object
      required:
      - error
      properties:
        error:
          type: object
          required:
          - code
          - message
          - requestId
          properties:
            code:
              type: string
            message:
              type: string
            requestId:
              type: string
    recording-detail-response:
      type: object
      required:
      - data
      properties:
        data:
          $ref: '#/components/schemas/recording-detail-item'
    recording-meeting-location-item:
      type: object
      required:
      - kind
      - value
      properties:
        kind:
          type:
          - string
          - 'null'
        value:
          type:
          - string
          - 'null'
    transcript:
      type: object
      required:
      - language
      - paragraphs
      properties:
        language:
          type: string
        paragraphs:
          type: array
          items:
            $ref: '#/components/schemas/transcript-paragraph'
    recording-transcript-response:
      type: object
      required:
      - data
      properties:
        data:
          $ref: '#/components/schemas/transcript'
    recording-participant-item:
      type: object
      required:
      - participantId
      - displayName
      - email
      - permission
      - attended
      properties:
        participantId:
          type: string
        displayName:
          type: string
        email:
          type:
          - string
          - 'null'
        permission:
          type: string
          enum:
          - customer
          - team
          - host
        attended:
          type: boolean
    transcript-sentence:
      type: object
      required:
      - startInSeconds
      - endInSeconds
      - text
      properties:
        startInSeconds:
          type: number
        endInSeconds:
          type: number
        text:
          type: string
    transcript-batch-request:
      type: object
      required:
      - recordingTokens
      properties:
        recordingTokens:
          type: array
          minItems: 1
          maxItems: 100
          uniqueItems: true
          items:
            type: string
        lang:
          type: string
          pattern: ^[a-z]{2}(-[A-Z]{2})?$
          description: 'Translate results to the provided language on-the-fly. Examples: `de`, `fr`, etc.'
        format:
          type: string
          enum:
          - json
          - plaintext
          default: json
          description: Whether you want structured data for every single sentence or the transcript merged into plain text.
    recording-detail-item:
      allOf:
      - $ref: '#/components/schemas/recording-list-item'
      - type: object
        required:
        - host
        - audioOnly
        - attendeeClassification
        - meetingLocation
        - participants
        - groups
        - statistics
        properties:
          host:
            description: The demo host, resolved inline. `null` when no host is assigned to the demo.
            oneOf:
            - $ref: '#/components/schemas/recording-host-item'
            - type: 'null'
          audioOnly:
            type: boolean
          attendeeClassification:
            type:
            - string
            - 'null'
            enum:
            - internal_meeting
            - external_meeting
            - 'null'
          meetingLocation:
            $ref: '#/components/schemas/recording-meeting-location-item'
          participants:
            type: array
            items:
              $ref: '#/components/schemas/recording-participant-item'
          groups:
            type: array
            description: Groups assigned to the demo. Only populated when access is `selected_groups`.
            items:
              $ref: '#/components/schemas/recording-group-item'
          statistics:
            $ref: '#/components/schemas/recording-statistics'
    transcript-batch-response:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/transcript-batch-item'
        meta:
          type: object
          required:
          - requestedCount
          properties:
            requestedCount:
              type: integer
              minimum: 1
    recording-group-item:
      type: object
      required:
      - groupId
      - groupName
      properties:
        groupId:
          type: string
          description: Internal group ID as string.
          examples:
          - '456'
        groupName:
          type: string
          description: Name of the group.
          examples:
          - Enterprise Sales
    recording-host-item:
      type: object
      description: The host of the demo, resolved inline so consumers don't need a follow-up users lookup.
      required:
      - userId
      - displayName
      - email
      properties:
        userId:
          type: string
          description: ID of the demo host user as string.
        displayName:
          type: string
          description: Human-readable name of the host.
        email:
          type: string
          description: Email address of the host.
    summary-list-item:
      type: object
      required:
      - summaryId
      - promptId
      - promptName
      - languageCode
      - content
      - htmlContent
      - createdAt
      - updatedAt
      properties:
        summaryId:
          type: string
        promptId:
          type:
          - string
          - 'null'
        promptName:
          type:
          - string
          - 'null'
        languageCode:
          type: string
        content:
          type: string
        htmlContent:
          type:
          - string
          - 'null'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    recording-index-response:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/recording-list-item'
        meta:
          type: object
          required:
          - hasNext
          - limit
          properties:
            hasNext:
              type: boolean
            limit:
              type: integer
            nextCursor:
              type:
              - string
              - 'null'
    transcript-speaker:
      type: object
      required:
      - displayName
      properties:
        displayName:
          type:
          - string
          - 'null'
    transcript-batch-item:
      type: object
      required:
      - recordingToken
      - status
      - transcript
      properties:
        recordingToken:
          type: string
        status:
          type: string
          enum:
          - ready
          - processing
          - not_requested
          - empty
          - not_found
        transcript:
          oneOf:
          - $ref: '#/components/schemas/transcript'
          - $ref: '#/components/schemas/transcript-plaintext'
          - type: 'null'
    scorecard-list-item:
      type: object
      required:
      - scorecardId
      - templateId
      - templateName
      - score
      - comment
      - languageCode
      - giverUserId
      - receiverUserId
      - createdAt
      - updatedAt
      - questions
      properties:
        scorecardId:
          type: string
        templateId:
          type:
          - string
          - 'null'
        templateName:
          type: string
        score:
          type:
          - integer
          - 'null'
          minimum: 1
          maximum: 5
        comment:
          type:
          - string
          - 'null'
        languageCode:
          type:
          - string
          - 'null'
        giverUserId:
          type:
          - integer
          - 'null'
        receiverUserId:
          type: integer
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        questions:
          type: array
          items:
            $ref: '#/components/schemas/scorecard-question-list-item'
    scorecard-question-list-item:
      type: object
      required:
      - questionId
      - position
      - name
      - text
      - criteria
      - score
      - comment
      - rawTimestamps
      properties:
        questionId:
          type: string
        position:
          type: integer
          minimum: 0
        name:
          type:
          - string
          - 'null'
        text:
          type: string
        criteria:
          type:
          - string
          - 'null'
        score:
          type:
          - integer
          - 'null'
          minimum: 1
          maximum: 5
        comment:
          type:
          - string
          - 'null'
        rawTimestamps:
          type:
          - string
          - 'null'
    transcript-paragraph:
      type: object
      required:
      - startInSeconds
      - endInSeconds
      - speaker
      - sentences
      properties:
        startInSeconds:
          type: number
        endInSeconds:
          type: number
        speaker:
          anyOf:
          - $ref: '#/components/schemas/transcript-speaker'
          - type: 'null'
        sentences:
          type: array
          items:
            $ref: '#/components/schemas/transcript-sentence'
    recording-scorecards-response:
      type: object
      required:
      - data
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/scorecard-list-item'
    recording-list-item:
      type: object
      required:
      - recordingToken
      - recordingId
      - demoId
      - userId
      - name
      - recordingWebUrl
      - temporaryDirectUrl
      - status
      - createdAt
      - updatedAt
      - demoStartDate
      - durationMs
      - postprocessingStatus
      - access
      properties:
        recordingToken:
          type: string
          description: Public recording token identifier.
        recordingId:
          type: string
          description: Internal recording id as string.
        demoId:
          type: string
          description: Internal demo id as string.
        userId:
          type: integer
          description: ID of the demo host user.
        name:
          type:
          - string
          - 'null'
        recordingWebUrl:
          type: string
          description: URL to the recording view in Demodesk.
        temporaryDirectUrl:
          type:
          - string
          - 'null'
          description: Direct link to the video file, signed with a temporary access token.
        status:
          type: string
          description: 'Recording status.

            - `pending`: recording is still in progress or processing

            - `ready`: recording can be consumed

            - `failure`: recording failed

            - `cancelled`: recording never started, eg. because the bot was not admitted

            - `expired`: recording expired

            '
          enum:
          - pending
          - ready
          - failure
          - cancelled
          - expired
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        demoStartDate:
          type:
          - string
          - 'null'
          format: date-time
        durationMs:
          type:
          - integer
          - 'null'
        postprocessingStatus:
          type:
          - string
          - 'null'
          description: 'Recording post-processing status. Post-processing includes transcription, participant data enrichment, meeting statistics, generating summaries, etc.

            '
          enum:
          - pending
          - done
          - failed
          - too_short
        access:
          type: string
          description: 'Access permission level of the demo.

            - `company_wide`: visible to all company members

            - `selected_groups`: visible to members of assigned groups

            - `host_and_participants`: visible only to the host and participants

            - `host_only`: visible only to the host

            '
          enum:
          - company_wide
          - selected_groups
          - host_and_participants
          - host_only
        groupIds:
          type: array
          description: IDs of groups assigned to the demo. Present in list responses only; detail responses use `groups` instead.
          items:
            type: string
    transcript-plaintext:
      type: object
      required:
      - language
      - text
      properties:
        language:
          type: string
        text:
          type: string
    recording-statistics:
      type: object
      required:
      - engagementScore
      - feedbackScore
      properties:
        engagementScore:
          type:
          - integer
          - 'null'
          minimum: 0
          maximum: 10
        feedbackScore:
          type:
          - number
          - 'null'
          format: float
  responses:
    not-found:
      description: Recording or related resource was not found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/error-response'
    unauthorized:
      description: API key is missing or invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/error-response'
    bad-request:
      description: Request validation failed.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/error-response'
    too-many-requests:
      description: Rate limit exceeded.
      headers:
        Retry-After:
          description: Seconds until the next request is allowed.
          schema:
            type: integer
    transcript-processing:
      description: Transcript is still being processed.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/error-response'
  parameters:
    limit:
      name: limit
      in: query
      required: false
      description: Page size for recordings list.
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 100
    transcript-format:
      name: format
      in: query
      required: false
      schema:
        type: string
        enum:
        - json
        - plaintext
        default: json
      description: Whether to return structured transcript JSON or plain text.
    recording-token:
      name: token
      in: path
      required: true
      description: Public recording token identifier.
      schema:
        type: string
      example: d0584dc4e31be4a0
    transcript-language:
      name: lang
      in: query
      required: false
      schema:
        type: string
        pattern: ^[a-z]{2}(-[A-Z]{2})?$
      description: Translate transcript on-the-fly to the provided language.
    cursor:
      name: cursor
      in: query
      required: false
      description: Opaque cursor returned by a previous list response.
      schema:
        type: string
    filter:
      name: filter
      in: query
      description: 'Filter recordings by their properties.


        This uses Ransack-style keys in a deep-object parameter. Filter attributes use snake_case.

        Common operators include `eq`, `not_eq`, `cont`, `gteq`, `lteq`, and `in`.


        Common examples

        - `/api/v2/recordings?filter[status_eq]=pending` (allowed public values: `pending`, `ready`, `failure`, `cancelled`, `expired`)

        - `/api/v2/recordings?filter[postprocessing_status_eq]=done` (You most-likely want this - eg. transcribed, summaries generated, CRM synced, ...)

        - `/api/v2/recordings?filter[status_in][]=ready&filter[status_in][]=failure` (Ransack `in` operator with repeated array params)

        - `/api/v2/recordings?filter[created_at_gteq]=2025-01-01T00:00:00Z`

        - `/api/v2/recordings?filter[demo_id_eq]=123`

        - `/api/v2/recordings?filter[host_eq]=me` (recordings hosted by the current authenticated user)

        - `/api/v2/recordings?filter[host_eq]=host@example.com` (recordings hosted by a specific user)

        - `/api/v2/recordings?filter[host_in][]=me&filter[host_in][]=manager@example.com` (multiple hosts)

        - `/api/v2/recordings?filter[attendee_eq]=prospect@example.com` (recordings where this attendee joined)

        - `/api/v2/recordings?filter[attendee_in][]=me&filter[attendee_in][]=champion@example.com` (multiple attendees)

        - `/api/v2/recordings?filter[group_id_eq]=99` (for `access: company_wide` or `host_only` recordings: matches by host''s group membership; for `access: selected_groups` recordings: matches by the demo''s assigned groups)

        - `/api/v2/recordings?filter[group_id_in][]=99&filter[group_id_in][]=100` (multiple groups)

        - `/api/v2/recordings?filter[access_eq]=host_only` (allowed values: `company_wide`, `selected_groups`, `host_only`)


        They can be combined freely.

        '
      required: false
      style: deepObject
      explode: true
      schema:
        type: object
        additionalProperties: false
        properties:
          created_at_eq:
            type: string
          created_at_not_eq:
            type: string
          created_at_lt:
            type: string
          created_at_lteq:
            type: string
          created_at_gt:
            type: string
          created_at_gteq:
            type: string
          created_at_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
          created_at_not_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
          created_at_cont:
            type: string
          updated_at_eq:
            type: string
          updated_at_not_eq:
            type: string
          updated_at_lt:
            type: string
          updated_at_lteq:
            type: string
          updated_at_gt:
            type: string
          updated_at_gteq:
            type: string
          updated_at_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
          updated_at_not_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
          updated_at_cont:
            type: string
          demo_start_date_eq:
            type: string
          demo_start_date_not_eq:
            type: string
          demo_start_date_lt:
            type: string
          demo_start_date_lteq:
            type: string
          demo_start_date_gt:
            type: string
          demo_start_date_gteq:
            type: string
          demo_start_date_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
          demo_start_date_not_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
          demo_start_date_cont:
            type: string
          duration_eq:
            type: string
          duration_not_eq:
            type: string
          duration_lt:
            type: string
          duration_lteq:
            type: string
          duration_gt:
            type: string
          duration_gteq:
            type: string
          duration_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
          duration_not_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
          duration_cont:
            type: string
          demo_id_eq:
            type: string
          demo_id_not_eq:
            type: string
          demo_id_lt:
            type: string
          demo_id_lteq:
            type: string
          demo_id_gt:
            type: string
          demo_id_gteq:
            type: string
          demo_id_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
          demo_id_not_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
          demo_id_cont:
            type: string
          host_eq:
            type: string
            description: Host selector. Accepts an email address or `me`.
            example: me
          host_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
            description: Host selectors. Each value accepts an email address or `me`.
            example:
            - me
            - host@example.com
          attendee_eq:
            type: string
            description: Attendee selector. Accepts an email address or `me`.
            example: attendee@example.com
          attendee_in:
            oneOf:
            - type: string
            - type: array
              items:
                type: string
            desc

# --- truncated at 32 KB (39 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/demodesk/refs/heads/main/openapi/demodesk-recordings-api-openapi.yml