API design: REST, ezjscore and headless
This page is for engineers who integrate Exponential with other systems, build a front end on it, or add endpoints of their own. The status of each protocol, with evidence and gaps, is in API and protocol standards.
Contents
- The interfaces
- The REST content API
- The ezjscore call interface
- Outgoing events
- Headless and hybrid
- Designing an endpoint of your own
- Known gaps
The interfaces
| Interface | For | Status |
|---|---|---|
REST content API (ezprestapi) | reading and writing content from other systems and apps | partly complies with HTTP semantics |
| OAuth 2.0 token endpoint | applications acting for a user | partly complies (no PKCE, no state on the server side) |
| Personal API keys | scripts acting as one user, with scopes | Exponential 6.0.15 |
ezjscore call interface | JSON calls to PHP from JavaScript and apps | complies (JSON) |
expservices | named remote services on top of the call interface | see Remote services and apps |
| Audit webhook | delivering audit records to another system, HMAC-signed | complies with its own contract |
| RSS and Atom | feeds (Atom export only) | RSS feeds |
| WebDAV, SOAP | file access, legacy integrations; SOAP is off by default | partly complies |
The REST content API
- One extension,
ezprestapi, serves/api/ezp/v1/...and/api/ezp/v2/...: reads at both versions, writes (create, update, delete) at v2 only.ezprestapiprovideris deprecated. - Authentication: an OAuth 2.0 token from
POST /api/auth/v1/oauth/token, or a personal API key asAuthorization: Bearer expk_.... - Every read and write checks the user's policies (
content/read,content/create,content/edit,content/remove), whatever authenticated the request. - An API key over its rate limit gets
429 Too Many RequestswithRetry-After. - Settings:
settings/rest.ini.
Guide with curl examples: REST API v1 and v2. For authors: REST API, for application creators.
The ezjscore call interface
/ezjscore/call/<class>::<method> calls a registered server function and answers JSON. Server functions are PHP
classes registered in ezjscore.ini, protected by role policies. Use it for the JavaScript of your own pages and for
small app calls (JSON endpoints with ezjscore,
Backend ezjscore services,
ezjscore server functions reference).
Outgoing events
Only the audit log sends webhooks: batches of records as JSON with a timestamp and an HMAC-SHA-256 signature,
delivered through a spool with retries, at least once and de-duplicated by event id (settings/audit.ini [AuditSink_webhook], off until a URL is set). Content events (publish, remove) have no generic outgoing webhook; to
react to them, use a workflow event or a listener in an extension (Events, extension points and plugins).
Headless and hybrid
You keep content classes, editing, versions, translations, workflows and permissions; the front end replaces page templates, menus and routing. Plan for:
- reads through the REST API, with the permissions of the authenticated user;
- rich text and images: rendered XHTML and image variations, served with their URLs;
- preview of drafts for editors;
- caching: the API's own cache settings and an HTTP cache in front for public reads;
- hybrid: templates for some pages, the API for apps, on the same content.
Details: Headless Exponential.
Designing an endpoint of your own
- Choose the home: a REST route in an extension for resources, an ezjscore server function for small calls from your own pages, a module view for HTML.
- Decide permissions with policy functions, never with user ids; check them on every call.
- Accept state changes only by POST (or PUT, PATCH, DELETE), with a form token for browser calls or a token or key for machines.
- Return JSON with
json_encode(); give errors a status code and a stable machine-readable field. - Keep the shape stable: a field removed or renamed is a break that needs upgrade notes (Backward compatibility policy).
- Version the path when the shape must change, as
ezprestapidoes with v1 and v2. - Log and audit writes; never log tokens or keys.
Known gaps
From the standard: no OpenAPI description of the kernel's REST API (Velocity publishes one for its own handlers only), no JSON:API, no conditional requests on writes found, no generic content webhooks, no incoming webhook endpoint, and the OAuth server lacks PKCE.
Related pages
- OAuth and API keys
- Remote services with expservices
- AJAX with ezjscore server functions
- Free documentation: this page is free to copy, translate and improve under the GNU FDL 1.3.
Become an Exponential Partner
Partners share their information, provide funding, contribute back and develop the products with us, and that is how Exponential grows. A partner website and dashboard are planned for mid-2027: please become a partner today to speed up our growth rate and land new features faster.