> ## Documentation Index
> Fetch the complete documentation index at: https://docs.terang.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Course Bundles

> Get recommended course bundles — pairs of courses offered together, often at a discounted price.



## OpenAPI

````yaml api-reference/lms-openapi.json GET /api/recommended-courses
openapi: 3.1.0
info:
  title: Terang AI LMS API
  description: >-
    LMS application endpoints (served from the LMS application host, e.g.
    https://lms.terang.ai). Maintained manually — NOT overwritten by the Core
    API auto-update.
  version: 1.0.0
servers:
  - description: LMS (Production)
    url: https://lms.terang.ai
  - description: LMS (Development)
    url: https://dev.lms.terang.ai
security: []
paths:
  /api/recommended-courses:
    get:
      tags:
        - Courses
      summary: Course Bundles
      description: >-
        Returns recommended course bundles. Each bundle pairs two courses from
        the same category, typically at a combined price lower than buying them
        separately.


        When `?public=true`, only bundles where both courses are published AND
        have IAI event codes are returned. API key users must pass
        `?public=true` explicitly to get the public view.
      operationId: getCourseBundles
      parameters:
        - name: public
          in: query
          required: false
          description: >-
            Set to `true` to only show bundles where both courses are published
            and have IAI event codes.
          schema:
            type: boolean
        - name: categoryId
          in: query
          required: false
          description: Filter bundles by category ID.
          schema:
            type: integer
      responses:
        '200':
          description: Bundles retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CourseBundlesResponse'
        '401':
          description: Unauthorized — missing or invalid API key / session
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: >-
            Rate limit exceeded — 100 requests per minute per API key. Retry
            after `Retry-After` seconds.
          headers:
            Retry-After:
              description: Seconds until the rate limit window resets
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - apiKey: []
        - cookieAuth: []
components:
  schemas:
    CourseBundlesResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        recommendedCourses:
          type: array
          items:
            $ref: '#/components/schemas/CourseBundle'
    ErrorResponse:
      type: object
      description: Error response returned for 4xx and 5xx status codes.
      properties:
        success:
          type: boolean
          example: false
        error:
          type: string
          example: Rate limit exceeded
    CourseBundle:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        description:
          type: string
        categoryId:
          type: integer
        category:
          $ref: '#/components/schemas/Category'
        courseOne:
          $ref: '#/components/schemas/BundleCourse'
        courseTwo:
          $ref: '#/components/schemas/BundleCourse'
        totalPrice:
          type: number
          description: Sum of the two individual course prices.
        price:
          type: number
          description: Bundle (discounted) price.
        priceNonMember:
          type: number
        kum:
          type: integer
          description: Total KUM/SKPK points for the bundle.
        currency:
          type: string
          example: IDR
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    Category:
      type: object
      properties:
        id:
          type: integer
          example: 1
        code:
          type: string
          example: MANAJEMEN
        name:
          type: string
          example: Manajemen
    BundleCourse:
      type: object
      properties:
        id:
          type: string
        numericId:
          type: integer
        name:
          type: string
        description:
          type: string
        instructor:
          type: string
        code:
          type: string
        eventCode:
          type: string
          nullable: true
        nilaiKum:
          type: number
          nullable: true
        thumbnail:
          type: string
          format: uri
          nullable: true
        price:
          type: number
        priceNonMember:
          type: number
          nullable: true
        currency:
          type: string
        categoryId:
          type: integer
          nullable: true
        enrollmentType:
          type: string
        isPurchasable:
          type: boolean
        previewMode:
          type: boolean
        startDate:
          type: string
          format: date-time
        endDate:
          type: string
          format: date-time
        modules:
          type: array
          items: {}
        certificate:
          $ref: '#/components/schemas/CertificateInfo'
        minPassingScore:
          type: integer
        totalProgress:
          type: integer
        status:
          type: string
        studentCount:
          type: integer
    CertificateInfo:
      type: object
      properties:
        isEligible:
          type: boolean
        isGenerated:
          type: boolean
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: x-api-key
      description: >-
        API key generated under Teacher Dashboard → Kunci API. Format:
        `lms_key_...`
    cookieAuth:
      type: apiKey
      in: cookie
      name: better-auth.session_token
      description: Browser session cookie (teacher/admin). Used by frontend clients.

````