API & MCP · Endpoints
REST endpoints
Base URL: https://academy.empomm.com. All endpoints require Authorization: Bearer empo_…. Errors return JSON { error: { code, message, field? } } with the appropriate HTTP status. New authoring mutations are REST/CLI-only; MCP exposes a selected documented subset, not every endpoint.
| Method | Path | Scope | Description |
|---|---|---|---|
| GET | /api/v1/me | any | Return the user that owns the bearer token + the key's scopes. |
| GET | /api/v1/courses | courses:read | List courses. Admins see all; instructors see own + collab. Supports `?limit=50` (max 100). |
| POST | /api/v1/courses | courses:write | Create a draft course. Body: { title, subtitle?, description?, categoryId?, price?, language? }. |
| GET | /api/v1/courses/{courseId} | courses:read | Get a course with modules and complete reconciliation lesson snapshots (metadata, assignment prompt, timestamps). |
| PATCH | /api/v1/courses/{courseId} | courses:write | Update fields incl. learningOutcomes, requirements, targetAudience, materialsIncluded (null/[] clears; strings max 200). REST/CLI-only. |
| POST | /api/v1/courses/{courseId}/status | courses:write | Transition course status. Body: { status: 'DRAFT' | 'PUBLISHED' | 'PRIVATE' | 'ARCHIVED' }. |
| POST | /api/v1/courses/{courseId}/modules | courses:write | Append a new module. Body: { title }. |
| POST | /api/v1/modules/{moduleId}/lessons | courses:write | Append {title,contentType?,videoUrl?,content?,assignmentPrompt?,duration?,isFree?}; ASSIGNMENT needs assignmentPrompt; returns block/hash. |
| PATCH | /api/v1/modules/{moduleId} | courses:write | Rename a module. Body: { title }. REST/CLI-only. |
| PATCH | /api/v1/lessons/{lessonId} | courses:write | Update title/contentType/duration/videoUrl; duration is seconds (null or 1..2,147,483,647), URL null or http(s) <=191. REST/CLI-only. |
| GET | /api/v1/lessons/{lessonId}/blocks | courses:read | List reachable nested blocks + hashes and totalBlockCount for every block row belonging to the lesson, including malformed orphans. |
| POST | /api/v1/lessons/{lessonId}/blocks | courses:write | Append a single block to a lesson. Sanitised at storage. |
| PATCH | /api/v1/lessons/{lessonId}/blocks/{blockId} | courses:write | Compare-and-update a block. Body: { expectedContentHash: sha256:<64 lowercase hex>, content: object }. REST/CLI-only. |
| DELETE | /api/v1/lessons/{lessonId}/blocks/{blockId} | courses:write | Compare-and-delete a block. Body: { expectedContentHash: sha256:<64 lowercase hex>, cascade?: boolean }. Returns 409 BLOCK_HAS_CHILDREN without cascade or BLOCK_INTEGRITY_CONFLICT for a cross-lesson descendant. REST/CLI-only. |
| POST | /api/v1/lessons/{lessonId}/blocks/from-markdown | courses:write | Bulk-create blocks from a Markdown body. Body: { markdown, replace? }. 250 KB cap; replace=true is atomic and returns 409 BLOCK_INTEGRITY_CONFLICT if existing descendants span lessons. |
| POST | /api/v1/scaffold-course | courses:write | One-shot scaffold: course + modules + lessons + markdown blocks. 1 MB cap. |
| POST | /api/v1/certificates/external | certificates:issue | Issue a single EMPO T&C certificate. Admin-only. Body: holder info + trainingTitle + trainingDate + deliveryFormat. |
| POST | /api/v1/certificates/external/batch | certificates:issue | Bulk issue T&C certs (up to 200 holders). Admin-only. Body: { trainingTitle, trainingDate, deliveryFormat, holders[] }. |
| POST | /api/v1/certificates/{certId}/revoke | certificates:manage | Revoke a certificate. Body: { reason }. Reason is shown on the public verification page. |
| POST | /api/v1/certificates/{certId}/resend-email | certificates:manage | Resend the certificate notification email to the holder. |
| GET | /api/v1/notifications/templates | (admin) | List all notification email templates with their current overrides. |
| PATCH | /api/v1/notifications/templates/{type} | templates:write | Override one template. Body: { subject?, greeting?, ctaLabel?, footer?, enabled? }. |
Error format
{
"error": {
"code": "INVALID_INPUT",
"message": "trainingDate is required",
"field": "trainingDate"
}
}Common codes: UNAUTHORIZED (401), FORBIDDEN (403), MISSING_SCOPE (403), NOT_FOUND (404), INVALID_INPUT (400), RATE_LIMITED (429), CONTENT_CONFLICT, BLOCK_HAS_CHILDREN, BLOCK_INTEGRITY_CONFLICT (409), PAYLOAD_TOO_LARGE (413), INTERNAL_ERROR (500).
Rate limits
60 requests/minute per API key, 120/minute per client IP. The stricter verdict wins. Every response carries RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset headers; 429s also set Retry-After. Need more headroom for a legitimate integration? Email academy@empomm.com before pointing prod traffic.
Looking for the MCP version?
MCP wraps a selected documented subset; the new authoring mutations are REST/CLI-only. See the MCP setup guide.