Resources
Source JSON Format

Source JSON Format

The canonical shape of the source JSON an expansion job accepts, the variants it also reads, and exactly which fields TutorFlow uses.

On this page

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:

JSON
{
  "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

FieldUsed forIf 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.idThe first half of sourceTitle, for example "Language Basics, A1 Foundations".Imported content.
level.title, else level.name, else level.idThe second half of sourceTitle, and the titles of the created module, video, and quiz.The category title.
level.lessonsThe lessons, in order. At least one is required, otherwise the request returns 400.400.
lesson.title, else lesson.nameThe lesson's title in the brief.Left out.
lesson.typeA label for the lesson in the brief.content.
The first 6 other lesson fields, in key orderThe lesson's material, each value included as JSON text.

Everything else is stored with the job but not used to create content. In particular:

  • id fields 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, or review. Descriptive names help more than codes like t3.
  • 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.

VariantExampleHow 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 title and type, and its important material in its first 6 fields.
  • language is set when the content is not English.
  • Quiz lessons include answers.
  • You own the content or have the right to process it through TutorFlow.

Was this page helpful?