RestQL (REST) MCe
Examples of Use

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.

CategoryOperatorDescription
Primaryx.mInstance field or property access. Any public field or property can be accessed.
Primaryx.m(...)Instance method invocation. The method must be public and supported. See full documentation for a list.
Primaryx[...]Array or indexer access. Multi-dimensional arrays are not supported.
PrimaryitCurrent instance. In contexts where members of a current object are implicitly in scope, it refers to the entire object itself.
Primarynp(x)Null-propagating expression. Expands it.Rel.Id to it != null && it.Rel != null ? it.Rel.Id : null.
Primarynp(x, y)Null-propagating expression with default value. Expands it.Rel.Id to it != null && it.Rel != null ? it.Rel.Id : y.
Primaryiif(x, y, z)Conditional expression. Alternate syntax for x ? y : z.
PrimaryAs(x) / As(x, y)Checks whether casting a class to a type is valid; returns null if not.
PrimaryIs(x)Determines whether the entity has a specific type.
Unary-xNegation. Supported types: Int32, Int64, Decimal, Single, Double.
Unary!x or not xLogical negation. Operand must be Boolean.
Multiplicativex * yMultiplication. Supported types: Int32, UInt32, Int64, UInt64, Decimal, Single, Double.
Multiplicativex / yDivision. Supported types: Int32, UInt32, Int64, UInt64, Decimal, Single, Double.
Multiplicativex % y or x mod yRemainder. Supported types: Int32, UInt32, Int64, UInt64, Decimal, Single, Double.
Additivex + yAddition or string concatenation. Concatenates if either operand is String; otherwise performs addition for Int32, UInt32, Int64, UInt64, Decimal, Single, Double, DateTime, TimeSpan.
Additivex - ySubtraction. Supported types: Int32, UInt32, Int64, UInt64, Decimal, Single, Double, DateTime, TimeSpan.
Additivex & yString concatenation. Operands may be of any type.
Relationalx = y / x == y / x eq y / x equal yEqual. Supported for reference and primitive types. Assignment is not supported. x == null is supported; x is null is not.
Relationalx != y / x <> y / x ne y / x neq y / x notequal yNot equal. Supported for reference and primitive types.
Relationalx < y / x lt y / x LessThan yLess than. Supported for all primitive types except Boolean, Object, and Guid.
Relationalx > y / x gt y / x GreaterThan yGreater than. Supported for all primitive types except Boolean, Object, and Guid.
Relationalx <= y / x le y / x LessThanEqual yLess than or equal. Supported for all primitive types except Boolean, Object, and Guid.
Relationalx >= y / x ge y / x GreaterThanEqual yGreater than or equal. Supported for all primitive types except Boolean, Object, and Guid.
Logical ANDx && y / x and y / x AndAlso yLogical AND. Operands must be Boolean.
Logical ORx || y / x or y / x OrElse yLogical OR. Operands must be Boolean.
Conditionalx ? y : zEvaluates y if x is true; evaluates z if x is false.
Conditionalx ?? yEvaluates 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 OperatorExample ExpressionReturn TypeInfo
Aggregate
dynamicDynamically runs an aggregate function.
Allseq.All(predicate)boolDetermines whether all the elements of a sequence satisfy a condition.
Anyseq.Any() or seq.Any(predicate)boolDetermines whether a sequence contains any elements.
Averageseq.Average(selector)Single numeric valueComputes the average of a sequence of numeric values.
Castseq.Cast(string type)QueryableConverts the elements of the specified type.
Containsseq.Contains(selector)boolDetermines whether a sequence contains a specified element.
Countseq.Count() > 3 or seq.Count(predicate)intReturns the number of elements in a sequence.
DefaultIfEmptyseq.DefaultIfEmpty() or seq.DefaultIfEmpty(defaultValue)QueryableReturns the elements of the specified sequence, or the type parameter's default value in a singleton collection if the sequence is empty.
Distinctseq.Distinct()QueryableReturns distinct elements from a sequence by using the default equality comparer to compare values.
Except
QueryableProduces the set difference of two sequences.
Firstseq.First() or seq.First(predicate)dynamicReturns the first element of a sequence.
FirstOrDefaultseq.FirstOrDefault() or seq.FirstOrDefault(predicate)dynamicReturns the first element of a sequence, or a default value if the sequence contains no elements.
GroupByseq.GroupBy(keySelector) or seq.GroupBy(keySelector, elementSelector)QueryableGroups the elements of a sequence according to a specified key string function and creates a result value from each group and its key.
GroupByMany
EnumerableGroups 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
QueryableCorrelates the elements of two sequences based on equality of keys and groups the results. The default equality comparer is used to compare keys.
Intersect
QueryableProduces the set intersection of two sequences.
Join
QueryableCorrelates the elements of two sequences based on matching keys. The default equality comparer is used to compare keys.
Lastseq.Last() or seq.Last(predicate)dynamicReturns the last element of a sequence. Maybe not supported in all scenarios.
LastOrDefaultseq.LastOrDefault() or seq.LastOrDefault(predicate)dynamicReturns the last element of a sequence, or a default value if the sequence contains no elements. Maybe not supported in all scenarios.
LongCountseq.LongCount() or seq.LongCount(predicate)longReturns the number of elements in a sequence as a long.
Maxseq.Max(selector)Single numeric valueReturns the maximum value in a sequence of values.
Minseq.Min(selector)Single numeric valueReturns the minimum value in a sequence of values.
OfTypeseq.OfType(string type)QueryableFilters the elements based on a specified type.
OrderByseq.OrderBy(selector) or seq.OrderByDescending(selector)OrderedQueryableSorts the elements of a sequence in ascending or descending order according to a key.
Reverse
QueryableInverts the order of the elements in a sequence. Maybe not supported in all scenarios.
Singleseq.Single() or seq.Single(predicate)dynamicReturns the only element of a sequence, and throws an exception if there is not exactly one element in the sequence.
SingleOrDefaultseq.SingleOrDefault() or seq.SingleOrDefault(predicate)dynamicReturns 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.
Skipseq.Skip(count)QueryableBypasses a specified number of elements in a sequence and then returns the remaining elements.
SkipWhileseq.SkipWhile(predicate)QueryableBypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements. Maybe not supported in all scenarios.
Sumseq.Sum(selector)Single numeric valueComputes the sum of a sequence of numeric values.
Takeseq.Take(count)QueryableReturns a specified number of contiguous elements from the start of a sequence.
TakeWhileseq.TakeWhile(predicate)QueryableReturns elements from a sequence as long as a specified condition is true. Maybe not supported in all scenarios.
ThenByseq.ThenBy(selector) or seq.ThenByDescending(selector)OrderedQueryablePerforms a subsequent ordering of the elements in a sequence in ascending order according to a key.
Whereseq.Where(predicate)QueryableFilters 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<%205

Paging

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=75

Ordering

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%20desc

Get 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,laborCosts

Example 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,signatures

Create & 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)