Skip to main content
DocsAPI ReferenceH5P Content API
Back to docs

H5P Content API

Content CRUD, editor, player, export

api
h5p-content

H5P Content API Reference

All H5P endpoints are mounted under the /h5p prefix on the Express server (see server/index.ts line 162). The @lumieducation/h5p-express library also serves built-in Ajax routes at the same prefix for core, editor, library, content, and temp file requests.

Base URL: /h5p


Table of Contents


Authentication

H5P endpoints use two authentication levels:

MiddlewareDescriptionApplies to
authMiddlewareRequires a valid JWT (Bearer token in Authorization header or token query parameter). Any authenticated user.Read-only endpoints (get, play, list libraries, content types)
teacherAuthMiddlewareRequires valid JWT and teacher role.Write endpoints (create, update, delete, editor, import, install libraries)

Token can be passed as:

  • Authorization: Bearer <token> header
  • ?token=<token> query parameter (used for H5P iframe embedding)

Content CRUD

List Content

Returns all H5P content owned by the authenticated user, enriched with curriculum metadata, quality scores, visibility, and view counts.

GET /h5p/content

Auth: teacherAuthMiddleware (teacher role required)

Response:

{
  "success": true,
  "data": [
    {
      "id": "123456789",
      "title": "Photosynthesis Quiz",
      "mainLibrary": "H5P.MultiChoice 1.16",
      "contentTypeSlug": "multiple-choice",
      "createdAt": "2025-03-15T10:00:00.000Z",
      "updatedAt": "2025-03-20T14:30:00.000Z",
      "embedTypes": ["iframe"],
      "curriculum": {
        "frameworkName": "NGSS",
        "stageName": "Middle School",
        "gradeName": "Grade 7",
        "subjectName": "Biology",
        "topicNames": ["Photosynthesis"],
        "difficulty": "intermediate",
        "language": "en"
      },
      "quality": {
        "overallScore": 85,
        "bloomsLevel": "Analyze"
      },
      "visibility": "public",
      "viewCount": 42
    }
  ]
}

Notes:

  • Only returns content where the current user is the recorded owner in the content_ownership table.
  • curriculum, quality, visibility, and viewCount are batch-enriched; if any lookup fails the field falls back to null, "private", or 0.
  • contentTypeSlug is derived from the mainLibrary using a hardcoded mapping (e.g. H5P.MultiChoice becomes multiple-choice). Unmapped types return "unknown".

Get Content

Returns metadata for a single content item.

GET /h5p/content/:contentId

Auth: authMiddleware (any authenticated user)

Path Parameters:

ParameterTypeDescription
contentIdstringH5P content ID

Response:

{
  "success": true,
  "data": {
    "id": "123456789",
    "title": "Photosynthesis Quiz",
    "mainLibrary": "H5P.MultiChoice 1.16",
    "createdAt": "2025-03-15T10:00:00.000Z",
    "updatedAt": "2025-03-20T14:30:00.000Z",
    "embedTypes": ["iframe"]
  }
}

Create Content

Creates new H5P content. Checks the user's subscription content creation quota before saving.

POST /h5p/content

Auth: teacherAuthMiddleware (teacher role required)

Request Body:

FieldTypeRequiredDescription
librarystringYesLibrary identifier, format: "H5P.LibraryName majorVersion.minorVersion" (e.g. "H5P.MultiChoice 1.16")
paramsobject or stringYesContent parameters (H5P content JSON). Can be a JSON string or object.
metadataobjectNoContent metadata overrides
metadata.titlestringNoContent title (default: "Untitled Content")
metadata.embedTypesstring[]NoEmbed types (default: ["iframe"])
metadata.languagestringNoContent language (default: "und")
metadata.defaultLanguagestringNoDefault language (default: "en")
metadata.licensestringNoLicense type (default: "U" for undisclosed)
tempImageFilesstring[]NoFilenames of AI-generated images in temp storage to attach

Request Example:

{
  "library": "H5P.MultiChoice 1.16",
  "params": {
    "question": "What is photosynthesis?",
    "answers": [
      { "text": "Process of converting light to energy", "correct": true },
      { "text": "Process of breathing", "correct": false }
    ]
  },
  "metadata": {
    "title": "Photosynthesis Quiz"
  }
}

Response (201):

{
  "success": true,
  "data": {
    "contentId": "987654321",
    "metadata": {
      "title": "Photosynthesis Quiz",
      "mainLibrary": "H5P.MultiChoice",
      "embedTypes": ["iframe"],
      "language": "und",
      "defaultLanguage": "en",
      "license": "U",
      "preloadedDependencies": [
        { "machineName": "H5P.MultiChoice", "majorVersion": 1, "minorVersion": 16 }
      ]
    }
  },
  "message": "Content created successfully"
}

Error Responses:

StatusCondition
400Missing params or library; invalid library format; invalid JSON in params
429Subscription content creation quota exceeded

Update Content

Updates an existing H5P content item. Only the content owner can update.

PUT /h5p/content/:contentId

Auth: teacherAuthMiddleware (teacher role required)

Path Parameters:

ParameterTypeDescription
contentIdstringH5P content ID

Request Body: Same as Create Content (library, params, metadata).

Response:

{
  "success": true,
  "data": {
    "contentId": "987654321",
    "metadata": {
      "title": "Updated Quiz Title",
      "mainLibrary": "H5P.MultiChoice",
      "embedTypes": ["iframe"],
      "language": "und",
      "defaultLanguage": "en",
      "license": "U",
      "preloadedDependencies": [
        { "machineName": "H5P.MultiChoice", "majorVersion": 1, "minorVersion": 16 }
      ]
    }
  },
  "message": "Content updated successfully"
}

Error Responses:

StatusCondition
400Missing params or library; invalid library format
403User is not the content owner

Ownership Note: If no ownership record exists (legacy content), the update is allowed and ownership is recorded for the requesting user.


Delete Content

Deletes an H5P content item and its ownership record.

DELETE /h5p/content/:contentId

Auth: teacherAuthMiddleware (teacher role required)

Path Parameters:

ParameterTypeDescription
contentIdstringH5P content ID

Response:

{
  "success": true,
  "message": "Content deleted successfully"
}

Error Responses:

StatusCondition
403User is not the content owner

Notes:

  • Deletes both the H5P content files (via h5pEditor.deleteContent) and the content_ownership database row.
  • Legacy content with no ownership record can be deleted by any teacher.

Editor Endpoints

These endpoints return the H5P editor model object (scripts, styles, integration config) needed by @lumieducation/h5p-react components on the frontend.

New Editor Model

Returns the editor model for creating new content.

GET /h5p/new

Auth: teacherAuthMiddleware (teacher role required)

Query Parameters:

ParameterTypeDefaultDescription
languagestring"en"Editor UI language
tokenstring-JWT token (injected into model AJAX URLs for iframe auth)

Response:

{
  "success": true,
  "data": {
    "integration": {
      "ajaxPath": "/h5p/ajax?token=...&action=",
      "ajax": { "setFinished": "...", "contentUserData": "..." },
      "editor": { "ajaxPath": "...", "contentLanguage": "en" },
      "contentLanguage": "en"
    },
    "scripts": ["/h5p/core/js/h5p.js", "..."],
    "styles": ["/h5p/core/styles/h5p.css", "..."],
    "contentLanguage": "en"
  }
}

Edit Editor Model

Returns the editor model pre-populated with existing content for editing.

GET /h5p/edit/:contentId

Auth: teacherAuthMiddleware (teacher role required)

Path Parameters:

ParameterTypeDescription
contentIdstringH5P content ID to edit

Query Parameters:

ParameterTypeDefaultDescription
languagestring"en"Editor UI language
tokenstring-JWT token (injected into model AJAX URLs)

Response:

The response includes the same editor model as /h5p/new plus three additional fields:

{
  "success": true,
  "data": {
    "integration": { "..." : "..." },
    "scripts": ["..."],
    "styles": ["..."],
    "library": "H5P.MultiChoice 1.16",
    "params": { "question": "...", "answers": ["..."] },
    "metadata": {
      "title": "Photosynthesis Quiz",
      "mainLibrary": "H5P.MultiChoice",
      "preloadedDependencies": ["..."]
    }
  }
}
Extra FieldDescription
libraryFull library string with version (e.g. "H5P.MultiChoice 1.16")
paramsParsed content.json (the content parameters)
metadataParsed h5p.json (content metadata including dependencies)

Player Endpoint

Play Content

Returns the H5P player model for rendering content to learners.

GET /h5p/play/:contentId

Auth: authMiddleware (any authenticated user)

Path Parameters:

ParameterTypeDescription
contentIdstringH5P content ID to play

Query Parameters:

ParameterTypeDefaultDescription
languagestring"en"Player UI language
tokenstring-JWT token (injected into model AJAX URLs)

Response:

{
  "success": true,
  "data": {
    "integration": { "..." : "..." },
    "scripts": ["/h5p/core/js/h5p.js", "..."],
    "styles": ["/h5p/core/styles/h5p.css", "..."]
  }
}

Side Effect: Increments the view_count in the content ownership record (fire-and-forget, does not block the response).


Visibility

Toggle Visibility

Changes content visibility between private and public. Only the content owner can toggle.

PATCH /h5p/content/:contentId/visibility

Auth: teacherAuthMiddleware (teacher role required)

Path Parameters:

ParameterTypeDescription
contentIdstringH5P content ID

Request Body:

{
  "visibility": "public"
}
FieldTypeRequiredValues
visibilitystringYes"private" or "public"

Response:

{
  "success": true,
  "data": {
    "contentId": "123456789",
    "visibility": "public"
  }
}

Error Responses:

StatusCondition
400Missing or invalid visibility value
401User not found in database
403User is not the content owner

Export

Export as H5P Package

Downloads the content as a .h5p file (ZIP archive containing content.json, h5p.json, and dependencies).

GET /h5p/content/:contentId/export

Auth: teacherAuthMiddleware (teacher role required)

Path Parameters:

ParameterTypeDescription
contentIdstringH5P content ID

Response:

  • Content-Type: application/zip
  • Content-Disposition: attachment; filename="<title>.h5p"
  • Body: binary ZIP stream

Export as SCORM Package

Downloads the content as a SCORM-compliant ZIP archive. Requires the scorm-export feature flag to be enabled for the user's subscription.

GET /h5p/content/:contentId/export-scorm

Auth: authMiddleware (any authenticated user) + requireFeature('scorm-export') gate

Path Parameters:

ParameterTypeDescription
contentIdstringH5P content ID

Query Parameters:

ParameterTypeDefaultDescription
versionstring"1.2"SCORM version. Accepted values: "1.2" or "2004"

Response:

  • Content-Type: application/zip
  • Content-Disposition: attachment; filename="<title>_scorm<version>.zip"
  • Body: binary ZIP stream

SCORM Package Contents:

FileDescription
imsmanifest.xmlSCORM manifest (1.2 or 2004 schema)
index.htmlLauncher HTML page
h5p-scorm-wrapper.jsSCORM API wrapper (cmi.core for 1.2, cmi for 2004)
content.jsonH5P content parameters
images/, videos/, audios/, files/Media subdirectories (if present)

Import

Import H5P Package

Uploads and imports a .h5p package file. Validates ZIP magic bytes before processing.

POST /h5p/import

Auth: teacherAuthMiddleware (teacher role required)

Content-Type: multipart/form-data

Form Fields:

FieldTypeRequiredDescription
h5pfileYesThe .h5p package file

File Constraints:

  • Maximum size: 500 MB
  • Accepted MIME types: application/zip, application/x-zip-compressed, application/octet-stream, or any file with .h5p extension
  • Must have valid ZIP magic bytes (PK\x03\x04)

Response (201):

{
  "success": true,
  "data": {
    "contentId": "987654321",
    "installedLibraries": 5
  },
  "message": "H5P package imported successfully"
}

Error Responses:

StatusCondition
400No file uploaded; not a valid ZIP archive; package contains only libraries (no content)

Media Management

Upload Media File

Uploads a base64-encoded media file to temporary storage. Used for AI-generated images and other media that need to be attached to content later.

POST /h5p/media/upload

Auth: authMiddleware (any authenticated user)

Request Body:

{
  "filename": "ai-generated-diagram.png",
  "data": "<base64-encoded-file-data>",
  "contentType": "image/png"
}
FieldTypeRequiredDescription
filenamestringYesTarget filename
datastringYesBase64-encoded file data
contentTypestringNoMIME type (default: "image/png")

Response (201):

{
  "success": true,
  "data": {
    "url": "/h5p/temp/uploads/ai-generated-diagram.png",
    "filename": "ai-generated-diagram.png",
    "size": 45678,
    "contentType": "image/png"
  }
}

Attach Media to Content

Copies uploaded media files from temporary storage into a content item's media directory.

POST /h5p/media/attach

Auth: authMiddleware (any authenticated user)

Request Body:

{
  "contentId": "987654321",
  "files": ["diagram.png", "chart.png"],
  "mediaType": "images"
}
FieldTypeRequiredDescription
contentIdstringYesTarget H5P content ID
filesstring[]YesArray of filenames (must exist in temp uploads)
mediaTypestringNoSubfolder type: "images", "videos", or "audios" (default: "images")

Response:

{
  "success": true,
  "data": {
    "contentId": "987654321",
    "attached": ["diagram.png", "chart.png"],
    "count": 2,
    "mediaType": "images"
  }
}

Notes:

  • Files are copied from h5p/temp/uploads/ to h5p/content/<contentId>/<mediaType>/.
  • Files not found in temp storage are silently skipped.

Quality Assessment

Get Quality Report

Returns an AI-generated quality assessment for the content. Results are cached in a quality.json file alongside the content. If no cached report exists, one is generated on the fly using the Creatiq AI quality critic.

GET /h5p/content/:contentId/quality

Auth: authMiddleware (any authenticated user)

Path Parameters:

ParameterTypeDescription
contentIdstringH5P content ID

Response:

{
  "success": true,
  "data": {
    "contentId": "987654321",
    "qualityReport": {
      "overallScore": 85,
      "bloomsLevel": "Analyze",
      "strengths": ["Clear question stem", "Good distractors"],
      "improvements": ["Add feedback for incorrect answers"],
      "accessibilityScore": 90
    },
    "generatedAt": "2025-03-20T14:30:00.000Z"
  }
}

Notes:

  • Cached reports are returned immediately. If the cache file is corrupted, it is deleted and regenerated.
  • Generation requires the content to have valid content.json and h5p.json files.

Library Management

List Content Types

Returns the H5P Hub content type cache (available content types that can be installed).

GET /h5p/content-types

Auth: authMiddleware (any authenticated user)

Response:

{
  "success": true,
  "data": {
    "contentTypes": [
      {
        "id": "H5P.MultiChoice",
        "title": "Multiple Choice",
        "description": "Create flexible multiple choice questions",
        "majorVersion": 1,
        "minorVersion": 16
      }
    ]
  }
}

List Installed Libraries

Returns all H5P libraries currently installed on the server.

GET /h5p/libraries

Auth: authMiddleware (any authenticated user)

Response:

{
  "success": true,
  "data": [
    {
      "machineName": "H5P.MultiChoice",
      "versions": ["1.16.10", "1.16.11"],
      "latestVersion": "1.16.11"
    }
  ]
}

Install Library from Hub

Installs a single library from the H5P Hub by machine name.

POST /h5p/libraries/install

Auth: teacherAuthMiddleware (teacher role required)

Request Body:

{
  "machineName": "H5P.MultiChoice"
}
FieldTypeRequiredDescription
machineNamestringYesH5P library machine name (e.g. "H5P.MultiChoice")

Response:

{
  "success": true,
  "message": "Library H5P.MultiChoice installed successfully"
}

Install Essential Libraries

Batch-installs all essential content types defined in the platform configuration. Updates the content type cache first.

POST /h5p/libraries/install-essentials

Auth: teacherAuthMiddleware (teacher role required)

Response:

{
  "success": true,
  "message": "Installed 28/30 content types",
  "data": {
    "total": 30,
    "installed": 28,
    "failed": 2,
    "results": [
      { "name": "H5P.MultiChoice", "success": true },
      { "name": "H5P.TrueFalse", "success": true },
      { "name": "H5P.SomeType", "success": false, "error": "Not found in hub" }
    ]
  }
}

Essential Content Types:

H5P.MultiChoice, H5P.TrueFalse, H5P.Blanks, H5P.DragText, H5P.DragQuestion, H5P.MarkTheWords, H5P.QuestionSet, H5P.InteractiveVideo, H5P.CoursePresentation, H5P.InteractiveBook, H5P.BranchingScenario, H5P.Timeline, H5P.ImageHotspots, H5P.MemoryGame, H5P.Flashcards, H5P.DialogCards, H5P.Accordion, H5P.Column, H5P.ImageSlider, H5P.Summary, H5P.SingleChoiceSet, H5P.Essay, H5P.SortParagraphs, H5P.ImageSequencing, H5P.FindTheWords, H5P.Crossword, H5P.ArithmeticQuiz, H5P.GuessTheAnswer, H5P.ImageJuxtaposition, H5P.Chart


Library Status

Returns installation status for all essential content types.

GET /h5p/libraries/status

Auth: authMiddleware (any authenticated user)

Response:

{
  "success": true,
  "data": {
    "total": 30,
    "installed": 25,
    "ready": true,
    "status": [
      { "name": "H5P.MultiChoice", "installed": true },
      { "name": "H5P.TrueFalse", "installed": true },
      { "name": "H5P.SomeRareType", "installed": false }
    ]
  }
}

Note: ready is true when at least 5 essential content types are installed.


Static File Routes

The H5P router serves several static file paths required by the H5P editor and player:

Route PatternSource DirectoryDescription
/h5p/core/*h5p/core/H5P core JavaScript and CSS files
/h5p/editor/*h5p/editor/H5P editor JavaScript and CSS files
/h5p/libraries/*h5p/libraries/Installed H5P library assets
/h5p/content/<id>/*h5p/content/<id>/Content-specific files (media, etc.)
/h5p/temp/*h5p/temp/Temporary files (uploads, editor temp). Searches all user subdirectories.
/h5p/custom/*h5p/custom/Custom CSS overrides (e.g. h5p-custom.css)

These static routes are served by a combination of:

  • h5pAjaxExpressRouter from @lumieducation/h5p-express (core, editor, libraries, content, temp)
  • Custom express.static middleware (custom CSS)
  • Custom handler for temp files that searches across all user temp subdirectories

Embedding Note: Routes under /h5p/ have relaxed X-Frame-Options and CSP frame-ancestors headers to allow loading inside iframes (e.g. BigBlueButton integration). The server sets frame-ancestors * for any request path starting with /h5p/.


Health Check

GET /h5p/health

Auth: None

Response:

{
  "success": true,
  "service": "h5p",
  "timestamp": "2025-03-20T14:30:00.000Z"
}

Content Ownership Model

Content ownership is tracked in the content_ownership PostgreSQL table:

ColumnTypeDescription
content_idVARCHAR(100) (PK)H5P content ID
user_idUUID (FK to creatiq_users)Owner's user ID
visibilityVARCHAR(20)"private" or "public" (default: "private")
published_atTIMESTAMPTZTimestamp when content was first made public
view_countINTEGERNumber of times the play endpoint was called (default: 0)
created_atTIMESTAMPTZOwnership record creation time
updated_atTIMESTAMPTZLast update time (auto-updated via trigger)

Key behaviors:

  • Ownership is recorded automatically when content is created via POST /h5p/content.
  • The owner is resolved from the JWT email claim via the creatiq_users table.
  • Only the owner can update, delete, or change visibility of content.
  • Legacy content with no ownership record can be edited or deleted by any teacher; the first teacher to edit it becomes the owner.
  • When content is deleted, the ownership record is also removed.
  • GET /h5p/content (list) returns only content owned by the requesting user.

Visibility States

StateDescription
privateDefault. Content is only visible to the owner. Listed only in the owner's content list.
publicContent is discoverable in the marketplace and community. Other users can view and play it.

Visibility is toggled via PATCH /h5p/content/:contentId/visibility. Only the content owner can change visibility.


File Upload Limits

LimitValueSource
H5P package import (POST /h5p/import)500 MBmulter config in h5p.ts
Global file upload (express-fileupload)500 MBserver/index.ts fileUpload middleware
Individual file size (h5pConfig.maxFileSize)100 MBserver/config/h5p.ts
Total content size (h5pConfig.maxTotalSize)500 MBserver/config/h5p.ts
JSON body size10 MBserver/index.ts express.json config
Media upload (POST /h5p/media/upload)Limited by JSON body size (base64 in body)Effective ~7.5 MB decoded

Accepted MIME types for import:

  • application/zip
  • application/x-zip-compressed
  • application/octet-stream
  • Any file with .h5p extension

Soft Delete Behavior

H5P content deletion is hard delete, not soft delete:

  • DELETE /h5p/content/:contentId calls h5pEditor.deleteContent() which removes the content directory from the filesystem (h5p/content/<contentId>/).
  • The content_ownership database row is deleted via deleteOwnership(contentId).
  • Any associated content_curriculum_metadata rows remain orphaned (no cascade from content side).
  • The quality cache file (quality.json) is removed along with the content directory.
  • There is no undo or trash/recycle bin mechanism. Deleted content cannot be recovered.

Standard Error Response Format

All endpoints return errors in a consistent format:

{
  "success": false,
  "error": "Human-readable error message"
}

Common HTTP status codes:

StatusMeaning
400Bad request (missing fields, invalid format)
401Not authenticated or user not found
403Not authorized (not the content owner)
429Subscription quota exceeded
500Internal server error
Back to docsdocs/product/api/h5p-content.md