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-versionResource model
| Resource | Collection path | Use it for |
|---|---|---|
| Module | /v1/content/modules | One interactive lesson or learning module. |
| Course | /v1/content/courses | Multi-lesson curriculum with chapters and lesson content. |
| Video | /v1/content/videos | Editable video plan, scenes, narration, and rendering control. |
| Slide | /v1/content/slides | Editable slide presentation content. |
| Test | /v1/content/tests | Assessment 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}/fullUse 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/cancelCreate 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}/submissionsUpdate 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
GETafterPOSTbefore showing content to reviewers. - Treat generated videos as editable until
POST /rendercompletes. - Never store or display internal authoring tokens.
- Use a separate Content API key for test and production.