API and protocol standards
This page is for developers who connect other systems to Exponential, and for administrators who must say which protocols an installation speaks and how far each one goes. For every API and network protocol in the 6.0.x line it says what the standard is, what Exponential implements, where the code, settings and tests are, and what is missing.
Note A protocol listed here is not necessarily switched on. Several (SOAP, LDAP, the audit webhook) are off until an administrator configures them; the section of each protocol names the setting.
Summary
| Standard | Version | Status | Evidence (short) |
|---|---|---|---|
| OAuth 2.0, authorization server for the REST API | RFC 6749 (2012) | Partly complies | kernel/private/modules/oauth/, kernel/private/rest/classes/oauth/, token endpoint POST /api/auth/v1/oauth/token; state and PKCE (RFC 7636, S256) since 6.0.15; no token_type |
| OAuth 2.0 Bearer tokens | RFC 6750 (2012) | Partly complies | Authorization: Bearer and OAuth accepted (ezpOauthUtility); token also accepted as a query parameter |
| OAuth 2.0 / OpenID Connect, client for social login | RFC 6749, RFC 7636, OIDC Core 1.0 | Partly complies | sevenx_authentication_2fa extension: authorization code flow with state and PKCE S256; ID tokens not validated |
| REST over HTTP | HTTP semantics, RFC 9110 | Partly complies | ezprestapi routes /api/ezp/v1, /api/ezp/v2; status classes in kernel/private/rest/classes/ |
| JSON | RFC 8259 | Complies | ezpRestJsonView (application/json, UTF-8), ezjscore call interface |
| OpenAPI description | 3.x | Not applicable (kernel) | no OpenAPI document for the kernel's REST API; Velocity publishes one for its own handlers |
| WebDAV | RFC 4918 | Partly complies | index_webdav.php, kernel/private/classes/webdav/ on se7enxweb/expcomponents-webdav; lock ownership not implemented |
| SOAP | 1.1 (W3C Note) | Partly complies | lib/ezsoap/, index_soap.php, settings/soap.ini [GeneralSettings] EnableSOAP=false; no WSDL |
| XML-RPC | 1999 specification | Not applicable | not implemented |
| RSS export and import | RSS 1.0, RSS 2.0 (export); 0.91 to 2.0 (import) | Complies | kernel/classes/ezrssexport.php, ezrssimport.php |
| Atom | RFC 4287 | Partly complies | export only (rss_version ATOM); no Atom import |
| Sitemaps | sitemaps.org 0.9 | Complies | bcgooglesitemaps extension writes http://www.sitemaps.org/schemas/sitemap/0.9 |
| SMTP mail transport | RFC 5321 | Complies | lib/ezutils/classes/ezsmtptransport.php on se7enxweb/expcomponents-mail (the fork of zetacomponents/mail, since 6.0.15); STARTTLS available |
| One-click unsubscribe | RFC 8058 | Complies | kernel/classes/mailpreferences/expmailgate.php adds List-Unsubscribe and List-Unsubscribe-Post |
| LDAP authentication | RFC 4511 (LDAPv3) | Partly complies | kernel/classes/datatypes/ezuser/ezldapuser.php; default LDAPVersion=2; no StartTLS |
| Webhooks (outgoing, signed) | no formal standard | Complies with its own documented contract | kernel/classes/audit/sinks/expauditwebhooksink.php, HMAC-SHA-256 signature |
| Model Context Protocol | 2025 specification | Not applicable (kernel) | the kernel repository ships no MCP server |
OAuth 2.0 (RFC 6749) and Bearer tokens (RFC 6750)
What it is
The OAuth 2.0 Authorization Framework (IETF RFC 6749, October 2012) lets an application act for a user without knowing the user's password. RFC 6750 defines how an access token is sent as a Bearer token. RFC 7636 (PKCE) and the OAuth 2.0 Security Best Current Practice (RFC 9700, 2025) harden the flows.
How Exponential complies
Exponential is an authorization server for its REST API:
- Clients are registered at Setup > oAuth admin (
/oauthadmin/list,kernel/private/modules/oauthadmin/). Each gets a client identifier, a secret and a redirect address. - The authorization endpoint is the view
oauth/authorize(kernel/private/modules/oauth/authorize.php). The user signs in and needs the policyoauth/authorize(upgrade note). Unknown clients and aredirect_urithat is not the client's end point get400 Bad Requestwith no redirect (RFC 6749, section 4.1.2.1), tested intests/tests/kernel/private/oauth/ezpOauthAuthorizeRedirectTest.php. - The token endpoint is
POST /api/auth/v1/oauth/token(kernel/private/rest/classes/controllers/oauth_token.php). It accepts the grantsauthorization_codeandrefresh_tokenonly (validateGrantType()), checks the client secret (ezpRestClient::validateSecret()) and answersaccess_token,refresh_tokenandexpires_in. - Tokens are 40 hexadecimal characters from
random_bytes()(ezpRestToken::generateToken()); their lifetime issettings/rest.ini [OAuthSettings] TokenTTL=3600, that of a refresh tokenRefreshTokenTTL=2592000(30 days,0= no expiry;ezpOauthUtility::refreshTokenExpired()). - A code or refresh token is redeemed only by the client it was issued to (
ezpOauthUtility::clientMatches()), and only while its user is enabled and not locked by failed logins (ezpOauthUtility::userMayUseToken()); the same check runs for every resource request inezpOauthFilter. - A resource request carries the token as
Authorization: Bearer <token>(RFC 6750), asAuthorization: OAuth <token>(an older draft), or as theoauth_tokenparameter of a POST form body (RFC 6750 section 2.2,AllowTokenInBody=enabled); theoauth_tokenquery parameter only withAllowTokenInQuery=enabled, off by default (RFC 6750 section 2.3;ezpOauthUtility::tokenSources()inkernel/private/rest/classes/oauth/utility.php). rest.ini [Authentication] RequireAuthentication=enabledandAuthenticationStyle=ezpRestOauthAuthenticationStyleare the defaults.- Personal API keys (
Authorization: Bearer expk_...) are a second credential on the same header, not an OAuth flow; see Personal API keys andkernel/private/rest/classes/auth/apikey_auth_filter.php.
The client side guide is REST API v1 and v2, section 3.
Status
Partly complies. The authorization code grant with refresh tokens works as RFC 6749 describes; several required or recommended parts are missing.
Known gaps
- Since 6.0.15
stateis returned unchanged with the code, the token and the error, and PKCE (RFC 7636) is supported:S256,plainonly whenAllowPlainPkceis enabled, required for public clients (stored without a secret) by default (PKCE and state). These are no longer gaps. - CORS for the REST interface exists since 6.0.15 and is off by default (
rest.ini [CorsSettings], CORS for the REST API). response_type=token(the implicit grant, discouraged by RFC 9700) is still accepted, and it returns the tokens in the query string instead of the URL fragment that RFC 6749 section 4.2.2 requires.- The token response has no
token_type(required by RFC 6749 section 5.1);Cache-Control: no-storeis not set by the controller. Unverified whether a layer below adds it. - No
client_credentialsorpasswordgrant, no token introspection (RFC 7662), no revocation endpoint (RFC 7009). - The token in a query parameter, which RFC 6750 allows only as a last resort, is refused by default since 6.0.15
(
AllowTokenInQuery=disabled, OAuth token hardening). This is no longer a gap unless a site enables it.
OpenID Connect and OAuth 2.0 as a client (social login)
What it is
OpenID Connect Core 1.0 (OpenID Foundation, 2014) is an identity layer on OAuth 2.0: an ID token (a signed JWT) and a userinfo endpoint tell the client who signed in.
How Exponential complies
The kernel has no OpenID Connect provider and no OpenID Connect client. The sevenx_authentication_2fa extension,
which the root composer.json requires (~1.0.12), adds social login as an OAuth 2.0 client:
- Base class
eZOAuthUser(extension/sevenx_authentication_2fa/classes/ezoauthuser.php): the authorization code grant; a randomstatekept in the session, bound to the provider and accepted once within 600 seconds (STATE_TTL); PKCE withcode_challenge_method=S256where configured; an address the provider marks as not verified (email_verifiedfalse) is not trusted. - Provider handlers in
login_handler/: Google, Facebook, Meta, Instagram, Twitter and ID.me. The ID.me handler (ezidmeuser.php) is described as "OAuth 2.0 / OpenID Connect", sends anonceand reads the OIDC/userinforesponse. - User documentation: Two-factor login, "Social login".
Status
Partly complies. The OAuth 2.0 client side follows current practice (state, PKCE). OpenID Connect is used only for its userinfo endpoint.
Known gaps
- ID tokens are not requested or validated (no JWT signature check, no JWKS); identity comes from the userinfo call.
- No OpenID Connect discovery (
/.well-known/openid-configuration); endpoints are configured per provider. - Exponential cannot act as an OpenID Connect provider for other applications.
REST over HTTP
What it is
REST is an architectural style, not a standard. What can be checked is the use of HTTP itself: HTTP Semantics (IETF RFC 9110, June 2022) defines methods and status codes.
How Exponential complies
- The REST framework is in the kernel (
kernel/private/rest/classes/: router, dispatcher, auth styles, views) onse7enxweb/expcomponents-mvc-tools(the fork ofzetacomponents/mvc-tools, since 6.0.15). The content API is theezprestapiextension (required as~1.2.6), routes/api/ezp/v1/...and/api/ezp/v2/...; reads answer at both versions, writes (create, update, delete) at v2 only. Every route checks the user's content policies (expRestContentPermission). - Status codes:
ezpHttpResponseCodes(kernel/private/rest/classes/http_response_codes.php) names 200, 400, 401, 403, 404, 405 and 500;kernel/private/rest/classes/status/response.phpalso knows 201 and 204;status/rate_limited.phpanswers429 Too Many RequestswithRetry-Afterfor an API key over its limit (RFC 6585). settings/rest.ini:[ApiProvider] ProviderClass[],[Authentication],[OAuthSettings],[ApiKeySettings],[CacheSettings].- Tests:
tests/tests/kernel/private/rest/(request parser, routes),tests/tests/kernel/classes/apikey/.
A second HTTP API is the ezjscore call interface /ezjscore/call/<class>::<method>, which the expservices
extension fills with named services (Remote services and apps).
Status
Partly complies.
Known gaps
GET /content/node/<id>/listAtomanswers 500 at v1 and v2 (REST API v1 and v2, "Known issue: listAtom").- Which status code each write route returns (for example 201 on create) is Unverified from code; the guide shows the JSON body only.
- No conditional requests (
ETag,If-Match) on writes were found. Unverified.
JSON (RFC 8259)
What it is
The JavaScript Object Notation (JSON) Data Interchange Format, IETF RFC 8259 (December 2017, also ECMA-404).
How Exponential complies
- REST answers are written by
ezpRestJsonView(kernel/private/rest/classes/views/json.php) with the content typeapplication/jsonand charset UTF-8, throughezcMvcJsonViewHandler. - The
ezjscorecall interface sendsContent-Type: application/json; charset=utf-8(extension/ezjscore/classes/runnable/views/ezjscore/call.php). - JSON is produced by PHP's
json_encode(), which writes UTF-8 JSON text as RFC 8259 requires.
Status
Complies.
Known gaps
- None found. RFC 8259 defines no
charsetparameter forapplication/json; sending one is harmless.
OpenAPI and JSON:API
The kernel's REST API has no OpenAPI description and does not follow JSON:API; neither standard applies to it.
Velocity, the separately packaged web server, publishes /.well-known/openapi.json for its own handlers
(Velocity discovery, federation and deploy); that
document does not describe the content API. Not applicable to the kernel.
WebDAV (RFC 4918)
What it is
HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV), IETF RFC 4918 (June 2007): methods such as PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK and UNLOCK. Class 1 is the basic set, class 2 adds locking.
How Exponential complies
- Entry point
index_webdav.phpat the installation root (webdav.phpuntil 6.0.14, now a forwarding shim, see the change); it uses the WebDAV server of the expcomponents, the fork of the Zeta Components (se7enxweb/expcomponents-webdav ~1.2.3incomposer.json) with the content backendkernel/private/classes/webdav/ezwebdavcontentbackend.phpand the authentication backendezwebdavcontentbackendauth.php(basic authentication against Exponential users). settings/webdav.ini([GeneralSettings],[FolderSettings]).- The lock plugin is registered (
ezcWebdavLockPluginConfigurationinindex_webdav.php) and the backend declaresezcWebdavLockBackend. - Request and response recordings for COPY, DELETE, GET, HEAD, MKCOL, MOVE, OPTIONS, PROPFIND, PROPPATCH and PUT are in
tests/tests/kernel/private/webdav/regression/. - History: the WebDAV engine of 4.1 and the design notes in doc/eng-us/specifications/trunk/webdav.
Status
Partly complies. Class 1 methods are implemented; locking (class 2) is incomplete.
Known gaps
- The lock authorizer's methods
assignLock(),ownsLock()andreleaseLock()inezwebdavcontentbackendauth.phpare empty (@todo implement); the backend's own comment says lock calls are still missing insetPropertyandremoveProperty. Whether LOCK and UNLOCK work with real clients is Unverified. - The regression recordings have no
*Test.phprunner, so no PHPUnit suite inphpunit.xmlruns them and CI does not test WebDAV.
SOAP 1.1
What it is
SOAP 1.1 is a W3C Note (May 2000) for XML messages over HTTP; SOAP 1.2 is a W3C Recommendation (2003, second edition 2007). WSDL describes SOAP services.
How Exponential complies
- Library
lib/ezsoap/classes/(client, server, envelope, codec, fault).eZSOAPEnvelopeuses the SOAP 1.1 namespaceshttp://schemas.xmlsoap.org/soap/envelope/and.../soap/encoding/. - Server entry point
index_soap.php(soap.phpbefore 6.0.15, now a shim that forwards to it); off by default:settings/soap.ini [GeneralSettings] EnableSOAP=false. Functions are added by extensions ([ExtensionSettings] SOAPExtensions[]). Thesyndicationextension, required by the rootcomposer.json, is a SOAP client and server between installations. - The client verifies TLS certificates by default:
tests/tests/lib/ezsoap/eZSOAPClientTlsOptionsTest.php(testTheCertificateIsVerifiedByDefault); further tests intests/tests/lib/ezsoap/(suitelib).
Status
Partly complies. SOAP 1.1 messages only.
Known gaps
- No SOAP 1.2 and no WSDL generation or reading (a search of
lib/ezsoapandkernel/for "wsdl" finds nothing). [GeneralSettings] Loggingis marked "not supported yet".
XML-RPC
Not implemented. The only reference in the kernel is the request classifier, which recognizes xmlrpc.php probes
as unwanted traffic (kernel/classes/requestclassifier/exprequestrulesetprobes.php). Not applicable.
RSS, Atom and OPML
What it is
RSS 2.0 (RSS Advisory Board, 2009 edition) and RSS 1.0 (RDF Site Summary, 2000) are web feed formats. Atom is the IETF Atom Syndication Format, RFC 4287 (December 2005). OPML 2.0 lists feeds.
How Exponential complies
- Export (
kernel/classes/ezrssexport.php,rssXmlContent()): RSS 1.0 (rss1), RSS 2.0 (rss2) and Atom (atom) through the feed library of the expcomponents (se7enxweb/expcomponents-feed ~1.4.7, the fork ofzetacomponents/feed1.4.6, since 6.0.15); Apple Podcasts feeds (ITUNES) and OPML are written by Exponential itself. Feeds are served at/rss/feed/<address>. - Import (
kernel/classes/ezrssimport.php, cronjob partkernel/private/classes/cronjobs/rssimport.php): RSS 0.91, 0.92, 1.0 and 2.0, read with external entities refused and the network switched off (LIBXML_NONET). - Guides: RSS feeds, RSS: Apple Podcasts feeds, a paged feed list and safer exports, OPML exports.
- Tests:
tests/tests/kernel/classes/eZRSSExportTest.php,eZRSSExportOPMLItemTest.php.
Status
- RSS: Complies.
- Atom: Partly complies (export only).
Known gaps
- Atom feeds cannot be imported: the import switches on the
versionattribute of an RSS root element only. - No feed validator runs in CI. Validity of generated feeds is Unverified beyond the tests above.
Sitemaps (sitemaps.org 0.9)
What it is
The Sitemaps protocol 0.9 (sitemaps.org, supported by the major search engines) lists a site's URLs in XML.
How Exponential complies
The bcgooglesitemaps extension (required as ~1.1.6.6, version 1.1.6.6) generates sitemaps by cronjob
(classes/runnable/cronjobs/generate.php, generatemultilingual.php) with the namespace
http://www.sitemaps.org/schemas/sitemap/0.9 and the elements loc, lastmod, changefreq and priority, and
serves them through its sitemap module.
Status
Complies.
Known gaps
- The 50,000 URL / 50 MB limit per file and sitemap index files were not checked for this page. Unverified.
SMTP (RFC 5321) and mail
What it is
The Simple Mail Transfer Protocol, IETF RFC 5321 (October 2008). STARTTLS for SMTP is RFC 3207. RFC 8058 (January
2017) defines one-click unsubscribe with List-Unsubscribe-Post.
How Exponential complies
settings/site.ini [MailSettings] Transportchoosessendmail(default),smtporfile(TransportAlias[smtp]=eZSMTPTransport). SMTP settings:TransportServer,TransportPort=25,TransportConnectionType(empty,ssl,tlsand older values),TransportUser,TransportPassword.eZSMTPTransport(lib/ezutils/classes/ezsmtptransport.php) sends throughezcMailSmtpTransportfromse7enxweb/expcomponents-mail ~1.10.2(the fork ofzetacomponents/mail1.10.1, since 6.0.15). In that librarytlsmeans STARTTLS: it requires the server to announceSTARTTLS(vendor/se7enxweb/expcomponents-mail/src/transports/smtp/smtp_transport.php;vendor/zetacomponents/mail/...before).- RFC 8058: mail preferences add
List-UnsubscribeandList-Unsubscribe-Postper recipient (kernel/classes/mailpreferences/expmailgate.php,expmailtoken.php); see Mail preferences compliance. - Tests:
tests/tests/lib/ezutils/eZMailTest.php,eZMailTransportSendTest.php,eZMailEzcTest.php,tests/tests/kernel/classes/mailpreferences/MailPreferencesComplianceTest.php.
Status
Complies (SMTP and RFC 8058).
Known gaps
- The comment in
site.inistill offerssslv2andsslv3as connection types, which are obsolete and insecure; whether current PHP accepts them at all is Unverified. - The
site.inicomment cites RFC 2821, which RFC 5321 replaced. - DKIM signing is not done by Exponential; it is left to the mail server.
LDAP (RFC 4511)
What it is
The Lightweight Directory Access Protocol version 3, IETF RFC 4511 (June 2006); filter strings are RFC 4515. LDAPv2 was declared historic in RFC 3494 (2003).
How Exponential complies
- Login handler
eZLDAPUser(kernel/classes/datatypes/ezuser/ezldapuser.php), switched on withsettings/site.ini [UserSettings] LoginHandler[]=LDAP(commented out by default). Settings insettings/ldap.ini [LDAPSettings]:LDAPServer,LDAPPort=389,LDAPBaseDn,LDAPBindUser,LDAPLoginAttribute=uid,LDAPVersion,LDAPFollowReferrals. - It uses PHP's
ldapextension and setsLDAP_OPT_PROTOCOL_VERSIONfromLDAPVersion. - The login is escaped before it goes into a filter (
eZLDAPUser::ldap_escape():*,(,),\and NUL as in RFC 4515; a separate set for DNs). - Tests:
tests/tests/kernel/datatypes/ezuser/eZLDAPUserTest.php(database group; skipped when theldapextension is not loaded).
Status
Partly complies.
Known gaps
- The default is
LDAPVersion=2; set it to 3 for any current directory server. - No StartTLS:
ldap_start_tls()is not called. Encryption depends on anldaps://server address, which is Unverified with the separateLDAPPortsetting. LDAPDebugTrace=enabledlogs logins and passwords (warning inldap.ini); keep it off in production.- The test points at a fixed remote LDAP server, not a CI service; whether it runs in CI is Unverified.
Webhooks
What it is
There is no formal webhook standard. Common practice is an HTTPS POST of JSON, signed with an HMAC so the receiver can check origin and freshness.
How Exponential complies
The audit log can deliver its records to a webhook (kernel/classes/audit/sinks/expauditwebhooksink.php,
settings/audit.ini [AuditSink_webhook], off until URL is set):
- Batches of records as JSON,
{"v":1,"installation":...,"events":[...]}. - Headers
X-Exponential-Timestamp,X-Exponential-BatchandX-Exponential-Signature: sha256=<hex>, the HMAC-SHA-256 oftimestamp + "." + bodywithSigningSecret; receivers reject timestamps more than 300 seconds away. - Delivery through a spool with retries (
Retries,RetryBackoff); at least once, de-duplicated by event id. - Tests:
tests/tests/kernel/classes/audit/expAuditSinksTest.php. Documentation: audit.
Status
Complies with its own documented contract.
Known gaps
- Only the audit log sends webhooks; content events (publish, remove) have no generic outgoing webhook.
- No incoming webhook endpoint.
Model Context Protocol (MCP)
The Exponential 6 kernel repository ships no MCP server. The extension point survey (Setup > RAD tools,
settings/rad.ini) can count MCP tool registries that an extension declares, and Velocity publishes
/.well-known/mcp.json for its own handlers
(Velocity discovery, federation and deploy); neither
was checked against the MCP specification for this page. Not applicable to the kernel.