Skip to content
Coursebox Support home
InboxAsk a human

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.

  1. Create the course: POST /Courses/createCourse sets the title, description and settings.

  2. Add sections (modules): POST /Courses/{courseId}/blocks, once per section.

  3. Add empty lessons: POST /Blocks/createActivity, once per lesson.

  4. Fill each lesson: POST /Activities/editPageContentAgentic with 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

name

string

Course title

description

string

Short description shown on the course page

language

string

Course language

avatarUrl

string

Course image (a public image URL)

backgroundUrl

string

Banner image (a public image URL)

about

object

Landing-page details, e.g. skills (list of strings)

passGrade

number

Pass mark in %; defaults to your portal's default pass grade

gradingMethod

string

How completion is graded; completion by default

points

number

Points awarded on completion

duration

object

Estimated length, e.g. { "hours": 2, "mins": 30 }

enrollmentLength

number

How long learners stay enrolled; empty = no limit

restartEnabled

boolean

Let learners restart the course

searchable

boolean

Show the course in the course catalogue once published

price, currency, tax

number, string, number

Paid courses; requires payments to be connected

enrollmentEmail

boolean

Email learners when they are enrolled

code

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 } ] } } }
  • id is the section the lesson goes in.

  • name is the lesson title.

  • Send content exactly 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

activityId

yes

The lesson to edit (from Step 2)

message

yes

Your instruction, in plain language. Max 8 KB (about 8,000 characters of English text)

deepEdit

no

true by default: the AI can take up to 30 steps on the request. false makes a quicker, lighter pass

What the response tells you:

Field

Meaning

autosaved

true when the lesson content was saved

autosaveError

Set if the save was refused, e.g. someone edited the lesson during the call. Retry the request

assistantMessage

The AI's reply, e.g. what it changed or a question if the instruction was unclear

operations

The list of edits it made

newTitle

The lesson's new title, if the instruction renamed it

quizChanged

true when a quiz was created or changed (Step 4)

error

Set when the request failed, with a readable userMessage and whether it is safe to retry

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.)

POST /api/Courses/updateAttributes

{ "attributes": { "id": "{courseId}", "name": "...", "description": "..." } }

Add a section

POST /api/Courses/{courseId}/blocks

As in Step 2

Rename a section

PATCH /api/Blocks/{sectionId}

{ "data": { "name": "Module 1: Foundations" } }

Delete a section and its lessons

DELETE /api/Blocks/{sectionId}

none

Add a lesson

POST /api/Blocks/createActivity

As in Step 2

Move a lesson within its section

POST /api/Blocks/{sectionId}/activity-position

{ "data": { "activityId": "{lessonId}", "position": 0 } } (0 = first)

Delete a lesson

POST /api/Activities/deleteActivity

{ "id": "{lessonId}" }

Lesson content, title, quiz, assignment

POST /api/Activities/editPageContentAgentic

As in Steps 3 and 4

Publish

POST /api/Courses/publish

{ "id": "{courseId}", "joinStatus": "public" }

Unpublish (back to draft)

POST /api/Courses/unpublish

{ "id": "{courseId}" }

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

editPageContentAgentic rate

30 requests per minute per user

Queue your calls; on 429, wait a few seconds and retry

Instruction size

8 KB per message

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 assistantMessage and send a follow-up

Quiz size per instruction

20 questions

Add more with a follow-up instruction

Courses

Your plan's course limit

createCourse returns 403 when it is reached

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:

  1. Generate the course specification on your side (e.g. with ChatGPT): title, description, modules, lessons, and each lesson's text and quiz.

  2. Create the course, then all sections, then all empty lessons (Steps 1-2). Store every id.

  3. For each lesson, send its content as an instruction (Step 3), then its quiz or assignment (Step 4).

  4. Check each response: autosaved: true, no error, and an assistantMessage that confirms the change.

  5. 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.