Coursebox API: Course Authoring Guide
Overview
You build a course through the API in 4 steps: create an empty course, add its sections, add empty lessons, then fill each lesson by sending plain-language instructions to one AI endpoint.
Create the course:
POST /Courses/createCoursesets the title, description and settings.Add sections (modules):
POST /Courses/{courseId}/blocks, once per section.Add empty lessons:
POST /Blocks/createActivity, once per lesson.Fill each lesson:
POST /Activities/editPageContentAgenticwith an instruction such as "Write an introduction to double-entry bookkeeping, then add a 5-question quiz". It writes text, quizzes and assignments, and saves them to the lesson.
The same natural-language endpoint also edits lessons in a course that already exists. It works like an MCP server: you describe the change, and Coursebox's AI makes it.
In Coursebox, a section is stored as a block and a lesson as an activity. The endpoint names below use those words.
Authentication and request format
Every request is authenticated with your API key, sent as the api-key header. The request then runs as the Coursebox user who owns that key, with that user's permissions.
Get your key: in Coursebox, go to Settings > Integrations > Open API, and copy the key or click Reset API key to generate a new one. Resetting invalidates the old key immediately. Treat the key like a password: keep it on your server, never in browser code or a public repository.
Base URL: https://<your-portal-domain>/api, for example https://my.coursebox.ai/api. Use the domain your portal is served on, since it identifies your account.
Every request:
POST https://my.coursebox.ai/api/Courses/createCourse api-key: YOUR_API_KEY Content-Type: application/json { "course": { "name": "Intro to Bookkeeping" } }
The key can also be passed as a query parameter (?api_key=YOUR_API_KEY), but the header is safer because URLs end up in logs.
Errors come back as JSON with an HTTP status:
Status | Meaning |
|---|---|
400 | A required field is missing or invalid |
401 | Missing or invalid API key |
403 | The key's user is not an admin of that course, or the plan does not allow the action |
404 | The course, section or lesson id does not exist |
429 | Too many requests; wait and retry |
{ "error": { "statusCode": 401, "name": "UnauthorizedError", "message": "..." } }
The user who owns the key becomes the owner and admin of every course it creates, so use a dedicated account for your integration if several people will manage the courses.
Step 1: Create the course
POST /Courses/createCourse creates an empty draft course and returns it with its id. Keep that id for every later call.
POST /api/Courses/createCourse api-key: YOUR_API_KEY Content-Type: application/json { "course": { "name": "Intro to Bookkeeping", "description": "A practical introduction to double-entry bookkeeping for small-business owners.", "language": "en", "passGrade": 80, "gradingMethod": "completion", "about": { "skills": ["Bookkeeping", "Accounting basics"] } } }
Response (shortened):
{ "id": "0192f3a1-...", "name": "Intro to Bookkeeping", "visibleStatus": "draft", "joinStatus": "private", ... }
Useful course fields:
Field | Type | What it does |
|---|---|---|
| string | Course title |
| string | Short description shown on the course page |
| string | Course language |
| string | Course image (a public image URL) |
| string | Banner image (a public image URL) |
| object | Landing-page details, e.g. |
| number | Pass mark in %; defaults to your portal's default pass grade |
| string | How completion is graded; |
| number | Points awarded on completion |
| object | Estimated length, e.g. |
| number | How long learners stay enrolled; empty = no limit |
| boolean | Let learners restart the course |
| boolean | Show the course in the course catalogue once published |
| number, string, number | Paid courses; requires payments to be connected |
| boolean | Email learners when they are enrolled |
| string | Your internal course code |
Notes:
New courses are always drafts: learners cannot see them until you publish (Step 5).
Creating courses counts toward your plan's course limit. Over the limit, the call returns 403.
You become the course owner and admin, which is what lets the same key add sections and lessons next.
Step 2: Add sections and empty lessons
Create each section on the course, then create the lessons inside it. Each call returns the new item's id.
Create a section (module):
POST /api/Courses/{courseId}/blocks api-key: YOUR_API_KEY Content-Type: application/json { "name": "Module 1: The basics", "position": 0 }
position is the section's place in the course, starting at 0. Create sections in order: 0, 1, 2 and so on.
Create an empty lesson in a section:
POST /api/Blocks/createActivity api-key: YOUR_API_KEY Content-Type: application/json { "id": "{sectionId}", "data": { "name": "What is double-entry bookkeeping?", "content": { "notes": [ { "text": "", "isPrivate": false } ] } } }
idis the section the lesson goes in.nameis the lesson title.Send
contentexactly as shown: it is an empty page, ready to be filled in Step 3.Lessons are added to the end of the section, in the order you create them.
The response is the lesson, including its id. You need that id for every content call.
Read back the course structure (all sections with their lessons):
GET /api/Blocks?filter={"where":{"courseId":"{courseId}"},"include":"activities","order":"position ASC"} api-key: YOUR_API_KEY
URL-encode the filter value when you send it.
A full course skeleton is therefore: 1 call for the course, 1 per section, 1 per lesson. A course with 5 modules of 4 lessons takes 1 + 5 + 20 = 26 calls, and runs in seconds because no AI is involved yet.
Step 3: Add and update lesson content
POST /Activities/editPageContentAgentic fills or edits one lesson from a plain-language instruction. Coursebox's AI reads the lesson's current content, makes the change and saves it; you do not send or merge any page structure yourself.
POST /api/Activities/editPageContentAgentic api-key: YOUR_API_KEY Content-Type: application/json { "data": { "activityId": "{lessonId}", "message": "Write this lesson for small-business owners with no accounting background. Cover: what double-entry bookkeeping is, why every transaction has two sides, and one worked example (buying stock for $500 in cash). Start with a short introduction, use headings, and finish with a 3-point summary." } }
Field | Required | What it does |
|---|---|---|
| yes | The lesson to edit (from Step 2) |
| yes | Your instruction, in plain language. Max 8 KB (about 8,000 characters of English text) |
| no |
|
What the response tells you:
Field | Meaning |
|---|---|
|
|
| Set if the save was refused, e.g. someone edited the lesson during the call. Retry the request |
| The AI's reply, e.g. what it changed or a question if the instruction was unclear |
| The list of edits it made |
| The lesson's new title, if the instruction renamed it |
|
|
| Set when the request failed, with a readable |
What you can ask for in a lesson:
Text: headings, paragraphs, lists, quotes, and rewriting, shortening or expanding any part
Layouts: card blocks, a hero/banner block, reordering, moving and deleting blocks, restyling the page
Scenarios: an interactive scenario block
Images: AI-generated images, image galleries, replacing or regenerating an image
Video: inserting or replacing a video
Translation: translating the whole page into another language
Lesson title: "Rename this lesson to ..."
Quizzes and assignments: see Step 4
Updating a lesson later is the same call with a new instruction, e.g. "Replace the worked example with one about paying rent" or "Make the tone more formal and cut the lesson to about 400 words". The AI edits the page as it stands, and keeps the rest.
Conversation memory: each lesson keeps its own conversation with the AI for your user. A follow-up such as "Now add a second example" understands what "the example" refers to.
Tips for good results:
Send the content you already have (from ChatGPT or your own specification) inside the message and say "use this text", rather than asking the AI to invent it.
One lesson per call. For long lessons, send 2 or 3 focused instructions (text first, then images, then the quiz) instead of one very large one.
Say who the learners are, the tone, and the length you want.
Check
assistantMessage: if the AI asked a question instead of editing, answer it with another call.
Step 4: Quizzes, questions and assignments
Quizzes and assignments use the same editPageContentAgentic call as Step 3. Each lesson can hold one quiz and one assignment.
Create a quiz with your questions:
{ "data": { "activityId": "{lessonId}", "message": "Add a quiz titled 'Check your understanding' with a pass mark of 80%. Use exactly these questions:\n1. (multiple choice) Every transaction affects at least how many accounts? Options: One; Two (correct); Three; Four.\n2. (multiple choice, select all that apply) Which are assets? Options: Cash (correct); Inventory (correct); Loan payable; Owner's equity.\n3. (short answer) Explain in one sentence why the accounting equation always balances. Model answer: Because every debit has an equal credit, so both sides change by the same amount." } }
You can also let the AI write the questions: "Add a 5-question multiple-choice quiz based on this lesson, pass mark 70%".
Question types:
Type | How it is marked | What to provide |
|---|---|---|
Multiple choice | Automatically | 2 to 8 options, at least one correct and one incorrect. More than one correct option makes it a select-all-that-apply question |
Short answer | By AI, against your model answer | The question and a model answer |
Limits: up to 20 questions per instruction (send another instruction to add more), question text up to 2,000 characters, each option up to 1,000, model answers up to 4,000.
Edit an existing quiz with instructions such as:
"Add 3 more questions about debits and credits""Change question 2 so that Loan payable is also correct""Delete the last question""Shuffle the answer options on every question""Remove the quiz from this lesson"
Quiz settings you can change by instruction:
Setting | Example instruction |
|---|---|
Pass mark (0-100%) | "Set the pass mark to 75%" |
Counts toward the course grade | "Make this quiz count toward the course grade" |
Clear answers when retaking | "Reset answers when a learner retries" |
Show partially correct answers | "Show partially correct answers as partly right" |
AI feedback on answers | "Turn on AI feedback for learner answers" |
Quiz changes are saved the moment they are made. The response has quizChanged: true.
Create an assignment (an open task the learner submits, marked against a rubric):
{ "data": { "activityId": "{lessonId}", "message": "Turn this lesson into an assignment titled 'Record a month of transactions'. Brief: record 10 transactions for a fictional cafe in a two-column journal and check that debits equal credits. Rubric: Accuracy of entries 50%; Correct account names 30%; Presentation 20%. Include a model answer for the AI grader." } }
An assignment has a brief (up to 20,000 characters), 1 to 10 rubric criteria with a name, description and weight (weights should add up to 100%), an optional model answer for the AI grader, and a title. Change it later with instructions like "Add a criterion for referencing, 10%, and reduce Presentation to 10%".
Use a quiz for questions with right answers, and an assignment for one open piece of work.
Step 5: Update and publish an existing course
Everything in Steps 1-4 also works on a course that already exists: use the course, section and lesson ids you stored, or read them back with the structure call in Step 2.
To change | Call | Body |
|---|---|---|
Course details (title, description, images, grading, price, etc.) |
|
|
Add a section |
| As in Step 2 |
Rename a section |
|
|
Delete a section and its lessons |
| none |
Add a lesson |
| As in Step 2 |
Move a lesson within its section |
|
|
Delete a lesson |
|
|
Lesson content, title, quiz, assignment |
| As in Steps 3 and 4 |
Publish |
|
|
Unpublish (back to draft) |
|
|
updateAttributes accepts the course fields listed in Step 1 and changes only the fields you send.
The natural-language endpoint can also make some course-level changes from inside any lesson, e.g. "Rename the course to Bookkeeping for Beginners" or "Set up the AI tutor for this course".
Publishing:
joinStatus: "public"lets anyone on your portal join the course.joinStatus: "private"means learners join only when you enrol or invite them.Publishing emails you (the course owner) a confirmation.
Edits after publishing go live straight away, so unpublish first if you are making large changes.
Limits and good practice
Limit | Value | What to do |
|---|---|---|
| 30 requests per minute per user | Queue your calls; on 429, wait a few seconds and retry |
Instruction size | 8 KB per | Split long lessons into several instructions |
Time per AI call | Up to 5 minutes | Set your HTTP client timeout to at least 5 minutes; most calls finish much sooner |
AI steps per call | Up to 30 | Very large requests may stop part-way; check |
Quiz size per instruction | 20 questions | Add more with a follow-up instruction |
Courses | Your plan's course limit |
|
AI credits. Every editPageContentAgentic call uses your account's AI credits, like the AI editor in the Coursebox app. When credits run out, the call returns a message instead of editing. Structure calls (course, section, lesson, publish) use no credits.
One call at a time per lesson. Different lessons can be filled in parallel (within the rate limit), but wait for one call on a lesson to finish before sending the next for the same lesson. Otherwise the second save is refused with autosaveError, to protect the first one.
Suggested pipeline:
Generate the course specification on your side (e.g. with ChatGPT): title, description, modules, lessons, and each lesson's text and quiz.
Create the course, then all sections, then all empty lessons (Steps 1-2). Store every id.
For each lesson, send its content as an instruction (Step 3), then its quiz or assignment (Step 4).
Check each response:
autosaved: true, noerror, and anassistantMessagethat confirms the change.Review the course in Coursebox, then publish (Step 5).
Retries. It is safe to retry a structure call only if it failed; a retried success creates a duplicate section or lesson. For AI calls, read assistantMessage before retrying, because the first attempt may have partly succeeded.
API add-on and terms of use. Access to the Coursebox API requires the API add-on on your account. Calling the API from outside the Coursebox platform without the add-on breaches our Terms and Conditions, and access without it will soon be blocked.
Help. If a call behaves differently from this guide, send us the endpoint, the request body (without your API key) and the response, and we will look into it.