Batch assembly / ingest
const url = 'https://example.com/wp-json/wp2lms-pro/embed-packages/v1/batch?blog_id=1';const options = { method: 'POST', headers: {'X-WP-Nonce': '<X-WP-Nonce>', 'Content-Type': 'application/json'}, body: '{"package":{"title":"HTML course","files":{"index.html":"<!doctype html><html lang=\"en\"><title>Start</title><h1>Start</h1></html>","module-2.html":"<!doctype html><html lang=\"en\"><title>Module 2</title><h1>Module 2</h1></html>"}},"pages":[{"title":"Start","launch_path":"index.html","slug":"start","page_status":"publish"},{"title":"Module 2","launch_path":"module-2.html","slug":"module-2"}],"link_replace":true,"page_status":"publish","alpine":false}'};
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/batch?blog_id=1' \ --header 'Content-Type: application/json' \ --header 'X-WP-Nonce: <X-WP-Nonce>' \ --data '{ "package": { "title": "HTML course", "files": { "index.html": "<!doctype html><html lang=\"en\"><title>Start</title><h1>Start</h1></html>", "module-2.html": "<!doctype html><html lang=\"en\"><title>Module 2</title><h1>Module 2</h1></html>" } }, "pages": [ { "title": "Start", "launch_path": "index.html", "slug": "start", "page_status": "publish" }, { "title": "Module 2", "launch_path": "module-2.html", "slug": "module-2" } ], "link_replace": true, "page_status": "publish", "alpine": false }'Creates package(s), optionally WordPress pages and bindings, in one request (BatchAssembler::run). This is not a single POST /packages and not Static Builder itself.
Two modes. If a package object is sent — one package for all: tree package.files / package.html (html → index.html when the tree has no index.html/Index.html), then either pages[] (new or existing WP pages with per-page launch_path) or materialize_all (same as materialize-pages). Empty package.title → “HTML package”. Package status is always publish. When package is present, items is ignored.
Otherwise a non-empty items[] is required (else 400 embed_packages_batch_empty): one package per item. create_pages defaults to true; link_replace defaults to true in both modes. Empty item title → “HTML {n}”.
Truthy build.run tries to start AgentBuild; a module failure does not fail HTTP — the message goes to warnings[], the response is still 201.
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 Bodyrequired
Section titled “Request Bodyrequired”object
object
Title of the shared package. Optional; empty → “HTML package”. Post status is always publish.
HTML that PHP puts into files[‘index.html’] when the tree does not already have index.html/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.
WordPress pages in shared-package mode (objects, not numbers). If empty and materialize_all — materialize the package index.
object
WordPress page title. Empty → launch_path basename or “Page {n}”.
Relative HTML in the shared package for this page (sanitized like PagePackageReplace). Not page_id.
Status of this page. Optional; else body.page_status, else publish.
post_name. Optional; else sanitize of the launch_path basename.
ID of an existing WordPress page (post_type=page) for wp_update_post instead of insert. Not the package id. 0 or not-a-page → a new page is created.
Alpine on this page when link_replace is on. Default false.
One-package-per-item mode. Ignored when a package object is sent. Empty array without package — 400 embed_packages_batch_empty.
object
Package title (and page title if create_pages). Empty → “HTML {n}”.
HTML placed in files[‘index.html’] if that key is not already set.
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.
Alpine when binding the created/updated page. Default false.
Launch override on the WordPress page. Empty → launch_path from package meta.
WordPress page status if create_pages. Default publish.
WordPress page post_name if create_pages.
Existing WordPress page ID for update instead of insert. Not the package id.
Package mode only: if pages is empty/missing, call PagesMaterializer. Default false.
Items mode only: create/update WordPress pages. Optional, default true.
Immediately PagePackageReplace::save on created pages. Optional, default true.
Default page status (materialize_all and pages[] without their own page_status). Default publish.
Alpine for materialize_all in package mode. Default false. pages[]/items[] have their own alpine.
object
If truthy — after ingest call AgentBuild (BuildService::create + run). A module failure does not change the HTTP status; the text goes to warnings[].
Extra WordPress page IDs in addition to those the batch created. Not package ids.
Build course_name. Optional, default “Agent embed batch”.
Build api_type. Optional, default local (sanitize_key).
Build export_mode. Optional, default package.
WordPress main page ID for AgentBuild, if the key is sent.
String course_id for the build, if the key is sent.
Build version ((float)), if the key is sent.
Second argument of BuildService::run($id, $sync). Default false.
Example
{ "package": { "title": "HTML course", "files": { "index.html": "<!doctype html><html lang=\"en\"><title>Start</title><h1>Start</h1></html>", "module-2.html": "<!doctype html><html lang=\"en\"><title>Module 2</title><h1>Module 2</h1></html>" } }, "pages": [ { "title": "Start", "launch_path": "index.html", "slug": "start", "page_status": "publish" }, { "title": "Module 2", "launch_path": "module-2.html", "slug": "module-2" } ], "link_replace": true, "page_status": "publish", "alpine": false}Responses
Section titled “Responses”Ingest completed. package mode — package_id, meta, launch_url, pages[], is_multipage. items mode — items[]. Always blog_id; optionally build and warnings.
Shape depends on the mode. package — package_id, meta, launch_url, pages, is_multipage. items — items[]. RestApi always adds blog_id. build — only if build.run succeeded without WP_Error; otherwise the text is in warnings.
object
Shared package ID (package mode). Absent in items mode.
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.
Launch URL of the shared package (package mode).
WordPress pages in package mode (not the same as meta.pages inside the package).
object
ID of the created shared package.
WordPress page ID (created or updated). Not the package id.
Page title as written in the batch.
Slug sent to post_name (may be empty if omitted and not derived from launch_path).
Page launch path (override or package default).
Copy of is_multipage of the shared package (package mode).
Items[] mode results (one package per item).
object
Package ID created for this items[] entry.
WordPress page ID, or null when create_pages=false.
Item title (package/page).
Override or launch_path from package meta.
Absolute launch URL of this package.
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.
Site ID after switch. RestApi always appends it.
AgentBuild wrapper { created, run } when build.run is truthy and the module did not return WP_Error. Otherwise the field is absent.
object
Messages (e.g. AgentBuild unavailable). Do not change HTTP 201.
Example
{ "package_id": 42, "meta": { "content_type": "web", "version": "", "activity_id": "https://lms.example.com/wp2lms-embed-package/42", "launch_path": "index.html", "title": "HTML course", "mastery_score": null, "content_tool": "", "storage_rel": "wp2lms-embed-packages/42/", "uploaded_at": 1726252800, "is_multipage": true, "pages": [ { "path": "index.html", "title": "Start" }, { "path": "module-2.html", "title": "Module 2" } ] }, "launch_url": "https://lms.example.com/wp-content/uploads/wp2lms-embed-packages/42/index.html", "pages": [ { "package_id": 42, "page_id": 128, "title": "Start", "slug": "start", "launch_path": "index.html" }, { "package_id": 42, "page_id": 129, "title": "Module 2", "slug": "module-2", "launch_path": "module-2.html" } ], "is_multipage": true, "blog_id": 1}Embed_packages_batch_empty — no package object and empty items[]; embed_packages_batch_package — package is not an object; embed_packages_batch_page — pages[n] is not an object; embed_packages_batch_item — items[n] is not an object; 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 — wp_insert_post/wp_update_post or tree ingest failure (empty_tree, unsafe_path, disallowed_extension, process_failed, write_failed, and similar). WordPress REST surfaces those as 500.
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 }}