Find Asset Administration Shells in the Digital Twin Registry

Specification defines endpoint for retrieving shells from the Digital Twin Registry:

The endpoint /aas-api/v3/shell-descriptors supports filtering and paging in order to give the client control over how many and which resources it wants to retrieve. All expressions are defined using the Asset Administration Shell as a reference.

Filtering reduces the amount of retrieved shells by given criteria, paging returns a subset of the results. With sorting, the retrieved results are returned in a predefined order.


The Digital Twin Registry API supports filtering via RQL (Resource Query Language)externallink 20.

Filtering of results is specified by the value of the query parameter filter.

The general form of the query string looks as follows:

filter={filter expression}&option=sort((+|-)field)

If there is an issue processing the filter expression, the response will provide details on the error.


The value for {filter expression} is an expression that uses one of the following operators.

  • For comparison:

    • eq

    • in

    • ne

    • gt

    • ge

    • lt

    • le

    • like

    • likeIgnoreCase

  • Logical operators:

    • and

    • or

    • not

The following examples illustrate how an RQL {filter expression} could look like.

Return Shells of assetKind "INSTANCE":


Return Shells that carry a label named "A":


Return Shells with a given assetKind and at least one label with name "Floor1":

and(eq(assetKind,"INSTANCE"), eq(,"Floor1"))

Return Shells with a given specificAssetIds

and(eq(,"AssetId"), eq(specificAssetIds.value,"Floor1"))

Supported properties

Table 1. Supported properties
Entity Properties


globalAssetId, idShort, assetType, assetKind, specificAssetIds, labels, groups


name, shell




name, value, shell

Supported operators:

  • Shell.assetType - provided value should be UTF8-BASE64-URL-encoded

  • Relation properties - are the properties used to cascade to another entity. Examples:

    • eq(, "Floor1")

    • in(, 1, 2)

    • and(eq(,"AssetId"), eq(shell.specificAssetIds.value,"Floor1"))

If additional properties are required, please contact us at


The endpoint provides cursor pagination.

Cursor pagination

The cursor pagination methodology divides the entities of a larger dataset, based on various criteria.

  • This type of pagination is broadly intended for continuous pagination and therefore lacks the option to select a specific page index, in favor of basing the pagination on a specific cursor, provided by the back-end in the first response (assuming the dataset has more than one page).

  • In cursor pagination, once the client obtains the first page and a cursor for the next one, it can send the next request with that cursor as an argument, in order to fetch the next page.

  • Cursor pagination uses logarithmic solutions to scroll through a sorted dataset, which tends to perform increasingly better than offset pagination the larger the dataset and the closer to its end data is being fetched.

  • In other words, cursor pagination is preferable in the vast majority of use cases

One criterion to cursor pagination methodology is the size of the page, expressed as the limit argument expressed as a query parameter (e.g., ?limit=50).

The default and maximum page size is 500 elements. When a desired page size exceeds the maximum page size, the request will fail with an error.

Cursor pagination interaction lifecycle

This section summarizes the typical exchanges between client and server when cursor pagination is involved.

  1. The client requests data via a cursor-paginating endpoint, optionally parameterizing the request with:

    • a limit value up to, and defaulting to 500 elements

    • an RQL filter query (defaults to "all elements of that type" if absent)

  2. The server responds with the first page of elements.

    • If more elements are present in the dataset, the response body will contain a nextCursor value, expressed as a string.

  3. If the server response contains a nextCursor value, the client copies it as cursor in the query parameters of the next request

  4. In turn, the server provides a page of elements after that cursor, and again the nextCursor value in the next response, if any more pages are available.

  5. This exchange iterates ad lib. until the server stops providing a nextCursor value in the response.

  6. Once no nextCursor value is available to the client, it "knows" it has reached the end of the desired dataset, and can stop sending further requests.


With user-defined sorting, the client is responsible for preventing an arbitrary order of the retrieved results.

User-defined sorting can be achieved by using the RQL options operator with the keyword sort.

Two different use cases exist:

  • Use case 1: The client provides sorting information via the sort keyword.

    • Example: option=sort(-id)

    • Sort direction for a property can be given with:

      • + for ascending order

      • - for descending order

  • Use case 2: The client does not provide any sorting information.

    • The results will be sorted by default sorting.

    • Default sorting happens by ID and in ascending order, which implicitly also yields an ascending order by creation time.

If sorting is used with a cursor, the sort expression must NOT change from the previous request. Otherwise incorrect results or an error may occur.

Note that:

  • User-defined sorting may result in longer response times.

  • Even if the client provides sorting information, arbitrary ordering can still occur if parallel accesses (deleting or adding elements) take place.

  • If it is important that all elements are returned, user-defined sorting cannot be used.

Currently, only reviewed attributes from the root entity can be used. If a desired attribute is not available, please contact us.


Load shells that have the label blue assigned (filter) and sort them by idShort.

filter=eq(, "blue")&option=sort(+idShort)


The select operator trims each response down to the set of attributes defined in the arguments of the query. User-defined select can be requested via RQL for a root entity. All child entities will then not be loaded.

This means that response times can be significantly improved if only the requested information is returned.


Load only twin IDs (select) and do that only for twins that have the label blue assigned (filter).

select=id&filter=eq(, "blue")