Skip to main content

Overview

The Courses API provides three endpoints for browsing and retrieving course data from the LMS catalog. Together they power the public course catalog, the course detail dialog, and the bundle/paket view.

Authentication

All three endpoints require authentication. Two methods are supported:
  1. API Key (Recommended for server-to-server) — Pass your API key in the x-api-key header.
  2. Session Cookie (For frontend clients) — Pass the browser session cookie.
Generate an API Key in the Kunci API sidebar under the Teacher Dashboard. Once generated, include it in your HTTP requests:
API key users always see the published, buyer-visible dataset on GET /api/courses — equivalent to ?public=true. For GET /api/recommended-courses, pass ?public=true explicitly to filter to bundles where both courses are published.

Method 2: Using Session Cookies (Development)

To test the endpoint locally or via curl using browser cookies:
  1. Sign in to the LMS dashboard via your browser.
  2. Open the browser’s developer tools and copy the session cookie value.
  3. Run the curl command below, replacing <cookie-value> with your token:
Session-authenticated teachers see their own courses (including drafts).

Quiz answer sanitization

For API key users and non-owner students, quiz questions are sanitized — answer keys are stripped before the response is sent. The following fields are removed from every question object:
  • correctAnswer / correct_answer
  • essayAnswer / essay_answer
  • explanation
  • isCorrect / is_correct / correct (from each option)
Only the course owner (teacher) or a super_admin can see answer keys, via session-based auth.

Rate limiting

All three endpoints are rate-limited per API key to 100 requests per minute. The limit is enforced using an in-memory sliding window tracked by the first 16 characters of the SHA-256 hash of your API key. When the limit is exceeded, the API returns:
The Retry-After header indicates the number of seconds to wait before retrying. Back off at least that long before making another request.

Frontend integration guide

Use these TypeScript interfaces and React hooks to integrate the courses API into your frontend.

TypeScript type definitions

Code example: React hooks for course catalog

This example shows a React hook to fetch paginated courses with loading and error states:

Code example: course detail hook

Code example: course bundles hook


Technical details

Public access filtering (buyer visibility)

A course is “buyer-visible” only when both conditions are met:
  1. is_published is true
  2. event_code is not null (the course has been registered with IAI)
This ensures only courses that are publish-ready and have received an IAI event code appear in the public catalog. Draft courses and courses pending IAI registration are hidden from API key users. The SQL query for the public listing:

Server-side pagination

When page and/or limit query parameters are provided, the list endpoint uses a two-query pattern:
  1. Count querySELECT count(*)::int with the same WHERE clause to determine total results.
  2. Data querySELECT ... LIMIT ${limit} OFFSET ${offset} with the same WHERE clause.
Pagination metadata is returned alongside courses:
When pagination parameters are omitted, the endpoint returns all matching courses (up to the server limit).

Category filtering

The categoryId query parameter accepts:
  • A numeric ID (e.g. 1) — filters to courses in that category.
  • The string uncategorized — filters to courses with no category (category_id IS NULL).

Search behavior

The search parameter performs a case-insensitive (ILIKE) search across six columns:
  • c.name
  • c.description
  • c.instructor
  • c.course_code
  • category.code
  • category.name

Bundle pricing logic

Each course bundle has three price fields:
  • totalPrice — Sum of the two individual course prices (what it would cost to buy separately).
  • price — The bundle’s own (discounted) price. Falls back to totalPrice when unset.
  • priceNonMember — Price for non-IAI-member buyers. Falls back to price when unset.
The kum field represents the total KUM/SKPK points earned by completing both courses in the bundle.

Quiz answer sanitization

The sanitizeQuestionsForStudent function strips answer keys from all question objects before sending them to API key users or non-owner students. This is applied per-question:

Draft/unpublished course protection

A course that has not been published or does not have an IAI event code returns 404 to anyone who is not its owner or a super_admin. This applies to GET /api/courses/{id} — draft courses are never accidentally leaked via the detail endpoint.