Skip to main content
On this page
Updated 6 min read

Migrating Content

Migrating Content to Adapto CMS

Native content import is currently in development. In the meantime, the Adapto CLI and an AI agent provide a reliable path for migrating structured content from other CMS platforms. The workflow covers articles, pages, custom collections, and media files.

How it works

Migration happens in three steps:

  1. Export — download your content from the source CMS in its native export format
  2. Parse and map — give the export file and the Adapto CLI spec to an AI agent; the agent reads your content structure and maps each field to its Adapto equivalent
  3. Insert — the agent issues adapto CLI commands to create content items one by one, pausing for your review at any point

Before you start

  • The Adapto CLI is installed and authenticated — see the CLI & AI Agents guide
  • You know which Adapto content type maps to your source content: articles for blog posts, pages for static pages, custom collections for structured data
  • Media files are migrated before content items, so that file URLs are available when creating articles or collection items

Step 1 — Export from your current CMS

Each platform exports content differently. The table below covers the export method and output format.

CMS

How to export

Output format

Note for the agent

Strapi

Run strapi export --no-encrypt --format dir in your Strapi project root

Directory of .jsonl files — one JSON object per line — split across entities/ and links/ folders

Point the agent at the entities/ folder. Relations live in links/ and may need to be resolved separately.

Contentful

Run contentful space export --space-id

SPACE_ID --management-token TOKEN

--export-dir ./export --download-assets

Single JSON file with top-level arrays: contentTypes, entries, assets, locales

Rich text fields are stored as a nested node tree (not HTML). The agent needs to convert them to HTML before inserting into Adapto.

Storyblok

Export per story via the Management API: GET https://mapi.storyblok.com/v1/spaces/:space_id/stories/:story_id/export.json — or use the Storyblok CLI to bulk-export

Flat key-value JSON where each key encodes a field path (uuid:component:field)

Rich text is decomposed into individual text nodes keyed by path. The agent must reassemble them into HTML before inserting.

Cosmic JS

In the Cosmic dashboard go to Bucket → Settings → Import / Export and click Export

Single bucket.json with three arrays: object_types, objects, media

Structure is straightforward. Field values are in each object's metadata or content keys depending on how the bucket was set up.

Sanity

Run sanity dataset export <dataset-name> export.ndjson in your Sanity project

NDJSON — one JSON document per line. Draft documents have IDs prefixed with drafts.

Rich text uses Portable Text (a nested block array). The agent needs to convert it to HTML. Asset documents are separate lines with _type: "sanity.imageAsset".

CSV / generic

Export from any spreadsheet or CMS that supports CSV

CSV with one row per content item

The first row should be headers matching field names. The agent maps column names to Adapto fields.

Step 2 — Prepare the agent

Before the agent can issue CLI commands, it needs two things: the Adapto CLI spec and your export file.

  1. Run adapto llm-info and include the output in your agent's system prompt or project instructions (see CLI & AI Agents).
  2. Provide the export file — or paste a representative sample if the file is large. Describe which content type you are migrating and what the target Adapto content type is.
  3. Tell the agent your project's active language code (e.g. en-US) and, for custom collections, the collection slug so it can look up the field schema with adapto collections get-by-slug <slug> --json.
  4. Ask the agent to process content in small batches (10–20 items) and pause for review before continuing, rather than inserting everything at once.

Step 3 — Map and insert content

Files and media

Upload media before creating content items. The agent uploads each file and captures the returned CDN URL, which is then used in subsequent article or collection item commands.

# Upload a file — returns the full file record including CDN URL
adapto files upload ./hero-image.jpg --json
# { "id": "file123", "url": "https://media.adaptocms.com/...", ... }

For large media sets, the agent can iterate over the export's asset list and upload each file, building a mapping from the original asset ID or URL to the new Adapto CDN URL for use in content fields.

Articles

Each article in the source CMS becomes an adapto articles create call. The agent maps the source fields to the required Adapto flags: --title, --slug, --content (HTML), --author, --language. Optional fields like --tags and --summary are included when present in the source data.

adapto articles create \
  --title "My Article" \
  --slug my-article \
  --content "<p>Body HTML here.</p>" \
  --author "Author Name" \
  --language en-US \
  --status draft \
  --json

Articles are created as drafts by default. After reviewing a batch, publish them individually or in bulk:

adapto articles publish ARTICLE_ID

Pages

Static pages follow the same pattern as articles but use adapto pages create. If the source CMS has a page hierarchy, the agent resolves parent pages first and passes the resulting Adapto page ID to --parent-id for child pages.

adapto pages create \
  --title "About Us" \
  --slug about \
  --content "<p>Page content.</p>" \
  --language en-US \
  --status draft \
  --json

Custom collections

The agent can derive a collection schema directly from the exported data — inferring field names, types, and whether they are required — and create the collection before inserting any items. This means no manual schema setup in the backoffice is needed.

# 1. Create the collection from the inferred schema
adapto collections create \
  --name "Team Members" \
  --slug team-members \
  --description "Migrated from source CMS" \
  --language en-US \
  --fields-json '[
    {"name":"role","label":"Role","type":"text","required":false,"multiple":false},
    {"name":"bio","label":"Bio","type":"rich_text","required":false,"multiple":false}
  ]' \
  --json

# 2. Insert items using the new collection's ID
adapto collections items create COLLECTION_ID \
  --title "Jane Doe" \
  --slug jane-doe \
  --language en-US \
  --status draft \
  --data-json '{"role":"Engineer","bio":"<p>Loves code.</p>"}' \
  --json

The agent inspects the full set of source records to determine field types before creating the collection, so that edge cases — a field that is numeric in most records but null in a few — are handled correctly.

Field mapping considerations

Rich text

Adapto stores rich text as HTML strings. Most source CMSes store rich text in a proprietary format — Contentful's Rich Text node tree, Sanity's Portable Text blocks, Storyblok's decomposed key-value structure. The agent converts these to clean HTML before passing them to --content or a rich_text collection field.

Multi-language content

Adapto models each language as a separate item linked by translation_of_id. When the source CMS uses a locale-per-field model (a single entry with values for each locale), the agent creates one Adapto item per language and links translations using adapto articles create-translation or the equivalent command for the content type.

Slugs

Slugs must be URL-safe and unique within a language. If the source CMS uses numeric IDs, UUIDs, or locale-prefixed slugs, the agent should clean them to plain kebab-case before inserting.

Media references in content

After uploading media (Step 3 above), the agent replaces original asset URLs embedded in HTML content with the new Adapto CDN URLs before creating articles or collection items.