ppcharlier / statamic-editor-api
Write API for Statamic with drafts & revisions, built for mobile editors.
Package info
github.com/ppcharlier/statamic-editor-api
Type:statamic-addon
pkg:composer/ppcharlier/statamic-editor-api
Requires
- php: ^8.3
- statamic/cms: ^6.0
Requires (Dev)
- laravel/sanctum: ^4.3
- orchestra/testbench: ^9.0 || ^10.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
Suggests
- laravel/sanctum: Required for the 'sanctum' auth driver (database-backed tokens)
Provides
None
Conflicts
None
Replaces
None
README
The write API that puts a pocket editor on your Statamic site.
Editor API adds a clean, complete REST write API to your Statamic 6 site — built as the backend of Editor for Drupal & Statamic, the iPhone and iPad app, and open to any client you want to build. No hosted service, no sync, no middleman: your site is the only backend. Free and open source.
The same contract exists as a module for Drupal 11: ppcharlier/drupal-editor-api.
Why
Statamic's Control Panel is excellent — on a laptop. Everything else (a phone, a
tablet, a script, a custom editor) needs an API that can write, and that means
more than POST and hope: drafts that don't go live, revisions you can restore,
permissions that already match your team, and payloads that come back exactly as
you sent them.
That's what this addon is.
Features
- Control Panel parity, the safe way. Everything goes through Statamic's public facades — the same path the CP uses. No private controllers, no fragile workarounds.
- Drafts by default, publishing on purpose.
PATCHsaves a working copy; publishing is an explicit action with its own endpoint and message. - Revisions & working copies. Full history and semantic restore, when revisions are enabled on the collection.
- Your permissions, untouched. Tokens are tied to real Statamic users, and
every request is authorized by Statamic's own policies — the same verdict the
CP gives, site access and "other authors" rules included. One extra permission,
access editor-api, decides who may use the API at all. - Clients know what they may do. Every payload carries a
canblock (edit,delete,publish,upload, …) and entries carry theirauthor, so an app greys out an action instead of discovering a403. - Stricter than the CP, if you want. Two optional per-collection permissions hide other authors' entries — or just their names — from an editor's listings. Off by default, on with one config key.
- Byte-faithful Bard. ProseMirror documents round-trip verbatim — unknown node types, custom attributes and whitespace included. What your editor sends is exactly what your site stores.
- Stored shapes are accepted back.
GETserves field values exactly as Statamic stores them, and a write may echo them unchanged — including the fieldtypes whose Control Panel submits another shape: single-item assets and relationships, anddatefields (storedY-m-d H:iin the site's timezone, validated by Statamic 6 against a Zulu ISO string). The addon runs their ownpreProcess()on the way in, so nothing is rejected on a field you did not touch. - Conflict detection.
X-Base-Modifiedguards every write — two editors can't silently overwrite each other (409on a stale base). - The whole surface. Entries, assets (upload included), taxonomy terms,
globals, navigations and form submissions — plus blueprints, so clients can
render forms dynamically: choice options arrive as an ordered list, and
GET /templatesgives atemplatefield the site's views. - Multi-site ready. Localized entries, linked localizations, per-site globals and terms.
Requirements
- Statamic 6
- PHP 8.3+
Works with and without Statamic Pro: without Pro the API degrades gracefully
(direct saves instead of working copies, a single user). Revisions, multiple
users and multi-site require Pro. Clients read GET /config and adapt their UI
per collection.
Installation
composer require ppcharlier/statamic-editor-api
Optionally publish the config:
php artisan vendor:publish --tag=editor-api-config
That's it — the routes are live under /api/editor/v1.
Quick start
1. Sign in with a Statamic user's credentials to get a token. A non-super
user needs the access editor-api permission on one of their roles (the
"Editor API" group in the role editor):
curl -X POST https://example.com/api/editor/v1/auth/tokens \ -H "Content-Type: application/json" \ -d '{"email": "jane@example.com", "password": "secret", "device_name": "iPhone"}'
{ "data": { "token": "3|kqZ…", "expires_at": "2026-11-29T10:12:00+00:00" } }
2. Discover the site — one call gives a client everything it needs to build its UI (sites, collections, blueprints, containers, revision flags):
curl https://example.com/api/editor/v1/config \
-H "Authorization: Bearer 3|kqZ…"
3. Save a draft, then publish it on purpose:
curl -X PATCH https://example.com/api/editor/v1/entries/abc-123 \ -H "Authorization: Bearer 3|kqZ…" \ -H "Content-Type: application/json" \ -H "X-Base-Modified: 2026-09-01T08:15:00+00:00" \ -d '{"data": {"title": "Low tide at the Aber Wrac’h", "body": [ … ]}}' curl -X POST https://example.com/api/editor/v1/entries/abc-123/published \ -H "Authorization: Bearer 3|kqZ…" \ -d '{"message": "Fixed the tide times"}'
The X-Base-Modified header is the last_modified you read. If someone else
changed the entry in between, you get a 409 instead of overwriting their work.
The API at a glance
All routes live under /api/editor/v1 (prefix configurable).
| Area | Endpoints |
|---|---|
| Auth | POST /auth/tokens · DELETE /auth/tokens/current · GET /me |
| Discovery | GET /config · GET /collections/{collection}/blueprints[/{blueprint}] · GET /templates |
| Entries | GET·POST /collections/{collection}/entries · GET·PATCH·DELETE /entries/{id} |
| Publishing | POST·DELETE /entries/{id}/published |
| Revisions | GET /entries/{id}/revisions · POST /entries/{id}/revisions/{revision}/restore |
| Localizations | POST /entries/{id}/localizations |
| Assets | GET·POST /assets/{container} · GET·PATCH·DELETE /assets/{container}/{path} |
| Globals | GET /globals · GET·PATCH /globals/{handle} |
| Taxonomies | GET /taxonomies · GET·POST /taxonomies/{taxonomy}/terms · PATCH·DELETE /taxonomies/{taxonomy}/terms/{slug} |
| Navigations | GET /navigations · GET·PATCH /navigations/{handle}/tree |
| Forms | GET /forms · GET /forms/{form}/submissions · DELETE /forms/{form}/submissions/{id} |
Every resource area can be disabled, or restricted to an allow-list of handles,
in the config — a disabled resource answers 404, so a client can't even tell it
exists.
📖 Full documentation — authentication and token drivers, every endpoint with its payloads, error codes, multi-site, permissions.
Permissions
Nothing to configure: the addon asks Statamic's policies the same questions the Control Panel asks. What a user may do in the CP, they may do through the API — no more, no less. Two things are specific to the addon:
access editor-api— the counterpart ofaccess cp. A non-super user must hold it to get a token, and on every request afterwards: revoking it cuts off tokens already issued.enforce_author_visibility(config,falseby default) — the CP shows every entry, author included, to anyone who may view a collection. Turn this on and two per-collection permissions,editor-api list other authors {collection} entriesandeditor-api view other authors of {collection} entries, decide who sees other authors' entries and who may know their names. Super users and roles that may already edit other authors' entries are never restricted.
Security
Tokens are hashed at rest, revocable, expire after 90 days by default, and are rate-limited per token and per IP. Two storage drivers ship with the addon:
file(default) — tokens on disk, no database required.sanctum— tokens in thepersonal_access_tokenstable (requireslaravel/sanctumand Eloquent users).
Found a vulnerability? Please report it privately rather than opening a public issue.
Testing
vendor/bin/pest
License
MIT — free, on as many sites as you like, nothing to buy on the Statamic Marketplace.
Versions v1.2.3 to v2.6.1 were published under a commercial licence and keep it; versions up to v1.2.2, and everything from the next release on, are MIT.