course.json describes the course and its outline. The content of each item lives in that item’s own file.
{
"schema_version": 1,
"id": "git-first-steps",
"title": "Git First Steps",
"summary": "One or two sentences shown on the catalog card.",
"description_md": "Longer Markdown description for the course page.",
"level": "beginner",
"track": "Developer Tools",
"tags": ["git", "version-control"],
"estimated_minutes": 45,
"prerequisites": "What learners should know or have installed.",
"cover": "assets/cover.png",
"access": { "visibility": "public", "enrollment": "open" },
"objectives": [ { "id": "o1", "text": "Explain what version control is for" } ],
"progression": { "mode": "sequential" },
"completion": { "require_quizzes": true, "certificate": false },
"modules": [ ]
}
Fields
| Field | Required | Rules |
|---|---|---|
schema_version |
Yes | Exactly 1. |
id |
Yes | An ID. |
title |
Yes | 1 to 120 characters. |
slug |
No | Leave it out, and the ID is used. |
summary |
No | Plain text, up to 500 characters. |
description_md |
No | Markdown, up to 100,000 characters. |
level |
No | beginner, intermediate, advanced, or null. |
track |
No | Up to 100 characters, or null. Groups courses in the catalog filter, so reuse exact spellings. |
tags |
No | Up to 20 unique strings of 1 to 40 characters. |
estimated_minutes |
No | 0 to 100,000, or null to add up the lessons’ duration_min. |
prerequisites |
No | Plain text, up to 1,000 characters. |
cover |
No | An assets/ path that exists in the zip, or null. |
access |
No | See Access. |
objectives |
No | Up to 30 of { "id", "text" }, with text up to 300 characters. |
progression |
No | See Progression. |
completion |
No | require_quizzes: quizzes must be passed to complete the course. certificate: issue a certificate on completion. |
modules |
Yes | Up to 100 modules. See Modules and items. |
No other top-level keys are allowed.
Access
"access": { "visibility": "public", "enrollment": "open", "price_minor": 2500, "currency": "usd", "duration_days": 365 }
| Key | Values | Meaning |
|---|---|---|
visibility |
public, unlisted, private |
Listed in the catalog, reachable by link only, or with no public course page at all. |
enrollment |
closed (default), open |
closed means people join with an invitation link or key. open allows self-enrollment in a free course, or purchase of a paid one. |
price_minor |
Integer, or null |
The price in minor units, such as cents: 2500 is 25.00. Absent, null, or 0 means free. |
currency |
Three letters, such as "usd" |
Only used with a price. Defaults to the site currency. |
duration_days |
1 to 3,650, or null |
Paid courses only: days of access per purchase. Absent or null means lifetime access. |
To offer a free and a paid copy of the same course, make two zips that differ only in access.
Progression
"progression": { "mode": "sequential" }
free: learners can open any item in any order.sequential: an item unlocks only when every required item before it, in course order, is complete. Items with"required": falsenever block anyone.
A module can override the course with "mode": "inherit", "free", or "sequential".
Modules and items
{
"id": "start-here",
"title": "Start here",
"summary": "Optional short text.",
"mode": "inherit",
"requires": [ ],
"items": [
{ "id": "what-is-git", "type": "lesson", "title": "What is Git?", "required": true },
{ "id": "knowledge-check", "type": "quiz", "title": "Knowledge check", "required": true,
"requires": [ { "type": "complete", "item": "what-is-git" } ] }
]
}
- A module needs
id,title, anditems, and can have asummary(up to 500 characters), amode, andrequires. Up to 200 items per module. - An item needs
id,type(lesson,quiz, orassignment), andtitle, and can haverequired(defaulttrue) andrequires. No other keys are allowed.
Unlock rules
requires is a list of up to 20 conditions, and all of them must be met:
| Condition | Shape |
|---|---|
| An item is complete | { "type": "complete", "item": "<item-id>" } |
| Every required item of a module is complete | { "type": "module_complete", "module": "<module-id>" } |
| A quiz score reaches a minimum | { "type": "quiz_score", "item": "<quiz-id>", "min_percent": 70 } |
Rules must point at items and modules that exist, and quiz_score must point at a quiz. An item can’t require itself or wait on its own module, and rules can’t wait on each other in a circle, including through sequential order. Make sure a learner can always reach every item.