# LitmusEdge 4.0.x API Documentation/DeviceHub/Devices - LE, LEM, LUNS API Docs

## List Devices

**POST** `{{edgeUrl}}/devicehub/v2`

# List Devices

Returns every configured device in DeviceHub along with the most commonly needed fields. This is the richer projection used by the LE UI's device list and by automation that needs to enumerate devices and their parameters in one call.

For a minimal (`ID` + `Name`) projection used by the dashboard tile, see `Dashboard > List of Devices`.

## Endpoint

```http
POST {{edgeUrl}}/devicehub/v2
Content-Type: application/json
```

## 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 (GraphQL)

```graphql
query ListDevices {
  ListDevices {
    ID
    Name
    DriverID
    AliasTopics
    CreationTime
    Description
    WorkerParams {
      Name
      Value
      Description
    }
    DHParams {
      Name
      Value
      Description
    }
  }
}
```

`ListDevices` accepts an optional `input: { IDs: [ID!] }` filter (omit to return every device). Any field on `Device` can be added to the selection, including `Properties`, `MetaData`, `Topics`, `StatusPollingRegister`, `StoppedState`, `Debug`.

## Response

`200 OK` -- `application/json`

| Field                  | GraphQL type | Description                                                                                                                   |
|------------------------|--------------|-------------------------------------------------------------------------------------------------------------------------------|
| `ID`                   | `ID`         | Device UUID. Used as `{{deviceID}}` in REST endpoints and as the foreign key from tags/registers.                              |
| `Name`                 | `String`     | Display name of the device. Must be unique across the device list.                                                            |
| `DriverID`             | `ID`         | UUID of the driver template bound to this device. Not re-assignable; create a new device to change drivers.                    |
| `AliasTopics`          | `Boolean`    | `true` means published MQTT topics use the human-readable register name; `false` means the raw register key.                  |
| `CreationTime`         | `Time`       | ISO 8601 timestamp the device was created. `1970-01-01T00:00:00Z` for devices created before this field was introduced.        |
| `Description`          | `String`     | Free-form description set by the operator.                                                                                    |
| `WorkerParams`         | `[Param!]!`  | Internal worker tunables (poll interval, batching, etc.). Each entry has `Name`, `Value`, `Description`.                       |
| `DHParams`             | `[Param!]!`  | DeviceHub-level parameters, same `{Name,Value,Description}` shape. Driver-agnostic settings.                                  |

### Example response

```json
{
  "data": {
    "ListDevices": [
      {
        "ID": "41FBD12E-C6CD-4539-AD50-336D278B207C",
        "Name": "Rack 2b M258",
        "DriverID": "2AF1FA08-D638-11E9-BB65-2A2AE2DBCCE4",
        "AliasTopics": false,
        "CreationTime": "1970-01-01T00:00:00Z",
        "Description": "",
        "WorkerParams": [
          {
            "Name": "PublishOnPollingInterval",
            "Value": "true",
            "Description": "true"
          }
        ],
        "DHParams": []
      }
    ]
  }
}
```

## Errors

GraphQL endpoints return `200 OK` even on logical errors. Inspect the `errors` array in the response body:

```json
{ "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 DriverID, etc.). |
| `NOT_FOUND`             | The targeted device, driver, or related 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**

```graphql
query ListDevices {
    ListDevices {
        ID
        Name
        DriverID
        AliasTopics
        CreationTime
        Description
        WorkerParams {
            Name
            Value
            Description
        }
        DHParams {
            Name
            Value
            Description
        }
    }
}

```

### Response

**Status**: 200 OK

```json
{
    "data": {
        "ListDevices": [
            {
                "ID": "41FBD12E-C6CD-4539-AD50-336D278B207C",
                "Name": "Rack 2b M258",
                "DriverID": "2AF1FA08-D638-11E9-BB65-2A2AE2DBCCE4",
                "AliasTopics": false,
                "CreationTime": "1970-01-01T00:00:00Z",
                "Description": "",
                "WorkerParams": [
                    {
                        "Name": "PublishOnPollingInterval",
                        "Value": "true",
                        "Description": "true"
                    }
                ],
                "DHParams": [
                    {
                        "Name": "WatchDogPeriod",
                        "Value": "30",
                        "Description": "30"
                    },
                    {
                        "Name": "WatchDog",
                        "Value": "true",
                        "Description": "true"
                    }
                ]
            }
        ]
    }
}
```

---

## Create New Device

**POST** `{{edgeUrl}}/devicehub/v2`

# Create New Device

Creates a single device bound to a driver template. The `Properties` array supplies the values for every property declared by the driver, and any property the driver marks as `Required` must be present here. The returned `ID` is the canonical device UUID used in every subsequent call.

Set `StoppedState: true` if you want the device to be created but **not** attempt to connect yet -- useful when you are programmatically populating properties or want to attach registers before the first poll. To start the device later, call `Start Device`.

## Endpoint

```http
POST {{edgeUrl}}/devicehub/v2
Content-Type: application/json
```

## 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 (GraphQL)

```graphql
mutation CreateDevice($input: CreateDeviceRequest!) {
  CreateDevice(input: $input) {
    ID
    Name
    DriverID
    Description
  }
}
```

### Variables (`input: CreateDeviceRequest!`)

| Field             | Type                         | Required | Description                                                                                                  |
|-------------------|------------------------------|----------|--------------------------------------------------------------------------------------------------------------|
| `DriverID`        | `ID`                         | Yes      | UUID of the driver template. List drivers via `DeviceHub > Drivers > List Drivers`.                          |
| `Name`            | `String`                     | Yes      | Display name. Must be unique across all devices.                                                             |
| `Properties`      | `[KeyValueInput!]!`          | Yes      | Driver-defined property values. Each entry is `{Key, Value}`. Required properties depend on the driver -- see the driver's template via `DeviceHub > Drivers > Get Driver`. |
| `Description`     | `String`                     | No       | Free-form description shown in the UI.                                                                       |
| `AliasTopics`     | `Boolean`                    | No       | Default `false`. `true` makes published MQTT topics use the human register name instead of the raw key.       |
| `Debug`           | `Boolean`                    | No       | Default `false`. Enables verbose driver logging for this device.                                              |
| `MetaData`        | `[KeyValueInput!]`           | No       | Operator-controlled metadata (e.g. `vendor`, `model`, `fw_ver`). Surfaced in the UI and downstream pipelines. |
| `StatusRegister`  | `RegisterInput`              | No       | Register definition whose value DeviceHub treats as the device's connection-status signal. `null` disables.   |
| `StoppedState`    | `Boolean`                    | No       | Default `false`. `true` creates the device in a stopped state -- no connection attempt until `Start Device`. |

### Variables (example)

```json
{
  "input": {
    "DriverID": "5BC98836-CFD4-11E9-BB65-2A2AE2DBCCE4",
    "Name": "apiTestDevice",
    "Description": "deviceCreatedfromAPI",
    "AliasTopics": true,
    "Debug": false,
    "MetaData": [],
    "Properties": [
      { "Key": "name",        "Value": "apiTestDevice" },
      { "Key": "description", "Value": "deviceCreatedfromAPI" }
    ],
    "StatusRegister": null,
    "StoppedState": false
  }
}
```

## Response

`200 OK` -- `application/json`

| Field                       | Type     | Description                                                       |
|-----------------------------|----------|-------------------------------------------------------------------|
| `data.CreateDevice.ID`      | `ID`     | UUID of the newly created device. Save this as `{{deviceID}}`.    |
| `data.CreateDevice.Name`    | `String` | Device name echoed back.                                          |
| `data.CreateDevice.DriverID`| `ID`     | Driver UUID echoed back.                                          |
| `data.CreateDevice.Description` | `String` | Description echoed back.                                       |

```json
{
  "data": {
    "CreateDevice": {
      "ID": "36B0FDFA-AFC8-4B84-B0B0-296893DB18ED",
      "Name": "apiTestDevice",
      "DriverID": "5BC98836-CFD4-11E9-BB65-2A2AE2DBCCE4",
      "Description": ""
    }
  }
}
```

## Errors

GraphQL endpoints return `200 OK` even on logical errors. Inspect the `errors` array in the response body:

```json
{ "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 DriverID, etc.). |
| `NOT_FOUND`             | The targeted device, driver, or related 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`.

### Common `BAD_USER_INPUT` causes

- `DriverID` does not match any installed driver.
- A required property for the driver is missing from `Properties`.
- A property value fails the driver's type/regex constraints (e.g. a port number outside `0..65535`, an enum value not in `ListValues`).
- `Name` collides with an existing device.

> **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**

```graphql
mutation CreateDevice($input: CreateDeviceRequest!) {
  CreateDevice(input: $input) {
    ID
    Name
    DriverID
    Description
  }
}
```

**Variables**

```json
{
  "input": {
    "AliasTopics": true,
    "Debug": false,
    "MetaData": [],
    "Name": "apiTestDevice",
    "Description": "",
    "Properties": [
      {
        "Key": "name",
        "Value": "apiTestDevice"
      },
      {
        "Key": "description",
        "Value": "deviceCreatedfromAPI"
      }
    ],
    "StatusRegister": null,
    "StoppedState": false,
    "DriverID": "5BC98836-CFD4-11E9-BB65-2A2AE2DBCCE4"
  }
}
```

### Response

**Status**: 200 OK

```json
{
    "data": {
        "CreateDevice": {
            "ID": "36B0FDFA-AFC8-4B84-B0B0-296893DB18ED",
            "Name": "apiTestDevice",
            "DriverID": "5BC98836-CFD4-11E9-BB65-2A2AE2DBCCE4",
            "Description": ""
        }
    }
}
```

---

## Update Device

**POST** `{{edgeUrl}}/devicehub/v2`

# Update Device

Replaces the mutable fields of an existing device. `Update Device` is a **full replacement** of the input shape, not a JSON-patch: every value you include is written, every value you omit is left at its server-side default. Provide the full `Properties` array even when changing a single key, otherwise unspecified keys are reset.

Use this to:

- Re-key the device (`Name`).
- Re-target a different host (`Properties` -> `networkAddress`, `networkPort`, etc.).
- Toggle `Debug` or `AliasTopics`.
- Refresh `MetaData` such as `vendor`, `model`, `fw_ver`.
- Reassign the `StatusRegister` used to compute the device's online/offline state.

You **cannot** change `DriverID`. To switch drivers, delete the device and create a new one with the desired driver.

## Endpoint

```http
POST {{edgeUrl}}/devicehub/v2
Content-Type: application/json
```

## 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 (GraphQL)

```graphql
mutation UpdateDevice($input: UpdateDeviceRequest!) {
  UpdateDevice(input: $input) {
    ID
    Name
    DriverID
    Description
    Debug
    Properties              { Name Value Description }
    Topics                  { Direction Encoding Format IntervalMs Topic }
    MetaData                { Name Value Description }
    AliasTopics
    CreationTime
    StatusPollingRegister {
      Name
      ValueType
      Properties { Name Value Description }
    }
  }
}
```

The selection set in the response is yours to choose; the example above mirrors the LE UI's projection.

### Variables (`input: UpdateDeviceRequest!`)

| Field            | Type                  | Required | Description                                                                                          |
|------------------|-----------------------|----------|------------------------------------------------------------------------------------------------------|
| `ID`             | `ID`                  | Yes      | UUID of the device to update.                                                                        |
| `Name`           | `String`              | Yes      | New name. Must remain unique across devices.                                                         |
| `Description`    | `String`              | No       | Free-form description.                                                                               |
| `Properties`     | `[KeyValueInput!]!`   | Yes      | Full driver property set. Send the complete list -- omitted keys are not preserved.                    |
| `MetaData`       | `[KeyValueInput!]`    | No       | Operator metadata. Same semantics as `Properties`: it is a full replacement.                          |
| `AliasTopics`    | `Boolean`             | No       | See `Create New Device`.                                                                              |
| `Debug`          | `Boolean`             | No       | See `Create New Device`.                                                                              |
| `StatusRegister` | `RegisterInput`       | No       | Reassign the status register. `null` removes it.                                                      |

### Variables (example)

```json
{
  "input": {
    "ID": "41FBD12E-C6CD-4539-AD50-336D278B207C",
    "Name": "Rack 2b M258",
    "Description": "Modbus TCP Ethernet Driver",
    "AliasTopics": false,
    "Debug": false,
    "MetaData": [
      { "Key": "device_type",     "Value": "PLC" },
      { "Key": "fw_ver",          "Value": "v2.0.31.40" },
      { "Key": "model",           "Value": "modicon m258" },
      { "Key": "vendor",          "Value": "schneider electric" },
      { "Key": "device_mac_addr", "Value": "00:80:f4:41:20:68" }
    ],
    "Properties": [
      { "Key": "name",                   "Value": "Rack 2b M258" },
      { "Key": "description",            "Value": "Modbus TCP Ethernet Driver" },
      { "Key": "networkAddress",         "Value": "10.17.0.8" },
      { "Key": "networkPort",            "Value": "502" },
      { "Key": "stationId",              "Value": "1" },
      { "Key": "Zero-Based Addressing",  "Value": "1" }
    ],
    "StatusRegister": null
  }
}
```

## Response

`200 OK` -- `application/json`. Echoes the new state of the device using the selection set from the request. Server-side defaults are filled in for any optional field you left out.

## Errors

GraphQL endpoints return `200 OK` even on logical errors. Inspect the `errors` array in the response body:

```json
{ "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 DriverID, etc.). |
| `NOT_FOUND`             | The targeted device, driver, or related 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`.

### Operational note

Updating connection-relevant `Properties` (host, port, credentials) does **not** automatically restart the device worker. If the device is already started, the worker is signaled to reconnect; for major topology changes (e.g. switching transport) consider an explicit `Stop Device` -> update -> `Start Device` sequence to avoid stale connections.

> **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**

```graphql
mutation UpdateDevice($input: UpdateDeviceRequest!) {
    UpdateDevice(input: $input) {
        ID
        Name
        DriverID
        Description
        Debug
        Properties {
            Name
            Value
            Description
        }
        Topics {
            Direction
            Encoding
            Format
            IntervalMs
            Topic
        }
        MetaData {
            Name
            Value
            Description
        }
        AliasTopics
        CreationTime
        StatusPollingRegister {
            Name
            ValueType
            Properties {
                Name
                Value
                Description
            }
        }
    }
}

```

**Variables**

```json
{
    "input": {
        "AliasTopics": false,
        "Debug": false,
        "MetaData": [
            {
                "Key": "device_type",
                "Value": "PLC"
            },
            {
                "Key": "fw_ver",
                "Value": "v2.0.31.40"
            },
            {
                "Key": "model",
                "Value": "modicon m258"
            },
            {
                "Key": "vendor",
                "Value": "schneider electric"
            },
            {
                "Key": "version",
                "Value": "schneider modocon 1.01"
            },
            {
                "Key": "device_mac_addr",
                "Value": "00:80:f4:41:20:68"
            }
        ],
        "Name": "Rack 2b M258",
        "Description": "Modbus TCP Ethernet Driver",
        "Properties": [
            {
                "Key": "name",
                "Value": "Rack 2b M258"
            },
            {
                "Key": "description",
                "Value": "Modbus TCP Ethernet Driver"
            },
            {
                "Key": "networkAddress",
                "Value": "10.17.0.8"
            },
            {
                "Key": "networkPort",
                "Value": "502"
            },
            {
                "Key": "stationId",
                "Value": "1"
            },
            {
                "Key": "Zero-Based Addressing",
                "Value": "1"
            }
        ],
        "StatusRegister": null,
        "ID": "41FBD12E-C6CD-4539-AD50-336D278B207C"
    }
}
```

### Response

**Status**: 200 OK

```json
{
    "data": {
        "UpdateDevice": {
            "ID": "41FBD12E-C6CD-4539-AD50-336D278B207C",
            "Name": "Rack 2b M258",
            "DriverID": "2AF1FA08-D638-11E9-BB65-2A2AE2DBCCE4",
            "Description": "",
            "Debug": false,
            "Properties": [
                {
                    "Name": "description",
                    "Value": "Modbus TCP Ethernet Driver",
                    "Description": "Modbus TCP Ethernet Driver"
                },
                {
                    "Name": "name",
                    "Value": "Rack 2b M258",
                    "Description": "Rack 2b M258"
                },
                {
                    "Name": "Zero-Based Addressing",
                    "Value": "1",
                    "Description": "1"
                },
                {
                    "Name": "stationId",
                    "Value": "1",
                    "Description": "1"
                },
                {
                    "Name": "networkPort",
                    "Value": "502",
                    "Description": "502"
                },
                {
                    "Name": "networkAddress",
                    "Value": "10.17.0.8",
                    "Description": "10.17.0.8"
                }
            ],
            "Topics": [
                {
                    "Direction": "Output",
                    "Encoding": "JSON",
                    "Format": "Raw",
                    "IntervalMs": null,
                    "Topic": "devicehub.plcstatus.41FBD12E-C6CD-4539-AD50-336D278B207C"
                },
                {
                    "Direction": "Input",
                    "Encoding": "JSON",
                    "Format": "Command",
                    "IntervalMs": null,
                    "Topic": "devicehub.manage.41FBD12E-C6CD-4539-AD50-336D278B207C"
                },
                {
                    "Direction": "Output",
                    "Encoding": "JSON",
                    "Format": "Raw",
                    "IntervalMs": null,
                    "Topic": "devicehub.raw.41FBD12E-C6CD-4539-AD50-336D278B207C.>"
                }
            ],
            "MetaData": [
                {
                    "Name": "device_type",
                    "Value": "PLC",
                    "Description": "PLC"
                },
                {
                    "Name": "device_mac_addr",
                    "Value": "00:80:f4:41:20:68",
                    "Description": "00:80:f4:41:20:68"
                },
                {
                    "Name": "version",
                    "Value": "schneider modocon 1.01",
                    "Description": "schneider modocon 1.01"
                },
                {
                    "Name": "vendor",
                    "Value": "schneider electric",
                    "Description": "schneider electric"
                },
                {
                    "Name": "model",
                    "Value": "modicon m258",
                    "Description": "modicon m258"
                },
                {
                    "Name": "fw_ver",
                    "Value": "v2.0.31.40",
                    "Description": "v2.0.31.40"
                }
            ],
            "AliasTopics": false,
            "CreationTime": "1970-01-01T00:00:00Z",
            "StatusPollingRegister": null
        }
    }
}
```

---

## Enable/Disable Device Data Storage

**PUT** `{{edgeUrl}}/stats/devices/{{deviceID}}`

# Enable/Disable Device Data Storage

Controls whether DeviceHub persists telemetry from this device to the on-device time-series store. This is the same setting exposed by the **Data Storage** toggle on the device page in the LE UI.

When `saveAll` is `false`, no tag values from this device are persisted (the device still publishes to MQTT/integrations in real time). When `true`, DeviceHub writes to the local store and retains data for `retentionHours`, rotating older points out.

This endpoint is REST -- the underlying stats service is not part of the DeviceHub GraphQL surface.

## Endpoint

```http
PUT {{edgeUrl}}/stats/devices/{{deviceID}}
Content-Type: application/json
```

`{{deviceID}}` is the UUID of the target device. Both the path parameter and the body include the ID; they must match.

## Authentication

HTTP Basic Auth. **Username** is your API token, **password** is empty. Tokens are managed under `System > Access Control > Tokens`.

## Request body

```json
{
  "ID": "{{deviceID}}",
  "name": "boiler-01",
  "retentionHours": 12,
  "saveAll": false
}
```

| Field            | Type     | Required | Description                                                                       |
|------------------|----------|----------|-----------------------------------------------------------------------------------|
| `ID`             | string   | Yes      | Device UUID. Must match the path parameter.                                       |
| `name`           | string   | Yes      | Device name. The stats service stores this alongside the ID; pass the current name. |
| `retentionHours` | integer  | Yes      | How many hours of data to keep. `0` disables retention even if `saveAll: true`.   |
| `saveAll`        | boolean  | Yes      | `true` enables persistence of all of the device's tags; `false` disables.         |

## Response

`200 OK` on success, with an empty body or a small JSON ack depending on build. The change applies immediately to the running worker -- no restart required.

## Errors

| HTTP status         | When it happens                                                                          |
|---------------------|------------------------------------------------------------------------------------------|
| `400 Bad Request`   | Body is missing a required field, or `ID` in body does not match path.                   |
| `401 Unauthorized`  | Missing or invalid credentials.                                                          |
| `403 Forbidden`     | Token lacks write access to the stats service.                                           |
| `404 Not Found`     | No device exists with the given ID.                                                      |
| `502` / `503`       | Stats service is not running.                                                            |

> **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

```json
{
    "ID": "{{deviceID}}",
    "name": "boiler-01",
    "retentionHours": 12,
    "saveAll": false
}
```

### Response

**Status**: 204 No Content

---

## Delete Devices graphQL

**POST** `{{edgeUrl}}/devicehub/v2`

# Delete Devices (GraphQL, bulk)

Deletes one or more devices in a single mutation. The operation is **bulk by design** -- pass a single-element `IDs` array to delete one device, or a list to delete many atomically.

Deletes cascade to the device's registers/tags, examples in `DeviceHub > Tags`, and any topic subscriptions tied to the device. They do **not** delete time-series data already persisted to the stats store; those points age out via the retention policy configured on the stats service.

For a single-device REST equivalent (path-parameter style), see `Delete Device Rest`.

## Endpoint

```http
POST {{edgeUrl}}/devicehub/v2
Content-Type: application/json
```

## 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 (GraphQL)

```graphql
mutation DeleteDevices($input: DeleteDevicesRequest!) {
  DeleteDevices(input: $input)
}
```

### Variables (`input: DeleteDevicesRequest!`)

| Field   | Type      | Required | Description                                                                |
|---------|-----------|----------|----------------------------------------------------------------------------|
| `IDs`   | `[ID!]!`  | Yes      | UUIDs of devices to delete. Unknown IDs are ignored (no per-ID error report). |

### Variables (example)

```json
{
  "input": {
    "IDs": [
      "4F2715FE-4B84-4223-AFA6-858504803ED7",
      "8CDACA9A-FF14-482B-B1A0-F85700525980"
    ]
  }
}
```

## Response

`200 OK` -- `application/json`. The mutation returns no data on success.

```json
{
  "data": {
    "DeleteDevices": null
  }
}
```

A `null` payload indicates the delete succeeded for the IDs that existed; silently-ignored unknown IDs do not surface as errors here. To confirm a specific device was removed, follow up with `List Devices` or `Get Device`.

## Errors

GraphQL endpoints return `200 OK` even on logical errors. Inspect the `errors` array in the response body:

```json
{ "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 DriverID, etc.). |
| `NOT_FOUND`             | The targeted device, driver, or related 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**

```graphql
mutation DeleteDevices ($input : DeleteDevicesRequest!) {
    DeleteDevices(input: $input)
}

```

**Variables**

```json
{
    "input": {
        "IDs": [
            "4F2715FE-4B84-4223-AFA6-858504803ED7",
            "8CDACA9A-FF14-482B-B1A0-F85700525980"
        ]
    }
}
```

### Response

**Status**: 200 OK

```json
{
    "data": {
        "DeleteDevices": null
    }
}
```

---

## Delete Device Rest

**DELETE** `{{edgeUrl}}/devicehub/devices/{{deviceID}}`

# Delete Device (REST, single)

Deletes a single device identified by path parameter. This is the REST equivalent of `Delete Devices graphQL` with a one-element `IDs` array. The GraphQL form is preferred for new automation -- this REST endpoint exists mainly for legacy clients.

Side effects are identical to the GraphQL form: the device's registers, topic subscriptions, and configuration are removed. Persisted time-series data ages out via the stats retention policy.

## Endpoint

```http
DELETE {{edgeUrl}}/devicehub/devices/{{deviceID}}
```

`{{deviceID}}` is the UUID of the device to delete.

## Authentication

HTTP Basic Auth. **Username** is your API token, **password** is empty. Tokens are managed under `System > Access Control > Tokens`.

## Parameters

| Location | Name         | Required | Description                                  |
|----------|--------------|----------|----------------------------------------------|
| Path     | `{{deviceID}}` | Yes    | UUID of the device to delete.                |

No request body.

## Response

`200 OK` with an empty body on success.

## Errors

| HTTP status         | When it happens                                                                   |
|---------------------|-----------------------------------------------------------------------------------|
| `401 Unauthorized`  | Missing or invalid credentials.                                                   |
| `403 Forbidden`     | Token lacks delete access to DeviceHub.                                           |
| `404 Not Found`     | No device exists with the given ID. (The GraphQL form silently ignores; this one surfaces a 404.) |
| `502` / `503`       | DeviceHub service is unreachable.                                                 |

> **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.


### Response

**Status**: 204 No Content

---

## Device Discovery Tool

**POST** `{{edgeUrl}}/devicehub/v2`

# Device Discovery Tool

Asks a driver to enumerate discoverable endpoints/servers reachable from its configured network neighborhood, without creating a device first. The returned `DiscoveryOptions` array is a menu of pre-filled property bundles the operator can pick from to spawn a device.

This feature is **only available for protocols that support discovery**. As of LE 4.0 those are:

- **BACnet** (the `BacnetIP` driver)
- **OPC UA** (the `OPC UA Client Advanced` driver)

Other drivers return an empty `DiscoveryOptions` array.

## Endpoint

```http
POST {{edgeUrl}}/devicehub/v2
Content-Type: application/json
```

## 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 (GraphQL)

```graphql
query Discovery($input: DiscoveryRequest!) {
  Discovery(input: $input) {
    DiscoveryOptions {
      Label
      Description
      Fields {
        Name
        Type
        Value
      }
    }
  }
}
```

### Variables (`input: DiscoveryRequest!`)

| Field         | Type                | Required | Description                                                                                       |
|---------------|---------------------|----------|---------------------------------------------------------------------------------------------------|
| `DriverID`    | `ID`                | Yes      | UUID of a discovery-capable driver (BACnet or OPC UA Advanced).                                   |
| `Properties`  | `[KeyValueInput!]!` | Yes      | Driver-specific seed properties controlling the search scope. For OPC UA, this is the discovery/server URL; for BACnet, the local interface and address range. |

### Variables (example, OPC UA)

```json
{
  "input": {
    "DriverID": "2372E68F-BA1B-46E9-BC07-E92CA006B377",
    "Properties": [
      {
        "Key": "Discovery Server URL (Servers and Endpoints Discovery)",
        "Value": "opc.tcp://localhost:4840"
      },
      {
        "Key": "Server URL (Endpoints Discovery)",
        "Value": "opc.tcp://10.17.2.198:53530/OPCUA/SimulationServer"
      }
    ]
  }
}
```

The exact `Key`s depend on the driver. List the supported keys via `DeviceHub > Drivers > Get Driver` and inspect the driver template's discovery-related properties.

## Response

`200 OK` -- `application/json`

| Field                                                 | Type            | Description                                                                       |
|-------------------------------------------------------|-----------------|-----------------------------------------------------------------------------------|
| `data.Discovery.DiscoveryOptions`                     | `[Option!]!`    | Menu of discovered endpoints. Empty array means no devices/endpoints found.       |
| `data.Discovery.DiscoveryOptions[].Label`             | `String`        | Human-readable name (e.g. `SimulationServer@DESKTOP - opc.tcp://...`).            |
| `data.Discovery.DiscoveryOptions[].Description`       | `String`        | Free-form annotation, often security policy/mode for OPC UA.                      |
| `data.Discovery.DiscoveryOptions[].Fields`            | `[Field!]!`     | Pre-filled property values for this option. Pass these as `Properties` to `Create New Device` to spawn the device. |
| `data.Discovery.DiscoveryOptions[].Fields[].Name`     | `String`        | Property key.                                                                     |
| `data.Discovery.DiscoveryOptions[].Fields[].Type`     | `String`        | Type hint (`String`, `Int`, enum index, etc.). Often `null` for free-text values. |
| `data.Discovery.DiscoveryOptions[].Fields[].Value`    | `String`        | Property value to copy into the new device.                                       |

### Example response

```json
{
  "data": {
    "Discovery": {
      "DiscoveryOptions": [
        {
          "Label": "SimulationServer@DESKTOP-IRI8PIH - opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer",
          "Description": "Security Policy: Basic256Sha256, Security Mode: Sign",
          "Fields": [
            { "Name": "SecurityPolicy", "Type": null, "Value": "3" },
            { "Name": "ServerURL", "Type": null, "Value": "opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer" }
          ]
        }
      ]
    }
  }
}
```

## Errors

GraphQL endpoints return `200 OK` even on logical errors. Inspect the `errors` array in the response body:

```json
{ "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 DriverID, etc.). |
| `NOT_FOUND`             | The targeted device, driver, or related 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`.

### Discovery-specific errors

- `BAD_USER_INPUT` with `DriverID is not discoverable`: the driver does not support discovery (only BACnet and OPC UA Advanced do).
- `INTERNAL_SERVER_ERROR` with a timeout: the discovery scope was too wide or the upstream server did not respond. Narrow the URL or address range.

> **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**

```graphql
query Discovery($input: DiscoveryRequest!) {
  Discovery(input: $input) {
    DiscoveryOptions {
      Label
      Description
      Fields {
        Name
        Type
        Value
      }
    }
  }
}
```

**Variables**

```json
{
  "input": {
    "DriverID": "2372E68F-BA1B-46E9-BC07-E92CA006B377",
    "Properties": [
      {
        "Key": "Discovery Server URL (Servers and Endpoints Discovery)",
        "Value": "opc.tcp://localhost:4840"
      },
      {
        "Key": "Server URL (Endpoints Discovery)",
        "Value": "opc.tcp://10.17.2.198:53530/OPCUA/SimulationServer"
      }
    ]
  }
}
```

### Response

**Status**: 200 OK

```json
{
    "data": {
        "Discovery": {
            "DiscoveryOptions": [
                {
                    "Label": "SimulationServer@DESKTOP-IRI8PIH - opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer",
                    "Description": "Security Policy: Basic256Sha256, Security Mode: Sign",
                    "Fields": [
                        {
                            "Name": "SecurityPolicy",
                            "Type": null,
                            "Value": "3"
                        },
                        {
                            "Name": "ServerURL",
                            "Type": null,
                            "Value": "opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer"
                        },
                        {
                            "Name": "SecurityMode",
                            "Type": null,
                            "Value": "1"
                        }
                    ]
                },
                {
                    "Label": "SimulationServer@DESKTOP-IRI8PIH - opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer",
                    "Description": "Security Policy: Basic128Rsa15, Security Mode: SignAndEncrypt",
                    "Fields": [
                        {
                            "Name": "SecurityMode",
                            "Type": null,
                            "Value": "2"
                        },
                        {
                            "Name": "ServerURL",
                            "Type": null,
                            "Value": "opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer"
                        },
                        {
                            "Name": "SecurityPolicy",
                            "Type": null,
                            "Value": "1"
                        }
                    ]
                },
                {
                    "Label": "SimulationServer@DESKTOP-IRI8PIH - opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer",
                    "Description": "Security Policy: Aes256Sha256RsaPss, Security Mode: SignAndEncrypt",
                    "Fields": [
                        {
                            "Name": "SecurityPolicy",
                            "Type": null,
                            "Value": "5"
                        },
                        {
                            "Name": "ServerURL",
                            "Type": null,
                            "Value": "opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer"
                        },
                        {
                            "Name": "SecurityMode",
                            "Type": null,
                            "Value": "2"
                        }
                    ]
                },
                {
                    "Label": "SimulationServer@DESKTOP-IRI8PIH - opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer",
                    "Description": "Security Policy: Basic128Rsa15, Security Mode: Sign",
                    "Fields": [
                        {
                            "Name": "SecurityPolicy",
                            "Type": null,
                            "Value": "1"
                        },
                        {
                            "Name": "SecurityMode",
                            "Type": null,
                            "Value": "1"
                        },
                        {
                            "Name": "ServerURL",
                            "Type": null,
                            "Value": "opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer"
                        }
                    ]
                },
                {
                    "Label": "SimulationServer@DESKTOP-IRI8PIH - opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer",
                    "Description": "Security Policy: Aes256Sha256RsaPss, Security Mode: Sign",
                    "Fields": [
                        {
                            "Name": "SecurityPolicy",
                            "Type": null,
                            "Value": "5"
                        },
                        {
                            "Name": "SecurityMode",
                            "Type": null,
                            "Value": "1"
                        },
                        {
                            "Name": "ServerURL",
                            "Type": null,
                            "Value": "opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer"
                        }
                    ]
                },
                {
                    "Label": "SimulationServer@DESKTOP-IRI8PIH - opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer",
                    "Description": "Security Policy: Basic256Sha256, Security Mode: SignAndEncrypt",
                    "Fields": [
                        {
                            "Name": "SecurityPolicy",
                            "Type": null,
                            "Value": "3"
                        },
                        {
                            "Name": "ServerURL",
                            "Type": null,
                            "Value": "opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer"
                        },
                        {
                            "Name": "SecurityMode",
                            "Type": null,
                            "Value": "2"
                        }
                    ]
                },
                {
                    "Label": "SimulationServer@DESKTOP-IRI8PIH - opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer",
                    "Description": "Security Policy: None, Security Mode: None",
                    "Fields": [
                        {
                            "Name": "ServerURL",
                            "Type": null,
                            "Value": "opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer"
                        },
                        {
                            "Name": "SecurityPolicy",
                            "Type": null,
                            "Value": "0"
                        },
                        {
                            "Name": "SecurityMode",
                            "Type": null,
                            "Value": "0"
                        }
                    ]
                },
                {
                    "Label": "SimulationServer@DESKTOP-IRI8PIH - opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer",
                    "Description": "Security Policy: Aes128Sha256RsaOaep, Security Mode: Sign",
                    "Fields": [
                        {
                            "Name": "ServerURL",
                            "Type": null,
                            "Value": "opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer"
                        },
                        {
                            "Name": "SecurityPolicy",
                            "Type": null,
                            "Value": "4"
                        },
                        {
                            "Name": "SecurityMode",
                            "Type": null,
                            "Value": "1"
                        }
                    ]
                },
                {
                    "Label": "SimulationServer@DESKTOP-IRI8PIH - opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer",
                    "Description": "Security Policy: Basic256, Security Mode: SignAndEncrypt",
                    "Fields": [
                        {
                            "Name": "SecurityPolicy",
                            "Type": null,
                            "Value": "2"
                        },
                        {
                            "Name": "ServerURL",
                            "Type": null,
                            "Value": "opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer"
                        },
                        {
                            "Name": "SecurityMode",
                            "Type": null,
                            "Value": "2"
                        }
                    ]
                },
                {
                    "Label": "SimulationServer@DESKTOP-IRI8PIH - opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer",
                    "Description": "Security Policy: Basic256, Security Mode: Sign",
                    "Fields": [
                        {
                            "Name": "SecurityPolicy",
                            "Type": null,
                            "Value": "2"
                        },
                        {
                            "Name": "SecurityMode",
                            "Type": null,
                            "Value": "1"
                        },
                        {
                            "Name": "ServerURL",
                            "Type": null,
                            "Value": "opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer"
                        }
                    ]
                },
                {
                    "Label": "SimulationServer@DESKTOP-IRI8PIH - opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer",
                    "Description": "Security Policy: Aes128Sha256RsaOaep, Security Mode: SignAndEncrypt",
                    "Fields": [
                        {
                            "Name": "SecurityPolicy",
                            "Type": null,
                            "Value": "4"
                        },
                        {
                            "Name": "SecurityMode",
                            "Type": null,
                            "Value": "2"
                        },
                        {
                            "Name": "ServerURL",
                            "Type": null,
                            "Value": "opc.tcp://DESKTOP-IRI8PIH:53530/OPCUA/SimulationServer"
                        }
                    ]
                }
            ]
        }
    }
}
```

---

## Device Connection Status

**POST** `{{edgeUrl}}/devicehub/v2`

# Device Connection Status (subscription)

Streams real-time updates whenever any device's connection state changes (connected, disconnected, reconnecting, error, ...). This is a GraphQL **subscription**, not a query -- it is designed for clients that hold an open connection and receive a push for every event, not for polling.

The LE UI uses this to live-update the green/red status dots on the device list. Use it from your own dashboards or alerting pipelines for sub-second reactions to device state changes.

## Transport

Subscriptions require a long-lived transport. A plain `POST` to `{{edgeUrl}}/devicehub/v2` will return only the first event (or none) and then close. The supported transports are:

| Transport                         | Path                                       | Notes                                                                       |
|-----------------------------------|--------------------------------------------|-----------------------------------------------------------------------------|
| **WebSocket (`graphql-ws`)**      | `wss://{{edgeHost}}/devicehub/v2`          | Preferred. Use `subscriptions-transport-ws` or `graphql-ws` on the client.  |
| **Server-Sent Events (`SSE`)**    | `{{edgeUrl}}/devicehub/v2` with `Accept: text/event-stream` | Best for one-way browser clients without a WS library.                      |

The Postman item is shown as a `POST` for documentation and one-shot testing; switch to a Postman **WebSocket** request (Files > New > WebSocket Request) when you need the streaming behavior.

## 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.

## Subscription body

```graphql
subscription DeviceChanged {
  DeviceChanged {
    DevStatus
    DeviceID
  }
}
```

`DeviceChanged` accepts no arguments and fires for every device. To distinguish events, branch on `DeviceID` client-side.

### Available fields

Selectable fields on `DeviceChanged` include all of:

| Field         | Type      | Description                                                                       |
|---------------|-----------|-----------------------------------------------------------------------------------|
| `DeviceID`    | `ID`      | UUID of the device whose state changed.                                           |
| `DevStatus`   | `String`  | New connection state. Common values: `Connected`, `Disconnected`, `Reconnecting`, `Error`, `Starting`, `Stopped`. |

## Event shape

Each event has the same `data`-wrapped shape as a query response:

```json
{
  "data": {
    "DeviceChanged": {
      "DevStatus": "Connected",
      "DeviceID": "79F3914E-32A1-4227-AC16-023E627EB26E"
    }
  }
}
```

A burst of `Reconnecting` followed by `Connected` is normal after network flaps; treat the most recent event as the source of truth.

## Errors

GraphQL endpoints return `200 OK` even on logical errors. Inspect the `errors` array in the response body:

```json
{ "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 DriverID, etc.). |
| `NOT_FOUND`             | The targeted device, driver, or related 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`.

### Subscription-specific notes

- If the transport closes unexpectedly, reconnect with backoff. There is no replay or "missed events" buffer -- events that occur while you are disconnected are lost.
- On reconnect, also issue `List Devices` to reconcile state.

> **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**

```graphql
subscription DeviceChanged {
    DeviceChanged {
        DevStatus
        DeviceID
    }
}

```

### Response

**Status**: 200 OK

```json
{
    "data": {
        "DeviceChanged": {
            "DevStatus": "Connected",
            "DeviceID": "79F3914E-32A1-4227-AC16-023E627EB26E"
        }
    }
}
```

---

## Start Device

**POST** `{{edgeUrl}}/devicehub/v2`

# Start Device

Starts the worker for one or more devices. After `StartDevices` returns, the device begins polling/connecting and `DeviceChanged` events will flow for it.

Idempotent for already-running devices -- starting a running device is a no-op and does not return an error.

## Endpoint

```http
POST {{edgeUrl}}/devicehub/v2
Content-Type: application/json
```

## 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 (GraphQL)

```graphql
mutation StartDevices($input: StartDevicesRequest!) {
  StartDevices(input: $input)
}
```

### Variables (`input: StartDevicesRequest!`)

| Field   | Type      | Required | Description                                                          |
|---------|-----------|----------|----------------------------------------------------------------------|
| `IDs`   | `[ID!]!`  | Yes      | UUIDs of devices to start. Pass a single-element array for one device. |

### Variables (example)

```json
{
  "input": {
    "IDs": ["{{deviceID}}"]
  }
}
```

## Response

`200 OK` -- `application/json`. The mutation returns no data on success.

```json
{
  "data": {
    "StartDevices": null
  }
}
```

A `null` payload means the start was accepted by DeviceHub. It does **not** mean the device is connected yet -- subscribe to `Device Connection Status` or poll `List Devices` to observe the transition from `Starting` to `Connected`.

## Errors

GraphQL endpoints return `200 OK` even on logical errors. Inspect the `errors` array in the response body:

```json
{ "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 DriverID, etc.). |
| `NOT_FOUND`             | The targeted device, driver, or related 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**

```graphql
mutation StartDevices($input: StartDevicesRequest!) {
    StartDevices(input: $input)
}
```

**Variables**

```json
{
    "input": {
        "IDs": [
            "{{deviceID}}"
        ]
    }
}
```

### Response

**Status**: 200 OK

```json
{
    "data": {
        "StartDevices": null
    }
}
```

---

## Stop Device

**POST** `{{edgeUrl}}/devicehub/v2`

# Stop Device

Stops the worker for one or more devices. The device disconnects from its upstream system, stops polling, and stops publishing to integrations. Its configuration remains intact -- use `Start Device` to resume, or `Delete Devices graphQL` to remove permanently.

Idempotent for already-stopped devices.

## Endpoint

```http
POST {{edgeUrl}}/devicehub/v2
Content-Type: application/json
```

## 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 (GraphQL)

```graphql
mutation StopDevices($input: StopDevicesRequest!) {
  StopDevices(input: $input)
}
```

The Postman item names the operation `StopDevicesRequest` for legacy reasons; the GraphQL operation name is `StopDevices` and what matters at the server is the field name in the selection set.

### Variables (`input: StopDevicesRequest!`)

| Field   | Type      | Required | Description                                                          |
|---------|-----------|----------|----------------------------------------------------------------------|
| `IDs`   | `[ID!]!`  | Yes      | UUIDs of devices to stop. Pass a single-element array for one device. |

### Variables (example)

```json
{
  "input": {
    "IDs": ["{{deviceID}}"]
  }
}
```

## Response

`200 OK` -- `application/json`. The mutation returns no data on success.

```json
{
  "data": {
    "StopDevices": null
  }
}
```

The device transitions through `Stopping` to `Stopped`. To observe the transition in real time, subscribe to `Device Connection Status`.

## Errors

GraphQL endpoints return `200 OK` even on logical errors. Inspect the `errors` array in the response body:

```json
{ "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 DriverID, etc.). |
| `NOT_FOUND`             | The targeted device, driver, or related 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**

```graphql
mutation StopDevicesRequest($input: StopDevicesRequest!) {
    StopDevices(input: $input)
}
```

**Variables**

```json
{
    "input": {
        "IDs": [
            "{{deviceID}}"
        ]
    }
}
```

### Response

**Status**: 200 OK

```json
{
    "data": {
        "StopDevices": null
    }
}
```

---

