Skip to the content
Documentation
Help Start
Reading in:
  • German: 9,059 of 9,059 pages
About the help in other languages
All help pages

Remote services with expservices

Using the expservices extension as an integration API - the JSON envelope, the catalog, tokens, reads and writes, an idempotent upsert by remote id over HTTP, content jobs, and adding a service domain of your own.

Download as PDF

Address: https://doc.exponential.earth/exphelp/viewer/help/integrators/remote-services-with-expservices.md

On this page

Remote services with expservices

This page is for integrators who need more over HTTP than the REST content API offers: updating objects, finding an object by its remote id, searching, running content jobs, reading users or the audit. The expservices extension turns the administration's functions into named services (about 1100), answered as JSON by the ezjscore call interface at /ezjscore/call/exp<domain>::<method>. The tutorial with the shipped clients is Remote services and apps; the developer reference is expservices.

Contents

  1. Why integrators use it
  2. The envelope and the rules
  3. Finding the service you need
  4. An upsert by remote id over HTTP
  5. Large operations as content jobs
  6. Your own service domain
  7. Settings

Why integrators use it

NeedREST API (ezprestapi)expservices
Read a node, its children, fieldsyesyes (expnode::get, expnode::children, expobject::dataMap)
Create and publishyes (no remote_id)yes, with remote_id (expobject::create)
Update an object with a new published versionno (501)yes (expobject::update)
Find an object by remote idnoyes (expobject::getByRemoteId)
Searchnoyes (expsearch::search, count, facets)
Move, hide, copy, state, section on a subtreenoyes, as content jobs (expcontentjob::*)
Users, roles, caches, cronjobs, packages, auditnoyes, each behind its policy

The envelope and the rules

Every answer has one shape inside the ezjscore wrapper:

{"error_text":"","content":{"ok":true,"data":...,"meta":{...}}}
{"error_text":"","content":{"ok":false,"error":{"code":403,"message":"..."}}}

Lists are paged; meta carries total, offset, limit, count and has_more (expservices.ini [Paging] DefaultLimit=25, MaxLimit=200). Error codes are HTTP-like: 400 arguments, 401 sign in, 403 denied, 404 not found, 409 conflict, 422 invalid.

RuleDetail
ArgumentsParts after the method, joined with :: (expnode::children::2::name::asc::5::0); write fields as POST
AccessEach service declares public, user or a policy [module, function]; the same policies the administration checks
WritesPOST; with a session also the form token (ezxform_token field and X-CSRF-Token header); with an expt_... token no form token
AuditEvery write is recorded as service.<domain>.<method> (expservices.ini [Writes] Audit=enabled)
CredentialsSession cookie, or a token from expsession::tokenCreate sent as Authorization: Bearer or X-Exp-Token

Finding the service you need

export EXPSERVICES_URL=https://example.com
cd extension/expservices/bin
./expservices-client.sh catalog object | grep -E '"method"|"summary"'
./expservices-client.sh catalog search

Each catalog entry lists the summary, access, write flag, arguments and an example. In the administration the same catalog is under Setup > RAD (/setup/rad).

An upsert by remote id over HTTP

When the calling system cannot run code on the Exponential server but must keep one object per external record, use the remote id. The shipped Python client (extension/expservices/bin/expservices_client.py, standard library only) does the envelope and the token:

import json, os, sys
sys.path.insert(0, "extension/expservices/bin")
from expservices_client import Client, ServiceError

c = Client("https://example.com", api_token=os.environ["EXPSERVICES_TOKEN"])

def upsert(remote_id, parent_node_id, class_identifier, attributes, language="eng-US"):
    try:
        found = c.call("expobject::getByRemoteId", remote_id)
    except ServiceError as e:
        if e.code != 404:
            raise
        found = None
    if found is None:
        return c.post("expobject::create", {
            "parent_node_id": parent_node_id, "class": class_identifier,
            "attributes": json.dumps(attributes), "language": language, "remote_id": remote_id})
    return c.post("expobject::update", {"attributes": json.dumps(attributes), "language": language},
                  args=(found["id"],))

upsert("erp-product-10442", 2, "folder", {"name": "Product 10442"})

expobject::create refuses a remote id that is already in use with 409, so a second run creates no duplicate. Do not run two writers for the same records at the same moment: the check and the create are two steps (on MySQL and PostgreSQL a unique index on ezcontentobject.remote_id stops the second insert; other engines are not covered by this page). Remote ids set through expobject::setRemoteId may contain letters, digits and _ . : -, up to 100 characters; use the same alphabet for ids you pass to create.

Note

Every update publishes a new version. Compare the incoming values with expobject::dataMap first and skip unchanged records, or the object's history fills with identical versions (Idempotent imports).

Large operations as content jobs

Moving, hiding, copying or removing a large subtree runs as a content job in batches. Over expservices:

./expservices-client.sh post expcontentjob::hide node_id=<node id>
./expservices-client.sh call expcontentjob::progress <job id>

expcontentjob::estimate says beforehand how many nodes an operation touches and whether it would run at once or as a job (Content jobs).

Your own service domain

An integration that offers its own functions to other systems (for example "the stock level of a product from the ERP") can add a domain: a class extending expServiceBase with its $services declarations, registered in your extension's settings/ezjscore.ini.append.php as [ezjscServer_<domain>] with Class=<your class>. It then gets the envelope, paging, access checks, the token, the audit and the catalog entry for free (Writing a service).

Settings

FileBlock and keyDefaultMeaning
expservices.ini[Services] Enabledenableddisabled: every service answers 403
expservices.ini[Paging] DefaultLimit, MaxLimit25, 200Page sizes
expservices.ini[Writes] RequireTokenenabledThe form token on session writes; switch off for tests only
expservices.ini[Writes] AuditenabledRecord every write

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.

Something wrong on this page? Report an error

Documentation license

This documentation is free documentation, licensed under the GNU Free Documentation License 1.3 or later.

Exponential Copyright © 1998-2026 Exponential Foundation and 7x