# Files

The data files of a Kapi Works course, and every key each one takes, from the published JSON Schemas.

A course is a folder of data files in the `kapi.works.package/2` format. Kapi's course player draws it, so it holds no page code. Each file can name its JSON Schema in `"$schema"`, so editors check it as you type.

| File | Holds | Schema |
| --- | --- | --- |
| `kapi-works.json` | What the package is, its language, how it plays, and the roles, tests, context, links, media and sources it declares | [`package-2.json`](https://docs.kapiworks.com/schemas/package-2.json) |
| `content.json` | A course's lessons, pages and blocks, in reading order | [`content-2.json`](https://docs.kapiworks.com/schemas/content-2.json) |
| `words/<language>.json` | Every text in the package, by text ID, up to 1 MiB | [`words-1.json`](https://docs.kapiworks.com/schemas/words-1.json) |
| `questions.json` | Every question, with its answer, feedback and explanation | [`questions-2.json`](https://docs.kapiworks.com/schemas/questions-2.json) |
| `support.json` | Where each answer key and fact comes from, quoted word for word from its source | [`support-1.json`](https://docs.kapiworks.com/schemas/support-1.json) |
| `look/theme.json` | The package's look, which starts from the company's brand | [`theme-1.json`](https://docs.kapiworks.com/schemas/theme-1.json) |

Pictures, sound and video sit under `media/`, each listed in `media` in `kapi-works.json`. `AGENTS.md` can hold notes for coding agents. The types of the data files, for code: [`kapi-works-package.d.ts`](https://docs.kapiworks.com/schemas/kapi-works-package.d.ts).

## `kapi-works.json`

What the package is, its language, how it plays, and the roles, tests, context, links, media and sources it declares. Every text is a text ID from the words file.

Every course has this file. Its schema: [`https://docs.kapiworks.com/schemas/package-2.json`](https://docs.kapiworks.com/schemas/package-2.json).

- `schema`: `"kapi.works.package/2"`, required. The format, kapi.works.package/2.
- `sdk`: SDK version, required. The SDK the package needs: 2 and a minor, such as 2.0. Kapi serves up to 2.1.
- `id`: ID, required. The package's ID, the same in every version: lower-case letters, digits and hyphens, up to 64 characters.
- `kind`: `"course"`, required. The kind of package: course.
- `courseType`: `"training-course"`, `"scenario-course"`, `"quick-refresher"`, `"certificate-course"`, `"walkthrough"`, `"what-changed"`, `"video"`. What the course is for, which sets the type checks Kapi runs. Left out, the course is a training-course. Only a course has one.
- `title`: text ID, required. The text ID of the package's title, such as course.title.
- `summary`: text ID. The text ID of a one-sentence summary, such as course.summary.
- `label`: text. The author's name for this version: one line, up to 40 characters.
- `languages`: object, required. The package's language and its words file. A package has one language for now.
- `languages.default`: language tag, required. The language, as a BCP 47 tag such as en-GB.
- `languages.files`: object, required. The words file for the default language, and only that one: { "en-GB": "words/en-GB.json" }.
- `view`: object, required. How the package plays: on Kapi's player for its kind.
- `view.player`: `"course"`, required. The player, the same as kind.
- `view.line`: `"1.0"`, required. The player's line: 1.0.
- `state`: text. The version of what the package saves for each person, such as new-hire-welcome/v2.
- `roles`: list of objects. The roles lessons can be required for, up to 32, with IDs all different. Mark one as the default.
- `roles[].id`: ID, required. The role's ID, which lessons name in requiredFor.
- `roles[].title`: text ID, required. The text ID of the role's name.
- `roles[].default`: `true`. true on the role for people whose job role isn't mapped to one of these.
- `assessments`: list of objects. The tests Kapi gives, up to 8, with IDs all different.
- `assessments[].id`: ID, required. The test's ID.
- `assessments[].title`: text ID, required. The text ID of the test's name.
- `assessments[].pool`: list of IDs, required. The IDs of the questions the test draws from. These held questions sit on no page.
- `assessments[].suggest`: object. The test settings Kapi suggests. The company decides.
- `assessments[].suggest.draw`: whole number, required. How many questions each attempt draws, up to 50.
- `assessments[].suggest.passMark`: whole number, required. The pass mark, as a percentage.
- `assessments[].suggest.attempts`: whole number. How many attempts a person gets, up to 10.
- `context`: object. What Kapi fills in when the package opens: required if the package needs it, optional if it works without it. See [Placeholders](https://docs.kapiworks.com/build/reference/placeholders).
- `links`: list of host names. The hosts the words may link to, up to 50. Every link in the words uses https and one of these hosts.
- `media`: list of objects. The package's pictures, sound, video and fonts, with IDs all different. Blocks show them by ID.
- `media[].id`: ID, required. The media ID blocks use, such as laptop-setup.
- `media[].file`: text, required. The file's path under media/, such as media/laptop-setup.png, up to 240 characters.
- `media[].kind`: `"image"`, `"audio"`, `"video"`, `"font"`, required. What the file is. Kapi reads its first bytes, not its name.
- `media[].sha256`: SHA-256 digest. The SHA-256 of a picture the company added in Works Studio, as sha256:\<64 hex digits\>. Kapi keeps that picture, so its file can stay out of the package. A file in the package must have this SHA-256.
- `media[].alt`: text ID or null. The text ID of what the picture shows, in a sentence. A picture needs it unless it's decorative.
- `media[].decorative`: `true`. true for a picture that adds nothing to the words, so screen readers skip it.
- `media[].speech`: true or false. true when the sound or video has speech, which needs captions.
- `media[].captions`: object. A WebVTT captions file under media/ for each language, such as { "en-GB": "media/welcome.vtt" }.
- `sources`: list of objects. The documents the package was made from.
- `sources[].title`: text, required. The document's title, up to 200 characters.
- `sources[].sha256`: SHA-256 digest. The SHA-256 of the document's text, as sha256:\<64 hex digits\>.

## `content.json`

A course's lessons, pages and blocks, in reading order. Every text is a text ID from the words file.

Every course has this file. Its schema: [`https://docs.kapiworks.com/schemas/content-2.json`](https://docs.kapiworks.com/schemas/content-2.json).

- `schema`: `"kapi.works.content/2"`, required. The format, kapi.works.content/2.
- `lessons`: list of lessons, required. The lessons, in order: up to 64 lessons and 200 pages in all.
- `lessons[].id`: ID, required. The lesson's ID. Its text IDs start with it, such as first-week.title. Lesson and page IDs are all different.
- `lessons[].title`: text ID, required. The text ID of the lesson's title.
- `lessons[].minutes`: whole number. About how many minutes the lesson takes, from 1 to 120.
- `lessons[].requiredFor`: list of IDs or null. The role IDs the lesson is required for. null or left out: it's for everyone.
- `lessons[].pages`: list of pages, required. The lesson's pages, in order.
- `lessons[].pages[].id`: page ID, required. The page's ID: lower-case letters, digits and hyphens, up to 48 characters. Its text IDs start with it.
- `lessons[].pages[].title`: text ID, required. The text ID of the page's title.
- `lessons[].pages[].blocks`: list of blocks. What the page shows, from top to bottom. See [Blocks](https://docs.kapiworks.com/build/reference/blocks).

## `words/<language>.json`

Every text in the package, by text ID, up to 1 MiB. A text ID's first part names what it belongs to: a lesson, page or question ID, or course, common, role or assessment. Words take only \*\*strong\*\*, \_emphasis\_, \[label\](https://host/path) links and line breaks.

Every course has this file. Its schema: [`https://docs.kapiworks.com/schemas/words-1.json`](https://docs.kapiworks.com/schemas/words-1.json).

- `<text ID>`: text. The words for this text ID.

## `questions.json`

Every question, with its answer, feedback and explanation. Practice questions sit on pages; held questions sit only in a test's pool.

A course may leave this file out. Its schema: [`https://docs.kapiworks.com/schemas/questions-2.json`](https://docs.kapiworks.com/schemas/questions-2.json).

- `schema`: `"kapi.works.questions/2"`, required. The format, kapi.works.questions/2.
- `questions`: list of questions, required. The questions, up to 2,000. Question IDs are all different. See [Questions](https://docs.kapiworks.com/build/reference/questions).

## `support.json`

Where each answer key and fact comes from, quoted word for word from its source.

A course may leave this file out. Its schema: [`https://docs.kapiworks.com/schemas/support-1.json`](https://docs.kapiworks.com/schemas/support-1.json).

- `schema`: `"kapi.works.support/1"`, required. The format, kapi.works.support/1.
- `items`: list of objects, required. One item for each answer key or fact.
- `items[].for`: text ID or ID, required. The question ID or text ID it supports.
- `items[].source`: SHA-256 digest, required. The source's SHA-256, as in kapi-works.json's sources.
- `items[].unit`: unit ID. The ID of the part of the source the quote is from, such as u2-1.
- `items[].quote`: text, required. The passage, word for word, up to 2,000 characters.

## `look/theme.json`

The package's look, which starts from the company's brand.

A course may leave this file out. Its schema: [`https://docs.kapiworks.com/schemas/theme-1.json`](https://docs.kapiworks.com/schemas/theme-1.json).

- `schema`: `"kapi.works.theme/1"`, required. The format, kapi.works.theme/1.
- `base`: `"company"`, required. What the look starts from: the company's brand.
- `tokens`: object. Changes to the player's look, each within its range.
- `tokens.radius`: number. How round corners are, in pixels, from 0 to 24.
- `tokens.density`: number. How much space there is, as a multiple of the player's, from 0.75 to 1.25.
- `tokens.typeScale`: number. How large text is, as a multiple of the player's, from 0.875 to 1.25.
- `cover`: object. The cover picture.
- `cover.media`: ID, required. The media ID of the cover picture.
- `cover.style`: `"split"`, `"full"`, `"band"`, required. The cover's layout: split, full, band.

## The bundle

A whole package's data as one JSON block, for an AI that writes text rather than files. Long output can come in parts, which Kapi joins in order. Kapi then reads the files as a package, under the same rules as an upload.

Its schema: [`https://docs.kapiworks.com/schemas/bundle-1.json`](https://docs.kapiworks.com/schemas/bundle-1.json).

- `schema`: `"kapi.works.bundle/1"`, required. The format, kapi.works.bundle/1.
- `part`: whole number. This part's number, when the bundle comes in parts. Left out: 1.
- `of`: whole number. How many parts the bundle comes in, up to 20. Left out: 1. Every part gives the same number.
- `files`: object, required. The package's files by path. Media come separately: Kapi asks for them by their IDs.

## What Kapi checks

| Check | Severity | What Kapi says |
| --- | --- | --- |
| [`file_not_allowed`](https://docs.kapiworks.com/build/reference/checks/file-not-allowed) | Error | This file isn't a file a package holds. Remove it, or move it where the docs say it goes. |
| [`unsafe_path`](https://docs.kapiworks.com/build/reference/checks/unsafe-path) | Error | This path isn't safe. Use lower-case relative paths with no "..", backslashes or hidden parts. |
| [`file_type`](https://docs.kapiworks.com/build/reference/checks/file-type) | Error | This file isn't the kind of file its place says. Kapi reads a file's first bytes, not its name. |
| [`not_utf8`](https://docs.kapiworks.com/build/reference/checks/not-utf8) | Error | This file isn't saved as UTF-8 text. Save it again as UTF-8. |
| [`json_invalid`](https://docs.kapiworks.com/build/reference/checks/json-invalid) | Error | This file isn't valid JSON. |
| [`schema_invalid`](https://docs.kapiworks.com/build/reference/checks/schema-invalid) | Error | This value should be as the format says. |
| [`unknown_key`](https://docs.kapiworks.com/build/reference/checks/unknown-key) | Error | "…" isn't part of the format. Remove it, or check its spelling. |
| [`file_missing`](https://docs.kapiworks.com/build/reference/checks/file-missing) | Error | The package needs this file. |
| [`id_duplicate`](https://docs.kapiworks.com/build/reference/checks/id-duplicate) | Error | The … ID is used twice. Every ID must be unique. |
| [`family_not_available`](https://docs.kapiworks.com/build/reference/checks/family-not-available) | Error | Kapi doesn't have a … player yet. Courses play today. |
| [`sdk_too_new`](https://docs.kapiworks.com/build/reference/checks/sdk-too-new) | Error | The package needs a newer SDK than Kapi serves. Kapi serves SDK …. |
| [`player_line_unknown`](https://docs.kapiworks.com/build/reference/checks/player-line-unknown) | Error | There's no such player line. Use …. |
