WordPress Migration
The migration workflow and helper plugin are included in the open-source Typeroll distribution. Typeroll Cloud also offers migration as an operated, supported service.
The import itself is a deterministic workflow with no AI rewriting. Plan, map, review and fix a migration with your own AI agent (Claude Code, Codex or another MCP client) through MCP. The built-in AI assistant in the portal has no migration tools.
Start with Choose your migration approach: choose preserve or renew for text and design/images independently. Every combination uses standard blocks first, custom block types when needed and rare documented HTML exceptions. Every old public URL must keep working or redirect to relevant content.
Start in a new agent conversation
Section titled “Start in a new agent conversation”Copy this prompt and replace [website URL] with your current website:
Help me migrate [website URL] to Typeroll CMS using https://github.com/Typeroll/site-starter. Read its AGENTS.md and guide me through the migration interview before starting work.The agent helps you set up your own workspace and follows the starter’s interview. You choose separately whether to preserve or renew the text and the design/images. The detailed prompts, decisions and routines stay in your workspace, ready for future sessions. If your agent cannot read GitHub, use the workspace setup guide to provide the files. Connect Typeroll MCP and confirm the target Site and Version before making changes; verified Organization storage is required before importing.
Overview
Section titled “Overview”The shared-taxonomy and fidelity-review changes described below require Core 0.2.5 and Helper 0.3.2. Cloud availability follows its Core rollout; self-hosted installations must update Core and the helper plugin.
Core 0.2.88 preserves publication/update instants from WordPress REST’s
date_gmt and modified_gmt, adding Z when those UTC fields omit it. Helper
0.3.3 explicitly labels its GMT dates with Z; Core also understands earlier
Helper exports. A local timestamp without a known timezone cannot establish an
instant: only its calendar date is retained when no valid GMT field is available.
Existing imports are not overwritten. Repair their dates from the original GMT
fields or another timestamp with an explicit offset, then publish; do not append
Z to an old local clock value. See publication dates.
The migration workflow imports accessible WordPress content (pages, posts and supported custom post types) into a new Typeroll site, transfers your media directly to your organization’s Cloudflare R2 storage, and generates redirects from old URLs to new slugs.
What it does:
- Imports pages and posts as Typeroll pages
- Transfers media (images) through the media API — only images actually used on imported pages
- Maps custom post types to Typeroll content types
- Generates URL redirects
- Imports categories and tags once as Pages with their own Content types; articles store references
- Preserves cleaned HTML without automatic AI rewriting or HTML-to-blocks conversion
- Leaves imported content in Review before going live
What it doesn’t do:
- Import your old WordPress theme or CSS (you build a new design in Typeroll)
- Import plugins or plugin-specific data that isn’t content
- Touch the source WordPress site in any way
Before you start
Section titled “Before you start”-
Build the target site’s design
Record the text decision separately from the design/image decision. Preserve wording when requested even when deliberately redesigning. Import does not automatically reproduce a WordPress theme. Configure shared presentation separately:
- Colour palette and fonts (
update_site_settings) - Header partial with navigation
- Footer partial
- Reusable article and archive Page templates, plus a home page
- Source screenshots at desktop and mobile widths for comparison
- Colour palette and fonts (
-
Check that the WP REST API is accessible
The migration needs
https://yoursite.com/wp-json/wp/v2/to be publicly accessible. Test it in your browser — you should see a JSON response.If the REST API is disabled or restricted, install the helper plugin which provides a separate authenticated export endpoint. Your administrator must allow access to that endpoint.
-
Optional: install the helper plugin
The helper plugin gives the AI agent access to custom post types, ACF fields and navigation menus that may not be exposed via the standard REST API.
Running the migration
Section titled “Running the migration”Start from your workspace
Section titled “Start from your workspace”The workspace starter owns
the detailed migration prompts and routines. Open workflows/migration.md there; if you want guidance,
the agent starts or resumes workflows/interview.md and saves progress in
brief/interview.md. The interview asks about text and design/images separately.
For example, retaining all text while refreshing the design is a supported choice.
Record both choices, source URLs, target Site/Version and exceptions in
brief/migration.md, with owner rules and SEO strategy in their own brief files.
Configure any WordPress Helper key through the agent’s secure credential mechanism,
never in the prompt or repository. Documentation provides the short starting pointer; detailed prompts
remain in the starter so you can adapt them in your own repository.
The AI agent will:
- Call
get_import_readinessand resolve any storage requirements, then verify the WP REST API connection - Build an inventory of all URLs on the site
- Import shared categories and tags, including archive URLs, hierarchy and available custom fields
- Import pages and posts with taxonomy references and cleaned HTML, transferring referenced images along the way; custom post types become Content types
- Rebuild the imported material as editable blocks and shared templates, after verifying representative prototypes
- Generate redirects and show coverage, reconstruction and review results separately
Server-side migration workflow
Section titled “Server-side migration workflow”The portal can also run the migration on the server: New site → Migrate from
WordPress creates a Site and starts it, and an existing Site runs it from
Workflows. The same workflow is available to agents and scripts. With an
organization API key, create_site_and_migrate (POST /api/v1/sites/create-and-migrate) creates the Site and starts the run;
start_workflow with type: "migration" runs it on an existing Site (write
permission). Poll get_workflow and approve its review step with
approve_workflow once you have checked it. Own storage must be verified
first in both the portal and the API. See
Organization, access and workflow tools.
The workflow reads only the source site, and the source must be reachable on
the public internet. The WordPress URL and every redirect it follows must
resolve to public addresses; private, loopback and link-local addresses are
refused. A self-hosted server can import an intranet WordPress by setting
TYPEROLL_ALLOW_PRIVATE_NETWORK_FETCH=1; see Self-hosting.
When an item’s REST body is nearly empty, the importer renders its page only if
the item’s link belongs to the source site (same host, ignoring www.). Other
links are logged and not fetched.
Password-protected posts and pages are not imported. WordPress omits their
content, so importing them would publish an empty page or the password form.
The workflow logs each one and lists it under protected_skipped in the import
step’s result. Recreate the content deliberately or redirect its old URL before
launch.
Reviewing the migration
Section titled “Reviewing the migration”After the content is imported, the AI agent shows a coverage report:
Migration complete. Here's the coverage:
✅ 24 pages migrated↪ 8 redirects created⬜ 12 URLs excluded (pagination, feeds, admin)⚠️ 3 URLs unhandled:
/category/projekt → needs a redirect or category page /tag/stockholm → needs its tag Page and membership listing /wp-content/uploads/2019/hero.pdf → file, needs manual handlingFor each unhandled URL, decide: add a redirect, create a page, or exclude it. Category and tag archives are content; do not routinely discard them.
Shared categories and repeat imports
Section titled “Shared categories and repeat imports”Each WordPress taxonomy becomes a Content type such as wp_taxonomy_category.
A category is a Page whose title is the category name; its custom fields can hold
an icon or emoji. Articles have a page_ref_list field pointing to those Pages.
Edit the category once in Pages, filtered by its Content type. On an article,
select category references instead of editing copied names or icons. Archive
listings use reverse references so membership stays consistent.
Tag Pages work the same way: an article can reference several tags, and each tag Page lists the articles referencing it. Category and tag Pages have editable block bodies, so you can add a unique introduction, images and other content alongside the generated listing. They can also use shared Page templates.
A Page has one parent in the site tree, even when it has several categories or tags. That parent supplies breadcrumb ancestry. URLs are separate: the Content type’s route template or a Page’s explicit path determines its address. Migration preserves the old paths; nesting an article under a category must not silently change its URL. Tags do not change an article’s parent or address.
The importer preserves term parents, archive URLs, page parents and manual order. An explicit primary category from the helper plugin is used for breadcrumbs; a single assigned category can also be used. With multiple categories and no primary choice, review the breadcrumb hierarchy instead of guessing a category.
Retries use stable source IDs and preserve existing imported Pages, including subsequent edits. They do not refresh existing content from WordPress. A different Page at an imported URL, missing term, or incompatible schema stops the import with an error. Resolve the mapping before retrying. Older flattened imports need a separately reviewed repair; rerunning the importer does not rewrite them.
Verify presentation and behavior
Section titled “Verify presentation and behavior”Compare a representative article sample, an archive and the home page against the source on desktop and mobile. Check heading sizes, text width, spacing, images, cards, breadcrumbs, table of contents and responsive behavior. Keep the table of contents as a generated block in a Page template, never a copied field on each article. Test forms and Extensions separately; a rendered shell does not prove that the integration works.
From imported HTML to editable blocks
Section titled “From imported HTML to editable blocks”HTML is the managed importer’s intermediate format, not the intended final result of a block-based migration. Your external AI agent reads that content and rebuilds it with the ordinary MCP/API block and template tools. You do not need to convert every Page manually in the editor. An agent can also create block Pages directly from the WordPress source, without staging an HTML Page first.
Typeroll has no automatic HTML-to-blocks converter. The old editor conversion preview was removed in Core 0.2.58. Switching a Page to block mode does not convert its HTML; on a newly imported Page there are no blocks to display yet. The agent must preserve the complete source, build the block tree, save and read it back, and review the result before publication. Work on the selected Version and keep incomplete Pages out of publication. A reviewed prototype establishes the reusable mapping before the agent applies it to the remaining Pages.
Conversion notes are included in the workflow review. Use an HTML block only for an uncommon source-specific exception; recurring requirements belong in reusable blocks or templates. Missing native features must be resolved before converting the remaining pages. Source styles and interactive scripts are not ported automatically.
Scripts, analytics and cookie consent
Section titled “Scripts, analytics and cookie consent”JavaScript from the WordPress site is not copied. Plugins and themes add their
own scripts, such as jQuery, sliders, form and cookie plugins, page builders and
calls to admin-ajax.php. On the new site they would load from, or send data
to, the old site. What they did is rebuilt with blocks, Forms and site settings,
or reported as a missing feature.
Analytics and advertising tags are the exception. They are carried over by ID, not as pasted code:
- Open Settings → Apps → Integrations and enter the IDs the old site used, for example the Google Analytics 4 measurement ID or the Meta Pixel ID.
- In Settings, turn on Enable cookie consent on this site, with your own text and a link to the privacy policy. Analytics and advertising tags then wait until the visitor accepts. The old site’s cookie plugin is not copied.
- A tag that the Integrations list doesn’t cover goes in the cookie banner’s optional scripts, never in the site-wide head or body scripts.
The launch report (get_migration_launch_report) is not ready to launch while
the site carries scripts from the old platform, tracking code outside the
consent gate, or analytics and advertising integrations with the cookie banner
off. Its scripts section lists each finding and where it is.
The launch report requires current URL/SEO evidence and recorded fidelity
review. Use record_migration_seo_acceptance with fidelity.desktop, mobile,
shared_data and integrations plus an evidence description. These are review
results, not automated screenshot tests. A successful HTTP request alone does
not show that the migration is correct.
Sites imported with an older Typeroll version may have WordPress entities or
markup left in titles and SEO text. Ask the AI agent to run
repair_migration_plain_text as a dry run and show every proposed change. The
repair is restricted to plain-text fields and leaves bodies, slugs, paths, and
URLs alone. Approve the write only after reviewing the returned diffs; records
with existing working copies are skipped rather than overwritten.
Publishing
Section titled “Publishing”All imported pages start as status: "review". When you’re satisfied:
Publish all the imported pages and deploy.Or review page by page and publish individually.
Helper plugin
Section titled “Helper plugin”The Typeroll Helper Plugin gives the migration workflow access to:
- Custom post types with
show_in_rest: false - ACF fields with image references normalised
- Navigation menus
- Builder JSON (Elementor, Breakdance)
- Complete canonical URL list
- Shared taxonomy terms, archive URLs, parents, term custom fields and primary categories
Installing:
- Download the
wp-helper-plugindirectory from the Typeroll repository and create a ZIP archive from it. - In WordPress: Plugins → Add New → Upload Plugin
- Activate the plugin
- Go to Settings → Typeroll Helper and generate an API key
- Give the agent the site URL and configure the key through its secure credential mechanism. Keep the key out of shared prompts and documentation.
Helper 0.4.0 and later accept the key only in the X-Typeroll-Key request
header, never as a ?key= URL parameter. The plugin exports published items of
public post types only; orders, coupons, form submissions and other non-public
plugin data are never exposed. Password-protected posts are listed with their
title and URL but without content or custom fields; the importer skips them (see
Server-side migration workflow). Deleting the plugin
in WordPress removes its stored key.
Large image libraries
Section titled “Large image libraries”Image transfers use a persistent lookup for each source URL, so repeated imports reuse verified media. Transfers overlap within limits for memory and each source host; temporary throttling slows requests down. A failed required file stops content import with an error; retry after resolving source or storage access. Verified files already transferred are reused. Keep the WordPress source available until all required media and references are verified.
With Core 0.1.93 and an updated shared build engine, imported originals also queue private background image preparation. Media shows progress. The Organization’s selected build provider performs encoding; the CMS does not publish draft images. See large libraries and first publication for setup, limits and recovery.
Direct transfers and recovery
Section titled “Direct transfers and recovery”With Core 0.1.97, Typeroll sets up a small transfer Worker in your organization’s connected Cloudflare account. It downloads originals from WordPress directly into your private R2 bucket and verifies the copied bytes before Typeroll updates references. File bodies do not pass through the Typeroll Cloud portal or the MCP server. This is independent of whether GitHub or Cloudflare builds your site.
The Media storage card shows transfer setup status. If an existing Cloudflare connection needs an additional permission, select Allow media transfers in Cloudflare, approve, and return. Your R2 keys remain in place. Advanced token connections need Workers Scripts: Edit as well as the documented R2 access.
Progress is saved per source file. Interrupted copies retain their media identity and can be retried from Publishing without duplicating completed imports. Typeroll preserves source URLs until verified copies are ready. Keep the source site available until the import and reference checks finish. Originals can be up to 25 MB each; responsive variants are prepared separately by the selected build provider.
The same storage prerequisite applies to self-hosted installations using these import tools. Configuring the operator’s draft bucket does not satisfy it: connect the organization’s own storage in Publishing.
Verify the design before importing in bulk
Section titled “Verify the design before importing in bulk”Ask the agent to read the shared tr-migration-evidence skill before applying
one template to many Pages. It starts with the original site’s fonts, colors,
header, hero, card grids and article/sidebar geometry. A partially migrated
Typeroll site is not the authoritative design reference. Use the source for preserved design; use approved prototypes for renewed design.
Review text separately against its own preserve/renew choice; ask when either
choice is missing before dependent writes.
First verify one saved prototype for each active layout: home, archive, long and short articles, downloads and any special template overrides. Compare matching viewports and screenshots, including lower-page content, mobile menus, scrolled contents lists, anchor landings and long links. Read saved block fields back and measure the output; a field that is stored but ignored is not working support. Missing native capability must be reported before bulk conversion.
The evidence record identifies source and target URLs, publication, actual screen width, screenshot state, expected/actual measurements, reviewer and intentional changes. Keep PASS, FAIL and UNVERIFIED separate for routing, visual quality, content, files and interactions. HTTP 200, all-native blocks and no horizontal scrolling do not prove visual fidelity. Responsive video that fixes source overflow is a useful deliberate change; losing a brand panel or changing a four-card grid without a reason is a regression. Updated templates or themes require a fresh comparison. Fidelity checkboxes record review, not automated screenshot analysis.
Check PDF/download links and historical image URLs separately from successful file transfer. Use redirects only for relevant, reviewed destination mappings; a source 404 is not permission to discard a URL. Classify contact forms, lead flows, affiliate links and partner embeds separately for each market. Test conversions only with authorized synthetic data and safe recipients. Delivery that cannot be tested remains unverified. Import completion, verified preview, publication and domain cutover are distinct milestones.
Visual coverage is separate from publication
Section titled “Visual coverage is separate from publication”Before conversion, inventory all source routes and whole-page regions, including
archive pagination, headers, footers and heroes outside the article’s main.
Compare every in-scope route at mobile and desktop, then check each distinct
component and state at its actual breakpoints. An open menu, unique embed or
footer omitted from the comparison remains unverified.
Keep source/target screenshots, content and template revisions, the reviewer and
separate results for appearance, content, link membership, link order, keyboard
behavior and delivery. Shared template or theme changes invalidate dependent
comparisons. Technical publication success does not constitute visual approval.
Intentional changes need explicit before/after evidence and an accepted benefit.
The agent skill tr-migration-evidence is the shared contract for WordPress, URL,
Astro and multisite work; its coverage starts unverified and requires no uncovered
surfaces or unexplained regressions before visual signoff.
Preserve media positions when converting HTML
Section titled “Preserve media positions when converting HTML”Import does not turn that HTML into blocks. The agent must preserve media order while reconstructing the content with block tools; changing the Page mode is not a conversion.
Work from the raw source and keep the original response for QA. Parsing and serializing malformed WordPress markup before reconstruction can move images across heading boundaries. Inspect the browser’s actual source layout as well as the markup. Restore lazy images before deciding that media is missing.
Rebuild image-only heading wrappers as native image blocks in their original body position, rather than empty headings or image entries in the generated table of contents. Preserve meaningful heading IDs and avoid duplicating noscript image fallbacks. These are checks the agent must perform, not automatic conversion features.
Compare the ordered sequence of headings, text, media and CTAs, not just counts or image presence. Include malformed heading wrappers, lazy images, related-content boundaries and images after several sections. For an already migrated site, repair affected block positions against the raw source and review the generated page; do not reimport over subsequent edits.
Preserve banners and missing-image states
Section titled “Preserve banners and missing-image states”An address/area banner that continues to a quote page is separate from the final submission integration. Migrate its inputs and service links even if the final provider is deferred. Navigation inputs and tabs can carry explicitly accepted defaults within the same browser tab and origin, without adding addresses to URLs or creating a submission. Test collapsed/open mobile states, switching services, keyboard navigation and storage denial.
Also compare cards with missing thumbnails and the footer’s actual link-row spacing. Configure shared card placeholders and compact navigation settings; do not add fake featured images or shrink text to compensate for a tall link box. Verify a mixed row of real and missing thumbnails, wrapped footer links, and both preview and static output.
Preserve originals before retiring WordPress
Section titled “Preserve originals before retiring WordPress”The starter’s workflows/source-media.md separates unused-source archiving from
CMS import. Inventory drafts, old attachments and print originals as well as
published-page references. Agree a private destination, verify copied bytes and
a restore, and retain a manifest before approving old-host retirement. An object
ETag is not universally an MD5 checksum. Archiving does not preserve public URLs;
verify those independently through the migration inventory.
From MCP 0.45.80, scripts/typeroll/prepare_image.py provides optional local
ICC-aware conversion to sRGB JPEG (Pillow/ImageCms required). It keeps the
original untouched and does not upload or edit content. Missing CMYK profiles
must be resolved instead of guessed; transparent or multi-page sources require
a reviewed separate workflow. Compare colour and framing visually before using
the derivative. Keep source files and reports in the starter’s ignored private
folders. See the generated script README for commands and limits.