mobieusKnow mobieusCore API — mobieusLearn History #634
Author
Patrick Bass
Submitted
Sep 5, 2026 4:34am
Summary
Family meal sharing tickets: the conditions behind the share button and the Share with family? switch, reading a "logged but not shared" report back to a member, sharing a saved meal without logging it, and what to escalate

The Learn surface of the public REST API. Manage courses, modules, lessons, activities, enrollments, attempts, certificates, certificate templates, and SCORM packages.

Added in API version 1.3.0 (2026-06-02).

Scopes

Scope Grants
learn:read List + read every Learn resource (courses, enrollments, attempts, certificates, templates, SCORM packages, nested entities).
learn:write Create / update / publish / archive courses, enroll users, cancel enrollments, revoke + regenerate certificates.
learn:cohorts:grant Grant cohort access via POST /api/v1/learn/cohorts/grant.

Generate a key at /admin/api-keys, tick the scopes you need. Pro / Creator Plus / Sovereign plans only, the same plan gate as the rest of the public API.

Quick start

curl https://YOUR-TENANT.mobieus.io/api/v1/learn/courses \
  -H 'Authorization: Bearer mc_live_...'

Returns up to 50 courses ordered by id desc. Use ?cursor=...&limit=N (max 100) to page; next_cursor is null on the last page.

Resources

Courses

Method Path Scope What
GET /api/v1/learn/courses learn:read List courses (cursor-paginated).
GET /api/v1/learn/courses/{id} learn:read Get a single course (with credit info, version status, owner author).
POST /api/v1/learn/courses learn:write Create a course. Required: slug, title. Optional: subtitle, summary, language, completion_rule, estimated_minutes, credit_hours, credit_kind, credit_expires_after_months.
POST /api/v1/learn/courses/{id} learn:write Update. Any field from create is acceptable; omitted fields keep their value.
POST /api/v1/learn/courses/{id}/publish learn:write Move to published.
POST /api/v1/learn/courses/{id}/unpublish learn:write Revert to draft.
POST /api/v1/learn/courses/{id}/archive learn:write Archive (hides from learners; preserves enrollments).

Example: enroll-on-purchase flow

# 1. Create a course
curl -X POST https://YOUR-TENANT.mobieus.io/api/v1/learn/courses \
  -H 'Authorization: Bearer mc_live_...' -H 'Content-Type: application/json' \
  -d '{"slug":"intro-to-mobieus","title":"Intro to Mobieus","completion_rule":"all_required","credit_hours":2.5,"credit_kind":"CEU"}'

# 2. After a Stripe checkout completes, enroll the buyer
curl -X POST https://YOUR-TENANT.mobieus.io/api/v1/learn/enrollments \
  -H 'Authorization: Bearer mc_live_...' -H 'Content-Type: application/json' \
  -d '{"user_id":123,"course_id":42,"source":"stripe_purchase"}'

Nested entities (read-only)

| GET | /api/v1/learn/courses/{id}/modules | learn:read | Modules in a course. | | GET | /api/v1/learn/modules/{mid}/lessons | learn:read | Lessons in a module. | | GET | /api/v1/learn/lessons/{lid}/activities | learn:read | Activities in a lesson. |

Writes for modules / lessons / activities go through the admin UI; they enforce per-course team membership. Per-course capability scoping for the API key model lands in a future minor version.

Enrollments

| GET | /api/v1/learn/enrollments?course_id=&user_id= | learn:read | List, optionally filtered. | | GET | /api/v1/learn/enrollments/{id} | learn:read | Get one. | | POST | /api/v1/learn/enrollments | learn:write | Enroll a user. Body: {"user_id", "course_id", "source"?, "expires_at"?}. | | POST | /api/v1/learn/enrollments/{id}/cancel | learn:write | Cancel (status flips to cancelled). |

Cohorts

| POST | /api/v1/learn/cohorts/grant | learn:cohorts:grant | Grant a user access to a cohort. |

Attempts

| GET | /api/v1/learn/attempts?enrollment_id=&activity_id= | learn:read | List. | | GET | /api/v1/learn/attempts/{id} | learn:read | Get one (includes score_raw, score_scaled, passed, status, time_started_at, time_ended_at, total_time_seconds). |

Certificates

| GET | /api/v1/learn/certificates?user_id=&course_id= | learn:read | List. | | GET | /api/v1/learn/certificates/{id} | learn:read | Get one. | | GET | /api/v1/learn/certificates/verify/{vid} | learn:read | Look up by the 40-char verification id. Returns is_revoked: true|false. | | POST | /api/v1/learn/certificates/{id}/revoke | learn:write | Revoke. Body: {"reason": "..."}. Required. | | POST | /api/v1/learn/certificates/{id}/regenerate-pdf | learn:write | Rebuild the PDF (after editing a template). Verification URL unchanged. |

Certificate templates

| GET | /api/v1/learn/cert-templates | learn:read | List templates (per-tenant brand identity for certs). | | GET | /api/v1/learn/cert-templates/{id} | learn:read | Get one. |

SCORM packages

| GET | /api/v1/learn/scorm-packages | learn:read | List packages (any status). | | GET | /api/v1/learn/scorm-packages/{id} | learn:read | Get one with its SCO list. |

Importing a SCORM package via API isn't supported in 1.3.0. Use the admin UI at /admin/learn/scorm/new. The endpoint is on the roadmap.

Response envelope

Every endpoint returns:

{
  "data": ...,
  "next_cursor": "...",
  "request_id": "req_..."
}

next_cursor is null when there's no more data.

Errors

Errors use the standard v1 envelope: a single error object with a code, a message, and the request_id:

{
  "error": {
    "code": "course_not_found",
    "message": "Course not found.",
    "request_id": "req_..."
  }
}

Common codes for the Learn surface:

Code When
404 course_not_found id doesn't match any row.
404 enrollment_not_found Same for enrollments.
404 certificate_not_found Same for certs (including /verify/{vid} misses).
400 invalid_argument The course service rejected the payload. The message explains.
400 missing_field A required body field was empty / absent.
403 insufficient_scope Key doesn't have learn:read (or learn:write).

What's not in 1.3.0 (defer to a future minor)

  • Import SCORM packages via API (use the admin UI today).
  • Assessment item authoring + question-bank CRUD via API.
  • Question pool CRUD via API.
  • Per-course team membership for API keys (today they get tenant-wide admin-equivalent capabilities).
The Learn surface of the public REST API. Manage courses, modules,
lessons, activities, enrollments, attempts, certificates,
certificate templates, and SCORM packages.

Added in API version **1.3.0** (2026-06-02).

## Scopes

| Scope | Grants |
|---|---|
| `learn:read` | List + read every Learn resource (courses, enrollments, attempts, certificates, templates, SCORM packages, nested entities). |
| `learn:write` | Create / update / publish / archive courses, enroll users, cancel enrollments, revoke + regenerate certificates. |
| `learn:cohorts:grant` | Grant cohort access via `POST /api/v1/learn/cohorts/grant`. |

Generate a key at **`/admin/api-keys`**, tick the scopes you need.
Pro / Creator Plus / Sovereign plans only, the same plan gate as the
rest of the public API.

## Quick start

```bash
curl https://YOUR-TENANT.mobieus.io/api/v1/learn/courses \
  -H 'Authorization: Bearer mc_live_...'
```

Returns up to 50 courses ordered by id desc. Use `?cursor=...&limit=N`
(max 100) to page; `next_cursor` is null on the last page.

## Resources

### Courses

| Method | Path | Scope | What |
|---|---|---|---|
| `GET`  | `/api/v1/learn/courses`                  | `learn:read`  | List courses (cursor-paginated). |
| `GET`  | `/api/v1/learn/courses/{id}`             | `learn:read`  | Get a single course (with credit info, version status, owner author). |
| `POST` | `/api/v1/learn/courses`                  | `learn:write` | Create a course. Required: `slug`, `title`. Optional: subtitle, summary, language, completion_rule, estimated_minutes, credit_hours, credit_kind, credit_expires_after_months. |
| `POST` | `/api/v1/learn/courses/{id}`             | `learn:write` | Update. Any field from create is acceptable; omitted fields keep their value. |
| `POST` | `/api/v1/learn/courses/{id}/publish`     | `learn:write` | Move to published. |
| `POST` | `/api/v1/learn/courses/{id}/unpublish`   | `learn:write` | Revert to draft. |
| `POST` | `/api/v1/learn/courses/{id}/archive`     | `learn:write` | Archive (hides from learners; preserves enrollments). |

**Example: enroll-on-purchase flow**

```bash
# 1. Create a course
curl -X POST https://YOUR-TENANT.mobieus.io/api/v1/learn/courses \
  -H 'Authorization: Bearer mc_live_...' -H 'Content-Type: application/json' \
  -d '{"slug":"intro-to-mobieus","title":"Intro to Mobieus","completion_rule":"all_required","credit_hours":2.5,"credit_kind":"CEU"}'

# 2. After a Stripe checkout completes, enroll the buyer
curl -X POST https://YOUR-TENANT.mobieus.io/api/v1/learn/enrollments \
  -H 'Authorization: Bearer mc_live_...' -H 'Content-Type: application/json' \
  -d '{"user_id":123,"course_id":42,"source":"stripe_purchase"}'
```

### Nested entities (read-only)

| `GET` | `/api/v1/learn/courses/{id}/modules`     | `learn:read` | Modules in a course. |
| `GET` | `/api/v1/learn/modules/{mid}/lessons`    | `learn:read` | Lessons in a module. |
| `GET` | `/api/v1/learn/lessons/{lid}/activities` | `learn:read` | Activities in a lesson. |

Writes for modules / lessons / activities go through the admin UI;
they enforce per-course team membership. Per-course capability
scoping for the API key model lands in a future minor version.

### Enrollments

| `GET`  | `/api/v1/learn/enrollments?course_id=&user_id=` | `learn:read`  | List, optionally filtered. |
| `GET`  | `/api/v1/learn/enrollments/{id}`                | `learn:read`  | Get one. |
| `POST` | `/api/v1/learn/enrollments`                     | `learn:write` | Enroll a user. Body: `{"user_id", "course_id", "source"?, "expires_at"?}`. |
| `POST` | `/api/v1/learn/enrollments/{id}/cancel`         | `learn:write` | Cancel (status flips to `cancelled`). |

### Cohorts

| `POST` | `/api/v1/learn/cohorts/grant` | `learn:cohorts:grant` | Grant a user access to a cohort. |

### Attempts

| `GET` | `/api/v1/learn/attempts?enrollment_id=&activity_id=` | `learn:read` | List. |
| `GET` | `/api/v1/learn/attempts/{id}` | `learn:read` | Get one (includes score_raw, score_scaled, passed, status, time_started_at, time_ended_at, total_time_seconds). |

### Certificates

| `GET`  | `/api/v1/learn/certificates?user_id=&course_id=` | `learn:read`  | List. |
| `GET`  | `/api/v1/learn/certificates/{id}`                | `learn:read`  | Get one. |
| `GET`  | `/api/v1/learn/certificates/verify/{vid}`        | `learn:read`  | Look up by the 40-char verification id. Returns `is_revoked: true|false`. |
| `POST` | `/api/v1/learn/certificates/{id}/revoke`         | `learn:write` | Revoke. Body: `{"reason": "..."}`. Required. |
| `POST` | `/api/v1/learn/certificates/{id}/regenerate-pdf` | `learn:write` | Rebuild the PDF (after editing a template). Verification URL unchanged. |

### Certificate templates

| `GET` | `/api/v1/learn/cert-templates`      | `learn:read` | List templates (per-tenant brand identity for certs). |
| `GET` | `/api/v1/learn/cert-templates/{id}` | `learn:read` | Get one. |

### SCORM packages

| `GET` | `/api/v1/learn/scorm-packages`      | `learn:read` | List packages (any status). |
| `GET` | `/api/v1/learn/scorm-packages/{id}` | `learn:read` | Get one with its SCO list. |

Importing a SCORM package via API isn't supported in 1.3.0. Use the
admin UI at `/admin/learn/scorm/new`. The endpoint is on the roadmap.

## Response envelope

Every endpoint returns:

```json
{
  "data": ...,
  "next_cursor": "...",
  "request_id": "req_..."
}
```

`next_cursor` is null when there's no more data.

## Errors

Errors use the standard v1 envelope: a single `error` object with a
`code`, a `message`, and the `request_id`:

```json
{
  "error": {
    "code": "course_not_found",
    "message": "Course not found.",
    "request_id": "req_..."
  }
}
```

Common codes for the Learn surface:

| Code | When |
|---|---|
| `404 course_not_found` | `id` doesn't match any row. |
| `404 enrollment_not_found` | Same for enrollments. |
| `404 certificate_not_found` | Same for certs (including `/verify/{vid}` misses). |
| `400 invalid_argument` | The course service rejected the payload. The message explains. |
| `400 missing_field` | A required body field was empty / absent. |
| `403 insufficient_scope` | Key doesn't have `learn:read` (or `learn:write`). |

## What's not in 1.3.0 (defer to a future minor)

- Import SCORM packages via API (use the admin UI today).
- Assessment item authoring + question-bank CRUD via API.
- Question pool CRUD via API.
- Per-course team membership for API keys (today they get tenant-wide
  admin-equivalent capabilities).