Create embed package
const url = 'https://example.com/wp-json/wp2lms-pro/embed-packages/v1/packages?blog_id=1';const options = { method: 'POST', headers: {'X-WP-Nonce': '<X-WP-Nonce>', 'Content-Type': 'application/json'}, body: '{"title":"Interactive lesson","status":"publish","html":"<!doctype html><html lang=\"en\"><title>Lesson</title><h1>Welcome</h1></html>"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url 'https://example.com/wp-json/wp2lms-pro/embed-packages/v1/packages?blog_id=1' \ --header 'Content-Type: application/json' \ --header 'X-WP-Nonce: <X-WP-Nonce>' \ --data '{ "title": "Interactive lesson", "status": "publish", "html": "<!doctype html><html lang=\"en\"><title>Lesson</title><h1>Welcome</h1></html>" }'Creates a CPT wp2lms_embed_pkg post (wp_insert_post) and optionally ingests content via ingest_from_request. This does not create WordPress pages: materialization is POST …/materialize-pages or POST …/batch.
title is optional: empty after sanitize_text_field becomes “Untitled package”. status is optional, default publish; only publish / draft / private (sanitize_key) are kept, anything else PHP rewrites to publish.
Content may be omitted — you get a package with no files (empty launch_path/meta). Otherwise one channel: a multipart file (file / package_file / package_zip, zip or html/htm), JSON files (path tree), or JSON html (stored as index.html). If both files and non-empty html are present and the tree has no index.html, PHP adds index.html from html. An empty files object is not treated as a tree.
Ingest error after insert — PHP wp_delete_post(..., true) and returns that WP_Error.
Requires manage_network (multisite) or edit_pages. Cookie + X-WP-Nonce or Application Password.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Query Parameters
Section titled “Query Parameters”Network site ID (switch_to_blog). Optional: defaults to the request current blog. Not a package id and not page_id. Minimum 1; on a single site it must match the current site — otherwise 400 embed_packages_invalid_blog. In a network an unknown site or a site from another network is 404 embed_packages_unknown_blog.
Example
1Request Body
Section titled “Request Body”object
Title of the new CPT post. Optional. Empty after sanitize → “Untitled package”. After ingest it may be overwritten from HTML/manifest.
Post_status of the new post. Optional, default publish. Any other value PHP rewrites to publish.
Raw HTML of a single launch file. Optional. Stored as index.html when there is no multipart and (if files is present) the tree does not already have index.html.
Map of relative paths to contents. Keys must be safe relative paths (PackageFilePolicy). An empty object is not treated as a tree. Unsafe path — unsafe_path; disallowed extension — disallowed_extension.
Example
{ "title": "Interactive lesson", "status": "publish", "html": "<!doctype html><html lang=\"en\"><title>Lesson</title><h1>Welcome</h1></html>"}object
First multipart field PHP looks at (file, then package_file, then package_zip). Extension .zip — ZipExtractor; .html/.htm — a single HTML file. Anything else — 400 embed_packages_bad_file.
Example
lesson.zipAlternate package file field name (same as file).
Example
lesson.zipThird possible multipart field name for ZIP/HTML.
Example
lesson.zipCPT title from form-fields (when there is no JSON body). Empty → “Untitled package”.
Example
Interactive lessonPost_status from form-fields. Not publish/draft/private → publish.
Example
publishResponses
Section titled “Responses”Package created. Body is package_payload (id, title, status, launch_*, meta).
package_payload response — CPT post plus flattened meta fields and the full meta object.
object
CPT wp2lms_embed_pkg post ID. Same number as {id} in GET/PATCH/DELETE/content/materialize-pages paths.
Package post_title. After ingest PHP may replace it with the launch file <title>.
Package post_status. REST accepts publish/draft/private; the response is whatever is stored.
Copy of meta.content_type (web / scorm1.2 / scorm2004 / xapi or empty).
Copy of meta.content_tool (authoring-tool detector or empty string).
Copy of meta.launch_path — relative launch HTML, not a URL.
Absolute launch URL (uploads baseurl + storage_rel + launch_path). Empty when there is no content.
Copy of meta.activity_id.
Copy of meta.storage_rel under uploads.
Copy of meta.is_multipage.
object
Relative HTML path inside the package directory (posix, no leading /). From PackagePagesIndex::scan.
Title from the file <title> tag, or the basename if the tag is missing.
Full post meta array _wp2lms_embed_package (PackageMeta::get). Empty object when the package has not been ingested.
object
Type after ManifestPipeline — web, scorm1.2, scorm2004, or xapi. Empty until content is uploaded.
Version from the manifest (for SCORM often the same as content_type). Empty string for typical web packages.
XAPI activity id. If the parser omitted it — {home}wp2lms-embed-package/{id}.
Relative launch HTML inside storage (from the manifest or fallback index.html).
Title from launch HTML / manifest; PHP may overwrite the package post_title with this value.
Mastery score from imsmanifest (SCORM), otherwise null.
Authoring-tool detector: ispring, articulate_rise, articulate_storyline, articulate_presenter, captivate, elucidat, or an empty string.
Path from uploads basedir, wp2lms-embed-packages/{id}/.
Unix time of the last successful content write (time() in finalize).
True only for content_type=web with no content_tool and ≥2 HTML files in the index.
HTML index (path+title) when the package is multipage; otherwise [].
object
Relative HTML path inside the package directory (posix, no leading /). From PackagePagesIndex::scan.
Title from the file <title> tag, or the basename if the tag is missing.
Example
{ "id": 42, "title": "Interactive lesson", "status": "publish", "content_type": "web", "content_tool": "", "launch_path": "index.html", "launch_url": "https://lms.example.com/wp-content/uploads/wp2lms-embed-packages/42/index.html", "activity_id": "https://lms.example.com/wp2lms-embed-package/42", "storage_rel": "wp2lms-embed-packages/42/", "is_multipage": false, "pages": [], "meta": { "content_type": "web", "version": "", "activity_id": "https://lms.example.com/wp2lms-embed-package/42", "launch_path": "index.html", "title": "Interactive lesson", "mastery_score": null, "content_tool": "", "storage_rel": "wp2lms-embed-packages/42/", "uploaded_at": 1726252800, "pages": [], "is_multipage": false }}Embed_packages_bad_file — multipart is not ZIP or HTML/HTM; embed_packages_invalid_blog — invalid blog_id.
object
WP_Error code (get_error_code()), e.g. embed_packages_not_found.
WP_Error text (get_error_message()).
WP_Error data. Usually { "status": 404 }. Ingest/materialize errors without status are returned by WordPress REST as 500.
object
Example
{ "code": "embed_packages_not_found", "message": "Package not found.", "data": { "status": 404 }}Not authenticated. WordPress REST: rest_forbidden (status 401) when there is no cookie session. Cookie + X-WP-Nonce or Application Password.
object
WP_Error code (get_error_code()), e.g. embed_packages_not_found.
WP_Error text (get_error_message()).
WP_Error data. Usually { "status": 404 }. Ingest/materialize errors without status are returned by WordPress REST as 500.
object
Example
{ "code": "embed_packages_not_found", "message": "Package not found.", "data": { "status": 404 }}Missing manage_network (multisite) or edit_pages (single site), or invalid REST nonce (rest_cookie_invalid_nonce).
object
WP_Error code (get_error_code()), e.g. embed_packages_not_found.
WP_Error text (get_error_message()).
WP_Error data. Usually { "status": 404 }. Ingest/materialize errors without status are returned by WordPress REST as 500.
object
Example
{ "code": "embed_packages_not_found", "message": "Package not found.", "data": { "status": 404 }}Embed_packages_unknown_blog — that site does not exist in the network, or it belongs to another network.
object
WP_Error code (get_error_code()), e.g. embed_packages_not_found.
WP_Error text (get_error_message()).
WP_Error data. Usually { "status": 404 }. Ingest/materialize errors without status are returned by WordPress REST as 500.
object
Example
{ "code": "embed_packages_not_found", "message": "Package not found.", "data": { "status": 404 }}WP_Error with no HTTP status in data — wp_insert_post or ingest failure (empty_tree, unsafe_path, disallowed_extension, zip_open, unzip_failed, process_failed, mkdir_failed, write_failed, invalid_file_value, unsafe_zip, missing_zip, and similar). WordPress REST surfaces those as 500. The package is deleted on ingest failure.
object
WP_Error code (get_error_code()), e.g. embed_packages_not_found.
WP_Error text (get_error_message()).
WP_Error data. Usually { "status": 404 }. Ingest/materialize errors without status are returned by WordPress REST as 500.
object
Example
{ "code": "embed_packages_not_found", "message": "Package not found.", "data": { "status": 404 }}