Build a course with the API¶
This guide creates a course, adds a chapter, adds a quiz lesson, reads it back, edits it safely, and finally deletes the course — using nothing but an API key.
You need a key with Edit on Courses, Units (API: chapters) and Lessons
(how to get one), stored in YOSHUKO_API_KEY.
Each call returns the object it created, including its id, which the next call uses.
1. Create a course¶
The slug becomes part of the course's web address, so it must be unique in your
organisation.
The response is 201 Created with the new course, including its id. Your plan's
course limit applies — the call is refused with an explanation if you are at it.
2. Add a chapter¶
curl -sS -X POST "https://www-dev.yoshuko.com/api/v1/agent/courses/$COURSE_ID/chapters/" \
-H "Authorization: Bearer $YOSHUKO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Unit 1: Materials", "position": 1}'
3. Add a quiz lesson¶
A lesson's content_type says what kind it is (lesson, quiz, assignment …) and
content holds the body. For a quiz, that is the questions:
{
"title": "Knowledge check",
"content_type": "quiz",
"position": 1,
"content": {
"title": "Knowledge check",
"passing_score": 50,
"questions": [{
"id": "q1",
"type": "multiple_choice",
"prompt_html": "<p>Which paper weight suits wet washes?</p>",
"points": 1,
"options": [
{"id": "a", "html": "300 gsm", "is_correct": true},
{"id": "b", "html": "80 gsm", "is_correct": false}
]
}]
}
}
POST it to /api/v1/agent/courses/{course_id}/chapters/{chapter_id}/lessons/. It is
validated by exactly the same rules as the course editor, so a malformed quiz is
refused with 400 and a message naming the field.
4. Edit a lesson without overwriting anyone¶
People may be editing the same course in the studio while your agent works. To make
sure you never silently overwrite their change, every lesson edit must send back the
updated_at value you last read:
curl -sS -X PATCH "https://www-dev.yoshuko.com/api/v1/agent/courses/$COURSE_ID/chapters/$CHAPTER_ID/lessons/$LESSON_ID/" \
-H "Authorization: Bearer $YOSHUKO_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"title\": \"Quick check\", \"updated_at\": \"$UPDATED_AT\"}"
If someone changed the lesson after you read it, you get 409 with
"code": "stale_lesson" and the lesson as it is now in lesson. Re-apply your
change to that version and send it again with its updated_at.
The whole thing, runnable¶
Copy either script, set YOSHUKO_API_KEY, and run it. It creates a course, a chapter and a
quiz, reads the quiz back, edits it, and deletes the course again so your catalogue is
left as it was.
#!/usr/bin/env bash
set -euo pipefail
BASE="https://www-dev.yoshuko.com/api/v1/agent"
AUTH="Authorization: Bearer $YOSHUKO_API_KEY"
JSON="Content-Type: application/json"
SLUG="api-demo-$(date +%s)"
COURSE_ID=$(curl -sSf -X POST "$BASE/courses/" -H "$AUTH" -H "$JSON" \
-d "{\"title\": \"API demo course\", \"slug\": \"$SLUG\"}" | jq -r .id)
echo "course $COURSE_ID"
CHAPTER_ID=$(curl -sSf -X POST "$BASE/courses/$COURSE_ID/chapters/" -H "$AUTH" -H "$JSON" \
-d '{"title": "Unit 1", "position": 1}' | jq -r .id)
echo "chapter $CHAPTER_ID"
LESSON=$(curl -sSf -X POST "$BASE/courses/$COURSE_ID/chapters/$CHAPTER_ID/lessons/" \
-H "$AUTH" -H "$JSON" -d '{
"title": "Knowledge check", "content_type": "quiz", "position": 1,
"content": {"title": "Knowledge check", "passing_score": 50, "questions": [{
"id": "q1", "type": "multiple_choice", "prompt_html": "<p>2 + 2?</p>", "points": 1,
"options": [{"id": "a", "html": "4", "is_correct": true},
{"id": "b", "html": "5", "is_correct": false}]}]}}')
LESSON_ID=$(echo "$LESSON" | jq -r .id)
UPDATED_AT=$(echo "$LESSON" | jq -r .updated_at)
echo "lesson $LESSON_ID"
curl -sSf -X PATCH "$BASE/courses/$COURSE_ID/chapters/$CHAPTER_ID/lessons/$LESSON_ID/" \
-H "$AUTH" -H "$JSON" -d "{\"title\": \"Quick check\", \"updated_at\": \"$UPDATED_AT\"}" \
| jq '{title, content_type, questions: (.content.questions | length)}'
curl -sSf -X DELETE "$BASE/courses/$COURSE_ID/" -H "$AUTH"
echo "deleted $COURSE_ID"
import os
import time
import requests
BASE = "https://www-dev.yoshuko.com/api/v1/agent"
HEADERS = {"Authorization": f"Bearer {os.environ['YOSHUKO_API_KEY']}"}
def call(method, path, **kwargs):
response = requests.request(method, BASE + path, headers=HEADERS, timeout=30, **kwargs)
response.raise_for_status()
return response.json() if response.content else None
course = call("POST", "/courses/", json={
"title": "API demo course", "slug": f"api-demo-{int(time.time())}"})
try:
chapter = call("POST", f"/courses/{course['id']}/chapters/",
json={"title": "Unit 1", "position": 1})
lessons = f"/courses/{course['id']}/chapters/{chapter['id']}/lessons/"
lesson = call("POST", lessons, json={
"title": "Knowledge check", "content_type": "quiz", "position": 1,
"content": {"title": "Knowledge check", "passing_score": 50, "questions": [{
"id": "q1", "type": "multiple_choice", "prompt_html": "<p>2 + 2?</p>",
"points": 1, "options": [{"id": "a", "html": "4", "is_correct": True},
{"id": "b", "html": "5", "is_correct": False}]}]},
})
edited = call("PATCH", f"{lessons}{lesson['id']}/",
json={"title": "Quick check", "updated_at": lesson["updated_at"]})
print(edited["title"], edited["content_type"], len(edited["content"]["questions"]))
finally:
call("DELETE", f"/courses/{course['id']}/")
print("deleted", course["id"])
Reference¶
Every field and response for these endpoints: Courses, Chapters, Lessons.