The RestQL API uses the same powerful data handling system that the other API's and the offline data sync system in Maintenance Connection Everywhere.
This means that you will already have access to many powerful examples of what the API can accomplish by looking at the configuration and query system.
This also means that using the RestQL API does NOT bypass the powerful data normalization and validation systems built into MCe.
The RestQL API was built and optimized assuming that your requests and updates are mostly or entirely correct so errors are reported more minimally than if you were using the GraphQL or DataHub APIs which were built with different use cases in mind.
Be aware that this low-level connection of the API with operational conditions of MCe means that the following changes are NOT considered to be breaking changes even though they may technically break the API:
- Changes to Preferences & User settings impacting the behavior of the User connected with the API Key
- Adding validation, including blocking validation that previously was allowed
- Configuration that makes properties required that aren't required by the base system
- Configuration that renames properties
- Configuration or Application Upgrades that adds new properties, children, entities, relationships, etc
Great pains are taken to avoid breaking changes, but this is a non-exhaustive list of changes that are not considered breaking.
In order to get started here are some common use cases and examples of use. These can be further customized in order to achieve the actual results desired.
Fetching Data
Retrieve a single entity
Example 1: An Asset with the ID of 'Example City'
GET https://<server-url>/mce/api/rest/v1/asset(Example%20City)
Example 2: An Asset with the sync GUID of 'c2bb7778-b0f8-4294-b748-9b282cd043f3'
GET https://<server-url>/mce/api/rest/v1/asset(c2bb7778-b0f8-4294-b748-9b282cd043f3)
Example 3: An Asset with the PK of '1'
GET https://<server-url>/mce/api/rest/v1/asset(pk=1)
Retrieve a filtered set of entities
The filtering and querying capabilities of the RestQL API is quite significant. Many options are available so if you need help you may wish to reach out to support for additional ideas and guidance.
The filters are the same filtering available in GraphQL and List/Picker queries in the MCe User Interface. You have full access in the request to using the powerful query language built into MCe. This query system is inspired by OData and has many enhancements (for instance standard OData comparison operators like: eq and lt work properly. Extended details are found in the Query Engine documentation.
Operators
The expression language supports the following operators in order of precedence from highest to lowest. Operators in the same category have equal precedence.
In the below table, x , y , and z denote expressions, T denotes a type, and m denotes a member.
| Category | Operator | Description |
|---|---|---|
| Primary | x.m | Instance field or property access. Any public field or property can be accessed. |
| Primary | x.m(...) | Instance method invocation. The method must be public and supported. See full documentation for a list. |
| Primary | x[...] | Array or indexer access. Multi-dimensional arrays are not supported. |
| Primary | it | Current instance. In contexts where members of a current object are implicitly in scope, it refers to the entire object itself. |
| Primary | np(x) | Null-propagating expression. Expands it.Rel.Id to it != null && it.Rel != null ? it.Rel.Id : null. |
| Primary | np(x, y) | Null-propagating expression with default value. Expands it.Rel.Id to it != null && it.Rel != null ? it.Rel.Id : y. |
| Primary | iif(x, y, z) | Conditional expression. Alternate syntax for x ? y : z. |
| Primary | As(x) / As(x, y) | Checks whether casting a class to a type is valid; returns null if not. |
| Primary | Is(x) | Determines whether the entity has a specific type. |
| Unary | -x | Negation. Supported types: Int32, Int64, Decimal, Single, Double. |
| Unary | !x or not x | Logical negation. Operand must be Boolean. |
| Multiplicative | x * y | Multiplication. Supported types: Int32, UInt32, Int64, UInt64, Decimal, Single, Double. |
| Multiplicative | x / y | Division. Supported types: Int32, UInt32, Int64, UInt64, Decimal, Single, Double. |
| Multiplicative | x % y or x mod y | Remainder. Supported types: Int32, UInt32, Int64, UInt64, Decimal, Single, Double. |
| Additive | x + y | Addition or string concatenation. Concatenates if either operand is String; otherwise performs addition for Int32, UInt32, Int64, UInt64, Decimal, Single, Double, DateTime, TimeSpan. |
| Additive | x - y | Subtraction. Supported types: Int32, UInt32, Int64, UInt64, Decimal, Single, Double, DateTime, TimeSpan. |
| Additive | x & y | String concatenation. Operands may be of any type. |
| Relational | x = y / x == y / x eq y / x equal y | Equal. Supported for reference and primitive types. Assignment is not supported. x == null is supported; x is null is not. |
| Relational | x != y / x <> y / x ne y / x neq y / x notequal y | Not equal. Supported for reference and primitive types. |
| Relational | x < y / x lt y / x LessThan y | Less than. Supported for all primitive types except Boolean, Object, and Guid. |
| Relational | x > y / x gt y / x GreaterThan y | Greater than. Supported for all primitive types except Boolean, Object, and Guid. |
| Relational | x <= y / x le y / x LessThanEqual y | Less than or equal. Supported for all primitive types except Boolean, Object, and Guid. |
| Relational | x >= y / x ge y / x GreaterThanEqual y | Greater than or equal. Supported for all primitive types except Boolean, Object, and Guid. |
| Logical AND | x && y / x and y / x AndAlso y | Logical AND. Operands must be Boolean. |
| Logical OR | x || y / x or y / x OrElse y | Logical OR. Operands must be Boolean. |
| Conditional | x ? y : z | Evaluates y if x is true; evaluates z if x is false. |
| Conditional | x ?? y | Evaluates x if not null; otherwise evaluates y (null-coalescing). |
Yes you can also use parenthesis to adjust order of precedence.
Sequence operators
The following standard query operators are supported/permitted, where *seq is a collection in an entity instance, *predicate is a boolean expression and selector is an expression of any type.
| Query Operator | Example Expression | Return Type | Info |
|---|---|---|---|
| Aggregate | |||
| dynamic | Dynamically runs an aggregate function. | ||
| All | seq.All(predicate) | bool | Determines whether all the elements of a sequence satisfy a condition. |
| Any | seq.Any() or seq.Any(predicate) | bool | Determines whether a sequence contains any elements. |
| Average | seq.Average(selector) | Single numeric value | Computes the average of a sequence of numeric values. |
| Cast | seq.Cast(string type) | Queryable | Converts the elements of the specified type. |
| Contains | seq.Contains(selector) | bool | Determines whether a sequence contains a specified element. |
| Count | seq.Count() > 3 or seq.Count(predicate) | int | Returns the number of elements in a sequence. |
| DefaultIfEmpty | seq.DefaultIfEmpty() or seq.DefaultIfEmpty(defaultValue) | Queryable | Returns the elements of the specified sequence, or the type parameter's default value in a singleton collection if the sequence is empty. |
| Distinct | seq.Distinct() | Queryable | Returns distinct elements from a sequence by using the default equality comparer to compare values. |
| Except | |||
| Queryable | Produces the set difference of two sequences. | ||
| First | seq.First() or seq.First(predicate) | dynamic | Returns the first element of a sequence. |
| FirstOrDefault | seq.FirstOrDefault() or seq.FirstOrDefault(predicate) | dynamic | Returns the first element of a sequence, or a default value if the sequence contains no elements. |
| GroupBy | seq.GroupBy(keySelector) or seq.GroupBy(keySelector, elementSelector) | Queryable | Groups the elements of a sequence according to a specified key string function and creates a result value from each group and its key. |
| GroupByMany | |||
| Enumerable | Groups the elements of a sequence according to multiple specified key string functions and creates a result value from each group (and subgroups) and its key. | ||
| GroupJoin | |||
| Queryable | Correlates the elements of two sequences based on equality of keys and groups the results. The default equality comparer is used to compare keys. | ||
| Intersect | |||
| Queryable | Produces the set intersection of two sequences. | ||
| Join | |||
| Queryable | Correlates the elements of two sequences based on matching keys. The default equality comparer is used to compare keys. | ||
| Last | seq.Last() or seq.Last(predicate) | dynamic | Returns the last element of a sequence. Maybe not supported in all scenarios. |
| LastOrDefault | seq.LastOrDefault() or seq.LastOrDefault(predicate) | dynamic | Returns the last element of a sequence, or a default value if the sequence contains no elements. Maybe not supported in all scenarios. |
| LongCount | seq.LongCount() or seq.LongCount(predicate) | long | Returns the number of elements in a sequence as a long. |
| Max | seq.Max(selector) | Single numeric value | Returns the maximum value in a sequence of values. |
| Min | seq.Min(selector) | Single numeric value | Returns the minimum value in a sequence of values. |
| OfType | seq.OfType(string type) | Queryable | Filters the elements based on a specified type. |
| OrderBy | seq.OrderBy(selector) or seq.OrderByDescending(selector) | OrderedQueryable | Sorts the elements of a sequence in ascending or descending order according to a key. |
| Reverse | |||
| Queryable | Inverts the order of the elements in a sequence. Maybe not supported in all scenarios. | ||
| Single | seq.Single() or seq.Single(predicate) | dynamic | Returns the only element of a sequence, and throws an exception if there is not exactly one element in the sequence. |
| SingleOrDefault | seq.SingleOrDefault() or seq.SingleOrDefault(predicate) | dynamic | Returns the only element of a sequence, or a default value if the sequence is empty; throws an exception if there is more than one element in the sequence. |
| Skip | seq.Skip(count) | Queryable | Bypasses a specified number of elements in a sequence and then returns the remaining elements. |
| SkipWhile | seq.SkipWhile(predicate) | Queryable | Bypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements. Maybe not supported in all scenarios. |
| Sum | seq.Sum(selector) | Single numeric value | Computes the sum of a sequence of numeric values. |
| Take | seq.Take(count) | Queryable | Returns a specified number of contiguous elements from the start of a sequence. |
| TakeWhile | seq.TakeWhile(predicate) | Queryable | Returns elements from a sequence as long as a specified condition is true. Maybe not supported in all scenarios. |
| ThenBy | seq.ThenBy(selector) or seq.ThenByDescending(selector) | OrderedQueryable | Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. |
| Where | seq.Where(predicate) | Queryable | Filters a sequence of values based on a predicate. |
In predicate and selector expressions, members of the current instance for a sequence operator are automatically in scope. The instance itself can be referenced using the keyword it.
Example:
Orders.Any(Total >= 1000)Filtering
Example 1 — Get all Work Orders in the Example Repair Center
GET https://<server-url>/mce/api/rest/v1/workOrders?$filter=RepairCenterID%20eq%20"Example"Example 2 — Get Assets modified since July 15, 2022
GET https://<server-url>/mce/api/rest/v1/assets?$filter=RowVersionDate%20%3E%20DateTime(2022,7,15)Example 3 — Get all open Work Orders with Target Hours < 5
GET https://<server-url>/mce/api/rest/v1/workOrders?$filter=IsOpen%20eq%20true%20and%20TargetHours%20<%205Paging
Paging is designed to cover most page-related use cases. When paging, it is strongly recommended to also apply ordering to ensure consistent results across multiple calls.
- Maximum page size: 500
Example — Fetch Accounts page 3 with page size 75
GET https://<server-url>/mce/api/rest/v1/accounts?pageNumber=3&pageSize=75Ordering
Ordering uses SQL-like expressions. List properties in priority order. If direction is omitted, ascending is assumed.
Example — Order Customers by Repair Center ID and Address descending
GET https://<server-url>/mce/api/rest/v1/customers?$orderby=RepairCenterID,%20Address%20descGet with Children
The RestQL API treats entities as a graph, allowing children to be requested alongside parent entities in a single request.
Example 1 — Fetch Tasks and Labor Costs for Work Orders
GET https://<server-url>/mce/api/rest/v1/workOrders?$expand=tasks,laborCostsExample 2 — Get Tasks of a Work Order plus images and signatures
GET https://<server-url>/mce/api/rest/v1/workOrder(TO-10982)/tasks?$expand=images,signaturesCreate & Update
The JSON parsing engine is tolerant and may accept values rejected by stricter parsers (e.g., trailing commas, comments, single quotes). This is useful for development and debugging but should not be relied on in production, as behavior may change between versions.
Create
Create operations use POST requests.
Because the data engine is optimized for offline and unreliable connectivity:
- A Create may automatically convert to an Update if the record already exists.
- Validation still runs as if creating a new row.
- Required fields must always be provided.
Example 1 — Create an Asset
POST https://<server-url>/mce/api/rest/v1/asset{
"syncGUID": "c5cb3682-487d-4d86-b1eb-a06843115aae",
"id": "Example City",
"name": "City of Example",
"typeValue": "L",
"classificationID": "CITY",
"repairCenterSyncGUID": "2f13bd5f-3b36-4c92-9c4c-88cf9b88fd1f",
"inService": false,
"requesterCanView": true,
"parentID": "AM",
"status": "C",
"address": "127 1ST. S.W."
}Example 2 — Create multiple Categories
POST https://<server-url>/mce/api/rest/v1/categories[
{
"syncGUID": "2ff2f4aa-4920-464e-951b-d462e76aa865",
"id": "API-Category-1",
"name": "API Category 1",
"active": true,
"udfBit2": true,
"udfChar1": "Example",
"udfDate1": "2023-04-20T23:23:51.412Z"
},
{
"syncGUID": "92eafb83-a053-4170-97d6-66dabf4c65fd",
"id": "API-Category-2",
"name": "API Category 2",
"active": true,
"udfBit2": true,
"udfChar1": "Example",
"udfDate1": "2023-04-20T23:23:51.412Z"
}
]Update
Update operations use PUT requests.
Because the engine is optimized for offline and high-latency environments:
- References can be provided using PK / ID / Name / SyncGUID.
- The system automatically resolves and corrects references.
- Sparse updates are supported.
- Repeat update detection is available via audit tables.
Example 1 — Update an Asset (key in JSON)
PUT https://<server-url>/mce/api/rest/v1/asset{
"syncGUID": "c5cb3682-487d-4d86-b1eb-a06843115aae",
"id": "Example City",
"name": "City of Example - Updated"
}Example 2 — Update an Asset (key in URL)
PUT https://<server-url>/mce/api/rest/v1/asset(c5cb3682-487d-4d86-b1eb-a06843115aae){
"name": "City of Example - Updated"
}Example 3 — Update multiple Categories
PUT https://<server-url>/mce/api/rest/v1/categories[
{
"syncGUID": "2ff2f4aa-4920-464e-951b-d462e76aa865",
"id": "API-Category-1",
"active": false
},
{
"syncGUID": "92eafb83-a053-4170-97d6-66dabf4c65fd",
"id": "API-Category-2",
"active": false
}
]Example 4 — Update Specifications for a Parent Asset
PUT https://<server-url>/mce/api/rest/v1/asset(Example%20city)/specifications[
{
"syncGUID": "a52854d6-ab78-4109-8fe3-f430d565d116",
"text": "Example Spec Value"
},
{
"syncGUID": "287e156e-3d24-477c-ae75-56292ff067ae",
"date": "2023-04-21T03:29:19.249Z"
}
]Delete
Delete operations use DELETE requests.
All operations pass through the audit system:
- Tracks who made the change and when
- Enables recovery from previous states in many cases
Deletes are not inherently safe, but auditing reduces business impact. They remain a standard API capability.
Example — Delete an Asset Specification
DELETE https://<server-url>/mce/api/rest/v1/asset(example%20city)/specifications(c4790717-3dc0-4474-a35c-f2d6dcb5be2b)