Bulk site operations
const url = 'https://example.com/wp-json/wp2/site-manager/v1/sites/bulk';const options = { method: 'POST', headers: {'X-WP-Nonce': '<X-WP-Nonce>', 'Content-Type': 'application/json'}, body: '{"blog_ids":[5,8],"action":"archive"}'};
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/wp2/site-manager/v1/sites/bulk \ --header 'Content-Type: application/json' \ --header 'X-WP-Nonce: <X-WP-Nonce>' \ --data '{ "blog_ids": [ 5, 8 ], "action": "archive" }'Applies one action to all blog_ids in the current network: archive, unarchive, activate, deactivate, delete, or patch.
Up to 10 sites inclusive — synchronous 200 `BulkSiteResult` (per-site errors in `failed`, HTTP still 200). More than 10 — enqueue `site_manager.bulk` and 202.
Non-empty `blog_ids` and non-empty `action` are required, otherwise 400 `missing_fields`. Requires `delete_sites` or `manage_network`. Multisite only.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”object
IDs of sites in the current network. Required; empty array — 400 missing_fields. More than 10 items — background job 202 (BulkSiteService::SYNC_THRESHOLD).
Action for each blog_id. Empty string — 400 missing_fields. Unknown value in a sync run goes into failed with invalid_action, HTTP 200.
Metadata body for action=patch. Omit for archive/delete. PHP passes the array as-is to SiteMetadataPatch::from_array.
object
New blogname. Optional; missing key — leave unchanged. PHP sanitizes and writes the blog option plus site meta blogname.
New address slug. Optional. PHP normalizes and checks domain_exists; another site with the same domain+path — 409 slug_conflict. Network main site — 403 main_site_slug_forbidden.
New blogdescription. Optional; PHP writes sanitize_text_field to the blog option.
Site Manager status ID or null to clear it. The key must be present (array_key_exists); otherwise PHP leaves status alone. Unknown term — 400 invalid_term.
Full set of site category IDs. If the key is present (even an empty array) PHP overwrites meta. Each id must be a category term; otherwise 400 invalid_term.
True — update_blog_status(..., archived, 1); false — clear archive. Main site — 403 main_site_forbidden. Not the integer flag in SiteDto.
1 — activate the site (WP_Site->public=1); otherwise deactivate (0). Main site — 403 main_site_forbidden.
Writes site meta wp2platform_site_manager_is_template (1 or 0). JSON key must be present, otherwise PHP leaves the flag alone.
Only for action=delete: true — wpmu_delete_blog($id, true) (drop site tables). PHP ignores it for other actions.
Example
{ "blog_ids": [ 5, 8 ], "action": "archive"}Responses
Section titled “Responses”Synchronous run (≤10 sites). succeeded — ok, failed — per-site WP_Error (main_site_forbidden, etc.).
object
Blog_ids for which the action completed without WP_Error.
Sites that failed; the overall HTTP status of a synchronous bulk remains 200.
object
Site on which the action returned WP_Error.
WP_Error code (main_site_forbidden, invalid_action, slug_conflict, etc.).
WP_Error message for this site.
Example
{ "succeeded": [ 5, 8 ], "failed": []}Async job (more than 10 sites). Location is poll_url, Retry-After=2.
object
PlatformJob UUID. The same id in GET /jobs/{job_id} and SSE /jobs/{job_id}/events.
Job status at the 202 moment (usually pending right after enqueue).
Site Manager job type. Duplicate create — site_manager.create_from_source; bulk — site_manager.bulk.
JSON poll URL (rest_url(…/jobs/{job_id})). Also sent as Location; Retry-After=2.
SSE progress URL (…/jobs/{job_id}/events), not the JSON poll.
Example
{ "job_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "status": "pending", "type": "site_manager.bulk", "poll_url": "https://example.com/wp-json/wp2/site-manager/v1/jobs/7c9e6679-7425-40de-944b-e07fc1f90ae7", "events_url": "https://example.com/wp-json/wp2/site-manager/v1/jobs/7c9e6679-7425-40de-944b-e07fc1f90ae7/events"}Headers
Section titled “Headers”GET URL for job state.
PHP sets 2.
Missing_fields — missing blog_ids or action; not_multisite.
object
Machine-readable error or event code.
Message text (log, PIN, or password reset).
Extra WP_Error or license payload.
object
Examplegenerated
{ "code": "example", "message": "example", "data": {}}Not authenticated. DualRest: rest_forbidden (status 401) when there is no cookie session. Cookie + X-WP-Nonce or Application Password.
object
Machine-readable error or event code.
Message text (log, PIN, or password reset).
Extra WP_Error or license payload.
object
Examplegenerated
{ "code": "example", "message": "example", "data": {}}Missing delete_sites/manage_network (code forbidden) or invalid REST nonce.
object
Machine-readable error or event code.
Message text (log, PIN, or password reset).
Extra WP_Error or license payload.
object
Examplegenerated
{ "code": "example", "message": "example", "data": {}}