openapi: 3.1.1
info:
  title: TimeBack Platform - Insights API
  description: Coaching insights and session analytics endpoints
  version: 1.0.0
tags:
  - name: Insights
    description: Coaching insights and session analytics endpoints
servers:
  - url: https://platform.dev.timeback.com
    description: integration
paths:
  /insights/1.0/users/{userId}:
    get:
      tags:
        - Insights
      summary: Get user insights
      description: >-
        Retrieve insights for a specific user with optional filtering and
        pagination
      operationId: getUserInsights
      parameters:
        - name: userId
          in: path
          description: User ID to retrieve insights for
          required: true
          schema:
            type: string
            format: uuid
        - name: applicationId
          in: query
          description: Optional application ID to scope insights to a specific application
          required: false
          schema:
            type: string
            format: uuid
        - name: toolId
          in: query
          description: >-
            Deprecated: Optional tool ID to scope insights to a specific tool.
            Use applicationId instead.
          required: false
          deprecated: true
          schema:
            type: string
            format: uuid
        - name: after
          in: query
          description: Filter insights after this timestamp (ISO 8601 format, inclusive)
          required: false
          schema:
            type: string
            format: date-time
        - name: before
          in: query
          description: Filter insights before this timestamp (ISO 8601 format, inclusive)
          required: false
          schema:
            type: string
            format: date-time
        - $ref: '#/components/parameters/limit1'
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/type'
        - $ref: '#/components/parameters/category'
      responses:
        '200':
          $ref: '#/components/responses/InsightsSuccess'
        '400':
          $ref: '#/components/responses/BadRequest6'
        '401':
          $ref: '#/components/responses/Unauthorized3'
        '403':
          $ref: '#/components/responses/Forbidden3'
        '500':
          $ref: '#/components/responses/InternalError6'
      security:
        - CognitoUserPoolAuthorizer:
            - https://purl.imsglobal.org/spec/caliper/v1p2/scope/events.readonly
  /insights/1.0/users/{userId}/sessions:
    get:
      tags:
        - Insights
      summary: Get user sessions
      description: >-
        Retrieve sessions for a specific user with optional application
        filtering and pagination
      operationId: getUserSessions
      parameters:
        - name: userId
          in: path
          description: User ID to retrieve sessions for
          required: true
          schema:
            type: string
            format: uuid
        - name: applicationId
          in: query
          description: Optional application ID to scope sessions to a specific application
          required: false
          schema:
            type: string
            format: uuid
        - name: toolId
          in: query
          description: >-
            Deprecated: Optional tool ID to scope sessions to a specific tool.
            Use applicationId instead.
          required: false
          deprecated: true
          schema:
            type: string
            format: uuid
        - name: startedAfter
          in: query
          description: >-
            Optional lower bound (inclusive) for session startedAtTime (ISO 8601
            date-time).
          required: false
          schema:
            type: string
            format: date-time
        - name: startedBefore
          in: query
          description: >-
            Optional upper bound (inclusive) for session startedAtTime (ISO 8601
            date-time).
          required: false
          schema:
            type: string
            format: date-time
        - name: isProctored
          in: query
          description: Optional flag to filter sessions by proctored status
          required: false
          schema:
            type: boolean
        - name: webcamEnabled
          in: query
          description: Optional flag to filter sessions by webcam enabled status
          required: false
          schema:
            type: boolean
        - $ref: '#/components/parameters/limit1'
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/category'
      responses:
        '200':
          $ref: '#/components/responses/SessionsSuccess'
        '400':
          $ref: '#/components/responses/BadRequest6'
        '401':
          $ref: '#/components/responses/Unauthorized3'
        '403':
          $ref: '#/components/responses/Forbidden3'
        '500':
          $ref: '#/components/responses/InternalError6'
      security:
        - CognitoUserPoolAuthorizer:
            - https://purl.imsglobal.org/spec/caliper/v1p2/scope/events.readonly
  /insights/1.0/sessions/{sessionId}:
    get:
      tags:
        - Insights
      summary: Get session insights
      description: Retrieve insights for a specific session with pagination
      operationId: getSessionInsights
      parameters:
        - name: sessionId
          in: path
          description: Session ID to retrieve insights for (URL-encoded if necessary)
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/limit1'
        - $ref: '#/components/parameters/offset'
        - name: isProctored
          in: query
          description: Optional flag to filter insights by proctored status
          required: false
          schema:
            type: boolean
        - $ref: '#/components/parameters/type'
        - $ref: '#/components/parameters/category'
      responses:
        '200':
          $ref: '#/components/responses/InsightsSuccess'
        '400':
          $ref: '#/components/responses/BadRequest6'
        '401':
          $ref: '#/components/responses/Unauthorized3'
        '403':
          $ref: '#/components/responses/Forbidden3'
        '404':
          $ref: '#/components/responses/NotFound4'
        '500':
          $ref: '#/components/responses/InternalError6'
      security:
        - CognitoUserPoolAuthorizer:
            - https://purl.imsglobal.org/spec/caliper/v1p2/scope/events.readonly
  /insights/1.0/users/{userId}/overview:
    get:
      tags:
        - Insights
      summary: Get user overview
      description: >-
        Retrieve aggregated overview (trend and breakdown) for a user within a
        date range
      operationId: getUserInsightsOverview
      parameters:
        - name: userId
          in: path
          description: User ID to retrieve overview for
          required: true
          schema:
            type: string
            format: uuid
        - name: startedAfter
          in: query
          description: Lower bound (inclusive) for time window (ISO 8601 date-time)
          required: true
          schema:
            type: string
            format: date-time
        - name: startedBefore
          in: query
          description: Upper bound (exclusive) for time window (ISO 8601 date-time)
          required: true
          schema:
            type: string
            format: date-time
        - name: targetTimezone
          in: query
          description: IANA timezone name for local-day bucketing (e.g., Asia/Karachi)
          required: true
          schema:
            type: string
      responses:
        '200':
          $ref: '#/components/responses/InsightsOverviewSuccess'
        '400':
          $ref: '#/components/responses/BadRequest6'
        '401':
          $ref: '#/components/responses/Unauthorized3'
        '403':
          $ref: '#/components/responses/Forbidden3'
        '500':
          $ref: '#/components/responses/InternalError6'
      security:
        - CognitoUserPoolAuthorizer:
            - https://purl.imsglobal.org/spec/caliper/v1p2/scope/events.readonly
components:
  schemas:
    PaginatedInsightsOutput:
      type: object
      description: Unified paginated insights response with session summary
      properties:
        session:
          $ref: '#/components/schemas/SessionSummaryOutput'
          description: Session summary information
        insights:
          type: array
          description: Array of insight objects
          items:
            $ref: '#/components/schemas/InsightOutput'
        offset:
          type: integer
          description: Number of items skipped in the result set
        limit:
          type: integer
          description: Maximum number of items returned
        total:
          type: integer
          description: Total number of items available
      required:
        - session
        - insights
        - offset
        - limit
        - total
    ApiError:
      type: object
      description: API error response
      properties:
        error:
          type: string
          description: Error type
        message:
          type: string
          description: Error message
      required:
        - error
        - message
    PaginatedSessionsOutput:
      type: object
      description: Paginated list of sessions
      properties:
        sessions:
          type: array
          description: Array of session objects
          items:
            $ref: '#/components/schemas/SessionOutput'
        offset:
          type: integer
          description: Number of items skipped in the result set
        limit:
          type: integer
          description: Maximum number of items returned
        total:
          type: integer
          description: Total number of items available
      required:
        - sessions
        - offset
        - limit
        - total
    GetUserInsightsOverviewOutput:
      type: object
      properties:
        summary:
          type: object
          properties:
            totalDurationInSeconds:
              type: integer
            totalWasteDurationInSeconds:
              type: integer
            wastePercentage:
              type: integer
            biggestInsight:
              allOf:
                - $ref: '#/components/schemas/InsightsBiggestInsight'
                - nullable: true
          required:
            - totalDurationInSeconds
            - totalWasteDurationInSeconds
            - wastePercentage
            - biggestInsight
        trend:
          type: object
          properties:
            buckets:
              type: array
              items:
                $ref: '#/components/schemas/InsightsTrendBucket'
          required:
            - buckets
        breakdown:
          type: object
          properties:
            levels:
              type: array
              items:
                $ref: '#/components/schemas/InsightsLevelBreakdown'
          required:
            - levels
      required:
        - summary
        - trend
        - breakdown
    SessionSummaryOutput:
      type: object
      description: Session summary information
      properties:
        startedAtTime:
          type: string
          format: date-time
          description: Timestamp when the session started
        endedAtTime:
          type: string
          format: date-time
          description: Timestamp when the session ended
        durationInSeconds:
          type: integer
          description: Total duration of the session in seconds
        wasteDurationInSeconds:
          type: integer
          description: Total waste duration of the session in seconds
        wastePercentage:
          type: integer
          description: Percentage of session time that was waste (0-100)
        recordingS3Key:
          type:
            - string
            - 'null'
          description: S3 key of the session recording, null if no recording exists
      required:
        - startedAtTime
        - endedAtTime
        - durationInSeconds
        - wasteDurationInSeconds
        - wastePercentage
        - recordingS3Key
    InsightOutput:
      type: object
      description: Unified insight information
      properties:
        id:
          type: string
          format: uuid
          description: Unique identifier for the insight
        insightType:
          $ref: '#/components/schemas/InsightType'
        reason:
          type: string
          description: Detailed reason or description for the insight
        startedAtTime:
          type: string
          format: date-time
          description: Timestamp when the insight period started
        endedAtTime:
          type: string
          format: date-time
          description: Timestamp when the insight period ended
        durationInSeconds:
          type: integer
          description: Duration of the insight in seconds
        version:
          $ref: '#/components/schemas/InsightVersion'
      required:
        - id
        - insightType
        - reason
        - startedAtTime
        - endedAtTime
        - durationInSeconds
        - version
    SessionOutput:
      type: object
      description: Session information
      properties:
        id:
          type: string
          description: Session identifier
        applicationId:
          type: string
          format: uuid
          description: Identifier for the LTI application used in the session
        toolId:
          type: string
          format: uuid
          deprecated: true
          description: >-
            Deprecated: Identifier for the LTI tool used in the session. Use
            applicationId instead.
        startedAtTime:
          type: string
          format: date-time
          description: Timestamp when the session started
        endedAtTime:
          type: string
          format: date-time
          description: Timestamp when the session ended
        durationInSeconds:
          type: integer
          description: Total duration of the session in seconds
        wasteDurationInSeconds:
          type: integer
          description: Total waste duration of the session in seconds
        wastePercentage:
          type: integer
          description: Percentage of session time that was waste (0-100)
        isProctored:
          type: boolean
          description: Indicates whether this session is considered proctored
        webcamEnabled:
          type: boolean
          description: Indicates whether the webcam was enabled for this session
        proctoredResult:
          type: string
          description: Result of the proctored session
        recordingStartedAtTime:
          type:
            - string
            - 'null'
          format: date-time
          description: Timestamp when the recording started
        recordingEndedAtTime:
          type:
            - string
            - 'null'
          format: date-time
          description: Timestamp when the recording ended
        recordingS3Key:
          type:
            - string
            - 'null'
          description: S3 key for the associated proctoring recording, if any
      required:
        - id
        - applicationId
        - toolId
        - startedAtTime
        - endedAtTime
        - durationInSeconds
        - wasteDurationInSeconds
        - wastePercentage
    InsightsBiggestInsight:
      type: object
      properties:
        level:
          $ref: '#/components/schemas/InsightsOverviewLevel'
        durationInSeconds:
          type: integer
          minimum: 0
        wastePercentage:
          type: integer
          minimum: 0
          maximum: 100
      required:
        - level
        - durationInSeconds
        - wastePercentage
    InsightsTrendBucket:
      type: object
      properties:
        startedAtTime:
          type: string
          format: date-time
        endedAtTime:
          type: string
          format: date-time
        totalDurationInSeconds:
          type: integer
          minimum: 0
        wasteDurationInSeconds:
          type: integer
          minimum: 0
        wastePercentage:
          type: integer
          minimum: 0
          maximum: 100
        levels:
          type: array
          items:
            $ref: '#/components/schemas/InsightsLevelSegment'
      required:
        - startedAtTime
        - endedAtTime
        - totalDurationInSeconds
        - wasteDurationInSeconds
        - wastePercentage
        - levels
    InsightsLevelBreakdown:
      type: object
      properties:
        level:
          $ref: '#/components/schemas/InsightsOverviewLevel'
        durationInSeconds:
          type: integer
          minimum: 0
        wastePercentage:
          type: integer
          minimum: 0
          maximum: 100
        subtypes:
          type: array
          items:
            $ref: '#/components/schemas/InsightsSubtypeSegment'
      required:
        - level
        - durationInSeconds
        - wastePercentage
        - subtypes
    InsightType:
      type: string
      description: >-
        Insight type slug (data-driven, see GET /insights/1.0/types for
        available types)
    InsightVersion:
      type: string
      description: Version of the insight
      maxLength: 16
    InsightsOverviewLevel:
      type: string
      enum:
        - StudentNotPresent
        - FocusAndIntensity
        - EnvironmentalDistractions
        - OnDeviceDistractions
        - LearningAppBestPractices
        - Unclassified
    InsightsLevelSegment:
      type: object
      properties:
        level:
          $ref: '#/components/schemas/InsightsOverviewLevel'
        durationInSeconds:
          type: integer
          minimum: 0
      required:
        - level
        - durationInSeconds
    InsightsSubtypeSegment:
      type: object
      properties:
        insightType:
          $ref: '#/components/schemas/InsightType'
        durationInSeconds:
          type: integer
          minimum: 0
      required:
        - insightType
        - durationInSeconds
  responses:
    InsightsSuccess:
      description: Successful response with insights and session summary
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PaginatedInsightsOutput'
    BadRequest6:
      description: Bad request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          examples:
            default:
              value:
                error: Bad request
                message: Invalid request parameters
    Unauthorized3:
      description: Unauthorized
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          examples:
            default:
              value:
                error: Unauthorized request
                message: Unauthorized request
    Forbidden3:
      description: Forbidden
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          examples:
            default:
              value:
                error: Access forbidden
                message: Access forbidden
    InternalError6:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          examples:
            default:
              value:
                error: Internal server error
                message: Internal server error
    SessionsSuccess:
      description: Successful response with sessions
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/PaginatedSessionsOutput'
    NotFound4:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          examples:
            default:
              value:
                error: Object not found
                message: Object with ID {id} not found
    InsightsOverviewSuccess:
      description: Successful response with overview data
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/GetUserInsightsOverviewOutput'
  securitySchemes:
    BasicAuth:
      type: http
      scheme: basic
      description: >-
        Basic authentication using client_id as username and client_secret as
        password
    CognitoUserPoolAuthorizer:
      type: oauth2
      description: >-
        OAuth 2.0 client credentials flow. Contact timeback@trilogy.com to
        request credentials for your application.
      flows:
        clientCredentials:
          tokenUrl: https://platform.dev.timeback.com/auth/1.0/token
          scopes:
            https://purl.imsglobal.org/spec/or/v1p2/scope/roster.readonly: roster readonly
            https://purl.imsglobal.org/spec/or/v1p2/scope/roster-core.readonly: roster-core readonly
            https://purl.imsglobal.org/spec/or/v1p2/scope/roster.createput: roster createput
            https://purl.imsglobal.org/spec/lti/v1p3/scope/lti.readonly: lti readonly
            https://purl.imsglobal.org/spec/or/v1p2/scope/roster-core.createput: roster-core createput
            https://purl.imsglobal.org/spec/or/v1p2/scope/roster.delete: roster delete
            https://purl.imsglobal.org/spec/or/v1p2/scope/roster-core.delete: roster-core delete
            https://purl.imsglobal.org/spec/case/v1p0/scope/case.readonly: case readonly
            https://purl.imsglobal.org/spec/case/v1p0/scope/case.createput: case createput
            https://purl.imsglobal.org/spec/case/v1p0/scope/case.delete: case delete
            https://timeback-platform.trilogy.com/competency-track/scope/competency-track.readonly: competency-track readonly
            https://timeback-platform.trilogy.com/competency-track/scope/competency-track.write: competency-track write
            https://timeback-platform.trilogy.com/competency-track/scope/competency-track.delete: competency-track delete
            https://purl.imsglobal.org/spec/caliper/v1p2/scope/events.write: events write
            https://purl.imsglobal.org/spec/caliper/v1p2/scope/events.readonly: events readonly
  parameters:
    limit:
      name: limit
      in: query
      description: Maximum number of items to return
      required: false
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 10
    offset:
      name: offset
      in: query
      description: Number of items to skip
      required: false
      schema:
        type: integer
        minimum: 0
        default: 0
    sort:
      name: sort
      in: query
      description: Field to sort by
      required: false
      schema:
        type: string
    orderBy:
      name: orderBy
      in: query
      description: Sort direction
      required: false
      schema:
        type: string
        enum:
          - ASC
          - DESC
        default: ASC
    fields:
      name: fields
      in: query
      description: >
        Comma-separated list of fields to include in the response.

        Available fields: sourcedId, status, dateCreated, dateLastModified,
        metadata, applicationType, name, description, isLtiCompliant,
        isLtiV1P3Compliant, isRecordingEnabled, idlingThreshold, launchUrl,
        landingUrl, homepageUrl, clientId, deploymentId, version, domains,
        fullDescription, learningAppSpecification, gradeRangeStart,
        gradeRangeEnd, languageSupport, caliperSupport, tags, publisherName,
        wasteMeter, idling, proctoringMode, subjects, logoUrl, coverImageUrl,
        tools (deprecated), tools.sourcedId (deprecated), tools.status
        (deprecated), tools.name (deprecated), tools.description (deprecated),
        tools.launchUrl (deprecated), tools.logoUrl (deprecated),
        tools.coverImageUrl (deprecated), tools.deploymentId (deprecated),
        tools.version (deprecated), tools.domains (deprecated),
        tools.fullDescription (deprecated), tools.gradeRangeStart (deprecated),
        tools.gradeRangeEnd (deprecated), tools.languageSupport (deprecated),
        tools.caliperSupport (deprecated), tools.tags (deprecated),
        tools.publisherName (deprecated), tools.wasteMeter (deprecated),
        tools.idling (deprecated), tools.proctoringMode (deprecated),
        tools.subjects (deprecated)
      required: false
      schema:
        type: string
      style: form
      explode: false
    filter:
      name: filter
      in: query
      description: >
        Filter results using OneRoster syntax: filter=<field><predicate><value>

        Predicates: = (equals), != (not equals), ~ (contains), >, >=, <, <=

        Logical operators: AND, OR (max 2 predicates per OneRoster spec)

        Available fields: sourcedId, status, name, description, tools.sourcedId
        (deprecated), tools.name (deprecated), tools.status (deprecated)

        Example: filter=name='MyApp'
      required: false
      schema:
        type: string
    documentIds:
      name: documentIds
      in: query
      description: >-
        Comma-separated list of document IDs to filter curriculum by specific
        packages
      required: true
      schema:
        type: string
      examples:
        default:
          value: >-
            c1d2e3f4-g5h6-7890-ijkl-mn1234567890,d2e3f4g5-h6i7-8901-jklm-no2345678901
    subjectIds:
      name: subjectIds
      in: query
      description: Comma-separated list of subject IDs to filter by
      required: true
      schema:
        type: string
      examples:
        default:
          value: >-
            fb375793-75d8-11ea-b707-067ee555162f,a1b2c3d4-e5f6-7890-abcd-ef1234567890
    courseIds:
      name: courseIds
      in: query
      description: Comma-separated list of course IDs to filter by
      required: false
      schema:
        type: string
      examples:
        default:
          value: >-
            5ef7ba25-fff5-11ed-bfbf-0e2145d6cfdf,b2c3d4e5-f6g7-8901-bcde-f23456789012
    limit1:
      name: limit
      in: query
      description: Maximum number of items to return
      required: false
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20
    type:
      name: type
      in: query
      description: >-
        Comma-separated list of insight type slugs to filter by (e.g.,
        Journal,AwayFromSeat). Takes precedence over category and default.
      required: false
      schema:
        type: string
        examples:
          - Journal,AwayFromSeat
    category:
      name: category
      in: query
      description: >-
        Comma-separated list of categories to filter by (e.g.,
        Engagement,Proctoring,Cheating). Takes precedence over default but is
        overridden by type.
      required: false
      schema:
        type: string
        examples:
          - Engagement,Proctoring,Cheating
    orderBy1:
      name: orderBy
      in: query
      description: Sort direction
      required: false
      schema:
        type: string
        enum:
          - ASC
          - DESC
    fields1:
      name: fields
      in: query
      description: Comma-separated list of fields to include
      required: false
      schema:
        type: string
      style: form
      explode: false
    fields2:
      name: fields
      in: query
      description: >
        Comma-separated list of fields to include in the response.

        Available fields: sourcedId, status, dateLastModified, username,
        enabledUser, givenName, familyName, middleName, email, phone, sms,
        identifier, roles, grades, agents, userIds, userMasterIdentifier,
        preferredFirstName, preferredMiddleName, preferredLastName,
        primaryOrg.sourcedId, primaryOrg.type, userProfiles,
        userProfiles.profileId, userProfiles.profileType, userProfiles.vendorId,
        userProfiles.applicationId
      required: false
      schema:
        type: string
      style: form
      explode: false
    filter1:
      name: filter
      in: query
      description: >
        Filter results using OneRoster syntax: filter=<field><predicate><value>

        Predicates: = (equals), != (not equals), ~ (contains), >, >=, <, <=

        Logical operators: AND, OR (max 2 predicates per OneRoster spec)

        Available fields: sourcedId, status, email, familyName, givenName,
        middleName, username, enabledUser, identifier, roles

        Example: filter=email='john@example.com'
      required: false
      schema:
        type: string
    sourcedId:
      name: sourcedId
      in: path
      description: Unique identifier of the CASE entity
      required: true
      schema:
        type: string
    fields21:
      name: fields
      in: query
      description: Comma-separated list of fields to include
      required: false
      schema:
        type: string
      style: form
      explode: false
    since:
      name: since
      in: query
      description: The date and time to filter credentials by
      required: false
      schema:
        type: string
        format: date-time
        examples:
          - {}
