Use this skill when a user asks to create, draft, scaffold, generate, or publish a course on ibl.ai / OpenEdX — including programmatic outlines, unit/component generation, or edits to an AI-generated course. Invoke to drive the ibl.ai Course Creation API end-to-end: create the task, build the course on EdX, generate the outline, draft unit content, review/edit structure, and publish. Do NOT invoke for enrollment, grading, agent configuration, or analytics queries — those are handled by other ...
15 stars
0 votes
0 copies
1 view
Added September 2, 2026
developmentpythonrustgobashrailsgitapidatabase
Works with
cli
api
Security analysis
B88/100
criticalSends environment variables or credentials to an external URL
Installs into .claude/skills of the current project.
Are you the author of Iblai Vibe Course Create?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/iblai-iblai-vibe-course-create)
---
name: iblai-vibe-course-create
description: Use this skill when a user asks to create, draft, scaffold, generate, or publish a course on ibl.ai / OpenEdX — including programmatic outlines, unit/component generation, or edits to an AI-generated course. Invoke to drive the ibl.ai Course Creation API end-to-end: create the task, build the course on EdX, generate the outline, draft unit content, review/edit structure, and publish. Do NOT invoke for enrollment, grading, agent configuration, or analytics queries — those are handled by other skills.
metadata:
kind: api
---
# /iblai-vibe-course-create

## Overview
This skill drives the IBL.AI Course Creation API to programmatically produce courses on an OpenEdX deployment. The API combines AI-generated structure (sections → subsections → units → components) with direct EdX writes, so **EdX is the single source of truth** for every structural operation. There are no intermediate database models for sections, subsections, units, or components — the API proxies every read/write to EdX.
Two workflow styles are supported:
1. **Stepwise (recommended for agents)** — create the task, create the empty course on EdX, generate the outline, review, edit structure if needed, draft unit content, verify, publish.
2. **Automated pipeline** — one call (`/start/`) runs the entire pipeline as a background Celery task; the agent polls for completion.
Prefer stepwise when the agent (or a user) wants to review or modify intermediate output. Prefer the pipeline when the brief is tight and no human-in-the-loop review is expected.
## Configuration
All endpoints are rooted at:
```
{base_url}/api/ai-mentor/orgs/{org}/users/{user_id}/
```
| Variable | Purpose | Env var |
| ----------- | -------------------------------------------------- | ----------------- |
| `base_url` | Server URL (default `https://api.$DOMAIN/dm`, `DOMAIN` from `iblai.env`) | `IBLAI_BASE_URL` |
| `org` | Platform / organization key | `IBLAI_ORG` |
| `user_id` | Authenticated user's username | `IBLAI_USER_ID` |
| `api_key` | Platform API key used as `Authorization: Api-Token <api_key>` | `IBLAI_API_KEY` |
| `mentor_id` | (Optional) Agent UUID for memory-aware generation | `IBLAI_MENTOR_ID` |
Every request MUST include:
```
Authorization: Api-Token {api_key}
Content-Type: application/json (for JSON bodies)
```
## EdX Block Hierarchy (critical)
```
Course
└── Section (chapter)
└── Subsection (sequential)
└── Unit (vertical)
└── Component (html | problem | video)
```
Each block has an `xblock_id` EdX locator:
```
block-v1:{org}+{course}+{run}+type@{type}+block@{slug}
```
The segment `type@<type>` identifies the block kind: `course`, `chapter`, `sequential`, `vertical`, `html`, `problem`, `video`. Parent/child relationships are enforced by the API:
| Child | Valid parent |
| ---------- | ------------ |
| section | course |
| subsection | section |
| unit | subsection |
| html | unit |
| problem | unit |
| video | unit |
Violating hierarchy returns `400`.
What the generated course looks like in edX Studio (course outline and a single unit with components):


## Task Lifecycle
A `CourseCreationTask` stores the generation brief and tracks progress. Its `status` transitions:
```
Pending → Scheduled → In Progress → Success | Failed | Cancelled
```
- `Pending` after `POST /tasks/`
- `Scheduled` after `GET /tasks/{id}/start/`
- `In Progress` while the Celery worker runs
- `Success` once the outline + content + publish have all completed
- `Failed` on any unrecoverable error (check task detail for `error` / `reason`)
## Primary Endpoints
All paths below are appended to the base root `{base_url}/api/ai-mentor/orgs/{org}/users/{user_id}/`.
### Task endpoints
| Action | Method | Path |
| --------------------- | ------ | -------------------------------------------------------- |
| Create task | POST | `course-creation/tasks/` |
| List tasks | GET | `course-creation/tasks/` |
| Task detail / status | GET | `course-creation/tasks/{task_id}/` |
| Delete task | DELETE | `course-creation/tasks/{task_id}/` |
| Create course on EdX | POST | `course-creation/tasks/{task_id}/create-course/` |
| Generate outline (AI) | POST | `course-creation/tasks/{task_id}/create-course-outline/` |
| Start full pipeline | GET | `course-creation/tasks/{task_id}/start/` |
| Cancel pipeline | GET | `course-creation/tasks/{task_id}/cancel/` |
### Course endpoints
| Action | Method | Path |
| ------------------------------ | ------ | --------------------------------------------------------------------------------- |
| List courses | GET | `course-creation/course/` |
| Course by task | GET | `course-creation/course/?task={task_id}` |
| Course detail | GET | `course-creation/course/{course_id}/` |
| Delete course record | DELETE | `course-creation/course/{course_id}/` |
| Outline (no component content) | GET | `course-creation/course/{course_id}/outline/` |
| Full structure | GET | `course-creation/course/{course_id}/full-structure/` |
| Full structure + HTML | GET | `course-creation/course/{course_id}/full-structure/?include_content=true` |
| Draft content for all units | GET | `course-creation/course/{course_id}/draft-content-for-all-units/` |
| Draft content for one unit | POST | `course-creation/course/{course_id}/draft-content-for-unit/` |
| Create xblock | POST | `course-creation/course/{course_id}/create-xblock/` |
| Update xblock | POST | `course-creation/course/{course_id}/update-xblock/` |
| Delete xblock | POST | `course-creation/course/{course_id}/delete-xblock/` |
| Reorder children | POST | `course-creation/course/{course_id}/reorder-children/` |
| Publish to EdX | POST | `course-creation/course/{course_id}/sync-to-edx/` |
| Student progress | GET | `course-creation/course/{course_id}/student-progress/?target_username={username}` |
### File endpoints
| Action | Method | Path |
| ----------- | ------ | ------------------------------------ |
| Upload file | POST | `course-creation/files/` (multipart) |
| List files | GET | `course-creation/files/` |
| Delete file | DELETE | `course-creation/files/{id}/` |
## Request Payloads
### Create task (`POST /course-creation/tasks/`)
```json
{
"name": "Introduction to Python Programming",
"description": "A beginner-friendly course covering Python fundamentals: variables, primitive data types, control flow (if/else, for, while), functions, error handling, and basic data structures (lists, tuples, dicts, sets). Emphasize hands-on coding with small, runnable exercises. Avoid object-oriented programming — that belongs in a follow-up course.",
"target_audience": "First-year undergraduates with no prior programming experience. English is a second language for ~40% of the cohort.",
"publish_course": true,
"provider": "openai",
"model": "gpt-4o-mini",
"desired_number_of_sections": 4
}
```
| Field | Required | Default | Notes |
| ---------------------------- | -------- | --------------- | ------------------------------------------------------------------------------ |
| `name` | Yes | — | Course display name |
| `description` | Yes | — | Detailed scope — see **Writing the description** below |
| `target_audience` | Yes | — | User profile |
| `publish_course` | No | `true` | Auto-publish on sync |
| `provider` | No | `"openai"` | LLM provider |
| `model` | No | `"gpt-4o-mini"` | Larger models give better structure; `gpt-4o` or `gpt-4.1` for complex courses |
| `desired_number_of_sections` | No | `4` | 3–6 is typical; higher values produce thinner sections |
### Create xblock (`POST /course-creation/course/{course_id}/create-xblock/`)
```json
{
"parent_xblock_id": "<parent locator>",
"xblock_type": "section | subsection | unit | html | problem | video",
"display_name": "Readable title",
"content": "<optional, only for html/problem/video>",
"position": "last"
}
```
`html` content is HTML. `problem` content is EdX OLX problem XML. `video` accepts HTML wrapping a video embed.
### Update xblock
```json
{
"xblock_id": "<locator>",
"display_name": "Optional new title",
"content": "<optional, only for html/problem>"
}
```
Omit either field to leave it unchanged. Sending neither returns `400`.
### Delete xblock
```json
{ "xblock_id": "<locator>" }
```
Deletions cascade (section → subsections → units → components) and are **irreversible**.
### Reorder children
```json
{
"xblock_id": "<parent locator>",
"children": ["<child locator 1>", "<child locator 2>", "..."]
}
```
You **must list every current child**. To move a block to a different parent, include it in the new parent's `children` array — EdX will detach it from its former parent automatically.
## Recommended Workflow (Stepwise)
```
1. POST /course-creation/tasks/ → task_id
2. POST /course-creation/tasks/{task_id}/create-course/ → course_id, course xblock_id
3. POST /course-creation/tasks/{task_id}/create-course-outline/ → sections/subsections/units on EdX
4. GET /course-creation/course/{course_id}/full-structure/ → review generated structure
5. (Optional) create/update/delete/reorder xblocks to fix the outline
6. GET /course-creation/course/{course_id}/draft-content-for-all-units/ → background content drafting
7. Poll /full-structure/?include_content=true until every unit has components
8. (Optional) update individual components via /update-xblock/
9. POST /course-creation/course/{course_id}/sync-to-edx/ → publish
```
### Polling for content completion
`/draft-content-for-all-units/` is asynchronous. Poll either the task status (`GET /tasks/{task_id}/`) or re-fetch `/full-structure/?include_content=true` and check that every unit's `components` array is non-empty. A typical course (3–5 sections, ~15 units) takes 1–3 minutes.
Back-off pattern: start at 20s, double up to 120s, cap at 5 minutes total wait before surfacing a timeout to the user.
## Alternative: Fully Automated Pipeline
```
1. POST /course-creation/tasks/ → task_id
2. GET /course-creation/tasks/{task_id}/start/
3. Poll /course-creation/tasks/{task_id}/ → wait for status == "Success"
4. GET /course-creation/course/?task={task_id}→ resolve course_id
```
Use this only when the user explicitly asks for a "one-shot" course and does not want to approve the outline.
## File Attachments (reference material)
Upload up to **4 files**, **20 MB each**, via multipart to `/course-creation/files/`:
```
POST /course-creation/files/
Content-Type: multipart/form-data
course_creation_task=<task_id>
file=<binary>
```
Supported content: PDFs, Word docs, Markdown, plain text, slide decks. The AI uses these as reference when generating outline and unit content. Upload files **before** calling `create-course-outline` or `start` — files attached later are ignored.
## Best Practices for Writing the Course Brief
The quality of `description` and `target_audience` dominates output quality. Before calling `POST /tasks/`, draft the brief following the template below. If the user's brief is vague, **ask clarifying questions first** — do not send an underspecified task, because outline regeneration is wasteful.
### The 5-field brief template
1. **Subject & scope** — what is in, and (explicitly) what is out.
2. **Learning outcomes** — 3–7 verbs-as-outcomes ("by the end, users can _build_, _diagnose_, _compare_…").
3. **Target audience** — prior knowledge, language level, motivation, typical study time/week.
4. **Pedagogy** — tutorial vs. project-based vs. assessment-heavy; how much theory vs. practice.
5. **Format constraints** — number of sections, approximate length, any required topics.
### Good vs. weak briefs
**Weak** (produces generic, thin course):
> `description`: "A course on Python."
> `target_audience`: "Beginners."
**Strong** (produces coherent, teachable course):
> `description`: "A project-based introduction to Python 3.11 focused on data-wrangling for non-engineers. Cover variables, control flow, functions, file I/O, and the csv + json modules. Every section ends with a mini-project using a realistic dataset (airline delays, climate records). Exclude object-oriented programming, decorators, async, and type hints — a follow-up course covers those. Use an encouraging, low-jargon tone."
> `target_audience`: "Working analysts (marketing, ops, journalism) with spreadsheet fluency but no programming background. ~4 hours/week, 6 weeks total."
### Refinement rules (apply _before_ submitting the task)
- **State exclusions explicitly.** LLMs over-include. Saying "do not cover OOP" is more effective than listing only what to include.
- **Convert vague adjectives into measurable ones.** "Beginner" → "assumes high-school algebra, no coding"; "advanced" → "assumes 2+ years professional experience with X".
- **Name the artefact type.** "Tutorial course with hands-on exercises", "exam-prep course with graded quizzes after every subsection", "capstone course culminating in a single graded project".
- **Right-size `desired_number_of_sections`.** 3 = short workshop, 4–5 = term course, 6–8 = bootcamp. Going above 8 usually produces weak sections.
- **Pick the model to match complexity.** `gpt-4o-mini` is fine for most intro courses; use `gpt-4o` / `gpt-4.1` when the description mentions formal reasoning, technical depth (graduate-level math, law, medicine), or multiple cross-referenced modules.
- **Attach reference files for domain specificity.** If the course must align with a textbook, syllabus, curriculum standard, or style guide, upload it. One good PDF outperforms several paragraphs of description.
### Clarifying-question checklist
If any of these is unknown after the user's initial brief, ask before creating the task:
1. Who is the user? (background + motivation)
2. What should they be able to _do_ by the end?
3. How long is the course? (weeks, sections, hours)
4. Tutorial, assessment-heavy, or project-based?
5. Any topics to exclude or must-cover topics?
6. Reference material to upload?
7. Publish immediately or keep in draft?
## Reviewing AI Output
After the outline (step 3) and again after content drafting (step 6), always pull `/full-structure/?include_content=true` and verify:
- **Every unit has ≥1 component.** Empty units are a drafting failure — re-run `draft-content-for-unit` for that locator.
- **Section/subsection titles are non-redundant.** LLMs sometimes produce "Introduction" as both section 1 and section 1's first subsection — rename via `update-xblock`.
- **Assessment problems have valid OLX.** Open the `content` field; it must contain a `<problem>…</problem>` wrapper with at least one response input. Malformed OLX silently fails on publish.
- **HTML components don't leak prompts.** Look for telltale phrases ("As an AI…", "Here is the lesson…"). If found, re-draft that unit.
- **Total length is coherent.** A course targeted at 4 hours/week for 6 weeks should have roughly 24 units of substantive content; adjust by adding/removing sections.
## Curl Recipes
```bash
# IBLAI_BASE_URL overrides; else compose from iblai.env's DOMAIN (default iblai.app).
DOMAIN=$(grep -m1 '^DOMAIN=' iblai.env 2>/dev/null | cut -d= -f2-)
export BASE="${IBLAI_BASE_URL:-https://api.${DOMAIN:-iblai.app}/dm}/api/ai-mentor/orgs/$IBLAI_ORG/users/$IBLAI_USER_ID"
export AUTH="Authorization: Api-Token $IBLAI_API_KEY"
# 1. Create task
TASK_ID=$(curl -s -X POST "$BASE/course-creation/tasks/" \
-H "$AUTH" -H "Content-Type: application/json" \
-d '{
"name": "Intro to Python for Analysts",
"description": "Project-based intro ... (full brief)",
"target_audience": "Working analysts ...",
"desired_number_of_sections": 5,
"provider": "openai",
"model": "gpt-4o"
}' | jq -r .id)
# 2. Create blank course on EdX
RESP=$(curl -s -X POST "$BASE/course-creation/tasks/$TASK_ID/create-course/" -H "$AUTH")
COURSE_ID=$(echo "$RESP" | jq -r .course_id)
COURSE_XBLOCK=$(echo "$RESP" | jq -r .xblock_id)
# 3. Generate outline (synchronous)
curl -s -X POST "$BASE/course-creation/tasks/$TASK_ID/create-course-outline/" -H "$AUTH"
# 4. Review outline
curl -s "$BASE/course-creation/course/$COURSE_ID/full-structure/" -H "$AUTH" | jq
# 5. Edit: add a bonus section
curl -s -X POST "$BASE/course-creation/course/$COURSE_ID/create-xblock/" \
-H "$AUTH" -H "Content-Type: application/json" \
-d "{\"parent_xblock_id\":\"$COURSE_XBLOCK\",\"xblock_type\":\"section\",\"display_name\":\"Bonus: Working with APIs\"}"
# 6. Draft all unit content (async)
curl -s "$BASE/course-creation/course/$COURSE_ID/draft-content-for-all-units/" -H "$AUTH"
# 7. Poll until content appears
until [ "$(curl -s "$BASE/course-creation/course/$COURSE_ID/full-structure/?include_content=true" \
-H "$AUTH" | jq '[.. | .components? // empty | length] | min')" != "0" ]; do
sleep 30
done
# 8. Publish
curl -s -X POST "$BASE/course-creation/course/$COURSE_ID/sync-to-edx/" -H "$AUTH"
```
## Error Handling
| Symptom | Likely cause | Remedy |
| --------------------------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------ |
| `create-course` returns 200 (not 201) | Course already exists for this task | Re-use returned `course_id`; a settings re-apply has been scheduled. |
| `create-course-outline` returns 400 | Outline LLM call failed (rate limit, bad brief) | Check `detail`; refine description and retry, or switch to bigger model. |
| `create-xblock` returns 400 "invalid parent" | Hierarchy violation | Confirm the parent `type@` segment matches required parent type. |
| `sync-to-edx` returns 400 "no task associated" | Course record lost its task link | Recreate via the task's `create-course` endpoint. |
| Task stuck in `In Progress` for > 15 min | Celery worker stalled or LLM timeout | `GET .../cancel/`, then restart with `GET .../start/`. |
| `draft-content-for-unit` succeeds but content looks empty | Component created without body | Re-draft; if persistent, update component manually via `update-xblock`. |
## Guardrails
- **Never publish without review.** Always fetch `/full-structure/?include_content=true` and confirm with the user before calling `sync-to-edx` — published EdX courses are visible to enrolled students and costly to retract.
- **Don't mass-delete.** `delete-xblock` on a section is irreversible and cascades. Confirm target before calling.
- **Respect file limits.** Max 4 files per task, 20 MB each. Over-limit uploads are rejected with 400.
- **Tenant isolation.** `org` and `user_id` must match the authenticated user's platform and username — cross-organization requests return 403. Do not attempt to create courses in an organization different from the caller's.
- **Do not leak the API key.** Treat `api_key` as a bearer secret; never echo it in responses, logs, or committed files.
- **Background tasks can fail silently to the caller.** After `/start/` or `/draft-content-for-all-units/`, always verify downstream (status polling, full-structure fetch) rather than trusting the 202/200 response.
- **Ask before picking a larger/more expensive model.** `gpt-4o`/`gpt-4.1` cost materially more than `gpt-4o-mini`. Confirm with the user when complexity warrants it; otherwise default to `gpt-4o-mini`.