Pluma
Docs index
docs/guides/migrate-from-files.md View markdown →

Migrate from repo files

This guide is for your agent. It's the "match": turning the content that lives today in repo files (JSON, markdown with frontmatter, config) into Pluma content types and entries, without losing anything and without breaking the site.

1. Find the content

Search the site's repo, in this order:

What to look for Examples In Pluma
Site config site-config.js, config.json, _data/site.yml A singleton content type (site_settings)
Collections src/content/blog/*.md, content/events/*.json One content type per collection
Frontmatter title, date, slug, tags, image One field per key
Markdown body Whatever comes after the frontmatter A rich_text field
Images public/images/…, src/assets/… Files, uploaded once and referenced

2. Propose the model, and show it before creating anything

Put together the list of types with their fields and show it to the site owner before creating anything. If the owner already told you to go ahead and publish ("I trust you"), that's the approval: say what you're about to create and continue. Mapping rules:

  • One-line text → symbol. Long plain text → text. Markdown → rich_text.
  • Dates → date. Numbers → integer or number. true/false → boolean.
  • Lists of text (tags, categories) → tags.
  • The URL identifier → slug, required.
  • One image → asset; several → assets.
  • Something that points to other content (author, category with its own page) → reference, with link_content_types.
  • If a key appears only in some files, the field is not required.
  • Field names: the same as in the frontmatter, in camelCase. That keeps the change in the site's code to a minimum.
  • The type's API ID: singular and in snake_case: blog_post, event, site_settings (the blog/ collection becomes the blog_post type).
  • Rows of the same small shape (a menu, opening hours, social links) → a list field with its item fields, not json: the owner edits them row by row. Keep json for data nobody edits by hand.
  • Dates: send them as they are in the files. A date without a time (2026-08-14) stays without a time; one with a time zone (-06:00) keeps the zone. Don't convert them to UTC. Within the same field, always use the same format.
  • Author without their own page: text (symbol). With their own page: their own type and a reference.
  • Links between pages inside markdown ([our rye](/menu/rye-seeded)): rewrite them as entry:ID links once the target exists, so they don't break when a slug changes.
  • Menu and social links from the config: a list field inside the singleton. If each item needs its own page or photo, a type of its own (menu_item) with references is better.
  • Site config: a type with "singleton": true; Pluma rejects a second entry of that type.

3. Create the types

With dry_run=true first, then for real. See Management API. Read your site's doc (GET /api/v1/spaces/:space_id/llms.txt) to confirm it came out the way you wanted.

4. Import

  • Images first (POST /assets), with a title and alt text. Keep the mapping file path → asset id.
  • Then the entries, replacing image paths with asset ids: in asset fields goes the id; inside markdown, ![alt](asset:ID).
  • References need the entry they point to to exist: import first what others reference (authors before posts).
  • Use the batch for entries: up to 100 operations per request, all or nothing, and with "$ref" a post points to the author created in the same batch. The pilot (73 entries, 66 images) took 212 one-at-a-time calls; with batches it's just a few.
  • Everything starts as a draft. Publish only after the owner has reviewed it.

5. Connect the site

  • Create a delivery key (see Agents → Keys for the site) and ask the owner to store it in the host's environment variables.
  • Request the body with rich_text=html (or markdown): images already come with their URL. If your framework renders markdown, markdown changes less code.
  • In the site's code, change a single place (the one that reads the files) so it reads from the Delivery API, with the files as a fallback while the trial lasts. Recipes in Frameworks.
  • Do it on a branch and open a PR. Never straight to main. If the site isn't in git, edit the file and describe the change in the report.

6. Report

Leave the owner a summary: types created, entries imported per type, files uploaded, and everything you couldn't map, with the file and the reason.