Course Schema
The course is a JSON structure: Course → Modules → Topics → Sections.
This document describes the schema at each level — from the top-level course object
down to format-specific section variants. The nested outline subtree is also
documented separately in Outline Schema.
Course
The top-level object representing a complete course.
| Field | Type | Description |
|---|---|---|
id | number | Course identifier |
title | string (max 255) | Original course title |
localized_title | string? | Localized course title (set during localization) |
language | string | Target language code (default: en) |
outline | Module[] | Array of modules (min 1). See Outline Schema. |
thumbnail | object | Cover image: { content_type, base64 } |
objectives | string? | Learner-facing learning objectives (HTML) |
description | string? | Learner-facing course description |
quiz | QuizQuestion[]? | Post-course assessment (10–20 questions) |
course_group_id | UUID? | Groups localized copies of the same course |
version | number | Schema version (default: 1) |
Module
| Field | Type | Description |
|---|---|---|
title | string (max 255) | Module title |
localized_title | string? | Localized module title |
summary | string | Module summary |
position | string | Position identifier (e.g., "1") |
topics | Topic[] | Array of topics (min 1) |
Topic
| Field | Type | Description |
|---|---|---|
title | string (max 255) | Topic title |
localized_title | string? | Localized topic title |
position | string | Position identifier (e.g., "1.1") |
blueprint | string | Topic-level blueprint — what this topic covers |
sections | Section[] | Array of sections (min 1) |
Structural invariants
Beyond the base JSON shape, the outline follows these planning invariants:
- Except for the fixed "Welcome to the training" topic at
1.1, every non-final topic in a module ends with aWhats_Nextsection. - The final topic of every non-last module ends with a
Key_takeawayssection and does not includeWhats_Next. - The final topic of the whole course ends with
Learning_Outcomesfollowed byReference_Listand does not includeWhats_NextorKey_takeaways.
Section (base)
Every section shares these fields regardless of format:
| Field | Type | Description |
|---|---|---|
section_type | string | Semantic type — what the section does. See Section types |
format | SectionFormat | Presentation format — how it is delivered. See Section formats |
title | string (max 255) | Section title |
section_title | string? (max 255) | Localized section title (set during localization) |
position | string | Position identifier (e.g., "1.1.3") |
blueprint | string | Content blueprint — the prompt for generation |
The blueprint is the bridge between planning and production: it tells the generator
what to produce for this section, while section_type and format tell it
how to frame and how to present the result.
Format-specific fields
The format field determines which additional fields the section carries.
The schema is a discriminated union on format.
Text formats
Text — plain HTML content:
| Field | Type |
|---|---|
content | string (HTML, no markdown) |
VideoPresentation / VideoTalkingHead / VideoScenario — video with script:
| Field | Type |
|---|---|
content | string (voiceover script) |
video_data | { platform, video_id } |
Text + Image formats
TextWithPhoto / TextWithIllustration / TextWithPortrait / TextWithDiagram:
| Field | Type |
|---|---|
content | string (HTML, no markdown) |
content_rendered | string (HTML with image embedded) |
image_data | { prompt?, url } |
Assessment formats
ChoiceQuestions — single or multiple choice:
| Field | Type |
|---|---|
questions | QuizQuestion[] (min 1) |
Where each question is either:
SingleChoiceQuestion:{ type, question, possible_answers: [{ text, correct, feedback_text }] }MultiChoiceQuestion:{ type, question, possible_answers: [{ text, correct }], correct_feedback_text, incorrect_feedback_text }
FillTheBlanks — cloze exercise:
| Field | Type |
|---|---|
content | string (HTML with [PLACEHOLDER_INPUT] or [PLACEHOLDER_DROPDOWN] markers) |
correct_feedback_text | string |
incorrect_feedback_text | string |
placeholders | [{ index, type: 'dropdown' | 'input', options: [{ text, correct }] }] |
QuestionWithFeedback — open-ended with LLM evaluation:
| Field | Type |
|---|---|
content | string (HTML) |
question_data | { criteria, question } |
Artifact — interactive exercise:
| Field | Type |
|---|---|
content | string (HTML) |
artifact_data | { path } |
Available formats
Text VideoPresentation VideoTalkingHead VideoScenario
TextWithPhoto TextWithIllustration TextWithPortrait TextWithDiagram
ChoiceQuestions FillTheBlanks QuestionWithFeedback Artifact
SingleChoiceQuestion MultiChoiceQuestion Question
For what each format means and when to use it, see Section formats. For what each section type means semantically, see Section types.
Validation
The schema is enforced at runtime with Zod. The discriminated union on format
ensures that format-specific fields are present and correctly typed. Key rules:
- All
contentfields in text-based sections must not contain markdown (**is rejected) - Quiz questions are limited to 255 characters
- FillTheBlanks
contentmust include at least one[PLACEHOLDER_INPUT]or[PLACEHOLDER_DROPDOWN] - Image URLs must be valid URLs
- All position fields are required strings (e.g.,
"1.2.3")
Related
- Outline schema — the nested outline object used inside
course.outline - Outline pipeline — the process that produces this JSON structure
- Section content — populates the section fields at generation time
- Localization — explains
localized_title/section_titlefields