See Agility CMS in action. Watch a product demo
Legacy Content Migration
Migrating from a legacy system means turning unstructured or page-bound HTML into Agility's structured, reusable content models — not just lifting markup into a rich-text field. Agility's Content Management API, accessed through the official Management SDK (TypeScript/JavaScript, or the .NET Agility.Web SDK), lets you create and update content models, containers, content items, assets, and pages programmatically — making it the engine for a repeatable, scriptable migration. The recommended methodology is: model the target content first, parse and map the legacy HTML into those structured fields, then load it through the Management SDK with validation and idempotency so the migration can be run, checked, and re-run safely.
Why "lift-and-shift HTML" is the wrong target
Legacy CMS content is often a blob of HTML per page. Dropping that into a single rich-text field preserves the old limitations (no reuse, no multi-channel delivery, weak governance). The goal of migration is structure: headings, images, CTAs, authors, dates, categories, and components become discrete, typed fields and linked content items that can be reused across web, mobile, and other channels.
Methodology
1. Model the target first
Design the destination content models and components before moving anything (see the Architect content-modeling guidance). Decide which legacy patterns become:
- Content models (e.g. Article, Author, Category) with typed fields,
- Component models (reusable page building blocks),
- Linked content relationships (e.g. Article → Author),
- Assets (images/files extracted from the HTML).
2. Extract and parse the legacy HTML
Pull the source content and parse the HTML into structured pieces — typically with a Node script using an HTML parser (e.g. cheerio) to select headings, body, images, metadata, and links, and to strip presentational markup you no longer want.
import * as cheerio from "cheerio";
function parseLegacyArticle(html) {
const $ = cheerio.load(html);
return {
title: $("h1").first().text().trim(),
body: $(".article-body").html(), // keep clean inner HTML, or convert to Markdown
heroImageUrl: $(".hero img").attr("src"),
author: $(".byline .name").text().trim(),
publishDate: $("time").attr("datetime"),
};
}
3. Map fields and convert markup
Map each parsed piece to a target model field. Convert body HTML to the chosen storage format (clean HTML or Markdown), rewrite internal links, and collect referenced images for upload. Normalize dates, resolve categories/tags to linked items, and decide defaults for fields the legacy system never had.
4. Migrate assets
For each referenced image/file, upload it to the Agility instance and capture the returned asset URL/ID, then reference it from the content item. The Management API supports uploading media and retrieving an asset's ID by path.
5. Load through the Management SDK
Authenticate (OAuth 2.0; request the offline_access scope if you need a refresh token for a long-running migration — access tokens last 24h, refresh tokens 30 days), then create models/containers if needed and write content items. For a migration you'll typically use the bulk methods for throughput:
| Single-item method | Bulk method | Use case |
|---|---|---|
saveContentItem() | saveContentItems(contentItems[]) | Create/update many items in one call |
publishContent() | batchWorkflowContent(contentIDs[]) | Publish/unpublish many items at once |
Bulk methods take an array and return content IDs in the same order as the input, so you can correlate results back to your source records. Other operations include upload media, get media ID by path, approve / request-approval / decline, and managing containers, models, and pages. See the Management SDK — Content reference for the full function list and signatures.
import * as mgmtApi from "@agility/management-sdk";
const api = new mgmtApi.ApiClient(/* options incl. token */);
// Bulk create/update — pass ALL items in one call
await api.contentMethods.saveContentItems(
parsedArticles.map((parsed) => ({
contentID: -1, // -1 = create new
properties: { referenceName: "Articles", definitionName: "Article" },
fields: {
Title: parsed.title,
Body: parsed.body,
PublishDate: parsed.publishDate,
// Author: linked item reference, HeroImage: uploaded asset reference, ...
},
})),
guid,
locale
);
Note that the Content Sync API is for pulling content only — it is not a path for importing or loading content; bulk import is done with the Management SDK bulk methods above. Verify exact field formats (linked content, assets) against the SDK reference when implementing.
6. Make it idempotent and validated
- Idempotency: key each migrated item to its legacy ID and check-before-write so re-runs update rather than duplicate. Avoid redundant saves — every update creates a new version, and unnecessary versions can affect your plan's performance tier, so only write when content actually changed.
- Validation: run a dry run, then reconcile counts and spot-check rendered output. Migrate to staging first and publish only after review (the API supports publish/approve actions when you're ready).
- Throughput: batch and rate-limit to stay within API limits.
7. Cut over
Stand up redirects from legacy URLs (the Management API can save URL redirections), validate SEO-critical pages, then publish and switch DNS/front-end as planned.
Shared responsibilities
| Area | Agility | Implementation partner / customer |
|---|---|---|
| Management API + SDKs | ✅ Provides Content Management API, JS/.NET SDKs, auth | — |
| Target model design | ✅ Platform + guidance | ✅ Designs models/components to fit the content |
| HTML parsing & field mapping | — | ✅ Builds the extraction/transform scripts |
| Asset migration | ✅ Media upload endpoints | ✅ Extracts, uploads, and re-references assets |
| Idempotency, validation, throughput | ✅ Versioning + rate limits | ✅ Implements safe, re-runnable migration with checks |
| Redirects & cutover | ✅ URL-redirection API | ✅ Maps legacy→new URLs, validates, schedules cutover |