The payload of an expansion job is your own JSON. You do not convert it to a TutorFlow format. TutorFlow reads a small, fixed set of fields from it, described below, and stores the whole payload with the job.
A JSON Schema of the accepted shapes is published at /resources/integrations/source-json.schema.json. Use it to validate payloads before you send them.
Canonical shape
Send one category, one level, and the level's ordered lessons:
{
"language": "en",
"category": {
"id": "language-basics",
"title": "Language Basics"
},
"level": {
"id": "level-a1",
"title": "A1 Foundations",
"lessons": [
{
"id": "lesson-greetings",
"type": "vocabulary",
"title": "Basic greetings",
"items": [
{ "term": "hello", "meaning": "a greeting used when meeting someone", "example": "Hello, Mina." }
]
},
{
"id": "lesson-dialogue",
"type": "dialogue",
"title": "Meeting a classmate",
"turns": [
{ "speaker": "A", "text": "Hello. What is your name?" },
{ "speaker": "B", "text": "My name is Mina." }
]
},
{
"id": "lesson-check",
"type": "quiz",
"title": "Greeting check",
"questions": [
{
"question": "Which phrase is a greeting?",
"choices": ["Hello", "Blue", "Desk"],
"answer": "Hello",
"explanation": "Hello is used to greet someone."
}
]
}
]
}
}What TutorFlow reads
| Field | Used for | If missing |
|---|---|---|
language (top level) | Recorded with the job's generation request. Write your lesson material in the language you want the content in. | en. A field named locale is not read. |
category.title, else category.name, else category.id | The first half of sourceTitle, for example "Language Basics, A1 Foundations". | Imported content. |
level.title, else level.name, else level.id | The second half of sourceTitle, and the titles of the created module, video, and quiz. | The category title. |
level.lessons | The lessons, in order. At least one is required, otherwise the request returns 400. | 400. |
lesson.title, else lesson.name | The lesson's title in the brief. | Left out. |
lesson.type | A label for the lesson in the brief. | content. |
| The first 6 other lesson fields, in key order | The lesson's material, each value included as JSON text. |
Everything else is stored with the job but not used to create content. In particular:
idfields are not used for generation. Keep them anyway: they are how you reconcile the job with your own records.- Fields on the level or category other than their title are not used.
- Lesson fields after the sixth are not used. Put the material that matters first, and nest long lists inside one field (for example
items) rather than spreading them across many fields. - The number of lessons sets the size of the video plan and quiz; see Limits.
Lesson types
Any type value is accepted. TutorFlow does not map types to templates: the value is written into the lesson brief as a label, next to the lesson's fields, and shapes the output only through that text. So:
- Keep your own type names, such as
vocabulary,example,dialogue,quiz, orreview. Descriptive names help more than codes liket3. - Keep quiz answers, hints, and explanations in the lesson. They are the only way the expanded quiz can follow your answer intent.
- A lesson that is a plain string instead of an object is included as text.
Accepted variants
TutorFlow also reads these shapes. They produce the same job as the canonical shape.
| Variant | Example | How it is read |
|---|---|---|
| Category as a string | "category": "Language Basics" | The string is the category title. |
Titles in name | "level": { "name": "A1 Foundations", ... } | name is used when title is absent, for categories, levels, and lessons. |
| Nested categories and levels | { "categories": [ { "title": "...", "levels": [ { "title": "...", "lessons": [...] } ] } ] } | The first level, in array order, that has at least one lesson is expanded. Every other level and category is ignored. Send one job per level. |
| Flat lessons | { "title": "A1 Foundations", "category": "Language Basics", "lessons": [...] } | The payload itself is the level, so its title is the level title. |
When a payload has both categories and level, a level found under categories wins.
One level per job
An expansion job expands exactly one level. To expand a course with several levels, send one job per level, each with its own Idempotency-Key, for example expansion:language-basics:level-a1:v3. This keeps each job small enough to review, and a failure in one level does not block the others.
Checklist
- The payload is a JSON object, under the 100 KB request limit together with the rest of the body.
- It has one level with at least one lesson.
- Each lesson has a descriptive
titleandtype, and its important material in its first 6 fields. languageis set when the content is not English.- Quiz lessons include answers.
- You own the content or have the right to process it through TutorFlow.