Upgrading to ElmapiCMS v4.0
This guide helps you upgrade from ElmapiCMS v3.3 to v4.0.
4.0 is a major release: Laravel 13, content versioning, Authentication changes, and several API breaking changes. Read the breaking changes checklist before you deploy.
Overview
Notable themes in 4.0:
- Content versioning - publish mints immutable snapshots; saves no longer flip live state
status→stateon content entries (API and dashboard)- Authentication - identity-only JWTs for end users
- Assets - WebP for new images, SVG uploads rejected, optional S3 direct upload
- Laravel 13 / PHP ^8.4, updated AI stack (
laravel/aiwithout Prism)
See the changelog for the full feature list. Companion packages: @elmapicms/js-sdk 1.0, @elmapicms/mcp-server 1.2, and agent skills.
Important: Backup First!
Before making any changes, create a complete backup:
- Database
- Uploaded files (
storage// object storage) - Configuration (
.env)
Step 1: Requirements
- PHP >= 8.4
- Composer dependencies for Laravel 13
- A queue worker in production (webhooks, and image finalize when using direct S3 upload)
Step 2: Update Application Files
Replace your installation with the v4.0 release (Git tag/release or CodeCanyon zip — see For CodeCanyon buyers if needed), keeping your existing .env and storage/ data.
Typical approach:
- Extract 4.0 into a staging directory
- Copy over your
.env, customizations, and storage - Swap the release into place (or deploy via your usual pipeline)
4.0 touches most of the stack (framework, migrations, frontend build). Prefer a full release replace over cherry-picking files.
Step 3: Install Dependencies
composer install --no-dev --optimize-autoloader
npm ci
npm run buildIf you cannot run Composer on the server, upload a vendor/ tree built on PHP 8.4+ with the same composer.lock.
Step 4: Environment Variables
Compare your .env with the new .env.example. Common additions:
# Content version retention (-1 = unlimited)
CONTENT_VERSIONS_PER_ENTRY=-1
# Optional S3/Spaces direct upload (project disk must be s3)
ASSET_DIRECT_UPLOAD=false
# Webhooks: allow HTTP endpoints locally only
# WEBHOOK_ALLOW_INSECURE_HTTP=false
# Authentication (defaults usually work)
# PROJECT_AUTH_ISSUER="${APP_URL}"
# PROJECT_AUTH_ACCESS_TOKEN_TTL_MINUTES=15
# PROJECT_AUTH_REFRESH_TOKEN_TTL_DAYS=30
# PROJECT_AUTH_MAX_SESSIONS_PER_USER=25See Environment Variables for the full list.
Step 5: Run Migrations
php artisan migrateImportant migrations include:
- Content entry
status→staterename content_entry_versionstable- Agent conversations participant morph (
participant_type/participant_id) - Authentication tables/columns (suspension, API keys, email verification) as needed
- Soft-delete of obsolete built-in project templates (
blog-next-js,landing-page-nextjs)
Until the AI participant migration runs, chats may stream but fail to persist (Unknown column 'participant_type').
Step 6: Clear Caches
php artisan optimize:clearRestart PHP-FPM / Octane / queue workers so they load the new code and config.
Breaking Changes Checklist
Use this list to update clients, themes, and integrations:
Content state (was status)
- Create/update bodies use
state:draft|published - List/show query filter uses
state(defaultpublished). Valuesonly_draft/with_draftare gone - Filters/sorts that used core column
statusmust usestate - Export/import and templates use
state - Webhook enabled field remains named
status(unchanged)
Saving is not the same as publishing
- Dashboard Save as draft and API saves only update the draft. They do not change what your live site gets from
state=published - Use Save & Publish / Publish, Unpublish, or Discard unpublished changes (dashboard or API) when you want to change what is live
state=publishedon list and get returns the latest published snapshot (not live draft fields)
Authentication
- JWTs are identity-only - no CMS
abilities; they cannot call ability-protected content/admin routes - Use project Sanctum tokens for CMS API access (
public_apiGET still works when enabled) /auth/meand tokens no longer expose anabilitiesarray; scope isopenid profile email- End-user
uak_…keys cannot call CMS content routes; introspect needs a project token withintrospect
Assets & uploads
- New images stored as WebP (original kept when available)
- SVG uploads rejected (existing SVGs remain)
- API file create returns
201 Created(was200) GET /uploads/...only serves known asset paths
API & platform
GET /apirequires project tokenreadability (Authentication JWTs cannot call it)- Cyclic relation nests return a stub instead of overflowing
- Built-in starter templates removed; blank / import / your own templates remain
- PHP ^8.4, Laravel 13, AI stack without Prism
After Upgrade
Update integrations as needed:
- Creating & Editing Content - versioning UX
- Authentication - identity tokens vs CMS tokens
- API Access
- Content API: publish, versions, bulk, locales, webhooks, Authentication sections
- MCP Server - use
ELMAPI_BASE_URLwith a project API token - JS SDK 1.0 - required
baseUrl, namespaced API (client.content.*, etc.)
Support
If you encounter issues, see Troubleshooting or Getting Help & Support.