Writing through expservices
This page is for app developers whose app changes content in ways the REST API does not offer, above all updating an
existing item. The node services are declared in extension/expservices/classes/content/expnodeservices.php; the
rules for writes are in the expservices reference.
Contents
Rules for every write
- A write needs
POST; its arguments are form fields in the body, except the ids that the service takes in the URL. - With an expservices token (
BearerandX-Exp-Token), no form token is needed. With a session cookie, fetch the form token fromexpsession::tokenand send it as the fieldezxform_tokenand the headerX-CSRF-Token. - The service checks its policy as the calling user. A missing policy answers
403inside the envelope. - Every successful write records the audit event
service.<domain>.<method>.
Create
curl -s -H "Authorization: Bearer $EXPSERVICES_TOKEN" -H "X-Exp-Token: $EXPSERVICES_TOKEN" \
--data-urlencode 'class=article' \
--data-urlencode 'attributes={"title":"Report from the field","intro":"<p>Short text</p>"}' \
--data-urlencode 'language=eng-US' \
'https://example.com/ezjscore/call/expnode::create::<parent node id>?ContentType=json'expnode::create needs content/create and publishes at once. POST fields: class, attributes (a JSON map of
identifier to string), language, and optionally remote_id. The service refuses a class the user may not create
below that parent (403), checks the class's required fields, and answers 409 when the remote_id is already in
use. Setting a remote_id of your own (for example a UUID made on the device) therefore lets the app retry safely:
a second attempt answers 409 instead of creating the item twice
(Node ids, remote ids and deep links). The field
identifiers in the example are placeholders; use those of your class.
Update
curl -s -H "Authorization: Bearer $EXPSERVICES_TOKEN" -H "X-Exp-Token: $EXPSERVICES_TOKEN" \
--data-urlencode 'attributes={"title":"Corrected title"}' --data-urlencode 'language=eng-US' \
'https://example.com/ezjscore/call/expnode::update::<node id>?ContentType=json'expnode::update needs content/edit, changes the attributes you send and publishes a new version. Fields you do not
send keep their values. Smaller changes have their own services: expnode::rename (POST name),
expnode::setPriority, expnode::hide, expnode::reveal.
Remove
curl -s -H "Authorization: Bearer $EXPSERVICES_TOKEN" -H "X-Exp-Token: $EXPSERVICES_TOKEN" \
-X POST 'https://example.com/ezjscore/call/expnode::remove::<node id>?ContentType=json'expnode::remove needs content/remove and removes a node and its subtree. The POST field move_to_trash defaults to
1, so the content goes to the trash, from where an editor can restore it. Large removals may be routed to a content
job and answer with the job instead of {removed}.
Fields that cannot be written
The node services never write fields of the datatypes ezimage, ezbinaryfile, ezmedia, ezuser, ezpassword
and ezflowblock remotely: their string import reads a local path or URL, or they hold credentials
(expContentBase::$deniedInputTypes). An app therefore cannot attach photos or files through these services
(Files and uploads).
After a write
- Read the item again (by node id, with the REST API) to show what was published; workflows on the server may have changed it.
- Invalidate the app's cache for the item and for its parent list (Caching in the app).
- If a write timed out, check for the item by its
remote_id(expnode::getByRemoteId) before you retry.
Related pages
- Calling expservices from an app
- Creating and removing content
- Permissions for app users
- 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.