Skip to content

domain-skills/skool: classroom course authoring API#491

Open
rodkend78 wants to merge 2 commits into
browser-use:mainfrom
rodkend78:skool-course-authoring
Open

domain-skills/skool: classroom course authoring API#491
rodkend78 wants to merge 2 commits into
browser-use:mainfrom
rodkend78:skool-course-authoring

Conversation

@rodkend78

@rodkend78 rodkend78 commented Jul 7, 2026

Copy link
Copy Markdown

Adds domain-skills/skool/course-authoring.md documenting Skool's private classroom-course API, discovered end-to-end while publishing a 28-lesson course.

POST /courses (course+sections+lessons, unit_type=module nested by parent_id, 3-level render), PUT /courses/{id} with FLAT {title,desc,transcript,video_id} for [v2] content, plus traps: 50-char title cap -> 422, don't navigate mid-loop, chunked payload + fire-and-poll for the socket, stale NEXT_DATA.

🤖 Generated with Claude Code


Summary by cubic

Adds domain-skills/skool/course-authoring.md, a guide to Skool’s private classroom-course API: POST /courses for course/lesson creation (hierarchy via parent_id, UI renders only 2 levels—create all lessons under the course and use numbered titles) and PUT /courses/{id} with a flat {title,desc,transcript,video_id} body to set [v2] content. Covers key gotchas: 50‑char title cap (422), creation‑order display, stale __NEXT_DATA__, and using chunked fire‑and‑poll loops to avoid socket timeouts.

Written for commit 9c53fbb. Summary will update on new commits.

Review in cubic

Full create/fill/delete contract for Skool classroom courses discovered while
publishing a 28-lesson course: POST /courses for course+sections+lessons
(unit_type=module, nested by parent_id, 3 levels render), PUT /courses/{id}
with a FLAT {title,desc,transcript,video_id} body to set [v2] content, and the
field-tested traps (50-char title cap = 422, don't navigate mid-loop, chunked
payload + fire-and-poll to beat the socket timeout, stale __NEXT_DATA__).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@browser-harness-review

Copy link
Copy Markdown

✅ Skill review passed

Reviewed 1 file(s) — no findings.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 1 file

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="domain-skills/skool/course-authoring.md">

<violation number="1" location="domain-skills/skool/course-authoring.md:4">
P2: Consider adding a minimal `fetch` template that includes `method`, `headers: {'Content-Type': 'application/json'}`, and `body: JSON.stringify(...)`. Without it, a reader might pass the shown JSON objects directly as `body`, which silently coerces to `[object Object]` and produces confusing failures against the API.</violation>

<violation number="2" location="domain-skills/skool/course-authoring.md:20">
P2: The TipTap document unwrapping example `"[v2]" + JSON.stringify(doc.content)` assumes `doc.content` is always present. If a TipTap doc is empty or partially constructed and lacks a `content` array, `JSON.stringify(undefined)` returns `undefined`, which string-concatenates to `[v2]undefined` — an invalid body that will likely be rejected or fail to render. Consider adding a fallback so the expression safely yields `[v2][]` when `content` is absent.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

# Skool — classroom course authoring (api2.skool.com)

Create and fill classroom courses via Skool's private API. Auth is httpOnly cookies, so make
credentialed `fetch(..., {credentials:"include"})` calls **from a logged-in Skool page context**

@cubic-dev-ai cubic-dev-ai Bot Jul 7, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Consider adding a minimal fetch template that includes method, headers: {'Content-Type': 'application/json'}, and body: JSON.stringify(...). Without it, a reader might pass the shown JSON objects directly as body, which silently coerces to [object Object] and produces confusing failures against the API.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At domain-skills/skool/course-authoring.md, line 4:

<comment>Consider adding a minimal `fetch` template that includes `method`, `headers: {'Content-Type': 'application/json'}`, and `body: JSON.stringify(...)`. Without it, a reader might pass the shown JSON objects directly as `body`, which silently coerces to `[object Object]` and produces confusing failures against the API.</comment>

<file context>
@@ -0,0 +1,75 @@
+# Skool — classroom course authoring (api2.skool.com)
+
+Create and fill classroom courses via Skool's private API. Auth is httpOnly cookies, so make
+credentialed `fetch(..., {credentials:"include"})` calls **from a logged-in Skool page context**
+(run them via `js(...)` / `Runtime.evaluate`). Must be a group admin.
+
</file context>
Fix with cubic


A lesson body (`desc`) is the literal string `[v2]` followed by compact JSON of a **top-level ARRAY**
of TipTap block nodes — NOT `{"type":"doc","content":[...]}`. If you have a doc-wrapped TipTap doc,
unwrap it: `"[v2]" + JSON.stringify(doc.content)`. Supported nodes seen in the editor toolbar:

@cubic-dev-ai cubic-dev-ai Bot Jul 7, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The TipTap document unwrapping example "[v2]" + JSON.stringify(doc.content) assumes doc.content is always present. If a TipTap doc is empty or partially constructed and lacks a content array, JSON.stringify(undefined) returns undefined, which string-concatenates to [v2]undefined — an invalid body that will likely be rejected or fail to render. Consider adding a fallback so the expression safely yields [v2][] when content is absent.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At domain-skills/skool/course-authoring.md, line 20:

<comment>The TipTap document unwrapping example `"[v2]" + JSON.stringify(doc.content)` assumes `doc.content` is always present. If a TipTap doc is empty or partially constructed and lacks a `content` array, `JSON.stringify(undefined)` returns `undefined`, which string-concatenates to `[v2]undefined` — an invalid body that will likely be rejected or fail to render. Consider adding a fallback so the expression safely yields `[v2][]` when `content` is absent.</comment>

<file context>
@@ -0,0 +1,75 @@
+
+A lesson body (`desc`) is the literal string `[v2]` followed by compact JSON of a **top-level ARRAY**
+of TipTap block nodes — NOT `{"type":"doc","content":[...]}`. If you have a doc-wrapped TipTap doc,
+unwrap it: `"[v2]" + JSON.stringify(doc.content)`. Supported nodes seen in the editor toolbar:
+paragraph, text (marks: bold, italic, strike, code, link), heading (H1–H4), bulletList/orderedList/
+listItem, blockquote, codeBlock, horizontalRule, hardBreak, image, video. Simplest reliable subset:
</file context>
Suggested change
unwrap it: `"[v2]" + JSON.stringify(doc.content)`. Supported nodes seen in the editor toolbar:
unwrap it: `"[v2]" + JSON.stringify(doc.content ?? [])`
Fix with cubic

… titles

Field-corrected while publishing: the classroom UI renders only 2 levels
(course -> lessons). A section-with-children stores fine via the API but shows
as an empty page with unreachable lessons. Use flat lessons under the course
root with '{m}.{l} ' title prefixes for module grouping.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="domain-skills/skool/course-authoring.md">

<violation number="1" location="domain-skills/skool/course-authoring.md:65">
P2: The concrete ~45-character base-title cap is based on a fixed 4-character prefix example (`"6.5 "`), but the prefix length grows with double-digit module or lesson numbers. For example, `"10.10 "` is 6 characters, so a 45-character base title would exceed the documented 50-character API limit and trigger the HTTP 422 error this section warns about. Consider phrasing this as a dynamic calculation (`base title length ≤ 49 - prefix.length`) or noting that the 4-char assumption only holds for single-digit module and lesson numbers.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic


- **Title max = 50 chars.** Titles ≥ 51 fail the create with **HTTP 422** (silent in the UI). Keep
sidebar titles ≤ 49; put the full title as an H1 in the body if needed. 50 exactly passed, but stay
under — and remember a `"6.5 "` numeric prefix eats 4 chars, so cap the base title at ~45.

@cubic-dev-ai cubic-dev-ai Bot Jul 7, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The concrete ~45-character base-title cap is based on a fixed 4-character prefix example ("6.5 "), but the prefix length grows with double-digit module or lesson numbers. For example, "10.10 " is 6 characters, so a 45-character base title would exceed the documented 50-character API limit and trigger the HTTP 422 error this section warns about. Consider phrasing this as a dynamic calculation (base title length ≤ 49 - prefix.length) or noting that the 4-char assumption only holds for single-digit module and lesson numbers.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At domain-skills/skool/course-authoring.md, line 65:

<comment>The concrete ~45-character base-title cap is based on a fixed 4-character prefix example (`"6.5 "`), but the prefix length grows with double-digit module or lesson numbers. For example, `"10.10 "` is 6 characters, so a 45-character base title would exceed the documented 50-character API limit and trigger the HTTP 422 error this section warns about. Consider phrasing this as a dynamic calculation (`base title length ≤ 49 - prefix.length`) or noting that the 4-char assumption only holds for single-digit module and lesson numbers.</comment>

<file context>
@@ -54,7 +61,8 @@ no-ops):
 - **Title max = 50 chars.** Titles ≥ 51 fail the create with **HTTP 422** (silent in the UI). Keep
-  sidebar titles ≤ 49; put the full title as an H1 in the body if needed. 50 exactly passed, but stay under.
+  sidebar titles ≤ 49; put the full title as an H1 in the body if needed. 50 exactly passed, but stay
+  under — and remember a `"6.5 "` numeric prefix eats 4 chars, so cap the base title at ~45.
 - **Update needs the flat body.** The create body's `{...,metadata:{desc}}` shape does nothing on PUT.
   Discover/confirm by editing one lesson in the UI and capturing the PUT (see below).
</file context>
Suggested change
under — and remember a `"6.5 "` numeric prefix eats 4 chars, so cap the base title at ~45.
+ under — and remember the prefix length varies (`"6.5 "` = 4 chars, `"10.10 "` = 6 chars, etc.), so cap the base title at `49 - prefix.length`.
Fix with cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant