Plan with AI

Get started →

Bulk Page Migration & Management

Overview

Use the Workvivo Pages API to migrate large volumes of pages from legacy intranets or document platforms into Workvivo in bulk. Rather than manually recreating hundreds or thousands of pages, organisations can programmatically extract content from their existing platform, transform it into Workvivo-compatible structures, and load it via the API, reducing migration timelines from months to days.

This use case covers the full migration lifecycle: creating pages in draft, validating content, updating pages post-review, and cleaning up failed or duplicate entries.

Value & Benefits

Accelerate platform adoption. Automate page creation at scale, eliminating the bottleneck of manual content entry during platform transitions.

Preserve content integrity. Programmatically map source content (titles, body HTML, metadata, authorship) to Workvivo page structures, minimising human error and content loss.

Reduce operational effort. Free content owners to focus on review and optimisation rather than copy-pasting from legacy systems.

Enable phased rollouts. Migrate content in controlled batches by department, region, or content type, supporting staged go-lives.

Support ongoing content management. Beyond initial migration, use the same endpoints to bulk-update, reorganise, or delete as the intranet evolves.

Applications

Full platform migration. Moving all page content from SharePoint, Confluence, LumApps, Unily, or a custom CMS into Workvivo as part of a complete platform switch.

Phased departmental rollout. Migrating content space-by-space or team-by-team to support a gradual transition with review gates between batches.

Content archival import. Ingesting static HTML archives or exported wiki content into Workvivo to preserve institutional knowledge.

Multi-source consolidation. Merging pages from multiple legacy systems (e.g., regional intranets) into a single Workvivo instance.

Ongoing bulk content operations. Periodically updating page metadata, reassigning ownership, or archiving outdated content across large page sets.

Technical Details

Before using these examples, complete Quick Start to create an app and API key and find your Base URL and Workvivo-Id.

Create a Page

Creating pages with is_draft=1 allows content owners to review before publishing. Send is_draft=0 on create or update when the page is ready to publish.

Required fields: title and html_content. Use space_id for space pages; omit it for global pages. The optional external_id must be an integer.

cURL note: use --form-string for html_content and any value that may start with <. Plain -F "html_content=<h2>..." makes cURL try to read a local file and fails before the request is sent.

curl -X POST https://api.workvivo.com/v1/pages \
  -H "Authorization: Bearer $WORKVIVO_TOKEN" \
  -H "Workvivo-Id: $WORKVIVO_ORG_ID" \
  -H "Accept: application/json" \
  -F "title=Company Travel Policy" \
  --form-string "html_content=<h2>Travel Guidelines</h2><p>All employees must book travel through the approved portal...</p>" \
  -F "space_id=15" \
  -F "external_id=7891" \
  -F "is_draft=1"
{
    "data": {
        "id": 1,
        "title": "Company Travel Policy",
        "slug": "company-travel-policy",
        "space_id": 15,
        "external_id": 7891,
        "is_draft": true,
        "permalink": "https://yourcompany.workvivo.com/pages/company-travel-policy"
    },
    "status": "success",
    "meta": {}
}

For page hierarchy migrations, migrate parent pages before their children. Use only one parent identifier:

  • parent_id for a Workvivo parent page ID.
  • external_parent_id for a parent page created with your integer external_id.

If you are also sending external_id for idempotent migration, use external_parent_id rather than parent_id.

Update a Page

Use this to correct content post-migration, update internal links once all pages exist, or promote drafts to published status in bulk. PUT requires the page fields again, including title, subtitle, and html_content; resend space_id for space pages, and send is_draft=0 to publish a draft.

curl -X PUT https://api.workvivo.com/v1/pages/{page_id} \
  -H "Authorization: Bearer $WORKVIVO_TOKEN" \
  -H "Workvivo-Id: $WORKVIVO_ORG_ID" \
  -H "Accept: application/json" \
  -F "title=Company Travel Policy (Updated)" \
  -F "subtitle=Employee travel rules and booking guidance" \
  -F "space_id=15" \
  --form-string "html_content=<h2>Travel Guidelines</h2><p>Updated content reflecting new policy...</p>" \
  -F "is_draft=0"
{
    "data": {
        "id": 1,
        "title": "Company Travel Policy (Updated)",
        "slug": "company-travel-policy",
        "space_id": 15,
        "external_id": 7891,
        "is_draft": false
    },
    "status": "success",
    "meta": {}
}

Pages created with an external_id can also be updated through PUT /v1/pages/by-external-id/{external_id}:

curl -X PUT https://api.workvivo.com/v1/pages/by-external-id/7891 \
  -H "Authorization: Bearer $WORKVIVO_TOKEN" \
  -H "Workvivo-Id: $WORKVIVO_ORG_ID" \
  -H "Accept: application/json" \
  -F "title=Company Travel Policy (Updated)" \
  -F "subtitle=Employee travel rules and booking guidance" \
  -F "space_id=15" \
  --form-string "html_content=<h2>Travel Guidelines</h2><p>Updated content reflecting new policy...</p>" \
  -F "is_draft=0"
{
    "data": {
        "id": 1,
        "title": "Company Travel Policy (Updated)",
        "slug": "company-travel-policy",
        "space_id": 15,
        "external_id": 7891,
        "is_draft": false,
        "permalink": "https://yourcompany.workvivo.com/pages/company-travel-policy"
    },
    "status": "success",
    "meta": {}
}

Delete a Page

Useful for rollback scenarios, removing duplicates or failed migration entries before re-running a batch.

curl -X DELETE https://api.workvivo.com/v1/pages/{page_id} \
  -H "Authorization: Bearer $WORKVIVO_TOKEN" \
  -H "Workvivo-Id: $WORKVIVO_ORG_ID" \
  -H "Accept: application/json"
{
    "data": {
        "id": 1,
        "external_id": null
    },
    "status": "success",
    "meta": {}
}

Pages created with an external_id can also be deleted through DELETE /v1/pages/by-external-id/{external_id}:

curl -X DELETE https://api.workvivo.com/v1/pages/by-external-id/7891 \
  -H "Authorization: Bearer $WORKVIVO_TOKEN" \
  -H "Workvivo-Id: $WORKVIVO_ORG_ID" \
  -H "Accept: application/json"
{
    "data": {
        "id": 1,
        "external_id": null
    },
    "status": "success",
    "meta": {}
}

JavaScript Integration Example

Create or update pages from a source-system export. Pass parent pages before their children so external_parent_id always refers to an existing Workvivo page.

const apiUrl = 'https://api.workvivo.com/v1';
const headers = {
    Authorization: `Bearer ${process.env.WORKVIVO_TOKEN}`,
    'Workvivo-Id': process.env.WORKVIVO_ORG_ID,
    Accept: 'application/json',
};

// Load and order the source pages in your migration system, then pass them
// to migratePages().

export async function migratePages(sourcePages) {
    for (const sourcePage of sourcePages) {
        await upsertPage({
            externalId: Number(sourcePage.id),
            externalParentId: sourcePage.parent_id
                ? Number(sourcePage.parent_id)
                : null,
            title: sourcePage.title,
            subtitle: sourcePage.subtitle ?? '',
            htmlContent: sourcePage.body_html,
            spaceId: sourcePage.workvivo_space_id,
        });
    }
}

export async function upsertPage(page) {
    const form = new FormData();
    form.set('title', page.title);
    form.set('subtitle', page.subtitle);
    form.set('html_content', page.htmlContent);
    form.set('space_id', page.spaceId);
    form.set('is_draft', '1');

    let response = await fetch(
        `${apiUrl}/pages/by-external-id/${page.externalId}`,
        { method: 'PUT', headers, body: form },
    );

    if (response.status === 404) {
        form.set('external_id', page.externalId);

        if (page.externalParentId) {
            form.set('external_parent_id', page.externalParentId);
        }

        response = await fetch(`${apiUrl}/pages`, {
            method: 'POST',
            headers,
            body: form,
        });
    }

    if (!response.ok) {
        throw new Error(`Page migration failed: ${response.status}`);
    }

    return response.json();
}