Skip to main content

Base URL

Substitute your assigned host if you are on a dedicated deployment. All endpoint paths in this documentation are relative to the base URL.

Content type

All request and response bodies are JSON. Set Content-Type: application/json on every POST, PUT, and PATCH.

Error envelope

Every error response uses this shape:
See the errors page for the full code reference.

Rate limits

Default: 60 requests per minute per account. Exceeding the limit returns 429:
The Retry-After response header gives the number of seconds to wait. Implement exponential backoff with jitter for production traffic.

Idempotency

POST /api/v1/courses/{course_id}/launch requires an Idempotency-Key header. The key is a unique string per intended launch (UUIDs work well).
  • First call with a given key starts the generation.
  • Subsequent calls with the same key while the course is already past awaiting_brief return the existing course without re-launching.
  • A missing key returns 400 with code: idempotency_key_required.
Use a fresh key for each new intended launch. Do not reuse keys across courses or across retries that you intend to be distinct operations.

Pagination

List endpoints currently return arrays without cursor pagination:
  • GET /api/v1/courses accepts ?external_ref= to narrow results.
  • GET /api/v1/presets/{id}/courses takes no query parameters and returns all courses for the preset.
Cursor pagination will be added before the per-account course count makes flat lists impractical. The endpoint signature is forward-compatible.

Timestamps

All timestamps are ISO-8601 strings in UTC, e.g. 2026-06-23T10:30:00Z. Do not assume a local timezone in your parser.

external_ref idempotency

external_ref is an optional caller-supplied string (max 255 chars) on course create. When provided, it acts as an idempotency key scoped to your account: if you POST a course with the same external_ref a second time, the API returns the existing course instead of creating a duplicate. The response shape is identical to a fresh create. Use external_ref to tie a course to an entity in your own system (a company ID, an onboarding session, a campaign run). It also powers per-ref breakdowns in the billing report.
A null or absent external_ref disables idempotency: each call creates a new course.

Cancel SLA

POST /api/v1/courses/{id}/cancel returns 200 immediately after setting the cancel signal. The worker picks up the signal within ~30 seconds (its heartbeat cycle), then finalizes the course to cancelled at the next pipeline-stage boundary. A long in-flight stage (for example video assembly) can push the actual transition well past 30 seconds, so do not assume a fixed deadline. Always poll GET /api/v1/courses/{id}/status and wait for status: "cancelled" before treating the course’s quota as freed. Do not reuse the course’s external_ref for a fresh course while the original is still in a non-terminal state.

Concurrency

Accounts have a per-account parallel generation cap. Submitting a launch when the cap is reached returns 429 quota_exceeded. Poll status on your in-flight courses and retry after one reaches a terminal state (ready, error, or cancelled). Completion order is not guaranteed: two courses launched at the same time may finish in any order depending on source complexity and module count. Drive your downstream logic from per-course status, not from submission order.

Source limits

The following hard limits apply to file and URL sources on course and preset create: Accepted file types for course sources: .pdf, .pptx, .docx, .txt. Accepted file types for preset brand sources (superset): .pdf, .pptx, .docx, .txt, .md, .png, .jpg, .jpeg, .webp, .bmp, .gif. Exceeding any limit returns 400 invalid_request_error.

Skipping optional brief questions

Brief questions can be blocking (must be answered to proceed) or optional. To skip an optional question without providing an answer, set "skip": true in the answers payload:
Attempting to skip a blocking question returns 422. Optional questions that are skipped do not re-appear in subsequent brief/next calls.

Versioning

The current API version is v1 (the /api/v1/... prefix). The conceptual model is sometimes referred to as “v2” because it succeeded an earlier preset-less v1; the URL path is v1 for historical reasons. Breaking changes to existing endpoints will introduce a new path prefix (/api/v2/...) and the old prefix will remain operational for at least 12 months.