Litmus Edge 4.4.x - Digital Twins
Dynamic attributes and transformations bind to live tag data, which arrives as a flat DeviceHub/DataHub message (fields
value,tagName,deviceID,datatype,timestamp, ... nopayloadwrapper). Full shape and field types: /reference/devicehub-message-format.md.
List Static Attributes
POST {{edgeUrl}}/digital-twins/v2
Updated in 4.3.x. This item replaces its 4.2.x counterpart.
The query itself is unchanged. What changed is AttributeType, which gains a third member: it is now Regular | UNS | AssetHierarchy.
| Value | Meaning |
|---|---|
Regular |
An ordinary static attribute |
UNS |
Derived from the Unified Namespace |
AssetHierarchy |
New in 4.3.x. Derived from the asset hierarchy set by SetInstanceAssetHierarchy |
This is the read path for asset hierarchy placement, and the reason this item sits in this folder.
Worth checking in client code: any switch or if/else over AttributeType written against 4.2.x handled two values and will now meet a third. The schema change is additive, but an exhaustive match that assumed two members is where it shows up. Make sure the default branch does something sensible.
Request Body
GraphQL Query
query ListStaticAttributes($input: ListStaticAttributeRequest!) {
ListStaticAttributes(input: $input) {
ID
ModelID
InstanceID
Key
Value
AttributeType
CreatedAt
UpdatedAt
}
}
Variables
{
"input": {
"ModelID": "{{dt_model_id}}",
"InstanceID": "{{dt_instance_id}}",
"Limit": 100,
"Offset": 0
}
}
Create Static Attribute
POST {{edgeUrl}}/digital-twins
Create Static Attribute
Requires: an existing model. The
ModelIDis not checked: an unknown ID still returns a new attribute that belongs to no model. Confirm the model withList Modelsbefore creating attributes.
Creates a new static attribute on a model. The attribute is part of the model's schema and is inherited by every instance unless overridden.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation CreateStaticAttributes($input: CreateStaticAttributesRequest!) {
CreateStaticAttributes(input: $input) {
ID
ModelID
InstanceID
Key
Value
CreatedAt
UpdatedAt
}
}
The singular
CreateStaticAttributemutation is deprecated in the schema ("No longer supported"). It still executes, but use the batch form above.
Variables (input: CreateStaticAttributesRequest!)
| Field | Type | Required | Description |
|---|---|---|---|
Items |
[Item!]! |
Yes | One entry per attribute to create. |
Items[].ModelID |
ID |
Yes | UUID of the model to attach the attribute to. |
Items[].Key |
String |
Yes | Attribute name. Must be unique within the model. |
Items[].Value |
String |
Yes | Attribute value. |
{
"input": {
"Items": [
{
"ModelID": "{{digital_twins_model_id}}",
"Key": "Model",
"Value": "New API Model"
}
]
}
}
Response
200 OK -- application/json. An array, one element per item submitted, each the same shape as one element of List Static Attributes.
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation CreateStaticAttributes ($input: CreateStaticAttributesRequest!) {
CreateStaticAttributes(input: $input) {
ID
ModelID
InstanceID
Key
Value
CreatedAt
UpdatedAt
}
}
Variables
{
"input": {
"Items": [
{
"ModelID": "{{digital_twins_model_id}}",
"Key": "Model",
"Value": "New API Model"
}
]
}
}
Response
Status: 200 OK
{
"data": {
"CreateStaticAttributes": [
{
"ID": "63E4BD05-CE85-41CD-99A6-F69CCD171240",
"ModelID": "3A923971-5DFC-4633-B5AF-A6BF8C0B60E9",
"InstanceID": null,
"Key": "Model",
"Value": "New API Model",
"CreatedAt": "2023-08-16T00:13:38.224123983Z",
"UpdatedAt": "2023-08-16T00:13:38.224126007Z"
}
]
}
}
Update Static Attribute
POST {{edgeUrl}}/digital-twins
Update Static Attribute
Updates the Key or Value of an existing static attribute. Pass the attribute's ID plus the new values. Identical schema is used to update both model-level definitions and instance-level overrides -- the endpoint behaves the same in either case.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation UpdateStaticAttributes($input: UpdateStaticAttributesRequest!) {
UpdateStaticAttributes(input: $input) {
ID
ModelID
InstanceID
Key
Value
CreatedAt
UpdatedAt
}
}
The singular
UpdateStaticAttributemutation is deprecated in the schema ("No longer supported"). It still executes, but use the batch form above.
Variables (input: UpdateStaticAttributesRequest!)
| Field | Type | Required | Description |
|---|---|---|---|
Items |
[Item!]! |
Yes | One entry per attribute to update. |
Items[].ID |
ID |
Yes | UUID of the static attribute. |
Items[].ModelID |
ID |
Yes | UUID of the parent model. |
Items[].Key |
String |
Yes | New attribute name. Must be unique within the model. |
Items[].Value |
String |
Yes | New attribute value. |
{
"input": {
"Items": [
{
"ID": "{{digital_twins_static_attribute_id}}",
"ModelID": "{{digital_twins_model_id}}",
"Key": "Model",
"Value": "Updated Value API Model"
}
]
}
}
Response
200 OK -- application/json. An array, one element per item submitted, same shape as List Static Attributes, reflecting the post-update values and a refreshed UpdatedAt.
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation UpdateStaticAttributes($input:UpdateStaticAttributesRequest!) {
UpdateStaticAttributes(input: $input) {
ID
ModelID
InstanceID
Key
Value
CreatedAt
UpdatedAt
}
}
Variables
{
"input": {
"Items": [
{
"ID": "{{digital_twins_static_attribute_id}}",
"ModelID": "{{digital_twins_model_id}}",
"Key": "Model",
"Value": "Updated Value API Model"
}
]
}
}
Response
Status: 200 OK
{
"data": {
"UpdateStaticAttributes": [
{
"ID": "63E4BD05-CE85-41CD-99A6-F69CCD171240",
"ModelID": "3A923971-5DFC-4633-B5AF-A6BF8C0B60E9",
"InstanceID": null,
"Key": "Model",
"Value": "Updated Value API Model",
"CreatedAt": "2023-08-16T00:13:38.224123983Z",
"UpdatedAt": "2023-08-16T00:23:12.872714078Z"
}
]
}
}
Delete Static Attribute
POST {{edgeUrl}}/digital-twins
Updated in 4.1.x:
DeleteStaticAttributeRequest.IDis now required (UUID!).
Delete Static Attribute
Deletes one or more static attributes by ID. Despite the singular name, the mutation accepts a list -- pass any number of items in the Items array.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation DeleteStaticAttributes($input: DeleteStaticAttributesRequest!) {
DeleteStaticAttributes(input: $input)
}
Variables
| Field | Type | Required | Description |
|---|---|---|---|
Items |
[Item!]! |
Yes | List of {ID} objects -- one per attribute to delete. |
Items[].ID |
ID |
Yes | UUID of the static attribute. |
{
"input": {
"Items": [
{ "ID": "{{digital_twins_static_attribute_id}}" }
]
}
}
Response
200 OK -- application/json. The mutation returns no data on success.
{ "data": { "DeleteStaticAttributes": null } }
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation DeleteStaticAttributes($input: DeleteStaticAttributesRequest!) {
DeleteStaticAttributes(input: $input)
}
Variables
{
"input": {
"Items": [
{
"ID": "{{digital_twins_static_attribute_id}}"
}
]
}
}
Response
Status: 200 OK
{
"data": {
"DeleteStaticAttributes": null
}
}
Upload an Image
POST {{edgeUrl}}/digital-twins
Upload an Image
Uploads an image to associate with a digital twin model (e.g. a photo or diagram of the asset). Multipart form-data request following the GraphQL multipart request spec.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body (form-data parts)
| Form key | Type | Description |
|---|---|---|
operations |
text |
GraphQL envelope describing the mutation, with null placeholder for the file. |
map |
text |
JSON mapping form file keys to placeholder locations within operations. |
1 |
file |
The image file (binary). The form key (1) must match what appears in map. |
operations value
{
"operationName": "UploadImage",
"variables": { "input": { "ObjectID": "{{digital_twins_model_id}}", "File": null } },
"query": "mutation UploadImage($input: ImageRequest!) { UploadImage(input: $input) }"
}
map value
{ "1": ["variables.input.File"] }
The ObjectID is the model UUID; the same endpoint is reused on instances by passing an instance UUID.
Response
200 OK -- application/json
| Field | Type | Description |
|---|---|---|
data.UploadImage |
Boolean |
true on success. |
{ "data": { "UploadImage": true } }
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
Response
Status: 200 OK
{
"data": {
"UploadImage": true
}
}
Delete an Image
POST {{edgeUrl}}/digital-twins
Delete an Image
Removes the image previously uploaded by Upload an Image from a model (or instance). Idempotent -- deleting when there is no image is a no-op.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation DeleteImage($input: DeleteImageRequest!) {
DeleteImage(input: $input)
}
Variables
| Field | Type | Required | Description |
|---|---|---|---|
ObjectID |
ID |
Yes | UUID of the model (or instance) whose image to delete. |
{
"input": {
"ObjectID": "{{digital_twins_model_id}}"
}
}
Response
200 OK -- application/json
| Field | Type | Description |
|---|---|---|
data.DeleteImage |
Boolean |
true on success. |
{ "data": { "DeleteImage": true } }
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation DeleteImage($input: DeleteImageRequest!) {
DeleteImage(input: $input)
}
Variables
{
"input": {
"ObjectID": "{{digital_twins_model_id}}"
}
}
Response
Status: 200 OK
{
"data": {
"DeleteImage": true
}
}
Get an Image
POST {{edgeUrl}}/digital-twins
Get an Image
Returns the image stored for one model or one instance, as a data URI ready to drop into an <img src>. This is how the Digital Twins UI shows the picture on a model or instance page. Upload with Upload an Image, remove with Delete an Image.
ObjectID takes a model UUID or an instance UUID. Instances hold their own image, which can differ from their model's.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
query GetImage($input: GetImageRequest!) {
GetImage(input: $input) {
ObjectID
Content
}
}
Variables (input: GetImageRequest!)
| Field | Type | Required | Description |
|---|---|---|---|
ObjectID |
UUID! |
Yes | Model or instance UUID. |
{
"input": {
"ObjectID": "{{digital_twins_model_id}}"
}
}
Response
200 OK -- application/json. data.GetImage is an Image:
| Field | Type | Description |
|---|---|---|
ObjectID |
UUID! |
The model or instance the image belongs to. |
Content |
String! |
Data URI, e.g. data:image/png;base64, ... or data:image/svg+xml;base64, ... (truncated in the examples). |
{
"data": {
"GetImage": {
"ObjectID": "05B0972B-CAEF-40C8-BE69-0CCB21C0E523",
"Content": "data:image/svg+xml;base64, PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIC4uLg=="
}
}
}
An object with no image, or an unknown UUID, returns key not found. To fetch several images in one call, and skip the missing ones instead of failing, use Get Images.
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
query GetImage($input: GetImageRequest!) {
GetImage(input: $input) {
ObjectID
Content
}
}
Variables
{
"input": {
"ObjectID": "{{digital_twins_model_id}}"
}
}
Response
Status: 200 OK
{
"data": {
"GetImage": {
"ObjectID": "05B0972B-CAEF-40C8-BE69-0CCB21C0E523",
"Content": "data:image/svg+xml;base64, PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNTIiIGhlaWdodD0iMTU2IiB2aWV3Qm94PSIwIDAgMjUyIDE1NiI+PHBhdGggZD0iTTU0IDUwIFE1NCA0NiA1OCA0NiBMMTEyIDQ2IFExMTYgNDYgMTE2IDUwIEwxMTYgNTggTDU0IDU4IFoiIGZpbGw9IiMzMTc4NWIiIHN0cm9rZT0iIzAwMTkxNCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48cGF0aCBkPSJNNzggNDggTDg4IDI2IEw4NCAyMCBRODMgMTIgOTEgMTIgTDEyNiAxMCBRMTMzIDEwIDEzMyAxNiBMMTMzIDIxIFExMzMgMjYgMTI3IDI2IEwxMDEgMjggTDkzIDQ4IFoiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzAwMTkxNCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48cmVjdCB4PSIxMjgiIHk9IjEyIiB3aWR0aD0iNiIgaGVpZ2h0PSIxMiIgcng9IjEiIGZpbGw9IiNiZGZmY2MiIHN0cm9rZT0iIzAwMTkxNCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48Y2lyY2xlIGN4PSIxMDQiIGN5PSIxMiIgcj0iMi41IiBmaWxsPSIjMzJkZDg5IiBzdHJva2U9IiMwMDE5MTQiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PHBhdGggZD0iTTk2IDMwIEwxMDAgMzYiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMTkxNCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48Y2lyY2xlIGN4PSI4NiIgY3k9IjUyIiByPSIyLjUiIGZpbGw9IiMzMmRkODkiIHN0cm9rZT0iIzAwMTkxNCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48cGF0aCBkPSJNMTM1IDE0IEwxODAgNiBMMTgwIDQyIEwxMzUgMjIgWiIgZmlsbD0iI2JkZmZjYyIgZmlsbC1vcGFjaXR5PSIwLjc1Ii8+PHJlY3QgeD0iMTgwIiB5PSI0IiB3aWR0aD0iNTAiIGhlaWdodD0iNTIiIHJ4PSIyIiBmaWxsPSIjMzJkZDg5IiBzdHJva2U9IiMwMDE5MTQiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PHJlY3QgeD0iMTg2IiB5PSIxMiIgd2lkdGg9IjM4IiBoZWlnaHQ9IjI2IiByeD0iMSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMDAxOTE0IiBzdHJva2Utd2lkdGg9IjMiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjxyZWN0IHg9IjE5MSIgeT0iMTYiIHdpZHRoPSIyIiBoZWlnaHQ9IjE2IiBmaWxsPSIjMDAxOTE0Ii8+PHJlY3QgeD0iMTk2IiB5PSIxNiIgd2lkdGg9IjMiIGhlaWdodD0iMTYiIGZpbGw9IiMwMDE5MTQiLz48cmVjdCB4PSIyMDIiIHk9IjE2IiB3aWR0aD0iMiIgaGVpZ2h0PSIxNiIgZmlsbD0iIzAwMTkxNCIvPjxyZWN0IHg9IjIwNyIgeT0iMTYiIHdpZHRoPSIxIiBoZWlnaHQ9IjE2IiBmaWxsPSIjMDAxOTE0Ii8+PHJlY3QgeD0iMjEzIiB5PSIxNiIgd2lkdGg9IjIiIGhlaWdodD0iMTYiIGZpbGw9IiMwMDE5MTQiLz48cGF0aCBkPSJNMTg2IDI0IEwyMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMTkxNCIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjxwYXRoIGQ9Ik0yNCA1OCBMMjI4IDU4IiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDE5MTQiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PC9zdmc+"
}
}
}
Get Images
POST {{edgeUrl}}/digital-twins
Get Images
Batch form of Get an Image: returns the images for a list of models and instances in one call. The Digital Twins UI uses it to draw the thumbnails on the model and instance grids.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
query GetImages($input: GetImagesRequest!) {
GetImages(input: $input) {
Images {
ObjectID
Content
}
}
}
Variables (input: GetImagesRequest!)
| Field | Type | Required | Description |
|---|---|---|---|
ObjectIDs |
[UUID!]! |
Yes | Model and instance UUIDs, in any mix. |
{
"input": {
"ObjectIDs": [
"{{digital_twins_model_id}}",
"{{digital_twins_instance_id}}"
]
}
}
Response
200 OK -- application/json. data.GetImages.Images holds one Image (ObjectID, Content as a data URI) per requested object that has an image. Objects without an image, and unknown UUIDs, are left out rather than raising an error, so match results on ObjectID instead of by position.
{
"data": {
"GetImages": {
"Images": [
{
"ObjectID": "05B0972B-CAEF-40C8-BE69-0CCB21C0E523",
"Content": "data:image/svg+xml;base64, PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIC4uLg=="
}
]
}
}
}
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
query GetImages($input: GetImagesRequest!) {
GetImages(input: $input) {
Images {
ObjectID
Content
}
}
}
Variables
{
"input": {
"ObjectIDs": [
"{{digital_twins_model_id}}",
"{{digital_twins_instance_id}}"
]
}
}
Response
Status: 200 OK
{
"data": {
"GetImages": {
"Images": [
{
"ObjectID": "05B0972B-CAEF-40C8-BE69-0CCB21C0E523",
"Content": "data:image/svg+xml;base64, PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNTIiIGhlaWdodD0iMTU2IiB2aWV3Qm94PSIwIDAgMjUyIDE1NiI+PHBhdGggZD0iTTU0IDUwIFE1NCA0NiA1OCA0NiBMMTEyIDQ2IFExMTYgNDYgMTE2IDUwIEwxMTYgNTggTDU0IDU4IFoiIGZpbGw9IiMzMTc4NWIiIHN0cm9rZT0iIzAwMTkxNCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48cGF0aCBkPSJNNzggNDggTDg4IDI2IEw4NCAyMCBRODMgMTIgOTEgMTIgTDEyNiAxMCBRMTMzIDEwIDEzMyAxNiBMMTMzIDIxIFExMzMgMjYgMTI3IDI2IEwxMDEgMjggTDkzIDQ4IFoiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzAwMTkxNCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48cmVjdCB4PSIxMjgiIHk9IjEyIiB3aWR0aD0iNiIgaGVpZ2h0PSIxMiIgcng9IjEiIGZpbGw9IiNiZGZmY2MiIHN0cm9rZT0iIzAwMTkxNCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48Y2lyY2xlIGN4PSIxMDQiIGN5PSIxMiIgcj0iMi41IiBmaWxsPSIjMzJkZDg5IiBzdHJva2U9IiMwMDE5MTQiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PHBhdGggZD0iTTk2IDMwIEwxMDAgMzYiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMTkxNCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48Y2lyY2xlIGN4PSI4NiIgY3k9IjUyIiByPSIyLjUiIGZpbGw9IiMzMmRkODkiIHN0cm9rZT0iIzAwMTkxNCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48cGF0aCBkPSJNMTM1IDE0IEwxODAgNiBMMTgwIDQyIEwxMzUgMjIgWiIgZmlsbD0iI2JkZmZjYyIgZmlsbC1vcGFjaXR5PSIwLjc1Ii8+PHJlY3QgeD0iMTgwIiB5PSI0IiB3aWR0aD0iNTAiIGhlaWdodD0iNTIiIHJ4PSIyIiBmaWxsPSIjMzJkZDg5IiBzdHJva2U9IiMwMDE5MTQiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PHJlY3QgeD0iMTg2IiB5PSIxMiIgd2lkdGg9IjM4IiBoZWlnaHQ9IjI2IiByeD0iMSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMDAxOTE0IiBzdHJva2Utd2lkdGg9IjMiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjxyZWN0IHg9IjE5MSIgeT0iMTYiIHdpZHRoPSIyIiBoZWlnaHQ9IjE2IiBmaWxsPSIjMDAxOTE0Ii8+PHJlY3QgeD0iMTk2IiB5PSIxNiIgd2lkdGg9IjMiIGhlaWdodD0iMTYiIGZpbGw9IiMwMDE5MTQiLz48cmVjdCB4PSIyMDIiIHk9IjE2IiB3aWR0aD0iMiIgaGVpZ2h0PSIxNiIgZmlsbD0iIzAwMTkxNCIvPjxyZWN0IHg9IjIwNyIgeT0iMTYiIHdpZHRoPSIxIiBoZWlnaHQ9IjE2IiBmaWxsPSIjMDAxOTE0Ii8+PHJlY3QgeD0iMjEzIiB5PSIxNiIgd2lkdGg9IjIiIGhlaWdodD0iMTYiIGZpbGw9IiMwMDE5MTQiLz48cGF0aCBkPSJNMTg2IDI0IEwyMjQgMjQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMTkxNCIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjxwYXRoIGQ9Ik0yNCA1OCBMMjI4IDU4IiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDE5MTQiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PC9zdmc+"
},
{
"ObjectID": "37A85041-81D8-4FC6-97E5-02BAEF438CFF",
"Content": "data:image/svg+xml;base64, PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNTIiIGhlaWdodD0iMTU2IiB2aWV3Qm94PSIwIDAgMjUyIDE1NiI+PHJlY3QgeD0iMzAiIHk9IjQ4IiB3aWR0aD0iMTcwIiBoZWlnaHQ9IjgiIHJ4PSIyIiBmaWxsPSIjMzE3ODViIiBzdHJva2U9IiMwMDE5MTQiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PHJlY3QgeD0iNDAiIHk9IjE4IiB3aWR0aD0iNTgiIGhlaWdodD0iMzAiIHJ4PSI1IiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiMwMDE5MTQiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PHBhdGggZD0iTTUyIDIyIEw1MiA0NCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAxOTE0IiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjxwYXRoIGQ9Ik02MiAyMiBMNjIgNDQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMTkxNCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48cGF0aCBkPSJNNzIgMjIgTDcyIDQ0IiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDE5MTQiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PHBhdGggZD0iTTgyIDIyIEw4MiA0NCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAxOTE0IiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjxyZWN0IHg9Ijk4IiB5PSIyOCIgd2lkdGg9IjEyIiBoZWlnaHQ9IjEwIiByeD0iMiIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMDAxOTE0IiBzdHJva2Utd2lkdGg9IjMiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjxjaXJjbGUgY3g9IjEzNyIgY3k9IjMzIiByPSIxNyIgZmlsbD0iIzMxNzg1YiIgc3Ryb2tlPSIjMDAxOTE0IiBzdHJva2Utd2lkdGg9IjMiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjxjaXJjbGUgY3g9IjEzNyIgY3k9IjMzIiByPSI2IiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiMwMDE5MTQiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PHBhdGggZD0iTTEzNyAxNiBMMTM3IDggTDIxNCA4IiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDE5MTQiIHN0cm9rZS13aWR0aD0iNSIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PHBhdGggZD0iTTIxNCAyIEwyMTQgMTQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMTkxNCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48cGF0aCBkPSJNMTU0IDMzIEwyMTQgMzMiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMTkxNCIgc3Ryb2tlLXdpZHRoPSI1IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48cGF0aCBkPSJNMjE0IDI3IEwyMTQgMzkiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwMTkxNCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48cGF0aCBkPSJNMTgwIDggbDYgMCBNMTgzIDUgbDMgMyBsLTMgMyIgc3Ryb2tlPSIjMDAxOTE0IiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjxwYXRoIGQ9Ik0yMCA2MiBMMjIwIDYyIiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDE5MTQiIHN0cm9rZS13aWR0aD0iMyIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PC9zdmc+"
}
]
}
}
}
Get Transformations
POST {{edgeUrl}}/digital-twins
Get Transformations
Lists every transformation defined on a model. A transformation is a named JSON Schema that dynamic attributes can reference to validate or shape their payload before it is published.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
query ListTransformations($input: ListTransformationRequest!) {
ListTransformations(input: $input) {
ID
Name
Schema
}
}
Variables
| Field | Type | Required | Description |
|---|---|---|---|
ModelID |
ID |
Yes | UUID of the model. |
{
"input": {
"ModelID": "{{digital_twins_model_id}}"
}
}
Response
200 OK -- application/json
| Field | Type | Description |
|---|---|---|
data.ListTransformations |
[Transformation!]! |
All transformations on the model. |
data.ListTransformations[].ID |
ID |
Transformation UUID. |
data.ListTransformations[].Name |
String |
Display name. |
data.ListTransformations[].Schema |
String |
JSON Schema as a string. Parse client-side. |
{
"data": {
"ListTransformations": [
{
"ID": "ABF0E58E-2D3C-4E85-A42F-18CFE9D94C91",
"Name": "apiTransformation",
"Schema": "{\"key\": \"value\"}"
}
]
}
}
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
query ListTransformations ($input:ListTransformationRequest!){
ListTransformations(input: $input) {
ID
Name
Schema
}
}
Variables
{
"input": {
"ModelID": "{{digital_twins_model_id}}"
}
}
Response
Status: 200 OK
{
"data": {
"ListTransformations": [
{
"ID": "ABF0E58E-2D3C-4E85-A42F-18CFE9D94C91",
"Name": "apiTransformation",
"Schema": "{\"key\": \"value\"}"
},
{
"ID": "30D86400-D103-4C5C-95E4-AB6F02FB00EE",
"Name": "Transformation",
"Schema": "{\n \"key\": \"value\"\n}"
}
]
}
}
Create Transformation
POST {{edgeUrl}}/digital-twins
Create Transformation
Creates a new transformation on a model. The Schema is a JSON Schema document represented as a string.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation CreateTransformation($input: CreateTransformationRequest!) {
CreateTransformation(input: $input) {
ID
Name
Schema
}
}
Variables
| Field | Type | Required | Description |
|---|---|---|---|
ModelID |
ID |
Yes | UUID of the parent model. |
Name |
String |
Yes | Transformation name. Must be unique in the model. |
Schema |
String |
Yes | JSON Schema as a string. |
{
"input": {
"ModelID": "{{digital_twins_model_id}}",
"Name": "apiTransformation",
"Schema": "{\"key\": \"value\"}"
}
}
Response
200 OK -- application/json. Returns the new transformation.
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation CreateTransformation($input: CreateTransformationRequest!) {
CreateTransformation(input: $input) {
ID
Name
Schema
}
}
Variables
{
"input": {
"ModelID": "{{digital_twins_model_id}}",
"Name": "apiTransformation",
"Schema": "{\"key\": \"value\"}"
}
}
Response
Status: 200 OK
{
"data": {
"CreateTransformation": {
"ID": "ABF0E58E-2D3C-4E85-A42F-18CFE9D94C91",
"Name": "apiTransformation",
"Schema": "{\"key\": \"value\"}"
}
}
}
Update Transformation
POST {{edgeUrl}}/digital-twins
Updated in 4.1.x:
UpdateTransformationRequest.IDis now required (UUID!).
Update Transformation
Updates the Name and/or Schema of an existing transformation.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation UpdateTransformation($input: UpdateTransformationRequest!) {
UpdateTransformation(input: $input) {
ID
Name
Schema
}
}
Variables
| Field | Type | Required | Description |
|---|---|---|---|
ID |
ID |
Yes | UUID of the transformation. |
Name |
String |
Yes | New transformation name. |
Schema |
String |
Yes | New JSON Schema (as a string). |
{
"input": {
"ID": "{{digital_twins_transformation_id}}",
"Name": "updatedTransformation",
"Schema": "{\"updatedKey\": \"updatedValue\"}"
}
}
Response
200 OK -- application/json. Returns the updated transformation.
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation UpdateTransformation ($input: UpdateTransformationRequest!) {
UpdateTransformation(input: $input) {
ID
Name
Schema
}
}
Variables
{
"input": {
"ID": "{{digital_twins_transformation_id}}",
"Name": "updatedTransformation",
"Schema": "{\"updatedKey\": \"updatedValue\"}"
}
}
Response
Status: 200 OK
{
"data": {
"UpdateTransformation": {
"ID": "ABF0E58E-2D3C-4E85-A42F-18CFE9D94C91",
"Name": "updatedTransformation",
"Schema": "{\"updatedKey\": \"updatedValue\"}"
}
}
}
Delete Transformation
POST {{edgeUrl}}/digital-twins
Updated in 4.1.x:
DeleteTransformationRequest.IDis now required (UUID!).
Delete Transformation
Deletes a single transformation by ID. Dynamic attributes that referenced the deleted transformation will fall back to no validation.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation DeleteTransformation($input: DeleteTransformationRequest!) {
DeleteTransformation(input: $input)
}
Variables
| Field | Type | Required | Description |
|---|---|---|---|
ID |
ID |
Yes | UUID of the transformation. |
{
"input": {
"ID": "{{digital_twins_transformation_id}}"
}
}
Response
200 OK -- application/json. No data returned on success.
{ "data": { "DeleteTransformation": null } }
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation DeleteTransformation ($input: DeleteTransformationRequest!){
DeleteTransformation(input: $input)
}
Variables
{
"input": {
"ID": "{{digital_twins_transformation_id}}"
}
}
Response
Status: 200 OK
{
"data": {
"DeleteTransformation": null
}
}
Validate Transformation
POST {{edgeUrl}}/digital-twins
Validate Transformation
Checks that a transformation expression compiles before you save it with Create Transformation or Update Transformation. The Digital Twins UI runs it as you type in the transformation editor. Nothing is stored.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
query ValidateTransformation($input: ValidateTransformationRequest!) {
ValidateTransformation(input: $input)
}
Variables (input: ValidateTransformationRequest!)
| Field | Type | Required | Description |
|---|---|---|---|
Expression |
String! |
Yes | The expression to check. |
SchemaType |
SchemaType! |
Yes | jsonata for a JSONata expression, json for a JSON document. |
{
"input": {
"Expression": "$.temperature * 1.8 + 32",
"SchemaType": "jsonata"
}
}
Response
200 OK -- application/json. data.ValidateTransformation is true when the expression is valid.
{ "data": { "ValidateTransformation": true } }
An invalid expression does not return false; it returns an error carrying the parser's message, which the UI shows under the editor:
{
"errors": [
{
"message": "failed to compile jsonata expression: JSONata error S0201 at token \"EOF\": unexpected end of expression",
"path": ["ValidateTransformation"]
}
],
"data": null
}
With SchemaType: json the message starts schema validation failed: json parsing error.
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
query ValidateTransformation($input: ValidateTransformationRequest!) {
ValidateTransformation(input: $input)
}
Variables
{
"input": {
"Expression": "$.temperature * 1.8 + 32",
"SchemaType": "jsonata"
}
}
Response
Status: 200 OK
{
"data": {
"ValidateTransformation": true
}
}
List Parameters
POST {{edgeUrl}}/digital-twins
List Parameters
Returns the parameters defined on the given owner. OwnerID is the UUID of either a model or an instance. Parameters are free-form Name/Value strings used by integrations and the LE UI for ad-hoc configuration that isn't surfaced as static/dynamic attributes.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
query ListParameters($input: ListParametersRequest) {
ListParameters(input: $input) {
ID
Name
Value
}
}
Variables
| Field | Type | Required | Description |
|---|---|---|---|
OwnerID |
ID |
Yes | UUID of the model or instance that owns the parameters. |
{
"input": {
"OwnerID": "{{digital_twins_model_id}}"
}
}
Response
200 OK -- application/json
| Field | Type | Description |
|---|---|---|
data.ListParameters |
[Parameter!]! |
All parameters on the owner. |
data.ListParameters[].ID |
ID |
Parameter UUID. |
data.ListParameters[].Name |
String |
Parameter name. |
data.ListParameters[].Value |
String |
Parameter value. |
{
"data": {
"ListParameters": [
{ "ID": "51D2E713-6150-4800-8779-9A1825BE19D7", "Name": "Updated key", "Value": "Updated value" },
{ "ID": "8FA45411-ECA0-44C0-BD2F-60341D55C581", "Name": "key1", "Value": "value1" }
]
}
}
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
query ListParameters($input: ListParametersRequest) {
ListParameters(input: $input) {
ID
Name
Value
}
}
Variables
{
"input": {
"OwnerID": "{{digital_twins_model_id}}"
}
}
Response
Status: 200 OK
{
"data": {
"ListParameters": [
{
"ID": "51D2E713-6150-4800-8779-9A1825BE19D7",
"Name": "Updated key",
"Value": "Updated value"
},
{
"ID": "8FA45411-ECA0-44C0-BD2F-60341D55C581",
"Name": "key1",
"Value": "value1"
}
]
}
}
Set Parameters
POST {{edgeUrl}}/digital-twins
Set Parameters (transactional)
Applies a set of parameter changes to a model atomically using the transactional model update mutation. The same mutation is used to create, update, and delete parameters in one call; Delete Parameters uses a payload that includes only the delete portion.
This is the same endpoint used by the LE UI to commit edits to a model's schema in one round-trip.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation TransactionalModelUpdate($input: TransactionalModelUpdateRequest!) {
TransactionalModelUpdate(input: $input)
}
Variables
The input is an envelope of per-operation arrays. Send only the arrays you need; the others can be empty Items: [].
| Field | Type | Description |
|---|---|---|
modelID |
ID |
UUID of the target model. |
createStaticAttributes |
{ Items } |
List of CreateStaticAttribute payloads to apply. |
updateStaticAttributes |
{ Items } |
List of UpdateStaticAttribute payloads. |
delStaticAttributes |
{ Items } |
List of {ID} objects for static attributes to delete. |
createDynamicAttributes |
{ Items } |
List of CreateDynamicAttribute payloads. |
updateDynamicAttributes |
{ Items } |
List of UpdateDynamicAttribute payloads. |
delDynamicAttributes |
{ Items } |
List of {ID} objects. |
createParameters |
{ Items } |
List of {Name, Value, OwnerID} parameter payloads to create. |
updateParameters |
{ Items } |
List of {ID, Name, Value} parameter payloads to update. |
delParameters |
{ Items } |
List of {ID} objects for parameters to delete. |
For the parameter-only use case, leave every attribute-related array empty and populate just createParameters / updateParameters / delParameters.
{
"input": {
"modelID": "{{digital_twins_model_id}}",
"createStaticAttributes": { "Items": [] },
"updateStaticAttributes": { "Items": [] },
"delStaticAttributes": { "Items": [] },
"createDynamicAttributes": { "Items": [] },
"updateDynamicAttributes": { "Items": [] },
"delDynamicAttributes": { "Items": [] }
}
}
Response
200 OK -- application/json. The mutation returns no data; failures surface in the GraphQL errors array.
{ "data": { "TransactionalModelUpdate": null } }
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation TransactionalModelUpdate($input: TransactionalModelUpdateRequest!) {
TransactionalModelUpdate(input: $input)
}
Variables
{
"input": {
"modelID": "{{digital_twins_model_id}}",
"createStaticAttributes": {
"Items": []
},
"updateStaticAttributes": {
"Items": []
},
"delStaticAttributes": {
"Items": []
},
"createDynamicAttributes": {
"Items": []
},
"updateDynamicAttributes": {
"Items": []
},
"delDynamicAttributes": {
"Items": []
},
"createTransformations": [],
"updateTransformations": [],
"deleteTransformations": [],
"setParameters": {
"Items": [
{
"ID": "8fa45411-eca0-44c0-bd2f-60341d55c581",
"Name": "key1",
"Value": "value1"
},
{
"ID": "51d2e713-6150-4800-8779-9a1825be19d7",
"Name": "Updated key",
"Value": "Updated value"
}
]
},
"deleteParameters": {
"Items": []
}
}
}
Response
Status: 200 OK
{
"data": {
"TransactionalModelUpdate": null
}
}
Delete Parameters
POST {{edgeUrl}}/digital-twins
Delete Parameters (transactional)
Variant of Set Parameters for the delete-only case. The mutation is identical (TransactionalModelUpdate); only the populated arrays differ.
To delete one or more parameters, populate delParameters.Items with {ID} objects.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation TransactionalModelUpdate($input: TransactionalModelUpdateRequest!) {
TransactionalModelUpdate(input: $input)
}
Variables (delete-only example)
{
"input": {
"modelID": "{{digital_twins_model_id}}",
"createStaticAttributes": { "Items": [] },
"updateStaticAttributes": { "Items": [] },
"delStaticAttributes": { "Items": [] },
"createDynamicAttributes": { "Items": [] },
"updateDynamicAttributes": { "Items": [] }
}
}
The above example shows the envelope without any deletions populated -- in practice you would fill delParameters.Items: [{ID: "..."}] with the parameter UUIDs to remove. See Set Parameters for the full set of arrays the envelope supports.
Response
200 OK -- application/json. No data returned on success.
{ "data": { "TransactionalModelUpdate": null } }
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation TransactionalModelUpdate($input: TransactionalModelUpdateRequest!) {
TransactionalModelUpdate(input: $input)
}
Variables
{
"input": {
"modelID": "{{digital_twins_model_id}}",
"createStaticAttributes": {
"Items": []
},
"updateStaticAttributes": {
"Items": []
},
"delStaticAttributes": {
"Items": []
},
"createDynamicAttributes": {
"Items": []
},
"updateDynamicAttributes": {
"Items": []
},
"delDynamicAttributes": {
"Items": []
},
"createTransformations": [],
"updateTransformations": [],
"deleteTransformations": [],
"setParameters": {
"Items": [
{
"ID": "56daf3aa-bdec-4e7e-8d6a-a5229eaa2988",
"Name": "hi",
"Value": "hello"
}
]
},
"deleteParameters": {
"Items": [
{
"ID": "51D2E713-6150-4800-8779-9A1825BE19D7"
},
{
"ID": "56DAF3AA-BDEC-4E7E-8D6A-A5229EAA2988"
}
]
}
}
}
Response
Status: 200 OK
{
"data": {
"TransactionalModelUpdate": null
}
}
Get Hierarchy
POST {{edgeUrl}}/digital-twins
Get Hierarchy
Returns the current hierarchy of a model as a nested tree. The hierarchy defines how attributes are organized into folders inside the model -- the same structure the LE UI shows in the model editor's tree view.
Each node references either an attribute (via AttributeID) or is a folder (IsFolder: true). Nodes may have child nodes via Childs.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
query GetHierarchy($input: GetHierarchyRequest!) {
GetHierarchy(input: $input)
}
Variables
| Field | Type | Required | Description |
|---|---|---|---|
ModelID |
ID |
Yes | UUID of the model. |
{
"input": {
"ModelID": "{{digital_twins_model_id}}"
}
}
Response
200 OK -- application/json. data.GetHierarchy is a single root node:
| Field | Type | Description |
|---|---|---|
Name |
String |
Node display name. "root" for the synthetic root. |
Node |
Node? |
Backing node record (null for the synthetic root). |
Node.ID |
ID |
Node UUID. |
Node.ModelID |
ID |
Parent model. |
Node.Position |
Int |
Display order among siblings (0-indexed). |
Node.ParentID |
ID? |
UUID of the parent node, or null for top-level nodes. |
Node.IsFolder |
Boolean |
true for organizational folders; false for attribute references. |
Node.AttributeID |
ID? |
UUID of the referenced static or dynamic attribute. Empty for folders. |
Attr |
object? | Resolved attribute record (denormalized for UI convenience). |
Childs |
[Node] |
Child nodes (recursive). |
{
"data": {
"GetHierarchy": {
"Name": "root",
"Node": null,
"Attr": null,
"Childs": [
{
"Name": "Manufacturer",
"Node": {
"ID": "d476d3df-8794-4d57-a991-623e8811fb6b",
"ModelID": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"Position": 0,
"ParentID": null,
"Name": "Manufacturer",
"IsFolder": false,
"AttributeID": "189edb70-1029-4ebf-ab40-27e5423c12ce"
}
}
]
}
}
}
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
query GetHierarchy ($input : GetHierarchyRequest!){
GetHierarchy(input: $input)
}
Variables
{
"input": {
"ModelID": "{{digital_twins_model_id}}"
}
}
Response
Status: 200 OK
{
"data": {
"GetHierarchy": {
"Name": "root",
"Node": null,
"Attr": null,
"Childs": [
{
"Name": "Manufacturer",
"Node": {
"ID": "d476d3df-8794-4d57-a991-623e8811fb6b",
"ModelID": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"Position": 0,
"ParentID": null,
"Name": "Manufacturer",
"IsFolder": false,
"AttributeID": "189edb70-1029-4ebf-ab40-27e5423c12ce",
"AttributeType": "static",
"CreatedAt": "2023-08-17T17:31:55.670316457Z",
"IndexUniqueAttrID": "root189edb70-1029-4ebf-ab40-27e5423c12ce",
"IndexType": "hierarchy_nodes",
"IndexModelID": "hierarchy_nodes3a923971-5dfc-4633-b5af-a6bf8c0b60e9"
},
"Attr": {
"ID": "189edb70-1029-4ebf-ab40-27e5423c12ce",
"ParentID": "00000000-0000-0000-0000-000000000000",
"ModelID": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"InstanceID": "00000000-0000-0000-0000-000000000000",
"Key": "Manufacturer",
"Value": "API Manufacturer",
"CreatedAt": "2023-08-16T00:03:50.174589529Z",
"UpdatedAt": "2023-08-16T00:23:38.740405183Z",
"IndexType": "static_attributes",
"IndexModelID": "static_attributes3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"IndexInstanceID": "static_attributes00000000-0000-0000-0000-000000000000",
"IndexUniqueModelIDName": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9Manufacturer"
},
"Childs": []
},
{
"Name": "New_Node",
"Node": {
"ID": "2562cb6d-36c1-47b2-bfac-1495377db3a5",
"ModelID": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"Position": 1,
"ParentID": null,
"Name": "New_Node",
"IsFolder": true,
"AttributeID": null,
"AttributeType": null,
"CreatedAt": "2023-08-17T17:31:55.670579242Z",
"IndexUniqueAttrID": "root2562cb6d-36c1-47b2-bfac-1495377db3a5",
"IndexType": "hierarchy_nodes",
"IndexModelID": "hierarchy_nodes3a923971-5dfc-4633-b5af-a6bf8c0b60e9"
},
"Attr": null,
"Childs": [
{
"Name": "Model",
"Node": {
"ID": "e028e1d1-9212-4e5b-8290-46104491ce93",
"ModelID": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"Position": 0,
"ParentID": "2562cb6d-36c1-47b2-bfac-1495377db3a5",
"Name": "Model",
"IsFolder": false,
"AttributeID": "3f44c811-d8cc-4735-b337-a0f30826641e",
"AttributeType": "static",
"CreatedAt": "2023-08-17T17:31:55.670918962Z",
"IndexUniqueAttrID": "2562cb6d-36c1-47b2-bfac-1495377db3a53f44c811-d8cc-4735-b337-a0f30826641e",
"IndexType": "hierarchy_nodes",
"IndexModelID": "hierarchy_nodes3a923971-5dfc-4633-b5af-a6bf8c0b60e9"
},
"Attr": {
"ID": "3f44c811-d8cc-4735-b337-a0f30826641e",
"ParentID": "00000000-0000-0000-0000-000000000000",
"ModelID": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"InstanceID": "00000000-0000-0000-0000-000000000000",
"Key": "Model",
"Value": "New API Model",
"CreatedAt": "2023-08-16T17:29:11.461961315Z",
"UpdatedAt": "2023-08-16T17:29:11.461962629Z",
"IndexType": "static_attributes",
"IndexModelID": "static_attributes3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"IndexInstanceID": "static_attributes00000000-0000-0000-0000-000000000000",
"IndexUniqueModelIDName": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9Model"
},
"Childs": []
}
]
}
]
}
}
}
Save All Hierarchy
POST {{edgeUrl}}/digital-twins
Save All Hierarchy
Replaces the entire hierarchy of a model in a single call. The mutation accepts the same nested tree shape that Get Hierarchy returns (with arrays of {Node, Childs} entries). Existing hierarchy nodes are reconciled; missing ones are deleted; new ones are created.
Use this when programmatically building or editing a model's tree -- it is much cheaper than per-node mutations.
Updated in 4.1.x:
UpdateHierarchyNodeRequest.ID / DeleteHierarchyNodeRequest.IDis now required (UUID!).
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation SaveAllHierarchy($modelId: UUID!, $input: [SaveAllHierarchyRequest]!) {
SaveAllHierarchy(modelId: $modelId, input: $input)
}
Variables
| Field | Type | Required | Description |
|---|---|---|---|
modelId |
UUID! |
Yes | UUID of the model whose hierarchy to replace. |
input |
[SaveAllHierarchyRequest]! |
Yes | Top-level nodes. Each node has Node (a {AttributeID, AttributeType, IsFolder, Name, Position} payload) and Childs (recursive). |
{
"modelId": "{{digital_twins_model_id}}",
"input": [
{
"Node": {
"AttributeID": "189edb70-1029-4ebf-ab40-27e5423c12ce",
"AttributeType": "static",
"IsFolder": false,
"Name": "Manufacturer",
"Position": 0
},
"Childs": []
}
]
}
AttributeType is "static" or "dynamic" and must match the kind of the attribute referenced by AttributeID.
Node.Name character rules
Node names are validated server-side and reject on a notAllowedCharacters tag.
Effective allowlist: Unicode letters, digits, space, _, - and .
Spaces and dots are permitted here, unlike Model.Name. Rejected: / & ( ) % ,
: + ' ° * ! ? @ # $ ^ ~ ; = [ ] { } | \ " < >. Unicode letters such as µ and
Ω pass; the Unicode symbol ° does not.
The ampersand is the common trip-up: folder names like Motor & Drive or
Process & Quality are rejected and have to be written Motor and Drive.
Response
200 OK -- application/json. Returns the persisted nodes with their freshly-assigned/refreshed UUIDs.
{
"data": {
"SaveAllHierarchy": [
{
"ID": "71ae472d-b2d5-43fd-8824-80aee150791b",
"ModelID": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"Position": 0,
"ParentID": "8cd29a23-816e-4f53-bd88-aed2a0564724",
"Name": "Model",
"IsFolder": false,
"AttributeID": "3f44c811-d8cc-4735-b337-a0f30826641e",
"AttributeType": "static",
"CreatedAt": "2023-08-18T21:57:49.16464345Z"
}
]
}
}
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation SaveAllHierarchy($modelId: UUID!, $input: [SaveAllHierarchyRequest]!) {
SaveAllHierarchy(modelId: $modelId, input: $input)
}
Variables
{
"modelId": "{{digital_twins_model_id}}",
"input": [
{
"Node": {
"AttributeID": "189edb70-1029-4ebf-ab40-27e5423c12ce",
"AttributeType": "static",
"IsFolder": false,
"Name": "Manufacturer",
"Position": 0
},
"Childs": []
},
{
"Node": {
"AttributeID": null,
"AttributeType": null,
"IsFolder": true,
"Name": "New_Node",
"Position": 1
},
"Childs": [
{
"Node": {
"AttributeID": "3f44c811-d8cc-4735-b337-a0f30826641e",
"AttributeType": "static",
"IsFolder": false,
"Name": "Model",
"Position": 0
},
"Childs": []
},
{
"Node": {
"AttributeID": "31A6EDDC-A455-4126-981C-0A6313915A89",
"AttributeType": "dynamic",
"IsFolder": false,
"Name": "Speed",
"Position": 1
},
"Childs": []
},
{
"Node": {
"AttributeID": null,
"AttributeType": null,
"IsFolder": true,
"Name": "New_Node",
"Position": 2
},
"Childs": [
{
"Node": {
"AttributeID": "3F44C811-D8CC-4735-B337-A0F30826641E",
"AttributeType": "static",
"IsFolder": false,
"Name": "Model",
"Position": 0
},
"Childs": []
}
]
}
]
}
]
}
Response
Status: 200 OK
{
"data": {
"SaveAllHierarchy": [
{
"ID": "71ae472d-b2d5-43fd-8824-80aee150791b",
"ModelID": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"Position": 0,
"ParentID": "8cd29a23-816e-4f53-bd88-aed2a0564724",
"Name": "Model",
"IsFolder": false,
"AttributeID": "3f44c811-d8cc-4735-b337-a0f30826641e",
"AttributeType": "static",
"CreatedAt": "2023-08-18T21:57:49.16464345Z",
"IndexUniqueAttrID": "8cd29a23-816e-4f53-bd88-aed2a05647243f44c811-d8cc-4735-b337-a0f30826641e",
"IndexType": "hierarchy_nodes",
"IndexModelID": "hierarchy_nodes3a923971-5dfc-4633-b5af-a6bf8c0b60e9"
},
{
"ID": "8424515e-a7f8-4cf4-b577-8ef999388538",
"ModelID": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"Position": 0,
"ParentID": null,
"Name": "Manufacturer",
"IsFolder": false,
"AttributeID": "189edb70-1029-4ebf-ab40-27e5423c12ce",
"AttributeType": "static",
"CreatedAt": "2023-08-18T21:57:49.163896372Z",
"IndexUniqueAttrID": "root189edb70-1029-4ebf-ab40-27e5423c12ce",
"IndexType": "hierarchy_nodes",
"IndexModelID": "hierarchy_nodes3a923971-5dfc-4633-b5af-a6bf8c0b60e9"
},
{
"ID": "d0d06af4-be65-4d2b-943c-4b2487f7e365",
"ModelID": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"Position": 0,
"ParentID": "92c483ac-5949-4a55-84c5-4e55c281ff87",
"Name": "Model",
"IsFolder": false,
"AttributeID": "3f44c811-d8cc-4735-b337-a0f30826641e",
"AttributeType": "static",
"CreatedAt": "2023-08-18T21:57:49.165856053Z",
"IndexUniqueAttrID": "92c483ac-5949-4a55-84c5-4e55c281ff873f44c811-d8cc-4735-b337-a0f30826641e",
"IndexType": "hierarchy_nodes",
"IndexModelID": "hierarchy_nodes3a923971-5dfc-4633-b5af-a6bf8c0b60e9"
},
{
"ID": "14ce6813-11b7-41d0-b785-ce1fdcdaf6b0",
"ModelID": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"Position": 1,
"ParentID": "8cd29a23-816e-4f53-bd88-aed2a0564724",
"Name": "Speed",
"IsFolder": false,
"AttributeID": "31a6eddc-a455-4126-981c-0a6313915a89",
"AttributeType": "dynamic",
"CreatedAt": "2023-08-18T21:57:49.165069353Z",
"IndexUniqueAttrID": "8cd29a23-816e-4f53-bd88-aed2a056472431a6eddc-a455-4126-981c-0a6313915a89",
"IndexType": "hierarchy_nodes",
"IndexModelID": "hierarchy_nodes3a923971-5dfc-4633-b5af-a6bf8c0b60e9"
},
{
"ID": "8cd29a23-816e-4f53-bd88-aed2a0564724",
"ModelID": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"Position": 1,
"ParentID": null,
"Name": "New_Node",
"IsFolder": true,
"AttributeID": null,
"AttributeType": null,
"CreatedAt": "2023-08-18T21:57:49.164185815Z",
"IndexUniqueAttrID": "root8cd29a23-816e-4f53-bd88-aed2a0564724",
"IndexType": "hierarchy_nodes",
"IndexModelID": "hierarchy_nodes3a923971-5dfc-4633-b5af-a6bf8c0b60e9"
},
{
"ID": "92c483ac-5949-4a55-84c5-4e55c281ff87",
"ModelID": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"Position": 2,
"ParentID": "8cd29a23-816e-4f53-bd88-aed2a0564724",
"Name": "New_Node",
"IsFolder": true,
"AttributeID": null,
"AttributeType": null,
"CreatedAt": "2023-08-18T21:57:49.165520481Z",
"IndexUniqueAttrID": "8cd29a23-816e-4f53-bd88-aed2a056472492c483ac-5949-4a55-84c5-4e55c281ff87",
"IndexType": "hierarchy_nodes",
"IndexModelID": "hierarchy_nodes3a923971-5dfc-4633-b5af-a6bf8c0b60e9"
}
]
}
}
Download Hierarchy CSV
POST {{edgeUrl}}/digital-twins
Download Hierarchy CSV
Exports a model's attribute hierarchy as CSV: one row per folder or attribute node. This is the Export CSV action on the model's Hierarchy tab, which saves the result as hierarchy-<ModelID>.csv. Use it to back up a hierarchy or edit it offline.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
query DownloadHierarchyCSV($input: DownloadCSVRequest!) {
DownloadHierarchyCSV(input: $input)
}
Variables (input: DownloadCSVRequest!)
| Field | Type | Required | Description |
|---|---|---|---|
ModelID |
UUID! |
Yes | Model whose hierarchy to export. |
InstanceID |
UUID |
No | Accepted, but on LE 4.0.x the output is the model's hierarchy either way. |
{
"input": {
"ModelID": "{{digital_twins_model_id}}"
}
}
Response
200 OK -- application/json. data.DownloadHierarchyCSV is the CSV as one string (type Any), header row first. Write it to a file as-is.
| Column | Description |
|---|---|
ID |
Node UUID. |
ModelID |
Owning model. |
Position |
Order among siblings, from 0. |
ParentID |
Parent node UUID; empty for a top-level folder. |
Name |
Node name. |
IsFolder |
true for a folder, false for an attribute. |
AttributeID |
Attribute UUID; the nil UUID for a folder. |
AttributeType |
static or dynamic for an attribute; empty for a folder. |
NodeType |
folder or attribute. |
{
"data": {
"DownloadHierarchyCSV": "ID,ModelID,Position,ParentID,Name,IsFolder,AttributeID,AttributeType,NodeType\n10796e93-725f-417e-9b75-957b6e32b90f,139fabdc-5ab1-461c-b231-5fb69c47ef77,1,c7a18a59-8b29-4749-8399-fbf75d98f492,LocationId,false,38e78057-60c1-4f21-9683-10693c49feeb,static,attribute\n...\nc7a18a59-8b29-4749-8399-fbf75d98f492,139fabdc-5ab1-461c-b231-5fb69c47ef77,0,,Properties,true,00000000-0000-0000-0000-000000000000,,folder\n"
}
}
Rows are not sorted by hierarchy; rebuild the tree from ParentID and Position. An unknown ModelID is not an error: it returns the header row only.
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
query DownloadHierarchyCSV($input: DownloadCSVRequest!) {
DownloadHierarchyCSV(input: $input)
}
Variables
{
"input": {
"ModelID": "{{digital_twins_model_id}}"
}
}
Response
Status: 200 OK
{
"data": {
"DownloadHierarchyCSV": "ID,ModelID,Position,ParentID,Name,IsFolder,AttributeID,AttributeType,NodeType\n10796e93-725f-417e-9b75-957b6e32b90f,139fabdc-5ab1-461c-b231-5fb69c47ef77,1,c7a18a59-8b29-4749-8399-fbf75d98f492,LocationId,false,38e78057-60c1-4f21-9683-10693c49feeb,static,attribute\n5597b5cd-4b14-4c31-8a88-1c6c1b98d285,139fabdc-5ab1-461c-b231-5fb69c47ef77,4,c7a18a59-8b29-4749-8399-fbf75d98f492,UpdatedAt,false,e8aeda58-b04f-4447-a859-51be459aa469,static,attribute\n609e32dd-730b-4739-bb8e-475e0df8e8b7,139fabdc-5ab1-461c-b231-5fb69c47ef77,2,c7a18a59-8b29-4749-8399-fbf75d98f492,GlobalUuid,false,593b3aab-afb6-42dd-ad90-cbc2188c52a6,static,attribute\nb968782d-8c4b-4065-96a2-7b0a75663af9,139fabdc-5ab1-461c-b231-5fb69c47ef77,3,c7a18a59-8b29-4749-8399-fbf75d98f492,CreatedAt,false,b2e7155f-41cf-4e47-bc71-4e01126111bd,static,attribute\nc7a18a59-8b29-4749-8399-fbf75d98f492,139fabdc-5ab1-461c-b231-5fb69c47ef77,0,,Properties,true,00000000-0000-0000-0000-000000000000,,folder\ncaf9472b-75bb-4394-b9bc-13c0ec2a18eb,139fabdc-5ab1-461c-b231-5fb69c47ef77,0,c7a18a59-8b29-4749-8399-fbf75d98f492,Status,false,0d047dc4-50f3-45af-97dc-a71842738b59,static,attribute\n"
}
}
List all Dynamic Attributes
POST {{edgeUrl}}/digital-twins/v2
Updated in 4.2.x. ListDynamicAttributeRequest gained Limit and Offset.
The replacement path for the removed Model.DynamicAttributes field. Same scoping and paging contract as ListStaticAttributes.
AttrDataType is json | string | number | boolean | array.
Request Body
GraphQL Query
query ListDynamicAttributes($input: ListDynamicAttributeRequest!) {
ListDynamicAttributes(input: $input) {
ID
ModelID
InstanceID
Name
Topic
Unit
DataType
AttrDataType
SkipParentKey
SchemaID
TopicExpression
TopicExpressionConverted
CreatedAt
UpdatedAt
}
}
Variables
{
"input": {
"ModelID": "{{dt_model_id}}",
"InstanceID": null,
"Limit": 100,
"Offset": 0
}
}
Create Dynamic Attribute
POST {{edgeUrl}}/digital-twins/v2
Requires: an existing model. The
ModelIDis not checked: an unknown ID still returns a new attribute that belongs to no model, so confirm the model withList Modelsfirst.DataTypemust be one ofjson,string,number,booleanorarray; anything else, such asfloat, fails withDT_VALIDATION_FAILED.
Updated in 4.3.x. This item replaces its 4.2.x counterpart.
CreateDynamicAttributeRequest gains NestedModelID and NestedInstanceID, and DynamicAttributeType gains a nestedModel member. Together they let one model reference another instead of restating its attributes.
| New field | Meaning |
|---|---|
AttrDataType |
Now json |
NestedModelID |
The model this attribute embeds |
NestedInstanceID |
The instance this attribute embeds |
Set NestedModelID to compose a model definition, or NestedInstanceID to point at one specific instance. They are alternatives: populate one, leave the other null. Both are ignored unless AttrDataType is nestedModel.
Both new fields are optional, so a 4.2.x request body still works unchanged and produces the same non-nested attribute it did before. The selection set above adds AttrDataType, NestedModelID and NestedInstanceID so the nesting is visible in the response; drop them if you do not use nesting.
Keep an eye on what you point at. Nothing stops a model from nesting a chain that leads back to itself, and a cycle only shows up when the twin is resolved, not when the attribute is created.
Topic, Name, Unit and DataType are NON_NULL in the schema: the request
fails validation if any of them is absent, even though Unit and Topic are
routinely empty strings. Send "" when there is no unit or topic.
Creating a Name that already exists on the model fails with
dynamic attribute id is not unique: operation not permitted. The message says
id, but the collision is on Name -- ID is server-assigned on create. Names
must be unique across the whole model, not per hierarchy node.
Name and Unit character rules
Neither field is a free-form string. Both are validated server-side and reject
on a notAllowedCharacters tag with no indication of the allowed set.
| Field | Allowed | Rejected |
|---|---|---|
Name |
any character | none observed |
Unit |
Unicode letters, digits, space, _, -, . |
everything else, listed below |
Unit rejects / & ( ) % , : + ' ° * ! ? @ # $ ^ ~ ; = [ ] { } | \ " < >. It
accepts µ and Ω (Unicode letters) but rejects ° (Unicode symbol).
Because the solidus is rejected, rate and ratio units such as mm/s, m/s2,
L/min and count/min cannot be stored as written, and neither can % or
°C. Rewrite them (mm per s) or store the unit elsewhere.
Request Body
GraphQL Query
mutation CreateDynamicAttribute ($input: CreateDynamicAttributeRequest!) {
CreateDynamicAttribute(input: $input) {
ID
ModelID
InstanceID
Topic
Name
Unit
DataType
AttrDataType
NestedModelID
NestedInstanceID
SchemaID
CreatedAt
UpdatedAt
}
}
Variables
{
"input": {
"ModelID": "{{dt_model_id}}",
"InstanceID": null,
"Topic": "line/1/motor",
"Name": "motor",
"Unit": "",
"DataType": "object",
"AttrDataType": "nestedModel",
"NestedModelID": "{{dt_nested_model_id}}",
"NestedInstanceID": null
}
}
Update Dynamic Attribute
POST {{edgeUrl}}/digital-twins/v2
Updated in 4.3.x. This item replaces its 4.2.x counterpart.
UpdateDynamicAttributeRequest gains the same NestedModelID and NestedInstanceID fields as the create mutation, so an existing attribute can be converted into a nested-model reference in place.
Set NestedModelID to compose a model definition, or NestedInstanceID to point at one specific instance. They are alternatives: populate one, leave the other null. Both are ignored unless AttrDataType is nestedModel.
This is a full update, not a patch: send the complete attribute. Omitting NestedModelID on an attribute that currently has one clears it, which silently turns a nested reference back into a plain attribute. Read the attribute first if you are updating only one field.
ID, Topic, Name, Unit and DataType are NON_NULL in the schema. Unit follows the same character rules as on create (see Name and Unit character rules under Create Dynamic Attribute).
Request Body
GraphQL Query
mutation UpdateDynamicAttribute ($input: UpdateDynamicAttributeRequest!) {
UpdateDynamicAttribute(input: $input) {
ID
ModelID
InstanceID
Topic
Name
Unit
DataType
AttrDataType
NestedModelID
NestedInstanceID
SchemaID
CreatedAt
UpdatedAt
}
}
Variables
{
"input": {
"ID": "{{dt_dynamic_attribute_id}}",
"ModelID": "{{dt_model_id}}",
"InstanceID": null,
"Topic": "line/1/motor",
"Name": "motor",
"Unit": "",
"DataType": "object",
"AttrDataType": "nestedModel",
"NestedModelID": "{{dt_nested_model_id}}",
"NestedInstanceID": null
}
}
Download Dynamic Attributes
POST {{edgeUrl}}/digital-twins
Download Dynamic Attributes
Exports the dynamic attributes of a model as a CSV string. The CSV is compatible with Upload Dynamic Attribute CSV for round-trip workflows: download, edit in a spreadsheet, re-upload.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
query DownloadDynamicAttributesCSV($input: DownloadCSVRequest!) {
DownloadDynamicAttributesCSV(input: $input)
}
Variables
| Field | Type | Required | Description |
|---|---|---|---|
ModelID |
ID |
Yes | UUID of the model to export. |
{
"input": {
"ModelID": "{{digital_twins_model_id}}"
}
}
Response
200 OK -- application/json. data.DownloadDynamicAttributesCSV is a CSV-formatted string with header row ID,ModelName,InstanceName,Topic,Name,Unit,DataType,SchemaID.
{
"data": {
"DownloadDynamicAttributesCSV": "ID,ModelName,InstanceName,Topic,Name,Unit,DataType,SchemaID\n68280ec1-1c12-4b0d-a7ea-5a9d5ac32092,apiImportModel,,,Speed,kmph,string,4c67a3e1-d442-4b39-b0f6-eb07cc200066\n"
}
}
Parse the string client-side (it is not a separate file download). To get a true file download, take the string and write it to disk yourself.
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
query DownloadDynamicAttributesCSV($input: DownloadCSVRequest!) {
DownloadDynamicAttributesCSV(input: $input)
}
Variables
{
"input": {
"ModelID": "{{digital_twins_model_id}}"
}
}
Response
Status: 200 OK
{
"data": {
"DownloadDynamicAttributesCSV": "ID,ModelName,InstanceName,Topic,Name,Unit,DataType,SchemaID\n68280ec1-1c12-4b0d-a7ea-5a9d5ac32092,apiImportModel,,,Speed,kmph,string,4c67a3e1-d442-4b39-b0f6-eb07cc200066\n"
}
}
Upload Dynamic Attribute CSV
POST {{edgeUrl}}/digital-twins
Upload Dynamic Attribute CSV
Imports dynamic attributes from a CSV file. Uses the GraphQL multipart spec, identical in shape to Models > Static Attributes > Upload an Image.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body (form-data parts)
| Form key | Type | Description |
|---|---|---|
operations |
text |
GraphQL envelope describing the mutation, with null placeholder for the file. |
map |
text |
JSON mapping form file keys to placeholder locations within operations. |
1 |
file |
The CSV file. The form key (1) must match the key referenced in map. |
operations value
{
"operationName": "UploadDynamicAttributesCSV",
"variables": { "input": { "ModelID": "{{digital_twins_model_id}}", "File": null } },
"query": "mutation UploadDynamicAttributesCSV($input: UploadCSVRequest!) { UploadDynamicAttributesCSV(input: $input) { Errors { Line Column Error } } }"
}
map value
{ "1": ["variables.input.File"] }
The CSV format must match what Download Dynamic Attributes produces (see that endpoint for the header row).
Response
200 OK -- application/json
| Field | Type | Description |
|---|---|---|
data.UploadDynamicAttributesCSV.Errors |
[CSVError!]! |
One entry per error row, plus a summary row. |
data.UploadDynamicAttributesCSV.Errors[].Line |
Int? |
1-indexed CSV line number with the error. null for the summary. |
data.UploadDynamicAttributesCSV.Errors[].Column |
Int? |
1-indexed column number with the error. null for the summary. |
data.UploadDynamicAttributesCSV.Errors[].Error |
String |
Error text (or created: N updated: N skipped: N summary). |
{
"data": {
"UploadDynamicAttributesCSV": {
"Errors": [
{
"Line": null,
"Column": null,
"Error": "created: 0 updated: 1 skipped: 0"
}
]
}
}
}
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
Response
Status: 200 OK
{
"data": {
"UploadDynamicAttributesCSV": {
"Errors": [
{
"Line": null,
"Column": null,
"Error": "created: 0 updated: 1 skipped: 0",
"__typename": "CSVError"
}
],
"__typename": "UploadCSVResponse"
}
}
}
Remove Dynamic Attribute
POST {{edgeUrl}}/digital-twins
Updated in 4.1.x:
DeleteDynamicAttributeRequest.IDis now required (UUID!).
Remove Dynamic Attribute
Deletes one or more dynamic attributes by ID. Despite the singular name, the underlying mutation accepts a list -- but the example below shows the single-ID form taken by the Postman item.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation DeleteDynamicAttributes($input: DeleteDynamicAttributesRequest!) {
DeleteDynamicAttributes(input: $input)
}
The singular
DeleteDynamicAttributemutation is deprecated in the schema ("No longer supported"). It still executes, but use the batch form above.
Variables (input: DeleteDynamicAttributesRequest!)
| Field | Type | Required | Description |
|---|---|---|---|
Items |
[Item!]! |
Yes | List of {ID} objects -- one per attribute to delete. |
Items[].ID |
ID |
Yes | UUID of the dynamic attribute to delete. |
{
"input": {
"Items": [
{ "ID": "{{digital_twins_dynamic_attribute_id}}" }
]
}
}
Response
200 OK -- application/json. No data returned on success.
{ "data": { "DeleteDynamicAttributes": null } }
An attribute that is referenced by a hierarchy node cannot be deleted:
attribute in use by hierarchy
Remove the node first with Save All Hierarchy (submit the tree without that
node), then delete the attribute.
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation DeleteDynamicAttributes ($input: DeleteDynamicAttributesRequest!) {
DeleteDynamicAttributes(input: $input)
}
Variables
{
"input": {
"Items": [
{ "ID": "{{digital_twins_dynamic_attribute_id}}" }
]
}
}
Response
Status: 200 OK
{
"data": {
"DeleteDynamicAttributes": null
}
}
Digital Twins Version
POST {{edgeUrl}}/digital-twins
Digital Twins Version
Returns the running version and git revision of the Digital Twins service. Use as a fast liveness probe or to record the build version for support tickets.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
query Version {
Version {
Git
Version
}
}
Response
200 OK -- application/json
| Field | Type | Description |
|---|---|---|
data.Version.Git |
String |
Short git commit hash of the deployed build. |
data.Version.Version |
String |
Semantic version of the Digital Twins service. |
{
"data": {
"Version": {
"Git": "20e4e53e40",
"Version": "1.2.4"
}
}
}
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
query ListModels {
Version {
Git
Version
}
}
Response
Status: 200 OK
{
"data": {
"Version": {
"Git": "20e4e53e40",
"Version": "1.2.4"
}
}
}
List Models
POST {{edgeUrl}}/digital-twins
Updated in 4.1.x:
Modelnow exposes four count fields (InstanceCount,StaticAttributeCount,DynamicAttributeCount,ParameterCount) and full nested lists (Instances,StaticAttributes,DynamicAttributes) -- seeList Models with Nested Data.
List Models
Returns every model defined on the device, projected to a minimal {ID, Name} shape. Use as the source for a model picker, or to enumerate models when iterating over instances. For full schema, extend the GraphQL selection (e.g. add Description, Assets, IsUNS, ConnectorName).
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
query ListModels {
ListModels {
ID
Name
}
}
Response
200 OK -- application/json
| Field | Type | Description |
|---|---|---|
data.ListModels |
[Model!]! |
All models on the device. |
data.ListModels[].ID |
ID |
Model UUID. |
data.ListModels[].Name |
String |
Display name. Unique across models. |
{
"data": {
"ListModels": [
{ "ID": "D9117AE5-3F3E-4436-9A2A-6DD725FBD8FB", "Name": "Digital-Twin-1" }
]
}
}
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
query ListModels {
ListModels {
ID
Name
DynamicAttributeCount
StaticAttributeCount
ParameterCount
InstanceCount
}
}
Response
Status: 200 OK
{
"data": {
"ListModels": [
{
"ID": "D9117AE5-3F3E-4436-9A2A-6DD725FBD8FB",
"Name": "Digital-Twin-1"
}
]
}
}
List Models with Nested Data
POST {{edgeUrl}}/digital-twins/v2
BREAKING in 4.2.x. This item replaces its 4.1.x counterpart.
Model no longer has StaticAttributes or DynamicAttributes. A query that still selects either field fails validation outright, so this is a hard break, not a silent change.
What to do
Keep using StaticAttributeCount and DynamicAttributeCount for counts, which are unchanged. When you need the attributes themselves, fetch them per model with ListStaticAttributes / ListDynamicAttributes, passing the model's ID and paging with Limit and Offset.
Before (4.1.x, now invalid)
query ListModels {
ListModels {
ID Name
StaticAttributes { ID Key Value AttributeType }
DynamicAttributes { ID Name AttrDataType Topic Unit }
}
}
After (4.2.x)
query ListModels {
ListModels { ID Name StaticAttributeCount DynamicAttributeCount }
}
query ListStaticAttributes($input: ListStaticAttributeRequest!) {
ListStaticAttributes(input: $input) { ID Key Value AttributeType }
}
Why it changed
Embedding every attribute in every model made ListModels grow without bound on installations with large models: listing ten models pulled back their entire attribute sets whether or not the caller wanted them. Splitting the two apart keeps ListModels a cheap, predictable call and puts attribute fetching under explicit paging.
Expect one extra round trip per model whose attributes you actually need. If you were listing models purely to read counts, you now do strictly less work.
Request Body
GraphQL Query
query ListModels {
ListModels {
ID
Name
Description
Version
Assets
IsUNS
ConnectorName
Source
IsTopicExpressionEnabled
InstanceCount
StaticAttributeCount
DynamicAttributeCount
ParameterCount
Instances {
ID
Name
Topic
Interval
Version
StaticAttributeCount
DynamicAttributeCount
}
}
}
Create Model
POST {{edgeUrl}}/digital-twins
Updated in 4.1.x:
CreateModelRequest.Nameand.Assetsare now required (String!) -- omitting either returns a GraphQL validation error. The response also exposes the new count fields.
Create Model
Creates a new digital twin model with the minimum properties needed for the LE UI to show it on the Models page. After creation, populate the schema via the Static Attributes, Dynamic Attribute, Transformation, and Hierarchy subfolders.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation CreateModel($input: CreateModelRequest!) {
CreateModel(input: $input) {
ID
Name
Assets
Description
IsUNS
ConnectorName
}
}
Variables (input: CreateModelRequest!)
| Field | Type | Required | Description |
|---|---|---|---|
Name |
String |
Yes | Display name. Must be unique across all models. Restricted character set -- see Name character rules below. |
Assets |
String |
Yes | Asset category for organising models. Default ASSET. Free-form, used for grouping in the UI. |
Description |
String |
Yes | Free-form description. May be empty ("") but the field must be present. |
IsUNS |
Boolean |
No | Default false. true if this model participates in the Unified Namespace via a UNS connector. |
ConnectorName |
String |
No | UNS connector to publish under when IsUNS: true (e.g. LitmusEdge). |
Two required fields behave differently when omitted, and neither failure is self-explanatory:
- Omitting
Descriptionfails GraphQL validation before the resolver runs:{"message":"must be defined","path":["variable","input","Description"],"extensions":{"code":"GRAPHQL_VALIDATION_FAILED"}}. The schema types itNON_NULL. - Omitting
Assetsfails with a bareinternal system errorand no field information, even when every other field is valid. The schema typesAssetsas nullable, so nothing catches it earlier. Always send it --"ASSET"or"[]".
Name character rules
Model.Name is validated by three separate server-side validators, each
reporting a different tag:
| Value | Result |
|---|---|
abc def |
rejected -- noWhitespaces |
abc.def |
rejected -- noDots |
abc/def |
rejected -- notAllowedCharacters |
Effective allowlist: Unicode letters, digits, _ and -. Everything else
is rejected, including space, ., and / & ( ) % , : + ' ° * ! ? @ # $ ^ ~ ; =
[ ] { } | \ " < >. Unicode letters outside ASCII pass (µ, Ω), Unicode
symbols do not (°).
A human-readable line name such as Line 1Z is therefore rejected; use
Line_1Z.
A duplicate name fails with an internal index identifier rather than a field name:
index name xNm: unique constraint violation
unique constraint violation
{
"input": {
"Name": "{{digital_twins_model}}",
"Assets": "ASSET",
"Description": "",
"IsUNS": true,
"ConnectorName": "{{uns_connector_name}}"
}
}
Response
200 OK -- application/json
| Field | Type | Description |
|---|---|---|
data.CreateModel.ID |
ID |
UUID of the newly created model. Save as {{digital_twins_model_id}}. |
data.CreateModel.Name |
String |
Model name echoed back. |
data.CreateModel.Assets |
String |
Asset category echoed back. |
data.CreateModel.Description |
String |
Description echoed back. |
{
"data": {
"CreateModel": {
"ID": "9C01CCFC-7A94-4554-AFAD-BB14942D5190",
"Name": "apiTest",
"Assets": "ASSET",
"Description": "",
"__typename": "Model"
}
}
}
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation CreateModel($input: CreateModelRequest!) {
CreateModel(input: $input) {
ID
Name
Assets
Description
IsUNS
ConnectorName
DynamicAttributeCount
StaticAttributeCount
ParameterCount
InstanceCount
}
}
Variables
{
"input": {
"Name": "{{digital_twins_model}}",
"Assets": "ASSET",
"Description": "",
"IsUNS": true,
"ConnectorName": "{{uns_connector_name}}"
}
}
Response
Status: 200 OK
{
"data": {
"CreateModel": {
"ID": "9C01CCFC-7A94-4554-AFAD-BB14942D5190",
"Name": "apiTest",
"Assets": "ASSET",
"Description": "",
"__typename": "Model"
}
}
}
Update Model
POST {{edgeUrl}}/digital-twins
Updated in 4.1.x:
UpdateModelRequest.IDis now required (UUID!). The response also exposes the new count fields.
Update Model
Updates the mutable top-level fields of a model. To edit the model's schema (attributes, hierarchy, transformations), use the sub-folder endpoints rather than this one.
UpdateModel is a full replacement of the input shape -- include every field you want to keep.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation UpdateModel($input: UpdateModelRequest!) {
UpdateModel(input: $input) {
ID
Name
Assets
Description
IsUNS
ConnectorName
__typename
}
}
Variables (input: UpdateModelRequest!)
Same fields as Create Model, plus:
| Field | Type | Required | Description |
|---|---|---|---|
ID |
ID |
Yes | UUID of the model to update. |
{
"input": {
"ID": "{{digital_twins_model_id}}",
"Name": "{{digital_twins_model}}",
"Assets": "ASSET",
"Description": "",
"IsUNS": true,
"ConnectorName": "{{uns_connector_name}}"
}
}
Response
200 OK -- application/json. Echoes the model's new state.
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation UpdateModel($input: UpdateModelRequest!) {
UpdateModel(input: $input) {
ID
Name
Assets
Description
IsUNS
ConnectorName
DynamicAttributeCount
StaticAttributeCount
ParameterCount
InstanceCount
__typename
}
}
Variables
{
"input": {
"Name": "{{digital_twins_model}}",
"Assets": "ASSET",
"Description": "",
"ID": "{{digital_twins_model_id}}",
"IsUNS": true,
"ConnectorName": "{{uns_connector_name}}"
}
}
Response
Status: 200 OK
{
"data": {
"UpdateModel": {
"ID": "5CF29323-A2C7-48E3-A356-6A046AF99B7F",
"Name": "apiTest",
"Assets": "ASSET",
"Description": "",
"IsUNS": true,
"ConnectorName": "LitmusEdge",
"__typename": "Model"
}
}
}
Delete Model
POST {{edgeUrl}}/digital-twins
Updated in 4.1.x:
DeleteModelRequest.IDis now required (UUID!).
Delete Model
Deletes a model together with its attributes, transformations and hierarchy. A model that still has instances cannot be deleted: the call fails with DT_MODEL_HAS_INSTANCES (Model '<name>' can't be deleted while 1 instance still uses it. Delete that instance first, then delete the model.). Delete its instances first (List Instances with the ModelID, then Delete Instance). Destructive and not idempotent: deleting a missing ID returns a BAD_USER_INPUT error.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation DeleteModel($input: DeleteModelRequest!) {
DeleteModel(input: $input)
}
Variables (input: DeleteModelRequest!)
| Field | Type | Required | Description |
|---|---|---|---|
ID |
ID |
Yes | UUID of the model to delete. |
{
"input": {
"ID": "{{digital_twins_model_id}}"
}
}
Response
200 OK -- application/json. No data returned on success.
{ "data": { "DeleteModel": null } }
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation DeleteModel($input: DeleteModelRequest!) {
DeleteModel(input: $input)
}
Variables
{
"input": {
"ID": "{{digital_twins_model_id}}"
}
}
Response
Status: 200 OK
{
"data": {
"DeleteModel": null
}
}
Clone a Model
POST {{edgeUrl}}/digital-twins
Clone a Model
Creates a deep copy of an existing model: static attributes, dynamic attributes, transformations, parameters, and hierarchy are all duplicated. Instances are not cloned -- the new model starts with no instances.
Use this to template new asset types from an existing definition without re-entering the schema.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation CloneModel($input: CloneModelRequest!) {
CloneModel(input: $input) {
ID
Assets
Name
Description
}
}
Variables (input: CloneModelRequest!)
| Field | Type | Required | Description |
|---|---|---|---|
ID |
ID |
Yes | UUID of the source model to clone. |
Name |
String |
Yes | Display name for the new model. Must be unique across models. |
{
"input": {
"ID": "{{digital_twins_model_id}}",
"Name": "{{digital_twins_clone}}"
}
}
Response
200 OK -- application/json
| Field | Type | Description |
|---|---|---|
data.CloneModel.ID |
ID |
UUID of the newly created clone. |
data.CloneModel.Assets |
String |
Asset category copied from the source. |
data.CloneModel.Name |
String |
Name as supplied. |
data.CloneModel.Description |
String |
Description copied from the source. |
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation CloneModel ($input: CloneModelRequest!) {
CloneModel(input: $input) {
ID
Assets
Name
Description
}
}
Variables
{
"input": {
"ID": "{{digital_twins_model_id}}",
"Name": "{{digital_twins_clone}}"
}
}
Response
Status: 200 OK
{
"data": {
"CloneModel": {
"ID": "46946ABE-2235-4B27-833D-CC335DDFCB4C",
"Assets": "ASSET",
"Name": "apiTest2",
"Description": "Digital Twin example 1"
}
}
}
Export Model
POST {{edgeUrl}}/digital-twins
Export Model
Serializes a model and its full schema (static attributes, dynamic attributes, transformations, hierarchy, image) to a JSON document suitable for Import Model on another device. Use this to move models between devices, source-control them, or take backups.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation ExportModel($input: ExportModelRequest!) {
ExportModel(input: $input)
}
Variables (input: ExportModelRequest!)
| Field | Type | Required | Description |
|---|---|---|---|
ModelID |
ID |
Yes | UUID of the model to export. |
{
"input": {
"ModelID": "{{digital_twins_model_id}}"
}
}
Response
200 OK -- application/json. data.ExportModel is an array of model documents (typically of length 1) containing every nested entity. Each document includes:
id,name,description,assets,imagedynamicAttributes[],staticAttributes[],transformations[]hierarchy[],parameters[]
Persist the response verbatim for re-import.
{
"data": {
"ExportModel": [
{
"id": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"assets": "ASSET",
"name": "apiTest",
"description": "updated model",
"image": "",
"dynamicAttributes": [
{
"id": "31a6eddc-a455-4126-981c-0a6313915a89",
"modelId": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"parentId": "00000000-0000-0000-0000-000000000000"
}
]
}
]
}
}
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation ExportModel ($input: ExportModelRequest!) {
ExportModel(input: $input)
}
Variables
{
"input": {
"ModelID": "{{digital_twins_model_id}}"
}
}
Response
Status: 200 OK
{
"data": {
"ExportModel": [
{
"id": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"assets": "ASSET",
"name": "apiTest",
"description": "updated model",
"image": "",
"dynamicAttributes": [
{
"id": "31a6eddc-a455-4126-981c-0a6313915a89",
"modelId": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"parentId": "00000000-0000-0000-0000-000000000000",
"instanceId": "00000000-0000-0000-0000-000000000000",
"topic": "",
"name": "Speed",
"unit": "kmph",
"dataType": "string",
"schemaId": "30d86400-d103-4c5c-95e4-ab6f02fb00ee"
}
],
"staticAttributes": [
{
"id": "189edb70-1029-4ebf-ab40-27e5423c12ce",
"parentId": "00000000-0000-0000-0000-000000000000",
"modelId": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"instanceId": "00000000-0000-0000-0000-000000000000",
"key": "Manufacturer",
"value": "API Manufacturer"
},
{
"id": "3f44c811-d8cc-4735-b337-a0f30826641e",
"parentId": "00000000-0000-0000-0000-000000000000",
"modelId": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"instanceId": "00000000-0000-0000-0000-000000000000",
"key": "Model",
"value": "New API Model"
},
{
"id": "3af47815-bb4f-4288-84da-02b259a75edb",
"parentId": "00000000-0000-0000-0000-000000000000",
"modelId": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"instanceId": "00000000-0000-0000-0000-000000000000",
"key": "Model1",
"value": "New API Model1"
}
],
"hierarchyNodes": [
{
"id": "60c1f5d0-25ab-4bc0-93a8-52a60a7087d0",
"modelId": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"position": 0,
"parentId": "4c224618-4e24-4bf7-af8b-d87188fdc413",
"name": "Model",
"isFolder": false,
"attributeId": "3f44c811-d8cc-4735-b337-a0f30826641e",
"attributeType": "static",
"CreatedAt": "2023-11-29T00:54:35.953112543Z"
},
{
"id": "81d4226b-12f9-4d89-957d-2f01a8e5bf12",
"modelId": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"position": 0,
"parentId": "a6a8cb25-ba33-41ca-8d2c-6f657949b849",
"name": "Model",
"isFolder": false,
"attributeId": "3f44c811-d8cc-4735-b337-a0f30826641e",
"attributeType": "static",
"CreatedAt": "2023-11-29T00:54:35.954122264Z"
},
{
"id": "997ed034-52de-4ce4-9f7d-731294f70e6a",
"modelId": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"position": 0,
"parentId": null,
"name": "Manufacturer",
"isFolder": false,
"attributeId": "189edb70-1029-4ebf-ab40-27e5423c12ce",
"attributeType": "static",
"CreatedAt": "2023-11-29T00:54:35.951975424Z"
},
{
"id": "4c224618-4e24-4bf7-af8b-d87188fdc413",
"modelId": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"position": 1,
"parentId": null,
"name": "New_Node",
"isFolder": true,
"attributeId": null,
"attributeType": null,
"CreatedAt": "2023-11-29T00:54:35.952255779Z"
},
{
"id": "a6a8cb25-ba33-41ca-8d2c-6f657949b849",
"modelId": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"position": 2,
"parentId": "4c224618-4e24-4bf7-af8b-d87188fdc413",
"name": "New_Node",
"isFolder": true,
"attributeId": null,
"attributeType": null,
"CreatedAt": "2023-11-29T00:54:35.953579776Z"
}
],
"transformationBackup": [
{
"ID": "30d86400-d103-4c5c-95e4-ab6f02fb00ee",
"ModelID": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"Name": "Transformation",
"Schema": "{\n \"key\": \"value\"\n}"
}
]
}
]
}
}
Import Model
POST {{edgeUrl}}/digital-twins
Import Model
Imports one or more model documents previously produced by Export Model (or hand-authored to the same shape). The mutation accepts the document verbatim -- pass an array of model objects.
Imported models keep their original UUIDs from the source export. If a matching ID already exists on the device, the import fails -- delete or clone the existing one first.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation ImportModel($input: Any!) {
ImportModel(input: $input)
}
The Any! scalar accepts the same JSON structure that Export Model returns. The example below imports a single, minimal model:
{
"input": [
{
"id": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"assets": "ASSET",
"name": "apiImportModel",
"description": "updated model",
"image": "",
"dynamicAttributes": [],
"staticAttributes": []
}
]
}
Response
200 OK -- application/json
| Field | Type | Description |
|---|---|---|
data.ImportModel |
Boolean |
true on success. |
{ "data": { "ImportModel": true } }
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation ImportModel($input: Any!) {
ImportModel(input: $input)
}
Variables
{
"input": [
{
"id": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"assets": "ASSET",
"name": "apiImportModel",
"description": "updated model",
"image": "",
"dynamicAttributes": [],
"staticAttributes": [
{
"id": "189edb70-1029-4ebf-ab40-27e5423c12ce",
"parentId": "00000000-0000-0000-0000-000000000000",
"modelId": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"instanceId": "00000000-0000-0000-0000-000000000000",
"key": "Manufacturer",
"value": "API Manufacturer"
}
],
"hierarchyNodes": [],
"transformationBackup": [
{
"ID": "30d86400-d103-4c5c-95e4-ab6f02fb00ee",
"ModelID": "3a923971-5dfc-4633-b5af-a6bf8c0b60e9",
"Name": "Transformation",
"Schema": "{\n \"key\": \"value\"\n}"
}
]
}
]
}
Response
Status: 200 OK
{
"data": {
"ImportModel": true
}
}
Upgrade Instances
POST {{edgeUrl}}/digital-twins
Upgrade Instances
Reconciles one or more instances with the current state of their parent model. Use this after editing a model's schema (adding/removing attributes, changing hierarchy) so that existing instances pick up the change without being deleted and recreated.
Without Upgrade Instances, existing instances continue to operate against the model schema they were created against.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation UpgradeInstances($input: [UUID!]) {
UpgradeInstances(input: $input)
}
Variables
| Field | Type | Required | Description |
|---|---|---|---|
input |
[UUID!] |
Yes | Instance UUIDs to upgrade. Pass a single-element array for one. |
{
"input": [
"{{digital_twins_instance_id}}"
]
}
Response
200 OK -- application/json
| Field | Type | Description |
|---|---|---|
data.UpgradeInstances |
Boolean |
true on success. |
{ "data": { "UpgradeInstances": true } }
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation UpgradeInstances($input: [UUID!]) {
UpgradeInstances(input: $input)
}
Variables
{
"input": [
"{{digital_twins_instance_id}}"
]
}
Response
Status: 200 OK
{
"data": {
"UpgradeInstances": true
}
}
List UNS Connectors
POST {{edgeUrl}}/digital-twins
List UNS Connectors
Lists the Litmus UNS connectors a model can publish through. The Digital Twins UI calls this to fill the connector picker when a model is created or edited with UNS publishing turned on. Pass the chosen connector's Name as ConnectorName (with IsUNS: true) to Create Model or Update Model.
A connector is an Integration instance of the Litmus UNS provider. The list is empty until one exists, which is what this example device returns.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
query ListUNSConnectors {
ListUNSConnectors {
Name
ConnectorID
}
}
No variables.
Response
200 OK -- application/json. data.ListUNSConnectors is a list of UNSConnector:
| Field | Type | Description |
|---|---|---|
Name |
String! |
Connector name, as used in ConnectorName. |
ConnectorID |
String! |
ID of the Integration instance behind it. |
{ "data": { "ListUNSConnectors": [] } }
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
query ListUNSConnectors {
ListUNSConnectors {
Name
ConnectorID
}
}
Response
Status: 200 OK
{
"data": {
"ListUNSConnectors": []
}
}
List Static Attributes
POST {{edgeUrl}}/digital-twins/v2
Updated in 4.3.x. This item replaces its 4.2.x counterpart.
Instance-scoped form. The 4.2.x version of this item did not select AttributeType at all; it is added here because the enum gained a member that matters for instances specifically.
AttributeType is now Regular | UNS | AssetHierarchy.
| Value | Meaning |
|---|---|
Regular |
An ordinary static attribute |
UNS |
Derived from the Unified Namespace |
AssetHierarchy |
New in 4.3.x. Derived from the asset hierarchy set by SetInstanceAssetHierarchy |
This is the read path for asset hierarchy placement. SetInstanceAssetHierarchy returns Empty, so this query is how you confirm a placement actually landed.
Any exhaustive match on AttributeType in client code was written against two members and will now meet a third. Check the default branch.
Request Body
GraphQL Query
query ListStaticAttributes($input: ListStaticAttributeRequest!) {
ListStaticAttributes(input: $input) {
ID
ModelID
InstanceID
Key
Value
AttributeType
CreatedAt
UpdatedAt
}
}
Variables
{
"input": {
"ModelID": null,
"InstanceID": "{{dt_instance_id}}"
}
}
Update Static Attribute
POST {{edgeUrl}}/digital-twins
Update Static Attribute (instance scope)
Identical to Models > Static Attributes > Update Static Attribute. The endpoint is reused for both model-level and instance-level updates -- when the static attribute UUID belongs to an instance override, the override is what's written; otherwise the model-level definition is updated.
See the Models-side endpoint for the request shape and field reference.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation UpdateStaticAttributes($input: UpdateStaticAttributesRequest!) {
UpdateStaticAttributes(input: $input) {
ID
ModelID
InstanceID
Key
Value
CreatedAt
UpdatedAt
}
}
The singular
UpdateStaticAttributemutation is deprecated in the schema ("No longer supported"). It still executes, but use the batch form above.
Variables (input: UpdateStaticAttributesRequest!)
| Field | Type | Required | Description |
|---|---|---|---|
Items |
[Item!]! |
Yes | One entry per attribute to update. |
Items[].ID |
ID |
Yes | UUID of the static attribute (model or instance override). |
Items[].ModelID |
ID |
Yes | UUID of the parent model. |
Items[].Key |
String |
Yes | New attribute name. |
Items[].Value |
String |
Yes | New attribute value. |
{
"input": {
"Items": [
{
"ID": "{{digital_twins_static_attribute_id}}",
"ModelID": "{{digital_twins_model_id}}",
"Key": "Model",
"Value": "Updated Value API Model"
}
]
}
}
Response
Same shape as Models > Static Attributes > Update Static Attribute.
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation UpdateStaticAttributes($input:UpdateStaticAttributesRequest!) {
UpdateStaticAttributes(input: $input) {
ID
ModelID
InstanceID
Key
Value
CreatedAt
UpdatedAt
}
}
Variables
{
"input": {
"Items": [
{
"ID": "{{digital_twins_static_attribute_id}}",
"ModelID": "{{digital_twins_model_id}}",
"Key": "Model",
"Value": "Updated Value API Model"
}
]
}
}
Response
Status: 200 OK
{
"data": {
"UpdateStaticAttributes": [
{
"ID": "63E4BD05-CE85-41CD-99A6-F69CCD171240",
"ModelID": "3A923971-5DFC-4633-B5AF-A6BF8C0B60E9",
"InstanceID": null,
"Key": "Model",
"Value": "Updated Value API Model",
"CreatedAt": "2023-08-16T00:13:38.224123983Z",
"UpdatedAt": "2023-08-16T00:23:12.872714078Z"
}
]
}
}
List all Dynamic Attributes
POST {{edgeUrl}}/digital-twins
List all Dynamic Attributes (instance scope)
Same shape as Models > Dynamic Attribute > List all Dynamic Attributes -- the endpoint is reused. Pass the parent ModelID to scope. The response includes per-instance overrides if InstanceID is non-zero on the returned records.
See the Models-side endpoint for the full field reference.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
query ListDynamicAttributes($input: ListDynamicAttributeRequest!) {
ListDynamicAttributes(input: $input) {
ID
ModelID
InstanceID
Topic
Name
Unit
DataType
SchemaID
CreatedAt
UpdatedAt
}
}
Variables
| Field | Type | Required | Description |
|---|---|---|---|
ModelID |
ID |
Yes | UUID of the parent model. |
{
"input": {
"ModelID": "{{digital_twins_model_id}}"
}
}
Response
Same shape as Models > Dynamic Attribute > List all Dynamic Attributes.
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
query ListDynamicAttributes ($input: ListDynamicAttributeRequest!) {
ListDynamicAttributes(input:$input) {
ID
ModelID
InstanceID
Topic
Name
Unit
DataType
SchemaID
CreatedAt
UpdatedAt
}
}
Variables
{
"input": {
"ModelID": "{{digital_twins_model_id}}"
}
}
Response
Status: 200 OK
{
"data": {
"ListDynamicAttributes": [
{
"ID": "77990102-3613-4D2B-ABFC-EE761383E631",
"ModelID": "3A923971-5DFC-4633-B5AF-A6BF8C0B60E9",
"InstanceID": "00000000-0000-0000-0000-000000000000",
"Topic": "",
"Name": "Dynamic attribute 1",
"Unit": "test1",
"DataType": "Dictionary",
"SchemaID": "30D86400-D103-4C5C-95E4-AB6F02FB00EE",
"CreatedAt": "2023-08-16T19:13:13.808757635Z",
"UpdatedAt": "2023-08-16T20:17:31.074886398Z"
}
]
}
}
Update Dynamic Attribute
POST {{edgeUrl}}/digital-twins/v2
Updated in 4.3.x. This item replaces its 4.2.x counterpart.
Instance-scoped form: set InstanceID and leave ModelID null. The model-scoped form of the same mutation is under Models > Dynamic Attribute.
UpdateDynamicAttributeRequest gains NestedModelID and NestedInstanceID, and DynamicAttributeType gains a nestedModel member.
Set NestedModelID to compose a model definition, or NestedInstanceID to point at one specific instance. They are alternatives: populate one, leave the other null. Both are ignored unless AttrDataType is nestedModel.
NestedInstanceID is the one that matters most here: it points this attribute at a specific instance rather than at a model definition, which is what you usually want when the attribute itself belongs to an instance.
This is a full update, not a patch. Send the complete attribute: omitting NestedInstanceID on an attribute that currently has one clears it and silently turns a nested reference back into a plain attribute.
Request Body
GraphQL Query
mutation UpdateDynamicAttribute ($input: UpdateDynamicAttributeRequest!) {
UpdateDynamicAttribute(input: $input) {
ID
ModelID
InstanceID
Topic
Name
Unit
DataType
AttrDataType
NestedModelID
NestedInstanceID
SchemaID
CreatedAt
UpdatedAt
}
}
Variables
{
"input": {
"ID": "{{dt_dynamic_attribute_id}}",
"ModelID": null,
"InstanceID": "{{dt_instance_id}}",
"Topic": "line/1/motor",
"Name": "motor",
"Unit": "",
"DataType": "object",
"AttrDataType": "nestedModel",
"NestedModelID": null,
"NestedInstanceID": "{{dt_nested_instance_id}}"
}
}
Upload Dynamic Attribute CSV
POST {{edgeUrl}}/digital-twins
Upload Dynamic Attribute CSV (instance scope)
Same multipart mutation as Models > Dynamic Attribute > Upload Dynamic Attribute CSV. The endpoint is reused; the difference is that the CSV typically contains per-instance overrides (with InstanceID set).
See the Models-side endpoint for the form-data shape, CSV format, and response field reference.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body (form-data parts)
| Form key | Type | Description |
|---|---|---|
operations |
text |
GraphQL envelope, see Models-side endpoint. |
map |
text |
{"1": ["variables.input.File"]} |
1 |
file |
The CSV file. |
The operations value uses $input.ModelID from the parent model -- the service applies overrides to the appropriate instance based on rows in the CSV.
Response
Same shape as Models > Dynamic Attribute > Upload Dynamic Attribute CSV.
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
Response
Status: 200 OK
{
"data": {
"UploadDynamicAttributesCSV": {
"Errors": [
{
"Line": null,
"Column": null,
"Error": "created: 0 updated: 1 skipped: 0",
"__typename": "CSVError"
}
],
"__typename": "UploadCSVResponse"
}
}
}
List Parameters
POST {{edgeUrl}}/digital-twins
List Parameters (instance scope)
Same query as Models > Parameters > List Parameters -- pass the instance UUID as OwnerID to scope to the instance. See the Models-side endpoint for the request shape and field reference.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
query ListParameters($input: ListParametersRequest) {
ListParameters(input: $input) {
ID
Name
Value
}
}
Variables
| Field | Type | Required | Description |
|---|---|---|---|
OwnerID |
ID |
Yes | UUID of the instance. |
{
"input": {
"OwnerID": "{{digital_twins_instance_id}}"
}
}
Response
Same shape as Models > Parameters > List Parameters.
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
query ListParameters($input: ListParametersRequest) {
ListParameters(input: $input) {
ID
Name
Value
}
}
Variables
{
"input": {
"OwnerID": "{{digital_twins_instance_id}}"
}
}
Response
Status: 200 OK
{
"data": {
"ListParameters": [
{
"ID": "51D2E713-6150-4800-8779-9A1825BE19D7",
"Name": "Updated key",
"Value": "Updated value"
},
{
"ID": "8FA45411-ECA0-44C0-BD2F-60341D55C581",
"Name": "key1",
"Value": "value1"
}
]
}
}
Get Instances
POST {{edgeUrl}}/digital-twins/v2
Updated in 4.3.x. This item replaces its 4.2.x counterpart.
Instance gains Source (local | external), added to the selection set here.
Filter on it client-side to separate instances defined on this device from those that came in from elsewhere. ListInstancesRequest still takes only ModelID, so there is no server-side filter by source in this release.
Everything else about this query is unchanged from 4.2.x. Removing Source from the selection gives you exactly the 4.2.x response.
Request Body
GraphQL Query
query ListInstances($input: ListInstancesRequest) {
ListInstances(input: $input) {
ID
ModelID
Name
Topic
Interval
FlatHierarchy
Source
DynamicAttributeCount
StaticAttributeCount
ParameterCount
}
}
Variables
{
"input": {
"ModelID": "{{dt_model_id}}"
}
}
Create Instance
POST {{edgeUrl}}/digital-twins/v2
Requires: an existing model (
ModelIDfromCreate ModelorList Models). An unknown ID fails withDT_NOT_FOUND.
Updated in 4.3.x. This item replaces its 4.2.x counterpart.
CreateInstanceRequest gains an optional Source field, and Instance returns it. InstanceSource is local | external.
| Value | Meaning |
|---|---|
local |
The instance is defined and owned on this device |
external |
The instance originates outside this device |
The field is optional, so a 4.2.x request body still works and the server applies its default. Send it explicitly once you have instances arriving from more than one place, since Source is what tells them apart afterwards.
Select Source in the response, as above, to confirm what was actually recorded rather than what you asked for.
Request Body
GraphQL Query
mutation CreateInstance($input: CreateInstanceRequest!) {
CreateInstance(input: $input) {
ID
ModelID
Name
Topic
Interval
FlatHierarchy
Source
}
}
Variables
{
"input": {
"ModelID": "{{dt_model_id}}",
"Name": "line-1-press",
"Topic": "plant/line1/press",
"Interval": 1000,
"FlatHierarchy": false,
"Source": "local"
}
}
Delete Instance
POST {{edgeUrl}}/digital-twins
Updated in 4.1.x:
DeleteInstanceRequest.IDis now required (UUID!).
Delete Instance
Note: a model cannot be deleted while any instance still uses it, so delete instances before their model.
Deletes an instance and all of its per-instance attribute overrides. The parent model is not affected.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation DeleteInstance($input: DeleteInstanceRequest!) {
DeleteInstance(input: $input)
}
Variables
| Field | Type | Required | Description |
|---|---|---|---|
ID |
ID |
Yes | UUID of the instance to delete. |
{
"input": {
"ID": "{{digital_twins_instance_id}}"
}
}
Response
200 OK -- application/json. No data returned on success.
{ "data": { "DeleteInstance": null } }
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation DeleteInstance ($input: DeleteInstanceRequest!) {
DeleteInstance(input:$input)
}
Variables
{
"input": {
"ID":"AB89BC5A-3E72-4CFD-B5FA-3F216BB33601"
}
}
Response
Status: 200 OK
{
"data": {
"DeleteInstance": null
}
}
Update Instance
POST {{edgeUrl}}/digital-twins
Updated in 4.1.x:
UpdateInstanceRequest.IDis now required (UUID!). The response also exposes the new count fields.
Update Instance
Updates the top-level fields of an instance (Name, Topic, Interval, FlatHierarchy). The ModelID is not re-assignable -- delete and recreate to change models.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation UpdateInstance($input: UpdateInstanceRequest!) {
UpdateInstance(input: $input) {
ID
ModelID
Name
Topic
Interval
FlatHierarchy
}
}
Variables
Same fields as Create Instance, plus:
| Field | Type | Required | Description |
|---|---|---|---|
ID |
ID |
Yes | UUID of the instance to update. |
{
"input": {
"ID": "{{digital_twins_instance_id}}",
"ModelID": "{{digital_twins_model_id}}",
"Name": "updated_{{digital_twins_instance}}",
"Interval": 1,
"Topic": "digitaltwins_updated_{{digital_twins_instance}}",
"FlatHierarchy": false
}
}
Response
200 OK -- application/json. Returns the updated instance.
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation UpdateInstance($input: UpdateInstanceRequest!) {
UpdateInstance(input: $input) {
ID
ModelID
Name
Topic
Interval
FlatHierarchy
DynamicAttributeCount
StaticAttributeCount
ParameterCount
}
}
Variables
{
"input": {
"ID": "{{digital_twins_instance_id}}",
"ModelID": "{{digital_twins_model_id}}",
"Name": "updated_{{digital_twins_instance}}",
"Interval": 1,
"Topic": "digitaltwins_updated_{{digital_twins_instance}}",
"FlatHierarchy": false
}
}
Response
Status: 200 OK
{
"data": {
"UpdateInstance": {
"ID": "D0C8844D-EF72-4933-BB5E-B74372B33F4B",
"ModelID": "D9117AE5-3F3E-4436-9A2A-6DD725FBD8FB",
"Name": "Instance1",
"Topic": "digitaltwins_Instance1",
"Interval": 1,
"FlatHierarchy": false
}
}
}
Set Instance State
POST {{edgeUrl}}/digital-twins
Set Instance State
Enables or disables an instance. A disabled instance is configured but does not publish to its topic; enabled instances actively run.
Use this to pause/resume publishing without deleting and recreating the instance.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation SetInstanceState($input: SetInstanceStateRequest!) {
SetInstanceState(input: $input)
}
Variables
| Field | Type | Required | Description |
|---|---|---|---|
InstanceID |
ID |
Yes | UUID of the instance. |
IsEnabled |
Boolean |
Yes | true to enable, false to disable. |
{
"input": {
"InstanceID": "{{digital_twins_instance_id}}",
"IsEnabled": true
}
}
Response
200 OK -- application/json
| Field | Type | Description |
|---|---|---|
data.SetInstanceState |
Boolean |
true on success. |
{ "data": { "SetInstanceState": true } }
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation SetInstanceState ($input: SetInstanceStateRequest!) {
SetInstanceState(input: $input)
}
Variables
{
"input": {
"InstanceID": "{{digital_twins_instance_id}}",
"IsEnabled": true
}
}
Response
Status: 200 OK
{
"data": {
"SetInstanceState": true
}
}
Save Instance/ Add Topic to Dynamic Attribute
POST {{edgeUrl}}/digital-twins/
Save Instance / Add Topic to Dynamic Attribute (transactional)
Applies one or more updates to an instance's per-instance static and dynamic attribute overrides in a single transaction. This is the same shape as Models > Parameters > Set Parameters, but scoped to instance overrides instead of model schema.
The most common use case is adding a topic to a dynamic attribute override -- this is the step that actually binds the dynamic attribute to a live data source (typically a DeviceHub tag topic).
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
mutation TransactionalInstanceUpdate($input: TransactionalInstanceUpdateRequest!) {
TransactionalInstanceUpdate(input: $input)
}
Variables
The input is an envelope of per-operation arrays. Send only the arrays you need; the others can be empty Items: [].
| Field | Type | Description |
|---|---|---|
updateStaticAttributes |
{ Items } |
List of per-instance static attribute payloads to update. |
updateDynamicAttributes |
{ Items } |
List of per-instance dynamic attribute payloads to update (e.g. set Topic). |
{
"input": {
"updateStaticAttributes": { "Items": [] },
"updateDynamicAttributes": {
"Items": [
{
"__typename": "DynamicAttribute",
"ID": "{{digital_twins_dynamic_attribute_id}}",
"ModelID": "{{digital_twins_model_id}}",
"InstanceID": "{{digital_twins_instance_id}}",
"Topic": "<devicehub-tag-topic>",
"Name": "Speed",
"Unit": "kmph",
"DataType": "string",
"SchemaID": "30D86400-D103-4C5C-95E4-AB6F02FB00EE"
}
]
}
}
}
To discover the right Topic value for a DeviceHub-backed signal, list the device's registers via DeviceHub > Tags > List Registers from Single Device and inspect Topics[].Topic.
Response
200 OK -- application/json. No data returned on success.
{ "data": { "TransactionalInstanceUpdate": null } }
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
mutation TransactionalInstanceUpdate($input: TransactionalInstanceUpdateRequest!) {
TransactionalInstanceUpdate(input: $input)
}
Variables
{
"input": {
"updateStaticAttributes": {
"Items": []
},
"updateDynamicAttributes": {
"Items": [
{
"__typename": "DynamicAttribute",
"ID":"{{digital_twins_dynamic_attribute_id}}",
"ModelID": "{{digital_twins_model_id}}",
"InstanceID": "{{digital_twins_instance_id}}",
"Topic": "devicehub.raw.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.1A5572B2-8242-40C8-BC63-FFEBC7DAF1E4",
"Name": "Speed",
"Unit": "kmph",
"DataType": "string",
"AttrDataType": "string",
"SchemaID": "4C67A3E1-D442-4B39-B0F6-EB07CC200066"
}
]
}
}
}
Response
Status: 200 OK
{
"data": {
"TransactionalInstanceUpdate": null
}
}
Subscribe Instance Status
POST {{edgeUrl}}/digital-twins
Subscribe Instance Status
Streams the enabled/disabled state of every instance on the device. The Digital Twins UI subscribes to it to keep the status badges on the instance list current. Change a state with Set Instance State.
Endpoint
GET wss://{{edgeHost}}/digital-twins
Sec-WebSocket-Protocol: graphql-transport-ws
WebSocket subscription. Run it over the graphql-transport-ws protocol at wss://{{edgeHost}}/digital-twins: send connection_init, then a subscribe message carrying the document below. A plain HTTP POST does not stream. After importing this item, convert it to a Postman WebSocket request.
Authentication
HTTP Basic Auth on the WebSocket upgrade request. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Subscription
subscription InstanceStatus {
InstanceStatus {
InstanceID
Status
}
}
No variables.
Messages
Each next message carries data.InstanceStatus, a list of InstanceStatusEvent:
| Field | Type | Description |
|---|---|---|
InstanceID |
UUID! |
The instance. |
Status |
InstanceStatus! |
Enabled or Disabled. |
The first message arrives right after subscribe and lists every instance, so it doubles as a snapshot. With no state changes, no further message followed in 25 seconds on LE 4.0.x.
{
"id": "1",
"type": "next",
"payload": {
"data": {
"InstanceStatus": [
{ "InstanceID": "0304E96A-F646-47A6-BDBC-D5F83A144A17", "Status": "Enabled" },
{ "InstanceID": "08F772EF-4EAC-5610-AB59-6D0E3C56052B", "Status": "Enabled" }
]
}
}
}
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
subscription InstanceStatus {
InstanceStatus {
InstanceID
Status
}
}
Response
Status: 200 OK
{
"data": {
"InstanceStatus": [
{
"InstanceID": "0304E96A-F646-47A6-BDBC-D5F83A144A17",
"Status": "Enabled"
},
{
"InstanceID": "08F772EF-4EAC-5610-AB59-6D0E3C56052B",
"Status": "Enabled"
},
{
"InstanceID": "09AF1BB5-CABC-5889-A331-5BF91AE3A12E",
"Status": "Enabled"
}
]
}
}
Subscribe Instance Data
POST {{edgeUrl}}/digital-twins
Subscribe Instance Data
Streams the live payload of one instance: its static attribute values and the latest value of each dynamic attribute, laid out in the instance's hierarchy. This is what the instance page in the Digital Twins UI shows. Export Instance Schema returns the JSON Schema this payload follows.
Endpoint
GET wss://{{edgeHost}}/digital-twins
Sec-WebSocket-Protocol: graphql-transport-ws
WebSocket subscription. Run it over the graphql-transport-ws protocol at wss://{{edgeHost}}/digital-twins: send connection_init, then a subscribe message carrying the document below. A plain HTTP POST does not stream. After importing this item, convert it to a Postman WebSocket request.
Authentication
HTTP Basic Auth on the WebSocket upgrade request. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Subscription
subscription DataAccess($input: DataAccessRequest!) {
DataAccess(input: $input)
}
Variables (input: DataAccessRequest!)
| Field | Type | Required | Description |
|---|---|---|---|
InstanceID |
UUID! |
Yes | Instance to stream. |
input is optional in the 4.0.x schema and required from 4.4.x, so always send it.
{
"input": {
"InstanceID": "{{digital_twins_instance_id}}"
}
}
Messages
Each next message carries data.DataAccess (type Any), the whole instance payload rather than a delta:
- Static attributes appear as plain values under their hierarchy folder (for example
PropertiesorIdentification). - Each dynamic attribute sits under its hierarchy folders, keyed by its full dotted name. Its value is the last message received on its topic: for a DeviceHub tag, an object with
value,timestamp,success,tagName,deviceName,deviceID,registerId,datatypeandmetadata. A dynamic attribute that has received nothing yet is{}.
The example is trimmed to one tag. Several messages arrive right after subscribe, sometimes repeating the same payload. Later messages follow as the instance publishes new data; none arrived in 20 seconds on instances whose source tags were idle.
{
"id": "1",
"type": "next",
"payload": {
"data": {
"DataAccess": {
"Identification": {
"AssetType": "Capper",
"Line": "Bottling Line 1",
"Site": "bottling-plant-01"
},
"PackML": {
"CurMachSpeed": {
"tagName": "CurMachSpeed",
"deviceName": "BL1_Capper",
"deviceID": "41E8271E-7E54-52D0-A9A8-0AB2DA882120",
"registerId": "943315E3-B6E5-50D2-A2F7-3817D7EC02FF",
"datatype": "float32",
"value": null,
"success": false,
"timestamp": 1791650818402
}
}
}
}
}
}
An unknown InstanceID returns key not found in the first message, followed by complete.
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
subscription DataAccess($input: DataAccessRequest!) {
DataAccess(input: $input)
}
Variables
{
"input": {
"InstanceID": "{{digital_twins_instance_id}}"
}
}
Response
Status: 200 OK
{
"data": {
"DataAccess": {
"Identification": {
"AssetType": "Capper",
"Line": "Bottling Line 1",
"ModbusUnitId": "2",
"Site": "bottling-plant-01",
"Standard": "ISA-TR88.00.02 PackML"
},
"PackML": {
"CurMachSpeed": {
"datatype": "float32",
"description": "",
"deviceID": "41E8271E-7E54-52D0-A9A8-0AB2DA882120",
"deviceName": "BL1_Capper",
"metadata": {
"asset_type": "capper",
"line": "Bottling Line 1",
"plant": "Bottling Plant 01"
},
"registerId": "943315E3-B6E5-50D2-A2F7-3817D7EC02FF",
"success": false,
"tagName": "CurMachSpeed",
"timestamp": 1791650818402,
"value": null
}
}
}
}
}
Export Instance Schema
POST {{edgeUrl}}/digital-twins
Export Instance Schema
Returns a JSON Schema (draft-07) describing the payload an instance publishes: its static attributes and its dynamic attributes, laid out in the instance's hierarchy. Use it to validate instance messages in a downstream consumer, or to generate typed models for them. Read-only.
This is the call behind Export JSON Schema on an instance card in the Digital Twins UI (Export Schema in the table view), which saves the result as a file.
Endpoint
POST {{edgeUrl}}/digital-twins
Content-Type: application/json
The operation is chosen by the GraphQL query body, not the URL path.
Authentication
HTTP Basic Auth. Username is your API token, password is empty. The same token is valid across /devicehub/v2, /analytics/v2, /cc, /opcua, and the other LE services.
Request body
query ExportInstanceSchema($input: ExportInstanceSchemaRequest!) {
ExportInstanceSchema(input: $input) {
Schema
Filename
ContentType
}
}
Variables (input: ExportInstanceSchemaRequest!)
| Field | Type | Required | Description |
|---|---|---|---|
InstanceID |
UUID! |
Yes | Instance to describe. Take it from Get Instances. |
{
"input": {
"InstanceID": "{{digital_twins_instance_id}}"
}
}
Response
200 OK -- application/json. data.ExportInstanceSchema is a JSONSchemaResponse:
| Field | Type | Description |
|---|---|---|
Schema |
String! |
The JSON Schema, serialized as a string. Parse it before use. |
Filename |
String! |
Suggested file name. Currently always instance.schema.json. |
ContentType |
String! |
Media type for the file. Currently application/schema+json. |
Parsed, the schema is an object with name set to the instance name and one property per top-level group:
Propertiesholds the static attributes, onestringproperty per key.- Every dynamic attribute name is split on
.into nested objects. Each leaf is keyed by the attribute's full dotted name and typedobject, since its value type is not known from the model.
Every listed property is also required.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"name": "Pump_P101",
"description": "Instance schema",
"type": "object",
"properties": {
"Nameplate": {
"type": "object",
"properties": {
"Nameplate.ManufacturerName": { "type": "object" },
"Nameplate.SerialNumber": { "type": "object" }
},
"required": ["Nameplate.ManufacturerName", "Nameplate.SerialNumber"]
},
"Properties": {
"type": "object",
"properties": {
"AssetKind": { "type": "string" },
"EquipmentTag": { "type": "string" }
},
"required": ["AssetKind", "EquipmentTag"]
}
},
"required": ["Nameplate", "Properties"]
}
An unknown InstanceID returns instance not found; a malformed one returns invalid UUID length: <n>.
Errors
GraphQL endpoints return 200 OK even on logical errors. Inspect the errors array in the response body:
{ "errors": [ { "message": "...", "path": ["..."], "extensions": { "code": "..." } } ] }
extensions.code |
Meaning |
|---|---|
UNAUTHENTICATED |
Missing or invalid API token. |
FORBIDDEN |
Token lacks permission for this operation. |
BAD_USER_INPUT |
Invalid argument (wrong type, missing required field, malformed UUID, unknown ID, etc.). |
NOT_FOUND |
The targeted entity does not exist. |
INTERNAL_SERVER_ERROR |
DeviceHub fault. Retry, then escalate via System > Support. |
A non-200 HTTP response means DeviceHub itself is unreachable. See Dashboard > DeviceHub Status.
TLS note: edge devices use a self-signed certificate by default. Either install the device CA in your client trust store or disable certificate verification when calling this endpoint directly.
Request Body
GraphQL Query
query ExportInstanceSchema($input: ExportInstanceSchemaRequest!) {
ExportInstanceSchema(input: $input) {
Schema
Filename
ContentType
}
}
Variables
{
"input": {
"InstanceID": "{{digital_twins_instance_id}}"
}
}
Response
Status: 200 OK
{
"data": {
"ExportInstanceSchema": {
"Schema": "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"name\": \"Pump_P101\",\n \"description\": \"Instance schema\",\n \"type\": \"object\",\n \"properties\": {\n \"Nameplate\": {\n \"type\": \"object\",\n \"properties\": {\n \"Nameplate.CompanyLogo\": {\n \"type\": \"object\"\n },\n \"Nameplate.ManufacturerName\": {\n \"type\": \"object\"\n },\n \"Nameplate.SerialNumber\": {\n \"type\": \"object\"\n },\n \"Nameplate.YearOfConstruction\": {\n \"type\": \"object\"\n }\n },\n \"required\": [\n \"Nameplate.CompanyLogo\",\n \"Nameplate.ManufacturerName\",\n \"Nameplate.SerialNumber\",\n \"Nameplate.YearOfConstruction\"\n ]\n },\n \"OperationalData\": {\n \"type\": \"object\",\n \"properties\": {\n \"AlarmHistory\": {\n \"type\": \"object\",\n \"properties\": {\n \"0\": {\n \"type\": \"object\",\n \"properties\": {\n \"OperationalData.AlarmHistory.0.Code\": {\n \"type\": \"object\"\n },\n \"OperationalData.AlarmHistory.0.Message\": {\n \"type\": \"object\"\n }\n },\n \"required\": [\n \"OperationalData.AlarmHistory.0.Code\",\n \"OperationalData.AlarmHistory.0.Message\"\n ]\n },\n \"1\": {\n \"type\": \"object\",\n \"properties\": {\n \"OperationalData.AlarmHistory.1.Code\": {\n \"type\": \"object\"\n },\n \"OperationalData.AlarmHistory.1.Message\": {\n \"type\": \"object\"\n }\n },\n \"required\": [\n \"OperationalData.AlarmHistory.1.Code\",\n \"OperationalData.AlarmHistory.1.Message\"\n ]\n }\n },\n \"required\": [\n \"0\",\n \"1\"\n ]\n },\n \"Measurements\": {\n \"type\": \"object\",\n \"properties\": {\n \"OperationalData.Measurements.FlowRate\": {\n \"type\": \"object\"\n },\n \"OperationalData.Measurements.RunningState\": {\n \"type\": \"object\"\n },\n \"OperationalData.Measurements.TemperatureRange\": {\n \"type\": \"object\"\n }\n },\n \"required\": [\n \"OperationalData.Measurements.FlowRate\",\n \"OperationalData.Measurements.RunningState\",\n \"OperationalData.Measurements.TemperatureRange\"\n ]\n },\n \"MotorAssembly\": {\n \"type\": \"object\",\n \"properties\": {\n \"OperationalData.MotorAssembly.MotorPower\": {\n \"type\": \"object\"\n }\n },\n \"required\": [\n \"OperationalData.MotorAssembly.MotorPower\"\n ]\n }\n },\n \"required\": [\n \"AlarmHistory\",\n \"Measurements\",\n \"MotorAssembly\"\n ]\n },\n \"Properties\": {\n \"type\": \"object\",\n \"properties\": {\n \"AssetKind\": {\n \"type\": \"string\"\n },\n \"EquipmentTag\": {\n \"type\": \"string\"\n },\n \"GlobalAssetId\": {\n \"type\": \"string\"\n },\n \"IdShort\": {\n \"type\": \"string\"\n },\n \"ShellId\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"AssetKind\",\n \"EquipmentTag\",\n \"GlobalAssetId\",\n \"IdShort\",\n \"ShellId\"\n ]\n }\n },\n \"required\": [\n \"Nameplate\",\n \"OperationalData\",\n \"Properties\"\n ]\n}",
"Filename": "instance.schema.json",
"ContentType": "application/schema+json"
}
}
}
Subscribe Playground Messages
POST {{edgeUrl}}/digital-twins/v2
New in 4.2.x. Streams live messages from a subject so a transformation can be tried against real traffic before it is saved to an attribute.
| Input | Meaning |
|---|---|
Subject |
Required. The topic or subject to watch. |
Expression |
Optional. Transformation to apply to each message. |
SchemaType |
json | jsonata. |
Each event carries the raw Payload, the Transformed result of applying Expression, and an Error when the expression fails on that message. Sending no Expression gives a plain topic tap.
DroppedSinceLast counts messages skipped since the previous event because the stream outran the consumer. A steadily rising value means what you are seeing is a sample, not the full feed.
WebSocket subscription. Convert to a Postman WebSocket request after import (URL: wss://.../digital-twins/v2).
Request Body
GraphQL Query
subscription PlaygroundMessages($input: PlaygroundSubscribeRequest!) {
PlaygroundMessages(input: $input) {
Topic
ReceivedAt
Payload
Transformed
Error
DroppedSinceLast
}
}
Variables
{
"input": {
"Subject": "{{dt_playground_subject}}",
"Expression": null,
"SchemaType": "jsonata"
}
}
Set Instance Asset Hierarchy
POST {{edgeUrl}}/digital-twins/v2
New in 4.3.x. Place one or more instances in an asset hierarchy. Returns Empty.
| Request field | Meaning |
|---|---|
Items |
List of assignments. Required, and the only field on the request |
Items[].InstanceID |
The instance being placed. Required |
Items[].HierarchyPath |
Its position in the hierarchy. Optional |
Items[].HierarchyMetadata |
Free-form metadata, typed Any. Optional |
The mutation is batch-first: Items is a list, so several instances can be placed in one call, which is the efficient way to build or restructure a hierarchy.
HierarchyMetadata is the Any scalar, so the server does not constrain its shape. That flexibility cuts both ways: a typo in a key is accepted silently. Settle on a metadata shape and validate it before sending.
The return type is Empty, so the response confirms nothing beyond the absence of an error. Read the placement back through the instance's static attributes, where hierarchy-derived entries carry AttributeType: AssetHierarchy.
Related: AttributeType gains AssetHierarchy in 4.3.x. See List Static Attributes in this folder.
Request Body
GraphQL Query
mutation SetInstanceAssetHierarchy($input: SetInstanceAssetHierarchyRequest!) {
SetInstanceAssetHierarchy(input: $input)
}
Variables
{
"input": {
"Items": [
{
"InstanceID": "{{dt_instance_id}}",
"HierarchyPath": "plant/line1/press",
"HierarchyMetadata": {
"area": "stamping",
"criticality": "high"
}
}
]
}
}