The data API is the REST surface the xMatix web and mobile clients themselves run on — there is no separate, lesser "integration API". Every entity in the tenant, standard or custom, is addressable by its logical name through one uniform set of endpoints under your organization's API base URL, with versioned routes (/api/v1/…) and JSON in both directions. Because it is the product's own API, two guarantees follow: requests execute as the authenticated user with the full security model applied on the server, and writes run the complete save pipeline — business rules, validations, scripts, workflows and approvals fire exactly as they do in the UI.
Authentication
Calls carry an OAuth 2.0 bearer token issued by your organization's sign-in authority — the same identity service that signs users into the product. The token identifies a user; the platform applies that user's security profiles, record security and restriction rules to every request. For an unattended integration, use a dedicated account granted exactly what the integration needs.
curl -H "Authorization: Bearer $TOKEN" \
"https://<your-api-host>/api/v1/data/Account?pageSize=50"
Entity names in routes are logical names as they appear in the data model (for example Account, not the display label) — find them in Setup → Design Studio → Entities, via the CLI's metadata pull, or through the MCP endpoint's describe tool.
Reading records
| Endpoint | What it returns |
|---|---|
GET /api/v1/data/{entity} | A page of records, with total count and paging echo |
GET /api/v1/data/{entity}/{id} | One record; ?relations= expands related records inline |
GET /api/v1/data/{entity}/{id}/{relation} | The records related through a named relation |
List queries compose through query parameters:
| Parameter | Meaning |
|---|---|
filter | A filter expression over the entity's fields, evaluated in the database — the query-filter side of Expressions |
search | Free-text term, matched across the entity's searchable fields and combined with filter |
orderBy | Sort expression |
relations | Related records to include inline |
ids | Comma-separated record ids — fetch a known set in one call |
additionalFields | Extra fields to project beyond the default set; lookup fields included here are enriched with their display-name sibling |
pageSize, pageNo | Paging — pageSize defaults to 25, pageNo starts at 1 |
The response wraps the page: the records under data, the total count, and the page size and number echoed back — everything needed to iterate a large result set deterministically.
Writing records
| Endpoint | What it does |
|---|---|
POST /api/v1/data/{entity} | Creates a record from a JSON body; returns 201 with the created record and its location |
PATCH /api/v1/data/{entity}/{id} | Partial update — send only the fields you are changing |
DELETE /api/v1/data/{entity}/{id} | Deletes the record (subject to the tenant's delete and recycle-bin behavior) |
POST /api/v1/data/{entity}/preview | Materializes the record a create would produce — defaults, rules and calculations applied — without persisting anything |
PATCH /api/v1/data/{entity}/{id}/preview | The same, for an update |
The preview endpoints are the API twin of what a form shows before you press save — useful for validating input and showing computed outcomes in your own UI without committing. Updates and deletes also respect the platform's record edit locks: while another user holds a record open for editing, the API rejects the write with a message naming the holder, exactly as the product does.
Actions and bulk operations
| Endpoint | What it does |
|---|---|
POST /api/v1/data/{entity}/{id}/{action} | Invokes a named entity action on one record — the same actions buttons invoke in the UI |
POST /api/v1/data/bulk/{entity}/{action} | Invokes an action across many records |
POST /api/v1/data/bulk/{entity} | Creates many records in one call |
PATCH /api/v1/data/bulk/{entity} | Updates many records in one call |
GET /api/v1/data/bulk/{entity}/{ids} | Fetches a set of records by id |
Actions are how server-side behavior is exposed by name — an action backed by an automation script or built-in handler does its work; an action with no handler succeeds without effect, so wire the handler before relying on the call.
Delta sync
For consumers that mirror data — the pattern behind the platform's offline mobile clients — GET /api/v1/sync/{entity}?since=<cursor> returns records changed or soft-deleted since a cursor (deletions arrive as records carrying a deletion timestamp), along with the next cursor to store. POST /api/v1/sync/manifest answers, for a batch of entity/cursor pairs in one round-trip, whether each has anything new. Polling deltas is far cheaper than re-querying, and the cursor model makes catch-up after downtime automatic.
Metadata, read-only
The model itself — entities, fields, views, relations — is readable under /api/v1/metadata/…, which is the surface the CLI's metadata commands ride on. For programmatic discovery ("what fields does this entity have?"), prefer the metadata routes or the MCP describe tool over guessing from record payloads.
Common questions
Do API writes trigger the same automation as the UI?
Yes — identically. A record created through POST /api/v1/data/{entity} passes through the same lifecycle as a form save: defaults, validations, business rules, bound scripts, workflow triggers, approval gates. This cuts both ways: your integration gets the tenant's business logic for free, and a validation that blocks users blocks the API too — what runs when you save a record explains the order.
How should an integration page through large tables?
With pageSize/pageNo for one-off reads, and the delta-sync cursor for anything that mirrors data continuously. A nightly "fetch everything" job re-reads mostly unchanged rows; the same job on sync/{entity} reads only what changed, and the manifest endpoint tells it in one call which entities are even worth visiting.
Why does a filtered query return records my filter should exclude?
First check the field name — filters reference logical field names, and an unknown name will not match the way you expect. Then check the value form: picklists compare stored values rather than display labels, and lookups are ids. When a filter still misbehaves, reproduce it with a minimal query and compare against a saved view with the same condition in the product — the query-filter reference covers the semantics shared by both surfaces.
