Resources
Content Resource API

Content Resource API

Create, list, update, and control TutorFlow courses, videos, slides, tests, and modules through Content API keys.

Use the Content Resource API when an external system needs direct control of TutorFlow-authored content. These endpoints use the dedicated tf_content_ bearer key. They do not use admin cookies after the key has been created.

All paths are relative to https://api.tutorflow.io.

Authorization: Bearer tf_content_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
Idempotency-Key: source-system-resource-version

Resource model

ResourceCollection pathUse it for
Module/v1/content/modulesOne interactive lesson or learning module.
Course/v1/content/coursesMulti-lesson curriculum with chapters and lesson content.
Video/v1/content/videosEditable video plan, scenes, narration, and rendering control.
Slide/v1/content/slidesEditable slide presentation content.
Test/v1/content/testsAssessment metadata, items, answers, scoring, and submissions.

The response is scoped to the Content API. Internal authoring tokens and internal workspace fields are not returned. Use the IDs returned by these endpoints for later reads, updates, deletes, and downstream reconciliation.

Modules

Create an interactive module:

curl -X POST "$TUTORFLOW_API_BASE_URL/v1/content/modules" \
  -H "Authorization: Bearer $TUTORFLOW_CONTENT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: module:intro-to-safety:v1" \
  -d '{
    "title": "Introduction to safety",
    "prompt": "Create an interactive lesson about workplace safety basics.",
    "language": "en",
    "type": "markdown",
    "hasQuiz": true
  }'

Common module endpoints:

GET    /v1/content/modules?limit=20&offset=0
GET    /v1/content/modules/{moduleId}
PATCH  /v1/content/modules/{moduleId}
DELETE /v1/content/modules/{moduleId}

Update a module:

{
  "title": "Workplace safety basics",
  "description": "A concise interactive module for new employees.",
  "lecture": "<h2>Safety first</h2><p>Always report hazards.</p>",
  "quizzes": [
    {
      "id": "quiz-1",
      "sequence": 1,
      "title": "Safety check",
      "question": "What should you do when you see a hazard?",
      "type": "multiple-choice",
      "options": ["Ignore it", "Report it", "Hide it"],
      "correctAnswers": ["Report it"],
      "showFeedback": true
    }
  ]
}

Courses

Create a course:

curl -X POST "$TUTORFLOW_API_BASE_URL/v1/content/courses" \
  -H "Authorization: Bearer $TUTORFLOW_CONTENT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: course:customer-onboarding:v1" \
  -d '{
    "title": "Customer onboarding",
    "prompt": "Create a 5 lesson onboarding course for customer success teams.",
    "language": "en",
    "lessonCount": 5,
    "hasQuiz": true,
    "hasPractice": true
  }'

Common course endpoints:

GET   /v1/content/courses?limit=20&offset=0
GET   /v1/content/courses/{courseId}
GET   /v1/content/courses/{courseId}/canonical
PATCH /v1/content/courses/{courseId}
PATCH /v1/content/courses/{courseId}/canonical
PATCH /v1/content/courses/{courseId}/chapters/{chapterId}
POST  /v1/content/courses/{courseId}/lessons
PATCH /v1/content/courses/{courseId}/lessons/{sequence}
GET   /v1/content/courses/{courseId}/lessons/by-id/{lessonId}/full
PATCH /v1/content/courses/{courseId}/lessons/by-id/{lessonId}/full

Use GET /canonical before structural edits. It returns chapter and lesson IDs that can be patched back through PATCH /canonical.

Videos

Create an editable video:

curl -X POST "$TUTORFLOW_API_BASE_URL/v1/content/videos" \
  -H "Authorization: Bearer $TUTORFLOW_CONTENT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: video:safety-summary:v1" \
  -d '{
    "title": "Safety summary",
    "prompt": "Create a 60 second summary video for workplace safety.",
    "language": "en",
    "sceneCount": 5,
    "aspectRatio": "16:9",
    "targetDurationSeconds": 60
  }'

Common video endpoints:

GET    /v1/content/videos
GET    /v1/content/videos/{videoId}
PATCH  /v1/content/videos/{videoId}
POST   /v1/content/videos/{videoId}/scenes
PATCH  /v1/content/videos/{videoId}/scenes/{sceneId}
DELETE /v1/content/videos/{videoId}/scenes/{sceneId}
PUT    /v1/content/videos/{videoId}/scenes/reorder
POST   /v1/content/videos/{videoId}/render
POST   /v1/content/videos/{videoId}/render/cancel

Create a scene:

{
  "order": 3,
  "script": "Report hazards immediately so the team can act.",
  "displayText": "Report hazards immediately",
  "duration": 8,
  "visualType": "text"
}

Trigger rendering only after the external system has reviewed the scenes:

curl -X POST "$TUTORFLOW_API_BASE_URL/v1/content/videos/$TUTORFLOW_VIDEO_ID/render" \
  -H "Authorization: Bearer $TUTORFLOW_CONTENT_API_KEY"

Slides

Create slides:

curl -X POST "$TUTORFLOW_API_BASE_URL/v1/content/slides" \
  -H "Authorization: Bearer $TUTORFLOW_CONTENT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: slides:safety-training:v1" \
  -d '{
    "title": "Safety training",
    "prompt": "Create a 10 slide workplace safety training deck.",
    "language": "en",
    "slideCount": 10,
    "theme": "minimal"
  }'

Common slide endpoints:

GET   /v1/content/slides
GET   /v1/content/slides/{slideId}
PATCH /v1/content/slides/{slideId}

Update slide content:

{
  "title": "Safety training",
  "description": "Updated deck for the July onboarding cohort.",
  "content": "<slide><h1>Safety training</h1></slide>"
}

Tests

Create a test:

curl -X POST "$TUTORFLOW_API_BASE_URL/v1/content/tests" \
  -H "Authorization: Bearer $TUTORFLOW_CONTENT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: test:safety-check:v1" \
  -d '{
    "title": "Safety check",
    "prompt": "Create a 10 question safety assessment.",
    "language": "en",
    "itemCount": 10,
    "timeLimit": 20
  }'

Common test endpoints:

GET    /v1/content/tests?limit=20&offset=0
GET    /v1/content/tests/{testId}
GET    /v1/content/tests/{testId}/full
PATCH  /v1/content/tests/{testId}
PATCH  /v1/content/tests/{testId}/full
POST   /v1/content/tests/{testId}/items
PATCH  /v1/content/tests/{testId}/items/by-id/{itemId}
DELETE /v1/content/tests/{testId}/items/by-id/{itemId}
PATCH  /v1/content/tests/{testId}/items/reorder
POST   /v1/content/tests/{testId}/items/generate
GET    /v1/content/tests/{testId}/submissions

Update a test item:

{
  "question": "What should you do when you see a hazard?",
  "type": "multiple-choice",
  "options": ["Report it", "Ignore it", "Hide it"],
  "correctAnswers": ["Report it"],
  "explanation": "Reporting hazards helps the team respond quickly.",
  "score": 10,
  "sequence": 1
}

Idempotency

Send an Idempotency-Key on every create request. Reusing the same key returns the original resource instead of creating duplicates.

Good key patterns:

module:{sourceLessonId}:{sourceVersion}
course:{sourceLevelId}:{sourceVersion}
video:{sourceLevelId}:summary:{sourceVersion}
slides:{sourceDeckId}:{sourceVersion}
test:{sourceAssessmentId}:{sourceVersion}

External system checklist

  • Store TutorFlow resource IDs next to source system IDs.
  • Store the idempotency key used for each create request.
  • Use GET after POST before showing content to reviewers.
  • Treat generated videos as editable until POST /render completes.
  • Never store or display internal authoring tokens.
  • Use a separate Content API key for test and production.