---
parent: /workflows/agents.md
back_out_url: /workflows/agents.md
---

# Workflow 17: Create a Digital Twin (Model + Instance)

> You are at https://api.litmus.io/workflows/create-digital-twin.md
> Parent: https://api.litmus.io/workflows/agents.md
> If this is the wrong workflow, back out to the index above.

## 17. Create a Digital Twin (Model + Instance)

**UI trigger**: Digital Twins -> Models -> Add Model -> (add attributes) -> Instances -> Add Instance -> Enable

A digital twin is a **model** (the schema: static and dynamic attributes) plus one or more **instances** of it that publish to a topic. All calls are GraphQL. Model, static attribute, state and delete operations go to `{{edgeUrl}}/digital-twins`; `CreateDynamicAttributes` and the 4.3.x-updated `CreateInstance` go to `{{edgeUrl}}/digital-twins/v2`. Full per-endpoint detail: /le/digital-twins.md

### Step table

| Step | API Name | Method | Endpoint / GraphQL Operation | Input | Output / What to capture |
|------|----------|--------|------------------------------|-------|--------------------------|
| 1 | Create Model | `POST` | `{{edgeUrl}}/digital-twins` -> `CreateModel` | `Name` (unique), `Assets` (e.g. `ASSET`), optional `Description`, `IsUNS`, `ConnectorName` | `ID` -> `{{digital_twins_model_id}}` |
| 2 | Create Static Attribute | `POST` | `{{edgeUrl}}/digital-twins` -> `CreateStaticAttributes` | `Items: [{ModelID, Key, Value}]`; `ModelID` from step 1, `Key` unique within the model. One call can add several | Array of attributes, one `ID` per item |
| 3 | Create Dynamic Attribute | `POST` | `{{edgeUrl}}/digital-twins/v2` -> `CreateDynamicAttributes` | `Items: [{ModelID, Topic, Name, Unit, DataType}]`; for nesting `AttrDataType: "nestedModel"` + `NestedModelID` or `NestedInstanceID`, with `Topic: ""` | Array of attributes, one `ID` and `SchemaID` per item |
| 4 | Create Instance | `POST` | `{{edgeUrl}}/digital-twins/v2` -> `CreateInstance` | `ModelID`, `Name`, `Topic`, `Interval` (ms), `FlatHierarchy`, optional `Source` (`local` / `external`) | `ID` -> `{{digital_twins_instance_id}}` |
| 5 | Enable Instance | `POST` | `{{edgeUrl}}/digital-twins` -> `SetInstanceState` | `InstanceID` (step 4), `IsEnabled: true` | `true`; the instance starts publishing to its topic |
| 6 | Delete Instance (clean up) | `POST` | `{{edgeUrl}}/digital-twins` -> `DeleteInstance` | `ID` of the instance | `null` on success |
| 7 | Delete Model (clean up) | `POST` | `{{edgeUrl}}/digital-twins` -> `DeleteModel` | `ID` of the model | `null` on success |

> **Use the plural attribute mutations**: the singular `CreateStaticAttribute` and `CreateDynamicAttribute` are deprecated on both Digital Twins endpoints ("No longer supported"). They still execute, but steps 2 and 3 use `CreateStaticAttributes` and `CreateDynamicAttributes`, which take an `Items` list of the same item shape and return an array. The collection chain still sends the singular forms.

> **Required fields (4.1.x+)**: `CreateModelRequest.Name` and `.Assets` are required; omitting either returns a GraphQL validation error. `DeleteInstanceRequest.ID` and `DeleteModelRequest.ID` are required.

> **`ModelID` is not checked on attribute creation**: steps 2 and 3 accept an unknown `ModelID` and return an attribute that belongs to no model. Confirm the model with `List Models` before adding attributes. `CreateInstance` does check it and fails with `DT_NOT_FOUND`.

> **Dynamic attribute `DataType`**: must be `json`, `string`, `number`, `boolean` or `array`; anything else (e.g. `float`) fails with `DT_VALIDATION_FAILED`. `NestedModelID` and `NestedInstanceID` are alternatives (set one, leave the other null) and are ignored unless `AttrDataType` is `nestedModel`. For a nested attribute the server stores `DataType` as `nestedModel` whatever you send. Nothing prevents a nesting cycle; it only surfaces when the twin is resolved.

> **Dynamic attribute `Topic`** (verified on LE 4.4.0): use letters, numbers, dots, dashes and underscores, with no leading, trailing or repeated dots; a `/` fails with `DT_VALIDATION_FAILED` (`line.1.speed` works, `line/1/speed` does not). A nested model attribute takes its topic from the instance it is bound to, so send `Topic: ""`; any other value is rejected. The collection chain's step 3 example (`"Topic": "line/1/motor"` on a nested attribute) fails for that reason. Instance topics (step 4) do accept `/`.

> **Disabled vs enabled**: a disabled instance is configured but does not publish. Use `SetInstanceState` to pause and resume instead of deleting and recreating.

> **Delete order**: a model with instances cannot be deleted (`DT_MODEL_HAS_INSTANCES`). Delete its instances first (step 6), then the model (step 7). Deleting a missing model ID returns `BAD_USER_INPUT`.

> **Variable names**: the collection chain uses `{{digital_twins_model_id}}` / `{{digital_twins_instance_id}}` in steps 1, 2, 5 and 7, but `{{dt_model_id}}` in steps 3 and 4. Set both to the same model ID when running the chain as-is.


---
