Upgrading to ElmapiCMS v4.0

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
  • statusstate on 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/ai without 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:

  1. Database
  2. Uploaded files (storage/ / object storage)
  3. 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:

  1. Extract 4.0 into a staging directory
  2. Copy over your .env, customizations, and storage
  3. 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 build

If 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=25

See Environment Variables for the full list.

Step 5: Run Migrations

php artisan migrate

Important migrations include:

  • Content entry statusstate rename
  • content_entry_versions table
  • 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:clear

Restart 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 (default published). Values only_draft / with_draft are gone
  • Filters/sorts that used core column status must use state
  • 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=published on 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_api GET still works when enabled)
  • /auth/me and tokens no longer expose an abilities array; scope is openid profile email
  • End-user uak_… keys cannot call CMS content routes; introspect needs a project token with introspect

Assets & uploads

  • New images stored as WebP (original kept when available)
  • SVG uploads rejected (existing SVGs remain)
  • API file create returns 201 Created (was 200)
  • GET /uploads/... only serves known asset paths

API & platform

  • GET /api requires project token read ability (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_URL with 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.

Search documentation

Find guides and reference pages