Migrations
Learn how to migrate your content from other CMS platforms to TentoCMS using the built-in migration tools.
Overview
TentoCMS provides a powerful migration framework that allows you to import content from other headless CMS platforms. The migration process handles:
- Schema Discovery - Automatically detects page types, collections, and components
- Content Import - Imports all pages, collection items, and their content
- Media Migration - Downloads and imports all media assets
- Field Mapping - Intelligently maps field types between platforms
Supported Platforms
- ButterCMS - Full support for page types, collections, and media
Migration Modes
TentoCMS offers four migration modes to suit different needs:
| Mode | Description | Use Case |
|---|---|---|
| Dry Run | Simulates the migration without making changes | Testing and validation |
| Schema Only | Imports only page types, collections, and components | Setting up structure first |
| Full | Imports everything (schema + content + media) | Complete migration |
| Incremental | Currently behaves identically to Full — see the note under Incremental below | Not yet a true ongoing-sync tool |
Before You Start
Prerequisites
- API Credentials - Obtain API key from your source CMS
- Developer Role - Migrations require
developerrole or higher - Backup - Always back up your existing data before migrating
Planning Your Migration
- Review source content - Understand what you're migrating
- Choose mode - Start with "Dry Run" to preview
- Schedule downtime - Large migrations can take time
- Test first - Use a staging environment for initial tests
Using the Migration Wizard
Step 1: Access Migrations
- Log in to the TentoCMS admin dashboard
- Navigate to Settings → Migrations
- Click New Migration
Step 2: Select Source Platform
Choose your source CMS platform. ButterCMS is currently the only supported source.
Step 3: Enter API Credentials
For ButterCMS:
- API Key - Your ButterCMS API token
- Found in ButterCMS dashboard under Settings → API Keys
- Example:
12345abcdef67890ghijklmn
- API URL (Optional)
- Leave blank to use default:
https://api.buttercms.com/v2 - Only change if using a custom endpoint
- Leave blank to use default:
Security Note: API keys are currently stored as plaintext in the database (not encrypted), and are returned unredacted to any teammate with developer role or higher who lists or views a migration in your project. Do not use a key that grants more than read access to your source CMS, and delete the migration once it's no longer needed to remove the stored key. See Is my API key secure? below.
Step 4: Validate Connection
The wizard automatically validates your credentials by:
- Testing API connectivity
- Verifying authentication
- Checking permissions
If validation fails:
- ✗ Invalid API key - Check your API token
- ✗ Connection error - Verify network connectivity
- ✗ Permission denied - Ensure API key has read access
Step 5: Choose Migration Mode
Select the appropriate mode for your needs:
Dry Run (Recommended First)
- What it does: Analyzes source content without importing
- Output: Shows what would be imported
- Time: Fast (under 1 minute)
- Use when: First-time migration, testing
Schema Only
- What it does: Imports page types, collections, components
- Output: Creates content structure only
- Time: Medium (2-5 minutes)
- Use when: Setting up structure before content
Full Migration
- What it does: Imports everything (schema + content + media)
- Output: Complete import of all data
- Time: Varies (5 minutes to several hours)
- Use when: One-time complete migration
Incremental
- What it actually does today: exactly the same as Full — a complete, from-scratch re-import of schema, content, and media. There is no diffing against a previous migration and no "only new/updated" filtering implemented yet, despite the mode existing as a selectable option.
- Output: A full duplicate import, same as Full
- Time: Same as Full
- Use when: Don't rely on this mode for ongoing sync yet — running it against a source you've already migrated will create duplicate pages/collection items (see Duplicates below)
Step 6: Review & Start
Review the migration summary:
- Source Platform: ButterCMS (or other)
- Mode: Selected mode
- Estimated Items: Approximate count
Click Start Migration to begin.
Step 7: Monitor Progress
The migration runs in the background and shows real-time progress:
Progress Indicators:
- Page Types: X of Y imported
- Components: X of Y imported
- Collections: X of Y imported
- Pages: X of Y imported
- Collection Items: X of Y imported
- Media: X of Y imported
Status Updates:
- Mapping - Analyzing source schema
- Validating - Checking compatibility
- Importing - Transferring data
- Completed - Migration finished
- Failed - Error occurred (see logs)
You can:
- Pause - Temporarily stop the migration
- View Logs - See detailed activity
- Leave Page - Migration continues in background
Step 8: Review Results
Once completed, review the migration summary:
Success Indicators:
- ✓ All items imported successfully
- ✓ No errors in logs
- ✓ Content visible in admin
Verify:
- Check page types in Settings → Page Types
- Browse imported pages in Pages
- Review collections in Collections
- Verify media in Media Library
Migration Logs
Viewing Logs
- Go to Settings → Migrations
- Click on a migration to view details
- Scroll to Migration Logs section
Log Levels
Logs are categorized by severity — there are three levels (no separate "debug" level):
- 🔴 Error - Failed operations (review these first)
- ⚠️ Warning - Potential issues (may need attention)
- ℹ️ Info - General progress updates, including individual discovered items
Filtering Logs
Use the log filter dropdown:
- All Logs - Show everything
- Errors Only - Show only failures
- Warnings - Show warnings and errors
- Info - Show info and above
Troubleshooting
Common Issues
"Invalid API key"
Problem: Authentication failed Solution:
- Verify API key is correct (no extra spaces)
- Check key has not expired
- Ensure key has read permissions
"Connection timeout"
Problem: Network connectivity issue Solution:
- Check internet connection
- Verify firewall settings
- Try again in a few minutes
"Schema validation failed"
Problem: Incompatible field types Solution:
- Review error logs for specific fields
- Use "Schema Only" mode first
- Manually adjust field types after import
"Media import failed"
Problem: Unable to download media Solution:
- Check source media URLs are accessible
- Verify sufficient storage space
- Review logs for specific files
"Migration stuck in 'Importing' status"
Problem: Migration appears frozen Solution:
- Check browser console for errors
- Refresh page (migration continues in background)
- Review logs for last activity
- Contact support if stuck for >1 hour
Getting Help
If you encounter issues:
- Review Logs - Check migration logs for errors
- Check Status - Verify migration status
- Try Dry Run - Test with dry run mode first
- Contact Support - Provide migration ID and logs
Best Practices
Before Migration
✅ DO:
- Run a dry run first
- Back up existing data
- Test in staging environment
- Schedule during low-traffic periods
- Communicate with your team
❌ DON'T:
- Migrate directly to production without testing
- Skip dry run mode
- Ignore validation warnings
- Run multiple migrations simultaneously
During Migration
✅ DO:
- Monitor progress regularly
- Review logs for errors
- Keep browser tab open (optional)
- Document any manual adjustments needed
❌ DON'T:
- Delete source content yet
- Make manual changes during migration
- Start another migration
- Close browser (migration continues, but you lose visibility)
After Migration
✅ DO:
- Verify all content imported
- Test page rendering
- Check media assets load
- Update any hardcoded references
- Clean up temporary data
❌ DON'T:
- Immediately delete source CMS data
- Skip verification steps
- Ignore warning logs
Advanced Topics
Field Type Mapping
TentoCMS automatically maps field types:
| ButterCMS | TentoCMS |
|---|---|
| text | text |
| textarea | textarea |
| wysiwyg | wysiwyg |
| number | number |
| boolean | boolean |
| date | date |
| datetime | datetime |
| select | select |
| radio | radio |
| checkbox | checkbox |
| image | image |
| file | file |
| media | image |
| repeater | repeater (a straight passthrough — sub-fields are inferred and mapped recursively, it is not converted to nestedComponent) |
| relationship | reference |
Two additional field types are inferred (not read from an explicit ButterCMS type) and mapped separately during schema import:
| Inferred source shape | TentoCMS field type |
|---|---|
A single embedded object without a {type, fields} wrapper (ButterCMS "single component" convention) | nestedComponent |
An array of {type, fields} objects (a component picker) | componentPicker |
In practice, ButterCMS doesn't send explicit field-type metadata at all — TentoCMS infers each field's type from the shape of a sample value. That inference logic never produces select, radio, checkbox, datetime, or file as a guessed type; those rows in the table above only matter if a value already arrives pre-labelled with one of those exact strings, which doesn't happen for ButterCMS. In practice, a select/radio/checkbox-style short string comes through as text, and a file upload comes through as image (via the CDN-URL/{url, file_name} shape checks) or text if it isn't a recognised media shape. Plain date-like strings (YYYY-MM-DD...) are correctly inferred as date.
Component Naming Convention (ButterCMS)
When migrating from ButterCMS, component types are converted from snake_case to kebab-case to match TentoCMS slug conventions:
| ButterCMS Type | TentoCMS Component Slug |
|---|---|
hero | hero |
cta_banner | cta-banner |
quote_section | quote-section |
text_block | text-block |
This applies to both component slugs and the componentSlug option on nested component fields in page type schemas. In the public API response, the component type appears as _type in kebab-case. If your codebase references component types by name (e.g. in frontend templates), update them from snake_case to kebab-case after migration.
Important API difference: ButterCMS returns the component name under the type property in snake_case. TentoCMS returns it under the _type property in kebab-case. Update your frontend templates accordingly:
// ButterCMS
component.type // "cta_banner"
// TentoCMS
component._type // "cta-banner"
Handling Complex Fields
Repeater Fields:
- Mapped straight through to TentoCMS's own
repeaterfield type (not converted to a nested component) - Sub-field structure preserved and mapped recursively
- May require manual field adjustments
Single Embedded Components:
- A single embedded object (ButterCMS's convention for one inline component, with no picker) is mapped to a
nestedComponentfield - An array of components (a picker where editors add/remove/reorder) is mapped to a
componentPickerfield
Rich Text Content:
- HTML is preserved
- Images embedded in content may need URL updates
- Review after import
References:
- Relationship fields mapped to references
- Links may need manual verification
Rate Limiting
- Delay: A defensive 100ms pause between every ButterCMS API request
- Pagination: Page types and collections are fetched at up to 100 items per page; blog posts are fetched at 10 items per page (a ButterCMS API limit)
- Media batching: media files are migrated into TentoCMS in batches of 50 per background step (unrelated to ButterCMS's own pagination — this is how TentoCMS fits large media libraries inside the Cloudflare Worker's execution-time limit)
- Retries: None. If a step fails, the whole migration is marked
failedand is not retried — TentoCMS's queue-based migration pipeline is deliberately "acks-and-never-retries" because re-running a step would re-create content that already succeeded, producing duplicates. If a migration fails partway through, create a new migration to try again.
Large migrations (>10,000 items) may take several hours.
Migration Scheduling
For large migrations:
- Plan timing - Schedule during off-peak hours
- Split migration - Use "Schema Only" first to set up structure, then a "Full" import for content — see the note on "Incremental" mode below before relying on it for a two-stage import
- Monitor resources - Watch database and storage usage
- Set expectations - Communicate timeline to stakeholders
API-Based Migrations
For programmatic migrations, use the Migration API:
# Create migration
curl -X POST https://tento-api.intelligentlending.co.uk/api/v1/admin/migrations \
-H "Cookie: cms_session=YOUR_SESSION" \
-H "Content-Type: application/json" \
-d '{
"sourcePlatform": "buttercms",
"mode": "full",
"config": {
"apiKey": "YOUR_BUTTERCMS_API_KEY"
}
}'
# Start migration
curl -X POST https://tento-api.intelligentlending.co.uk/api/v1/admin/migrations/{id}/start \
-H "Cookie: cms_session=YOUR_SESSION"
# Check status
curl https://tento-api.intelligentlending.co.uk/api/v1/admin/migrations/{id} \
-H "Cookie: cms_session=YOUR_SESSION"
See Migration API Documentation for full details.
FAQ
How long does a migration take?
Dry Run: Under 1 minute Schema Only: 2-5 minutes Full Migration:
- Small (<1,000 items): 5-15 minutes
- Medium (1,000-10,000 items): 30 minutes - 2 hours
- Large (>10,000 items): 2-12 hours
Can I pause a migration?
Yes, click Pause in the migration details page. Note:
- Current batch will complete before pausing
- A paused migration cannot be resumed or restarted — you must create a new migration to import again
What happens if migration fails?
- Changes are NOT rolled back automatically — content already imported before the failure stays in TentoCMS
- Failed items are logged in migration logs
- A failed migration cannot be restarted — starting a migration only works from
pendingstatus, so you must create a new migration to import again - Re-running a new migration against the same source does not dedupe against what the failed run already created — see "Duplicates and re-runs" below
The "Edit & Retry" button doesn't resume the failed job. It's available from the migrations list for failed jobs (and completed dry runs), and it opens the new-migration wizard pre-filled with the same platform, mode, and connection config as the original job — letting you tweak something (like a bad API key) before trying again. Submitting that wizard creates a brand-new migration with its own ID; the original failed job is left as-is in the list, not updated or removed.
Duplicates and re-runs
There is currently no content deduplication. Pages and collection items are always created fresh — TentoCMS does not look up an existing item by slug/ID and update it instead. Running a migration twice against the same source (whether that's retrying after a failure, or running "Incremental" after a "Full") will create a second, duplicate set of pages and collection items.
The exceptions are:
- Media is deduplicated by file content hash — re-migrating the same file reuses the existing asset instead of duplicating it.
- Blog authors are deduplicated by email/slug/name, so re-running a migration with blog content won't create duplicate author accounts (though it will still create duplicate posts).
If you need to re-run a migration, delete the pages/collection items/blog posts it created first, or only ever run a given source migration once per project.
Will this overwrite existing content?
- Dry Run: No changes made
- Schema Only: Creates new schemas; running it again against an already-migrated source will attempt to create the same schemas again (subject to normal slug-uniqueness rules)
- Full/Incremental: Both always create new pages and collection items — neither mode matches by slug to update existing content, and nothing is ever deleted. See "Duplicates and re-runs" above before running either mode more than once against the same source.
Can I undo a migration?
No automatic undo. Best practices:
- Always run dry run first
- Test in staging environment
- Back up before migrating
- Use soft delete if available
Is my API key secure?
No — today it is not:
- API keys (and any other
configvalues, such aspassword) are stored as plaintext in the database — not encrypted - Returned unredacted to any teammate with
developerrole or higher who lists or views a migration in your project - Not exposed outside the TentoCMS admin session (no public/unauthenticated endpoint returns it) — but any developer+ teammate on the project can read it directly
- Deleting the migration removes the stored key, since deletion is the only way to clear it
Use a scoped, read-only API key where your source CMS supports it, and delete the migration once you've confirmed the import succeeded.
Next Steps
After completing your migration:
- Verify Content - Review imported pages and collections
- Test Publishing - Ensure publish workflow works
- Update References - Fix any broken links
- Configure Preview - Set up preview environments
- Train Team - Onboard team to TentoCMS interface
See Also
- ButterCMS to TentoCMS Application Migration Guide — Covers application-level code changes after content import
Need Help? Contact your TentoCMS administrator or support channel.

