Draft a course from videos
US Region: https://public-api.sessionboard.com · EU Region: https://public-api-eu.sessionboard.com
Starts making a course from 1–10 videos or clips. The videos are checked now (same organization, a transcript, no cuts); then the course is generated in the background: lesson-sized segments, a clip cut for each, and per lesson a short reading, in-video questions and a quiz, every question grounded in that clip's transcript. Answers 202 with the draft (status: queued); poll GET /v1/organization/{orgId}/learning/drafts/{draftId} until status is ready or failed (usually a minute or two per video). Nothing is published and no one is notified. Requires write:learning. Also at POST /v1/learning/drafts/create.
Request example
curl --request POST \ --url 'https://public-api.sessionboard.com/v1/organization/{orgId}/learning/drafts/create' \ --header 'x-access-token: <api-token>' \ --header 'Content-Type: application/json' \ --data '{ "mediaItemIds": [ "3c90c3cc-0d44-4b50-8888-8dd25736052a" ], "title": "<string>", "audience": "<string>", "certification": true, "validityMonths": 1, "questionTypes": [ "multiple_choice" ], "checkpointsPerLesson": 0, "quizQuestions": 1, "maxLessonsPerVideo": 1}'const response = await fetch('https://public-api.sessionboard.com/v1/organization/{orgId}/learning/drafts/create', { method: 'POST', headers: { "x-access-token": "<api-token>", "Content-Type": "application/json" }, body: JSON.stringify({ "mediaItemIds": [ "3c90c3cc-0d44-4b50-8888-8dd25736052a" ], "title": "<string>", "audience": "<string>", "certification": true, "validityMonths": 1, "questionTypes": [ "multiple_choice" ], "checkpointsPerLesson": 0, "quizQuestions": 1, "maxLessonsPerVideo": 1 }),});
const data = await response.json();console.log(data);import requests
response = requests.request( "POST", "https://public-api.sessionboard.com/v1/organization/{orgId}/learning/drafts/create", headers={ "x-access-token": "<api-token>", "Content-Type": "application/json" }, json={ "mediaItemIds": [ "3c90c3cc-0d44-4b50-8888-8dd25736052a" ], "title": "<string>", "audience": "<string>", "certification": True, "validityMonths": 1, "questionTypes": [ "multiple_choice" ], "checkpointsPerLesson": 0, "quizQuestions": 1, "maxLessonsPerVideo": 1 },)
print(response.json())<?php
$curl = curl_init();
curl_setopt_array($curl, [ CURLOPT_URL => "https://public-api.sessionboard.com/v1/organization/{orgId}/learning/drafts/create", CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => "POST", CURLOPT_POSTFIELDS => '{ "mediaItemIds": [ "3c90c3cc-0d44-4b50-8888-8dd25736052a" ], "title": "<string>", "audience": "<string>", "certification": true, "validityMonths": 1, "questionTypes": [ "multiple_choice" ], "checkpointsPerLesson": 0, "quizQuestions": 1, "maxLessonsPerVideo": 1}', CURLOPT_HTTPHEADER => [ "x-access-token: <api-token>", "Content-Type: application/json", ],]);
$response = curl_exec($curl);curl_close($curl);
echo $response;package main
import ( "fmt" "io" "net/http" "strings")
func main() { req, _ := http.NewRequest("POST", "https://public-api.sessionboard.com/v1/organization/{orgId}/learning/drafts/create", strings.NewReader(`{ "mediaItemIds": [ "3c90c3cc-0d44-4b50-8888-8dd25736052a" ], "title": "<string>", "audience": "<string>", "certification": true, "validityMonths": 1, "questionTypes": [ "multiple_choice" ], "checkpointsPerLesson": 0, "quizQuestions": 1, "maxLessonsPerVideo": 1}`)) req.Header.Add("x-access-token", "<api-token>") req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))}HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://public-api.sessionboard.com/v1/organization/{orgId}/learning/drafts/create")) .header("x-access-token", "<api-token>") .header("Content-Type", "application/json") .method("POST", HttpRequest.BodyPublishers.ofString("{\n \"mediaItemIds\": [\n \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n ],\n \"title\": \"<string>\",\n \"audience\": \"<string>\",\n \"certification\": true,\n \"validityMonths\": 1,\n \"questionTypes\": [\n \"multiple_choice\"\n ],\n \"checkpointsPerLesson\": 0,\n \"quizQuestions\": 1,\n \"maxLessonsPerVideo\": 1\n}")) .build();
HttpResponse<String> response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());Try it
Response headers
Authorizations
x-access-tokenstringheaderOrganization API token. Generate from Organization Settings → API Tokens.
AuthorizationBearer <token>headerorOAuth 2.1 access token. Obtained via the OAuth PKCE flow at
/oauth/token.
Send one of these. See Authentication.
Path parameters
orgIdstringpathrequiredYour organization ID. Any other organization returns
404.
Request body
mediaItemIdsstring<uuid>[]requiredVideos / clips from List videos, in course order.
- Min items
1- Max items
10
titlestringCourse title. Defaults to the video's title when there is one video.
- Max length
255
audiencestringWho the course is for. Shapes the writing; it does not decide who is assigned.
- Max length
500
certificationbooleanAdd a certification earned by completing every lesson.
validityMonthsintegerHow long the certification lasts. Omit for never expires.
- Minimum
1- Maximum
120
questionTypesstring[]Default
multiple_choice,true_false,multi_select.- Allowed values
"multiple_choice", "true_false", "short_answer", "multi_select", "ordering", "matching", "numeric"
checkpointsPerLessonintegerQuestions that pause the video, per lesson. Default 2.
- Minimum
0- Maximum
3
quizQuestionsintegerFinal quiz questions per lesson. Default 4.
- Minimum
1- Maximum
10
maxLessonsPerVideointegerUpper bound on lessons from one long video, default 8. Exceeded only when fewer lessons would each run past 10 minutes.
- Minimum
1- Maximum
12
{ "mediaItemIds": [ "3c90c3cc-0d44-4b50-8888-8dd25736052a" ], "title": "<string>", "audience": "<string>", "certification": true, "validityMonths": 1, "questionTypes": [ "multiple_choice" ], "checkpointsPerLesson": 0, "quizQuestions": 1, "maxLessonsPerVideo": 1}Responses
202application/json
Queued.
payloadobjectShow child attributes
draftCourseDraftSummaryShow child attributes
idstring<uuid>statusstring- Allowed values
"queued", "generating", "ready", "applying", "applied", "failed"
titlestring | nullmedia_item_idsstring<uuid>[]progressobject | nullShow child attributes
stepstringdoneintegertotalinteger
errorstring | nulllesson_countintegerapplied_atstring<date-time> | nullcreated_atstring<date-time>updated_atstring<date-time>Send back as
baseUpdatedAtwhen you save.
pollstringInternal path of the draft; poll the Get a course draft operation.
400
A video is not in this organization, has no transcript, or has cuts.
403
SCOPE_REQUIRED— the token lacksread:learning(reads) orwrite:learning(writes). Legacy tokens with no scopes cannot write.FORBIDDEN— Learning is not on for the organization, or the token's user lacks the Lessons permission.
Response examples
{ "payload": { "draft": { "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a", "status": "queued", "title": "<string>", "media_item_ids": [ "3c90c3cc-0d44-4b50-8888-8dd25736052a" ], "progress": { "step": "<string>", "done": 123, "total": 123 }, "error": "<string>", "lesson_count": 123, "applied_at": "2026-01-15T09:30:00Z", "created_at": "2026-01-15T09:30:00Z", "updated_at": "2026-01-15T09:30:00Z" }, "poll": "<string>" }}