Family index for authoring Open edX courses directly on Studio (studio.learn.iblai.app) and verifying them on the LMS (lms.ibl.ai) from the terminal — preflight, build order, how to plan a production-grade outline, the bulk outline builder versus block-by-block calls, and which /iblai-api-studio-* skill covers sign-in, course create, sections/subsections/units, HTML/PDF/problem components, settings, team, grading, publish and LMS-side checks. Use when the user says Studio, Open edX, edX, xblo...
Scanned 9/20/2026
Install to Claude Code
npx -y skills add iblai/vibe --skill iblai-api-studio --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Iblai Api Studio?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/iblai-iblai-api-studio)More formats (shields.io, HTML) on the badges page.
---
name: iblai-api-studio
description: Family index for authoring Open edX courses directly on Studio (studio.learn.iblai.app) and verifying them on the LMS (lms.ibl.ai) from the terminal — preflight, build order, how to plan a production-grade outline, the bulk outline builder versus block-by-block calls, and which /iblai-api-studio-* skill covers sign-in, course create, sections/subsections/units, HTML/PDF/problem components, settings, team, grading, publish and LMS-side checks. Use when the user says Studio, Open edX, edX, xblock, course outline, "build a course on our LMS", or asks which Studio skill to run.
metadata:
kind: guide
---
# iblai-api-studio — author a course on Open edX Studio
Studio is the Open edX CMS behind the ibl.ai LMS. The `/iblai-api-studio-*`
skills drive it over its own session-authenticated HTTP endpoints (no UI, no
Api-Token) and check the result on the LMS the way `lms.ibl.ai` does. Each
skill is one capability with verified endpoints; this page is the map.
**A full build needs most of them — read the ones in the build order now, in
one pass, before the first call.**
| Where | Production | Test server |
|---|---|---|
| Studio (authoring) | `https://studio.learn.iblai.app` | `https://studio.learn.iblai.org` |
| LMS API (verification) | `https://learn.iblai.app` | `https://learn.iblai.org` |
| LMS app (what users open) | `https://lms.ibl.ai` | `https://lms.iblai.org` |
## Preflight (30 seconds, always)
```bash
node .claude/skills/iblai-api-studio-auth/scripts/studio-login.mjs --check \
&& grep -q '^STUDIO_ORG=.\+' studio.env && echo "STUDIO_ORG set" || echo "STOP: run /iblai-api-studio-auth (and set STUDIO_ORG)"
```
No `studio.env`, an expired session, or an empty `STUDIO_ORG` → `/iblai-api-studio-auth`
first. Do not start creating anything until this prints two `ok`s and the org key.
## Build order
```
0. /iblai-api-studio-auth preflight above; sign in once → studio.env
1. plan the outline (below) sections → subsections → units → components, on paper first
2. /iblai-api-studio-course-create POST /api/ibl/manage/course/ → course_key (+ delete)
3. /iblai-api-studio-settings dates, pacing, about page, images, catalog fields — BEFORE any publish
/iblai-api-studio-grading assignment types + weights — subsections reference these
4. /iblai-api-studio-outline ONE call builds every section/subsection/unit (+ html/problem) and publishes
then, always: rename the html blocks (the builder names them all "Text"),
set graderType per graded subsection (the spec has no such key), republish
— or block by block —
/iblai-api-studio-section · -subsection · -unit POST /xblock/ {parent_locator, category}
5. /iblai-api-studio-html text blocks: data = HTML fragment
/iblai-api-studio-problem one problem block per unit holding all its questions (OLX)
/iblai-api-studio-pdf pdf blocks (advanced module + asset + save_pdf handler)
6. /iblai-api-studio-team staff / instructor by email (any time)
7. /iblai-api-studio-publish {publish:"make_public"} on the course root; verify states
8. /iblai-api-studio-lms enroll, read the published outline as a user, catalog check
```
**Settings before content, content before the first publish.** Pacing
(`self_paced`) cannot change once the course has started, enrollment must open
before the start date, and a published-but-empty course is what users see
in the catalog. Configure, build, review the outline, then publish once.
## Bulk or one block at a time?
Both are first-class; pick by situation, not by convenience:
| Situation | Use |
|---|---|
| **New course**, outline known up front, text + questions | `/iblai-api-studio-outline` — `POST /api/v1/ibl/course/create_full_course` builds and publishes the whole tree in one request (a 5-section course is 1 call instead of ~120) |
| **Existing course, one change**: add one section or lesson, insert a unit, fix one component's text, replace a quiz, reorder, rename, hide, delete a single block, work in an unpublished draft | the individual skills (`-section`, `-subsection`, `-unit`, `-html`, `-problem`, `-pdf`) — they act on exactly one block by locator and never touch the rest |
| **Existing course, many changes**: rewrite all readings, add units across lessons, restructure sections | `/iblai-api-studio-outline` "Bulk edits — reconcile": `scripts/reconcile.py` diffs a spec against the live outline and creates/updates/reorders (and, only on request, deletes) idempotently, then publishes what changed |
| Both | build the skeleton + text with the bulk call, then add PDFs and later edits block by block or by reconcile, then republish what changed — the reconcile driver adopts the builder's `"Text"` html blocks on its first run, provided the spec keeps the built component order |
The same block is named three ways across endpoints — `locator`/`id` +
`display_name` + `category` (stock), `xblock_locator`/`id` + `display_name` +
`type` (v1), `block_id` + `name` + `block_type` (container children). The
table in `/iblai-api-studio-outline` maps them.
Never run the bulk builder against a course that already has content to
"add a section": it appends **and publishes everything**, including drafts.
**Deleting a course** (`/iblai-api-studio-course-create`) is permanent and
destroys user enrollments and progress. It is guarded by explicit checks
in that skill; hiding a course (`/iblai-api-studio-settings`) is almost always
what is meant.
Budget for block-by-block: 1 call per section/subsection/unit, 2 per html or
problem block, 4 per pdf block, plus 1 publish. Keep every returned locator
in a local JSON (`build.json`) keyed by the outline path so a re-run resumes
instead of duplicating.
## Planning a production-grade outline
Enterprise courses are judged on structure before content. Plan the tree
before the first request and keep it consistent:
- **Start from outcomes.** 3–6 measurable learning objectives for the course
(verbs from Bloom's taxonomy: describe, apply, analyse, evaluate); every
section maps to at least one, every graded subsection assesses one.
- **Sections = modules or weeks** (4–8 for a short course), each with a
one-line goal in its name: `Week 2 · Reading data critically`.
- **Subsections = lessons** (2–4 per section, 20–40 minutes each). Name them
by topic, not by type. Mark graded ones with `graderType` and a due date.
- **Units = one idea per page** (3–6 per subsection, 5–10 minutes each), in a
repeatable rhythm: *context/overview* → *content* (html, video, pdf) →
*practice* (one problem block with several questions) → *summary/next steps*.
- **Assessment alignment.** Formative checks (unlimited attempts, hints,
`showanswer: finished`) inside lessons; summative work (limited attempts,
weighted) in dedicated subsections whose grader type exists in the policy.
- **Consistency.** Same naming pattern at every level, same unit rhythm, same
HTML component styles (see `/iblai-api-studio-html`), a course-level intro
section (welcome, objectives, how grading works, syllabus PDF) and a closing
section (summary, further reading, feedback).
- **Accessibility and scope.** Alt text on every image, headings in order,
readings under ~1,500 words per unit, video under 10 minutes.
Write the plan as the JSON spec that `/iblai-api-studio-outline` accepts; it
doubles as the review artefact for the user before anything is created.
## Content quality — and how to keep sub-agents on the rails
Course content is the product. Every page a user opens must be **well
written, presentable and beautiful, informative without being distracting,
well structured and organized, carefully planned and reviewed.** That bar is
easy to state and easy to lose when writing is split up.
**Sub-agents drift.** When sections or units are delegated to parallel agents,
the pieces come back with different voices, different heading levels,
different callout styles, uneven depth (one unit a rich reading, the next
three sentences), invented terminology, or questions that do not match the
objectives. Nothing in Studio stops that. Regulate it explicitly:
1. **One content brief, given to every agent verbatim** — course objectives,
audience and reading level, tone (second person, plain, no filler), the
unit rhythm (overview → content → one practice block → summary), the
naming pattern, the HTML patterns allowed (`/iblai-api-studio-html`), the
question rules (`/iblai-api-studio-problem`), target length per unit
(300–1,200 words of reading, 3–5 questions), and the glossary of terms
already defined in earlier units.
2. **Produce the spec, not the calls.** Sub-agents return JSON fragments for
`/iblai-api-studio-outline` (or `data` strings for single blocks); only the
coordinating agent posts to Studio, after review. No sub-agent gets
`studio.env`.
3. **Review every fragment against the brief before it is posted:** objective
coverage, depth (no unit under ~150 words of real content, no placeholder
text, no "in this section we will…" padding), consistent headings and
callouts, valid HTML fragment and valid OLX, a title on every block, hints
and solutions on every question, terminology consistent with earlier
units, nothing that repeats a neighbouring unit. Send thin or divergent
pieces back with the specific gap; do not "fix in post" by posting and
patching.
4. **Read the whole course once, in order,** as a user would (the published
outline from `/iblai-api-studio-lms`, or the spec end to end) before the
first publish. Transitions between sections, an intro that sets
expectations, an outro that closes them.
Beautiful means calm: consistent components, generous spacing, one accent
colour, no decoration that competes with the reading.
## Keys you will pass around
| Thing | Shape | Where it comes from |
|---|---|---|
| course key | `course-v1:<org>+<number>+<run>` | course create response `course_key` |
| course root block | `block-v1:<org>+<number>+<run>+type@course+block@course` | derived from the course key; `parent_locator` for sections and the bulk builder |
| any block (usage key) | `block-v1:<org>+<number>+<run>+type@<category>+block@<hex>` | `locator` from `POST /xblock/` / `xblock_locator` from the v1 routes |
| category | `chapter` · `sequential` · `vertical` · `html` · `problem` · `pdf` | fixed |
**URL-encode course keys in query strings** (`course_key=course-v1%3Aorg%2Bnum%2Brun`);
path segments accept them raw.
## The snippet every skill starts with
```bash
set -a; . ./studio.env; set +a
S=(-s -b "studio_session_id=$STUDIO_SESSION; csrftoken=$STUDIO_CSRF" -H "X-CSRFToken: $STUDIO_CSRF" \
-H "Origin: $STUDIO_URL" -H "Referer: $STUDIO_URL/" -H "Accept: application/json" -H "Content-Type: application/json")
```
`Accept: application/json` makes Studio answer JSON; `X-CSRFToken` + `Origin`
make writes pass CSRF. `302` to `/login` = expired session; a `500` HTML
"Studio Server Error" on a documented call = a malformed cookie (see
`/iblai-api-studio-auth` "Reading failures") or a parameter the skill marks as
broken.
## Which skill when
| The user wants… | Skill |
|---|---|
| "connect", "log in to Studio", a call returned 302/403/500 | `/iblai-api-studio-auth` |
| a new course, list my courses, the course key, delete a course | `/iblai-api-studio-course-create` |
| the whole outline (and text/questions) in one go | `/iblai-api-studio-outline` |
| weeks/modules/sections; rename, reorder, release dates, hide from users | `/iblai-api-studio-section` |
| lessons/subsections; due dates, graded as Homework/Exam, timed | `/iblai-api-studio-subsection` |
| units/pages inside a lesson; order components; duplicate; delete | `/iblai-api-studio-unit` |
| text, reading, rich content, callouts, tables, images, embeds | `/iblai-api-studio-html` |
| a PDF reader block | `/iblai-api-studio-pdf` |
| quiz, questions, multiple choice, checkbox, numeric, text, dropdown | `/iblai-api-studio-problem` |
| dates, pacing, description, about page, images, intro video, catalog tags, advanced settings, uploads | `/iblai-api-studio-settings` |
| who can edit the course; add/remove staff or instructors | `/iblai-api-studio-team` |
| grading policy, assignment types, weights, pass mark, grace period | `/iblai-api-studio-grading` |
| make it live, publish, discard draft, "why can't users see it" | `/iblai-api-studio-publish` |
| enroll a user, what the user sees, is it in the catalog | `/iblai-api-studio-lms` |
Outside this family: AI-generated course drafts through the ibl.ai pipeline →
`/iblai-vibe-course-create`; org roles (`course-creator`, `org-instructor`) and
user admin → `/iblai-api-management`; catalog programs/pathways/enrollment with
an Api-Token → `/iblai-api-catalog`; Canvas → `/iblai-api-canvas-course-builder`.
## Reporting back
End with the course key, the LMS app URL
`$LMS_APP_URL/platform/<org>/courses/<course_key>`, counts (sections /
subsections / units / components), publish state, and anything that failed.
An unpublished build is invisible to users — say so explicitly.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!