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

## List All Tags

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

# List All Tags

Returns a paginated roster of every tag (`Register`) on every device. Use this to render the global "Tags" page in the UI, or to bulk-export tag metadata to an external system.

This is the cross-device counterpart of `List Registers from Single Device`. If you already know which device you want, the single-device endpoint is much cheaper.

## 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 ListRegisters {
  ListRegistersFromAllDevices(input: { Limit: 100 }) {
    TotalCount
    Registers {
      ID
      DeviceID
      TagName
      Description
      ValueType
      PublishCoV
      TagFormula
    }
  }
}
```

### Arguments (`input: ListRegistersFromAllDevicesRequest!`)

| Field           | Type      | Required | Description                                                                                       |
|-----------------|-----------|----------|---------------------------------------------------------------------------------------------------|
| `Limit`         | `Int`     | Yes      | Page size, `1..1000`. Larger values are clamped to 1000.                                          |
| `ContinueFrom`  | `ID`      | No       | Cursor from the previous page. Omit (or `null`) on the first page.                                |

The selection on `Registers` is yours to expand -- the example uses the small projection. Available fields include `Name`, `Properties`, `Topics`, `MetaData`, `Description`.

## Response

`200 OK` -- `application/json`

| Field                                        | Type            | Description                                                                                 |
|----------------------------------------------|-----------------|---------------------------------------------------------------------------------------------|
| `data.ListRegistersFromAllDevices.TotalCount`| `Int`           | Server-wide total. Use to render a progress bar across pages.                               |
| `data.ListRegistersFromAllDevices.Registers` | `[Register!]!`  | One page of registers.                                                                      |
| `Registers[].ID`                             | `ID`            | Register UUID.                                                                              |
| `Registers[].DeviceID`                       | `ID`            | UUID of the parent device.                                                                  |
| `Registers[].TagName`                        | `String`        | Operator-chosen alias used in topics and downstream pipelines.                              |
| `Registers[].Description`                    | `String`        | Free-form description.                                                                      |
| `Registers[].ValueType`                      | `String`        | One of `int`, `uint`, `int64`, `uint64`, `float32`, `float64`, `bool`, `string`, `bytes`. Driver-dependent. |
| `Registers[].PublishCoV`                     | `Boolean`       | If `true`, publish only on Change-of-Value (CoV) rather than every poll.                    |
| `Registers[].TagFormula`                     | `String`        | Optional expression evaluated on every read (e.g. `value * 0.1 + 32`). `null` for raw values. |

### Pagination

When the response contains `Limit` items, request the next page using the last `ID` you received as `ContinueFrom`. Stop when fewer than `Limit` items come back (or when `Last == true` if you selected that field on related endpoints).

### Example response

```json
{
  "data": {
    "ListRegistersFromAllDevices": {
      "TotalCount": 11,
      "Registers": [
        {
          "ID": "1A5572B2-8242-40C8-BC63-FFEBC7DAF1E4",
          "DeviceID": "779F42F3-54FC-4A87-B9A6-CD39CFA4A97D",
          "TagName": "apiTag1",
          "Description": "",
          "ValueType": "float64",
          "PublishCoV": false,
          "TagFormula": null
        }
      ]
    }
  }
}
```

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

```graphql
query ListRegisters {
    ListRegistersFromAllDevices(input: {Limit: 100}) {
        TotalCount
        Registers {
            ID
            DeviceID
            TagName
            Description
            ValueType
            PublishCoV
            TagFormula
        }
    }
}

```

### Response

**Status**: 200 OK

```json
{
    "data": {
        "ListRegistersFromAllDevices": {
            "TotalCount": 11,
            "Registers": [
                {
                    "ID": "1A5572B2-8242-40C8-BC63-FFEBC7DAF1E4",
                    "DeviceID": "779F42F3-54FC-4A87-B9A6-CD39CFA4A97D",
                    "TagName": "apiTag1",
                    "Description": "",
                    "ValueType": "float64",
                    "PublishCoV": false,
                    "TagFormula": null
                },
                {
                    "ID": "0BB9BE0A-EBC8-4932-9CCC-D9E3A994D986",
                    "DeviceID": "89C52E9A-0901-4D8D-B842-7B533F64E1B7",
                    "TagName": "motionTopic8",
                    "Description": "",
                    "ValueType": "float64",
                    "PublishCoV": false,
                    "TagFormula": null
                },
                {
                    "ID": "2ADE6AFF-A950-413B-82EA-2D142A76C741",
                    "DeviceID": "89C52E9A-0901-4D8D-B842-7B533F64E1B7",
                    "TagName": "motionTopic1",
                    "Description": "",
                    "ValueType": "float64",
                    "PublishCoV": false,
                    "TagFormula": null
                },
                {
                    "ID": "5DA5BD59-D25E-488C-A37B-B0E90649F22D",
                    "DeviceID": "89C52E9A-0901-4D8D-B842-7B533F64E1B7",
                    "TagName": "motionTopic6",
                    "Description": "",
                    "ValueType": "float64",
                    "PublishCoV": false,
                    "TagFormula": null
                },
                {
                    "ID": "665E3EA1-7FFA-469F-ADCB-F9FB5472D16B",
                    "DeviceID": "89C52E9A-0901-4D8D-B842-7B533F64E1B7",
                    "TagName": "motionTopic5",
                    "Description": "",
                    "ValueType": "float64",
                    "PublishCoV": false,
                    "TagFormula": null
                },
                {
                    "ID": "A8091B99-DBCD-4C9B-BEE1-EE571D84797A",
                    "DeviceID": "89C52E9A-0901-4D8D-B842-7B533F64E1B7",
                    "TagName": "motionTopic0",
                    "Description": "",
                    "ValueType": "float64",
                    "PublishCoV": false,
                    "TagFormula": null
                },
                {
                    "ID": "A9D97FFC-72B6-4775-AA5E-895378650B74",
                    "DeviceID": "89C52E9A-0901-4D8D-B842-7B533F64E1B7",
                    "TagName": "motionTopic2",
                    "Description": "",
                    "ValueType": "float64",
                    "PublishCoV": false,
                    "TagFormula": null
                },
                {
                    "ID": "E3DEC8C0-EE07-4D7F-8525-26C585E2B603",
                    "DeviceID": "89C52E9A-0901-4D8D-B842-7B533F64E1B7",
                    "TagName": "motionTopic3",
                    "Description": "",
                    "ValueType": "float64",
                    "PublishCoV": false,
                    "TagFormula": null
                },
                {
                    "ID": "E4E22269-5B67-4EF2-BFD8-578072AE1A29",
                    "DeviceID": "89C52E9A-0901-4D8D-B842-7B533F64E1B7",
                    "TagName": "motionTopic4",
                    "Description": "",
                    "ValueType": "float64",
                    "PublishCoV": false,
                    "TagFormula": null
                },
                {
                    "ID": "5EAEBA93-B17D-45FC-8291-CA5CFCEFB994",
                    "DeviceID": "CDC283FB-72E2-42DF-A587-4D6299D70C9A",
                    "TagName": "sine1",
                    "Description": "",
                    "ValueType": "float64",
                    "PublishCoV": false,
                    "TagFormula": null
                },
                {
                    "ID": "A21EB566-296F-41A8-8436-C5C73E2C5448",
                    "DeviceID": "CDC283FB-72E2-42DF-A587-4D6299D70C9A",
                    "TagName": "sine2",
                    "Description": "",
                    "ValueType": "float64",
                    "PublishCoV": false,
                    "TagFormula": null
                }
            ]
        }
    }
}
```

---

## List Registers from Single Device

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

# List Registers from Single Device

Returns the paginated list of tags belonging to one device, with a richer projection than `List All Tags`. Use this on a device-detail page or any workflow scoped to a single 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
query ListRegisters($input: ListRegistersRequest!) {
  ListRegisters(input: $input) {
    Registers {
      ID
      DeviceID
      Name
      TagName
      Description
      ValueType
      Properties { Name Value Description }
      Topics     { Direction Encoding Format IntervalMs Topic }
      PublishCoV
      MetaData   { Name Value Description }
      TagFormula
    }
    TotalCount
    Last
  }
}
```

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

| Field           | Type   | Required | Description                                                                              |
|-----------------|--------|----------|------------------------------------------------------------------------------------------|
| `DeviceID`      | `ID`   | Yes      | UUID of the device whose tags to list.                                                   |
| `Limit`         | `Int`  | Yes      | Page size, `1..1000`.                                                                    |
| `ContinueFrom`  | `ID`   | No       | Cursor from the previous page (last `ID` you received). Omit on the first page.          |

```json
{
  "input": {
    "DeviceID": "{{deviceID}}",
    "Limit": 10
  }
}
```

## Response

`200 OK` -- `application/json`

| Field                                | Type           | Description                                                                                  |
|--------------------------------------|----------------|----------------------------------------------------------------------------------------------|
| `data.ListRegisters.Registers`       | `[Register!]!` | One page of registers.                                                                       |
| `data.ListRegisters.TotalCount`      | `Int`          | Total tags on this device, regardless of page size.                                          |
| `data.ListRegisters.Last`            | `Boolean`      | `true` if this is the final page. Loop until `Last == true`.                                 |

### `Register` field reference (richer projection)

| Field              | Type                      | Description                                                                                |
|--------------------|---------------------------|--------------------------------------------------------------------------------------------|
| `ID`               | `ID`                      | Register UUID.                                                                             |
| `DeviceID`         | `ID`                      | Parent device UUID.                                                                        |
| `Name`             | `String`                  | Register name at the source (driver-defined identifier).                                   |
| `TagName`          | `String`                  | Operator alias used in topics and downstream pipelines.                                    |
| `Description`      | `String`                  | Free-form description.                                                                     |
| `ValueType`        | `String`                  | Wire type. See `List All Tags` for the value enumeration.                                  |
| `Properties`       | `[Param!]!`               | Register properties (e.g. `address`, `pollingInterval`, `valueType`). Schema is determined by the parent driver's `SupportedRegisters[].RegisterProperties`. |
| `Topics`           | `[TopicConfig!]!`         | Each topic the register publishes to. `Direction` = `Input` / `Output`; `Encoding` = `JSON` / `Binary`; `Format` = payload shape; `IntervalMs` = publish cadence (0 = on every poll). |
| `PublishCoV`       | `Boolean`                 | Change-of-Value publishing mode.                                                           |
| `MetaData`         | `[Param!]!`               | Operator metadata.                                                                         |
| `TagFormula`       | `String`                  | Optional value transform expression.                                                       |

```json
{
  "data": {
    "ListRegisters": {
      "Registers": [
        {
          "ID": "007398CD-51DC-47B8-A3FD-F51929EA61DF",
          "DeviceID": "779F42F3-54FC-4A87-B9A6-CD39CFA4A97D",
          "Name": "S",
          "TagName": "WindSpeed16ProcessVariable",
          "Description": "",
          "ValueType": "float64",
          "Properties": [
            { "Name": "pollingInterval", "Value": "1000", "Description": "" }
          ],
          "Topics": [],
          "PublishCoV": false,
          "MetaData": [],
          "TagFormula": null
        }
      ],
      "TotalCount": 11,
      "Last": false
    }
  }
}
```

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

```graphql
query ListRegisters($input: ListRegistersRequest!) {
    ListRegisters(input: $input) {
        Registers {
        ID
        DeviceID
        Name
        TagName
        Description
        ValueType
        Properties {
            Name
            Value
            Description
        }
        Topics {
            Direction
            Encoding
            Format
            IntervalMs
            Topic
        }
        PublishCoV
        MetaData {
            Name
            Value
            Description
        }
        TagFormula
        }
        TotalCount
        Last
    }
}
```

**Variables**

```json
{
    "input": {
        "DeviceID": "{{device_id}}",
        "Limit": 10
    }
}
```

### Response

**Status**: 200 OK

```json
{
    "data": {
        "ListRegisters": {
            "Registers": [
                {
                    "ID": "007398CD-51DC-47B8-A3FD-F51929EA61DF",
                    "DeviceID": "779F42F3-54FC-4A87-B9A6-CD39CFA4A97D",
                    "Name": "S",
                    "TagName": "WindSpeed16ProcessVariable",
                    "Description": "",
                    "ValueType": "float64",
                    "Properties": [
                        {
                            "Name": "pollingInterval",
                            "Value": "1000",
                            "Description": null
                        },
                        {
                            "Name": "count",
                            "Value": "1",
                            "Description": null
                        },
                        {
                            "Name": "formula",
                            "Value": "sin(t/1000) * 2",
                            "Description": null
                        },
                        {
                            "Name": "max_value",
                            "Value": "50",
                            "Description": null
                        },
                        {
                            "Name": "min_value",
                            "Value": "0",
                            "Description": null
                        },
                        {
                            "Name": "address",
                            "Value": "60",
                            "Description": null
                        }
                    ],
                    "Topics": [
                        {
                            "Direction": "Output",
                            "Encoding": "JSON",
                            "Format": "Raw",
                            "IntervalMs": 1000,
                            "Topic": "devicehub.raw.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.007398CD-51DC-47B8-A3FD-F51929EA61DF"
                        },
                        {
                            "Direction": "Input",
                            "Encoding": "JSON",
                            "Format": "Command",
                            "IntervalMs": null,
                            "Topic": "devicehub.pollonce.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.007398CD-51DC-47B8-A3FD-F51929EA61DF"
                        }
                    ],
                    "PublishCoV": false,
                    "MetaData": [],
                    "TagFormula": null
                },
                {
                    "ID": "01133640-EFBC-44F1-8188-A1447C00A5CC",
                    "DeviceID": "779F42F3-54FC-4A87-B9A6-CD39CFA4A97D",
                    "Name": "S",
                    "TagName": "WaterPressure7SetPoint",
                    "Description": "",
                    "ValueType": "float64",
                    "Properties": [
                        {
                            "Name": "formula",
                            "Value": "sin(t/1000) * 2",
                            "Description": null
                        },
                        {
                            "Name": "max_value",
                            "Value": "50",
                            "Description": null
                        },
                        {
                            "Name": "min_value",
                            "Value": "0",
                            "Description": null
                        },
                        {
                            "Name": "address",
                            "Value": "280",
                            "Description": null
                        },
                        {
                            "Name": "pollingInterval",
                            "Value": "1000",
                            "Description": null
                        },
                        {
                            "Name": "count",
                            "Value": "1",
                            "Description": null
                        }
                    ],
                    "Topics": [
                        {
                            "Direction": "Output",
                            "Encoding": "JSON",
                            "Format": "Raw",
                            "IntervalMs": 1000,
                            "Topic": "devicehub.raw.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.01133640-EFBC-44F1-8188-A1447C00A5CC"
                        },
                        {
                            "Direction": "Input",
                            "Encoding": "JSON",
                            "Format": "Command",
                            "IntervalMs": null,
                            "Topic": "devicehub.pollonce.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.01133640-EFBC-44F1-8188-A1447C00A5CC"
                        }
                    ],
                    "PublishCoV": false,
                    "MetaData": [],
                    "TagFormula": null
                },
                {
                    "ID": "035CD2C9-35CF-405D-A984-1E5DFC816583",
                    "DeviceID": "779F42F3-54FC-4A87-B9A6-CD39CFA4A97D",
                    "Name": "S",
                    "TagName": "WaterPressure13ProcessVariable",
                    "Description": "",
                    "ValueType": "float64",
                    "Properties": [
                        {
                            "Name": "min_value",
                            "Value": "0",
                            "Description": null
                        },
                        {
                            "Name": "address",
                            "Value": "144",
                            "Description": null
                        },
                        {
                            "Name": "pollingInterval",
                            "Value": "1000",
                            "Description": null
                        },
                        {
                            "Name": "count",
                            "Value": "1",
                            "Description": null
                        },
                        {
                            "Name": "formula",
                            "Value": "sin(t/1000) * 2",
                            "Description": null
                        },
                        {
                            "Name": "max_value",
                            "Value": "50",
                            "Description": null
                        }
                    ],
                    "Topics": [
                        {
                            "Direction": "Output",
                            "Encoding": "JSON",
                            "Format": "Raw",
                            "IntervalMs": 1000,
                            "Topic": "devicehub.raw.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.035CD2C9-35CF-405D-A984-1E5DFC816583"
                        },
                        {
                            "Direction": "Input",
                            "Encoding": "JSON",
                            "Format": "Command",
                            "IntervalMs": null,
                            "Topic": "devicehub.pollonce.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.035CD2C9-35CF-405D-A984-1E5DFC816583"
                        }
                    ],
                    "PublishCoV": false,
                    "MetaData": [],
                    "TagFormula": null
                },
                {
                    "ID": "049ED31A-F0A3-491B-92BC-D47E85237174",
                    "DeviceID": "779F42F3-54FC-4A87-B9A6-CD39CFA4A97D",
                    "Name": "S",
                    "TagName": "Pressure18ProcessVariable",
                    "Description": "",
                    "ValueType": "string",
                    "Properties": [
                        {
                            "Name": "address",
                            "Value": "34",
                            "Description": null
                        },
                        {
                            "Name": "pollingInterval",
                            "Value": "1000",
                            "Description": null
                        },
                        {
                            "Name": "DB",
                            "Value": "75",
                            "Description": null
                        },
                        {
                            "Name": "count",
                            "Value": "1",
                            "Description": null
                        }
                    ],
                    "Topics": [
                        {
                            "Direction": "Output",
                            "Encoding": "JSON",
                            "Format": "Raw",
                            "IntervalMs": 1000,
                            "Topic": "devicehub.raw.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.049ED31A-F0A3-491B-92BC-D47E85237174"
                        },
                        {
                            "Direction": "Input",
                            "Encoding": "JSON",
                            "Format": "Command",
                            "IntervalMs": null,
                            "Topic": "devicehub.pollonce.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.049ED31A-F0A3-491B-92BC-D47E85237174"
                        }
                    ],
                    "PublishCoV": false,
                    "MetaData": [],
                    "TagFormula": null
                },
                {
                    "ID": "04C0FEB1-227D-400B-87F7-6C8478DA7944",
                    "DeviceID": "779F42F3-54FC-4A87-B9A6-CD39CFA4A97D",
                    "Name": "S",
                    "TagName": "WindDuration14SetPoint",
                    "Description": "",
                    "ValueType": "string",
                    "Properties": [
                        {
                            "Name": "address",
                            "Value": "74",
                            "Description": null
                        },
                        {
                            "Name": "pollingInterval",
                            "Value": "1000",
                            "Description": null
                        },
                        {
                            "Name": "DB",
                            "Value": "155",
                            "Description": null
                        },
                        {
                            "Name": "count",
                            "Value": "1",
                            "Description": null
                        }
                    ],
                    "Topics": [
                        {
                            "Direction": "Output",
                            "Encoding": "JSON",
                            "Format": "Raw",
                            "IntervalMs": 1000,
                            "Topic": "devicehub.raw.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.04C0FEB1-227D-400B-87F7-6C8478DA7944"
                        },
                        {
                            "Direction": "Input",
                            "Encoding": "JSON",
                            "Format": "Command",
                            "IntervalMs": null,
                            "Topic": "devicehub.pollonce.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.04C0FEB1-227D-400B-87F7-6C8478DA7944"
                        }
                    ],
                    "PublishCoV": false,
                    "MetaData": [],
                    "TagFormula": null
                },
                {
                    "ID": "05E3241F-43BB-4045-BF69-4A26EE4F5705",
                    "DeviceID": "779F42F3-54FC-4A87-B9A6-CD39CFA4A97D",
                    "Name": "S",
                    "TagName": "WindDuration11SetPoint",
                    "Description": "",
                    "ValueType": "string",
                    "Properties": [
                        {
                            "Name": "address",
                            "Value": "68",
                            "Description": null
                        },
                        {
                            "Name": "pollingInterval",
                            "Value": "1000",
                            "Description": null
                        },
                        {
                            "Name": "DB",
                            "Value": "155",
                            "Description": null
                        },
                        {
                            "Name": "count",
                            "Value": "1",
                            "Description": null
                        }
                    ],
                    "Topics": [
                        {
                            "Direction": "Output",
                            "Encoding": "JSON",
                            "Format": "Raw",
                            "IntervalMs": 1000,
                            "Topic": "devicehub.raw.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.05E3241F-43BB-4045-BF69-4A26EE4F5705"
                        },
                        {
                            "Direction": "Input",
                            "Encoding": "JSON",
                            "Format": "Command",
                            "IntervalMs": null,
                            "Topic": "devicehub.pollonce.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.05E3241F-43BB-4045-BF69-4A26EE4F5705"
                        }
                    ],
                    "PublishCoV": false,
                    "MetaData": [],
                    "TagFormula": null
                },
                {
                    "ID": "05FE7CD0-8751-4C0B-AE1F-185B86E974F8",
                    "DeviceID": "779F42F3-54FC-4A87-B9A6-CD39CFA4A97D",
                    "Name": "S",
                    "TagName": "WaterDelay16SetPoint",
                    "Description": "",
                    "ValueType": "string",
                    "Properties": [
                        {
                            "Name": "count",
                            "Value": "1",
                            "Description": null
                        },
                        {
                            "Name": "address",
                            "Value": "222",
                            "Description": null
                        },
                        {
                            "Name": "pollingInterval",
                            "Value": "1000",
                            "Description": null
                        },
                        {
                            "Name": "DB",
                            "Value": "155",
                            "Description": null
                        }
                    ],
                    "Topics": [
                        {
                            "Direction": "Output",
                            "Encoding": "JSON",
                            "Format": "Raw",
                            "IntervalMs": 1000,
                            "Topic": "devicehub.raw.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.05FE7CD0-8751-4C0B-AE1F-185B86E974F8"
                        },
                        {
                            "Direction": "Input",
                            "Encoding": "JSON",
                            "Format": "Command",
                            "IntervalMs": null,
                            "Topic": "devicehub.pollonce.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.05FE7CD0-8751-4C0B-AE1F-185B86E974F8"
                        }
                    ],
                    "PublishCoV": false,
                    "MetaData": [],
                    "TagFormula": null
                },
                {
                    "ID": "0671BF01-6365-482B-A08D-404ABB2BB075",
                    "DeviceID": "779F42F3-54FC-4A87-B9A6-CD39CFA4A97D",
                    "Name": "S",
                    "TagName": "WindDuration19SetPoint",
                    "Description": "",
                    "ValueType": "string",
                    "Properties": [
                        {
                            "Name": "count",
                            "Value": "1",
                            "Description": null
                        },
                        {
                            "Name": "address",
                            "Value": "84",
                            "Description": null
                        },
                        {
                            "Name": "pollingInterval",
                            "Value": "1000",
                            "Description": null
                        },
                        {
                            "Name": "DB",
                            "Value": "155",
                            "Description": null
                        }
                    ],
                    "Topics": [
                        {
                            "Direction": "Output",
                            "Encoding": "JSON",
                            "Format": "Raw",
                            "IntervalMs": 1000,
                            "Topic": "devicehub.raw.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.0671BF01-6365-482B-A08D-404ABB2BB075"
                        },
                        {
                            "Direction": "Input",
                            "Encoding": "JSON",
                            "Format": "Command",
                            "IntervalMs": null,
                            "Topic": "devicehub.pollonce.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.0671BF01-6365-482B-A08D-404ABB2BB075"
                        }
                    ],
                    "PublishCoV": false,
                    "MetaData": [],
                    "TagFormula": null
                },
                {
                    "ID": "07611094-25D2-4FF5-BD6F-8EB4B00C5CE4",
                    "DeviceID": "779F42F3-54FC-4A87-B9A6-CD39CFA4A97D",
                    "Name": "S",
                    "TagName": "WaterDelay13SetPoint",
                    "Description": "",
                    "ValueType": "string",
                    "Properties": [
                        {
                            "Name": "address",
                            "Value": "216",
                            "Description": null
                        },
                        {
                            "Name": "pollingInterval",
                            "Value": "1000",
                            "Description": null
                        },
                        {
                            "Name": "DB",
                            "Value": "155",
                            "Description": null
                        },
                        {
                            "Name": "count",
                            "Value": "1",
                            "Description": null
                        }
                    ],
                    "Topics": [
                        {
                            "Direction": "Output",
                            "Encoding": "JSON",
                            "Format": "Raw",
                            "IntervalMs": 1000,
                            "Topic": "devicehub.raw.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.07611094-25D2-4FF5-BD6F-8EB4B00C5CE4"
                        },
                        {
                            "Direction": "Input",
                            "Encoding": "JSON",
                            "Format": "Command",
                            "IntervalMs": null,
                            "Topic": "devicehub.pollonce.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.07611094-25D2-4FF5-BD6F-8EB4B00C5CE4"
                        }
                    ],
                    "PublishCoV": false,
                    "MetaData": [],
                    "TagFormula": null
                },
                {
                    "ID": "08463862-6FF0-48DB-9B38-3B25AC7F13FC",
                    "DeviceID": "779F42F3-54FC-4A87-B9A6-CD39CFA4A97D",
                    "Name": "S",
                    "TagName": "WaterDuration12SetPoint",
                    "Description": "",
                    "ValueType": "string",
                    "Properties": [
                        {
                            "Name": "pollingInterval",
                            "Value": "1000",
                            "Description": null
                        },
                        {
                            "Name": "DB",
                            "Value": "155",
                            "Description": null
                        },
                        {
                            "Name": "count",
                            "Value": "1",
                            "Description": null
                        },
                        {
                            "Name": "address",
                            "Value": "246",
                            "Description": null
                        }
                    ],
                    "Topics": [
                        {
                            "Direction": "Output",
                            "Encoding": "JSON",
                            "Format": "Raw",
                            "IntervalMs": 1000,
                            "Topic": "devicehub.raw.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.08463862-6FF0-48DB-9B38-3B25AC7F13FC"
                        },
                        {
                            "Direction": "Input",
                            "Encoding": "JSON",
                            "Format": "Command",
                            "IntervalMs": null,
                            "Topic": "devicehub.pollonce.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.08463862-6FF0-48DB-9B38-3B25AC7F13FC"
                        }
                    ],
                    "PublishCoV": false,
                    "MetaData": [],
                    "TagFormula": null
                }
            ],
            "TotalCount": 202,
            "Last": false
        }
    }
}
```

---

## Create Tag

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

# Create Tag

Creates one or more tags on a device in a single batched mutation. Despite the singular name, this is the **batch create** endpoint -- pass a single-element `Registers` array for one tag, or many for bulk.

Tags are validated against the parent device's driver template before they are committed. For a non-destructive pre-flight check, use `Validate Update Registers` (the same validation surface is reused).

## 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 CreateRegisters($input: CreateRegistersRequest!) {
  CreateRegisters(input: $input) {
    ID
    DeviceID
    Name
    TagName
    Description
    ValueType
    PublishCoV
    TagFormula
  }
}
```

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

| Field        | Type                  | Required | Description                                                                                          |
|--------------|-----------------------|----------|------------------------------------------------------------------------------------------------------|
| `Registers`  | `[RegisterInput!]!`   | Yes      | One or more register inputs (see below).                                                             |

### `RegisterInput` fields

| Field          | Type                | Required | Description                                                                                          |
|----------------|---------------------|----------|------------------------------------------------------------------------------------------------------|
| `DeviceID`     | `ID`                | Yes      | UUID of the device that will own this register.                                                      |
| `Name`         | `String`            | Yes      | Source-system register identifier (driver-defined). Not the operator alias.                          |
| `TagName`      | `String`            | Yes      | Operator alias used in topics and downstream systems. Must be unique within the device.              |
| `ValueType`    | `String`            | Yes      | Wire type: `int`, `uint`, `int64`, `uint64`, `float32`, `float64`, `bool`, `string`, `bytes`.        |
| `Properties`   | `[KeyValueInput!]!` | Yes      | Register property values. Keys are defined by the parent driver's `SupportedRegisters[N].RegisterProperties`. Driver-specific examples include `address`, `pollingInterval`, `count`, `formula`, `valueType`. |
| `Description`  | `String`            | No       | Free-form description.                                                                               |
| `PublishCoV`   | `Boolean`           | No       | Default `false`. `true` to publish only on Change-of-Value.                                          |
| `TagFormula`   | `String`            | No       | Optional value-transform expression, applied on every read.                                          |
| `MetaData`     | `[KeyValueInput!]`  | No       | Operator metadata.                                                                                   |

### Variables (example)

```json
{
  "input": {
    "Registers": [
      {
        "DeviceID": "{{deviceID}}",
        "Name": "S",
        "TagName": "apiTag",
        "ValueType": "float64",
        "Description": "",
        "PublishCoV": false,
        "TagFormula": null,
        "MetaData": [],
        "Properties": [
          { "Key": "valueType",       "Value": "float64" },
          { "Key": "pollingInterval", "Value": "1000" },
          { "Key": "tagName",         "Value": "apiTag" },
          { "Key": "description",     "Value": "" },
          { "Key": "address",         "Value": "1" },
          { "Key": "formula",         "Value": "sin(t/1000) * 2" },
          { "Key": "min_value",       "Value": "-50" },
          { "Key": "max_value",       "Value": "50" },
          { "Key": "count",           "Value": "1" }
        ]
      }
    ]
  }
}
```

## Response

`200 OK` -- `application/json`. Returns the newly created registers with their server-assigned `ID`s in the same order as the request.

```json
{
  "data": {
    "CreateRegisters": [
      {
        "ID": "1A5572B2-8242-40C8-BC63-FFEBC7DAF1E4",
        "DeviceID": "779F42F3-54FC-4A87-B9A6-CD39CFA4A97D",
        "Name": "S",
        "TagName": "apiTag1",
        "Description": "",
        "ValueType": "float64",
        "PublishCoV": false,
        "TagFormula": null
      }
    ]
  }
}
```

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

### Common `BAD_USER_INPUT` causes

- `DeviceID` does not exist.
- `Properties` is missing a key the driver marks `Required`.
- `Properties.Value` fails the driver's type or range constraints (e.g. `address` out of `NumberMin..NumberMax`).
- `TagName` collides with an existing tag on the same device.
- `ValueType` is not in the supported set for this driver/register kind.

> **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 CreateRegisters($input: CreateRegistersRequest!) {
    CreateRegisters(input: $input) {
        ID
        DeviceID
        Name
        TagName
        Description
        ValueType
        PublishCoV
        TagFormula
    }
}

```

**Variables**

```json
{
  "input": {
    "Registers": [
      {
        "PublishCoV": false,
        "TagFormula": null,
        "MetaData": [],
        "Name": "S",
        "Description": "",
        "DeviceID": "{{deviceID}}",
        "Properties": [
          {
            "Key": "valueType",
            "Value": "float64"
          },
          {
            "Key": "pollingInterval",
            "Value": "1000"
          },
          {
            "Key": "tagName",
            "Value": "apiTag"
          },
          {
            "Key": "description",
            "Value": ""
          },
          {
            "Key": "address",
            "Value": "1"
          },
          {
            "Key": "formula",
            "Value": "sin(t/1000) * 2"
          },
          {
            "Key": "min_value",
            "Value": "-50"
          },
          {
            "Key": "max_value",
            "Value": "50"
          },
          {
            "Key": "count",
            "Value": "1"
          }
        ],
        "TagName": "apiTag",
        "ValueType": "float64"
      }
    ]
  }
}
```

### Response

**Status**: 200 OK

```json
{
    "data": {
        "CreateRegisters": [
            {
                "ID": "1A5572B2-8242-40C8-BC63-FFEBC7DAF1E4",
                "DeviceID": "779F42F3-54FC-4A87-B9A6-CD39CFA4A97D",
                "Name": "S",
                "TagName": "apiTag1",
                "Description": "",
                "ValueType": "float64",
                "PublishCoV": false,
                "TagFormula": null
            }
        ]
    }
}
```

---

## Update a Tag

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

# Update a Tag

Replaces the mutable fields of one or more existing tags. Despite the singular name, this is a **batch update** -- pass a single-element `Registers` array for one tag, or many for bulk.

`UpdateRegisters` is a **full replacement** of the input shape, like `Update Device`. Provide the complete `Properties` array even when changing a single key; omitted keys are reset to server defaults. The `ID` field is required on each register input.

## 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 UpdateRegisters($input: UpdateRegistersRequest!) {
  UpdateRegisters(input: $input) {
    ID
    Name
    Description
    TagName
    DeviceID
    ValueType
    Properties { Value Name }
    Topics     { Topic Format Direction Encoding IntervalMs }
    PublishCoV
    MetaData   { Name Value Description Type }
    TagFormula
  }
}
```

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

| Field        | Type                  | Required | Description                              |
|--------------|-----------------------|----------|------------------------------------------|
| `Registers`  | `[RegisterInput!]!`   | Yes      | One or more register inputs (see below). |

### `RegisterInput` fields for update

Same as `Create Tag`, plus:

| Field   | Type | Required | Description                                                |
|---------|------|----------|------------------------------------------------------------|
| `ID`    | `ID` | Yes      | UUID of the existing register to replace.                  |

### Variables (example)

```json
{
  "input": {
    "Registers": [
      {
        "ID": "1A5572B2-8242-40C8-BC63-FFEBC7DAF1E4",
        "DeviceID": "779F42F3-54FC-4A87-B9A6-CD39CFA4A97D",
        "Name": "S",
        "TagName": "apiTag",
        "ValueType": "float64",
        "Description": "",
        "PublishCoV": false,
        "TagFormula": null,
        "MetaData": [],
        "Properties": [
          { "Key": "valueType",       "Value": "float64" },
          { "Key": "pollingInterval", "Value": "1000" },
          { "Key": "address",         "Value": "1" }
        ]
      }
    ]
  }
}
```

## Response

`200 OK` -- `application/json`. Echoes the new state of each updated register using the selection from the request.

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

### Operational note

Updating `Properties` does not stop the device's poller. The new values are picked up on the next poll cycle. For changes that affect addressing or data shape, consider `Stop Device` -> update -> `Start Device` to avoid a brief window of stale reads.

> **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 UpdateRegisters($input: UpdateRegistersRequest!) {
    UpdateRegisters(
        input: $input
    ) {
        ID
        Name
        Description
        TagName
        DeviceID
        ValueType
        Properties {
            Value
            Name
        }
        Topics {
            Topic
            Format
            Direction
            Encoding
            IntervalMs
        }
        PublishCoV
        MetaData {
            Name
            Value
            Description
            Type
        }
        TagFormula
    }
}

```

**Variables**

```json
{
  "input": {
    "Registers": [
      {
        "PublishCoV": false,
        "TagFormula": null,
        "MetaData": [],
        "Name": "S",
        "Description": "",
        "DeviceID": "779F42F3-54FC-4A87-B9A6-CD39CFA4A97D",
        "Properties": [
          {
            "Key": "valueType",
            "Value": "float64"
          },
          {
            "Key": "pollingInterval",
            "Value": "1000"
          },
          {
            "Key": "tagName",
            "Value": "apiTag"
          },
          {
            "Key": "description",
            "Value": ""
          },
          {
            "Key": "address",
            "Value": "1"
          },
          {
            "Key": "formula",
            "Value": "sin(t/1000) * 2"
          },
          {
            "Key": "min_value",
            "Value": "-50"
          },
          {
            "Key": "max_value",
            "Value": "50"
          },
          {
            "Key": "count",
            "Value": "1"
          }
        ],
        "TagName": "apiTag",
        "ValueType": "float64",
        "ID": "1A5572B2-8242-40C8-BC63-FFEBC7DAF1E4"
      }
    ]
  }
}
```

### Response

**Status**: 200 OK

```json
{
    "data": {
        "UpdateRegisters": [
            {
                "ID": "1A5572B2-8242-40C8-BC63-FFEBC7DAF1E4",
                "Name": "S",
                "Description": "",
                "TagName": "apiTag",
                "DeviceID": "779F42F3-54FC-4A87-B9A6-CD39CFA4A97D",
                "ValueType": "float64",
                "Properties": [
                    {
                        "Value": "float64",
                        "Name": "valueType"
                    },
                    {
                        "Value": "apiTag",
                        "Name": "tagName"
                    },
                    {
                        "Value": "",
                        "Name": "description"
                    },
                    {
                        "Value": "sin(t/1000) * 2",
                        "Name": "formula"
                    },
                    {
                        "Value": "-50",
                        "Name": "min_value"
                    },
                    {
                        "Value": "1000",
                        "Name": "pollingInterval"
                    },
                    {
                        "Value": "1",
                        "Name": "address"
                    },
                    {
                        "Value": "50",
                        "Name": "max_value"
                    },
                    {
                        "Value": "1",
                        "Name": "count"
                    }
                ],
                "Topics": [
                    {
                        "Topic": "devicehub.raw.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.1A5572B2-8242-40C8-BC63-FFEBC7DAF1E4",
                        "Format": "Raw",
                        "Direction": "Output",
                        "Encoding": "JSON",
                        "IntervalMs": 1000
                    },
                    {
                        "Topic": "devicehub.pollonce.779F42F3-54FC-4A87-B9A6-CD39CFA4A97D.1A5572B2-8242-40C8-BC63-FFEBC7DAF1E4",
                        "Format": "Command",
                        "Direction": "Input",
                        "Encoding": "JSON",
                        "IntervalMs": null
                    }
                ],
                "PublishCoV": false,
                "MetaData": [],
                "TagFormula": null
            }
        ]
    }
}
```

---

## Delete Tag/s

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

# Delete Tag/s

Deletes one or more tags by ID. Despite the singular `Tag/s`, this is the batched delete endpoint -- pass any number of IDs in the array. To remove **every** tag on a device, use `Delete all Tags of a Device` instead.

## 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 DeleteRegisters {
  DeleteRegisters(input: {
    IDs: [
      "C09BCEFB-AE93-4B3E-AA72-54B91E7155E2"
    ]
  })
}
```

### Arguments

| Argument     | GraphQL type | Required | Description                                                                  |
|--------------|--------------|----------|------------------------------------------------------------------------------|
| `input.IDs`  | `[ID!]!`     | Yes      | UUIDs of registers to delete. Unknown IDs are silently ignored (no per-ID error). |

## Response

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

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

To confirm a specific register was removed, follow up with `List Registers from Single 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 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**

```graphql
mutation DeleteRegisters {
    DeleteRegisters(input: {
        IDs: [
            "C09BCEFB-AE93-4B3E-AA72-54B91E7155E2"
            ]
        }
    )
}
```

### Response

**Status**: 200 OK

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

---

## Delete all Tags of a Device

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

# Delete all Tags of a Device

Deletes every tag belonging to a single device, in one call. Useful when re-onboarding a device with a different register set, or as part of an automation that wipes-and-recreates from a CSV.

This does **not** delete or stop the device itself -- only its tags. The device remains configured and can be re-populated via `Create Tag`, `Upload CSV`, or `DeviceHub > Browse`.

## 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 DeleteDeviceRegisters($input: DeleteDeviceRegistersRequest!) {
  DeleteDeviceRegisters(input: $input)
}
```

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

| Field    | Type | Required | Description                                                                       |
|----------|------|----------|-----------------------------------------------------------------------------------|
| `DevID`  | `ID` | Yes      | UUID of the device whose tags should all be removed. Note the field name is `DevID`, not `DeviceID`. |

```json
{
  "input": {
    "DevID": "{{deviceID}}"
  }
}
```

## Response

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

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

```graphql
mutation DeleteDeviceRegisters($input: DeleteDeviceRegistersRequest!) {
  DeleteDeviceRegisters(input: $input)
}
```

**Variables**

```json
{
  "input": {
    "DevID": "{{deviceID}}"
  }
}
```

---

## Browse Tags

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

# Browse Tags

Browses the **native namespace** of the upstream system reachable by the device -- e.g. the OPC UA address space, the BACnet object list, or the S7 program structure -- without first persisting registers. Operators typically pick interesting nodes from a browse tree and then create tags from them.

This is a driver capability: only drivers with `Meta.BrowseSupported = true` (see `DeviceHub > Drivers > Get Driver Template by DriverID > Meta`) respond meaningfully. Unsupported drivers return a `BAD_USER_INPUT` error.

This is the **inspection** endpoint; the cart-style multi-step workflow that selects and imports browse results into actual tags lives in `DeviceHub > Browse`.

## 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 Browse($input: BrowseRequest!) {
  Browse(input: $input) {
    Type
    Fields { Name Value }
    Leaves {
      Type
      Fields { Name Value }
    }
  }
}
```

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

| Field         | Type        | Required | Description                                                                                                  |
|---------------|-------------|----------|--------------------------------------------------------------------------------------------------------------|
| `DeviceID`    | `ID`        | Yes      | UUID of the device to browse through.                                                                        |
| `Path`        | `[String!]` | Yes      | Hierarchy path of the current browse cursor. Pass `[]` for the root, then append child folder names to descend. |
| `ForceUpdate` | `Boolean`   | No       | Default `false`. If `true`, bypass any cached browse result and re-query the upstream device.                |

```json
{
  "input": {
    "DeviceID": "{{deviceID}}",
    "Path": [],
    "ForceUpdate": false
  }
}
```

## Response

`200 OK` -- `application/json`

| Field                          | Type           | Description                                                                                                |
|--------------------------------|----------------|------------------------------------------------------------------------------------------------------------|
| `data.Browse.Type`             | `String`       | Kind of node at the current `Path`. Typical values: `Folder`, `Variable`, `Object`. Driver-defined.        |
| `data.Browse.Fields`           | `[Field!]!`    | Metadata for the current node (e.g. `name`, `description`, `path`).                                        |
| `data.Browse.Leaves`           | `[Leaf!]!`     | Immediate children of the current node. Use one of their `Fields[Name='path']` to build the next `Path`.   |
| `data.Browse.Leaves[].Type`    | `String`       | Kind of the child (`Folder` to descend further, `Variable` for a leaf you can turn into a tag).            |
| `data.Browse.Leaves[].Fields`  | `[Field!]!`    | Metadata for the child.                                                                                    |

### Example response (root)

```json
{
  "data": {
    "Browse": {
      "Type": "Folder",
      "Fields": [
        { "Name": "description", "Value": "" },
        { "Name": "name",        "Value": "root" }
      ],
      "Leaves": [
        {
          "Type": "Folder",
          "Fields": [
            { "Name": "path", "Value": "Global" }
          ]
        }
      ]
    }
  }
}
```

To descend, set `Path: ["Global"]` (or whatever the child's `path` field holds) and re-issue the query.

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

### Browse-specific errors

- `BAD_USER_INPUT` with `driver does not support browse`: the parent device's driver has `Meta.BrowseSupported = false`.
- `INTERNAL_SERVER_ERROR` with a timeout: the upstream device did not respond. Retry with `ForceUpdate: true`, or check the connection state via `Dashboard > List of Devices` and `Device Connection 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 Browse($input: BrowseRequest!) {
    Browse(input:$input){
        Type
        Fields {
            Name
            Value
        }
        Leaves {
            Type
            Fields {
                Name
                Value
            }
        }
    }
}

```

**Variables**

```json
{
    "input": {
        "DeviceID": "{{deviceID}}",
        "Path": [], 
        "ForceUpdate": false
    }
}
```

### Response

**Status**: 200 OK

```json
{
    "data": {
        "Browse": {
            "Type": "Folder",
            "Fields": [
                {
                    "Name": "description",
                    "Value": ""
                },
                {
                    "Name": "name",
                    "Value": "root"
                }
            ],
            "Leaves": [
                {
                    "Type": "Folder",
                    "Fields": [
                        {
                            "Name": "path",
                            "Value": "Global"
                        },
                        {
                            "Name": "name",
                            "Value": "Global"
                        },
                        {
                            "Name": "description",
                            "Value": ""
                        }
                    ]
                },
                {
                    "Type": "Folder",
                    "Fields": [
                        {
                            "Name": "path",
                            "Value": "Program:AM_MCP"
                        },
                        {
                            "Name": "name",
                            "Value": "Program:AM_MCP"
                        },
                        {
                            "Name": "description",
                            "Value": ""
                        }
                    ]
                },
                {
                    "Type": "Folder",
                    "Fields": [
                        {
                            "Name": "description",
                            "Value": ""
                        },
                        {
                            "Name": "path",
                            "Value": "Program:Cell_Control"
                        },
                        {
                            "Name": "name",
                            "Value": "Program:Cell_Control"
                        }
                    ]
                },
                {
                    "Type": "Folder",
                    "Fields": [
                        {
                            "Name": "path",
                            "Value": "Program:Common_Controls"
                        },
                        {
                            "Name": "name",
                            "Value": "Program:Common_Controls"
                        },
                        {
                            "Name": "description",
                            "Value": ""
                        }
                    ]
                },
                {
                    "Type": "Folder",
                    "Fields": [
                        {
                            "Name": "path",
                            "Value": "Program:Data_Transfer"
                        },
                        {
                            "Name": "name",
                            "Value": "Program:Data_Transfer"
                        },
                        {
                            "Name": "description",
                            "Value": ""
                        }
                    ]
                },
                {
                    "Type": "Folder",
                    "Fields": [
                        {
                            "Name": "path",
                            "Value": "Program:DT_MCP"
                        },
                        {
                            "Name": "name",
                            "Value": "Program:DT_MCP"
                        },
                        {
                            "Name": "description",
                            "Value": ""
                        }
                    ]
                },
                {
                    "Type": "Folder",
                    "Fields": [
                        {
                            "Name": "description",
                            "Value": ""
                        },
                        {
                            "Name": "path",
                            "Value": "Program:Inputs"
                        },
                        {
                            "Name": "name",
                            "Value": "Program:Inputs"
                        }
                    ]
                },
                {
                    "Type": "Folder",
                    "Fields": [
                        {
                            "Name": "path",
                            "Value": "Program:IO_Diagnostics"
                        },
                        {
                            "Name": "name",
                            "Value": "Program:IO_Diagnostics"
                        },
                        {
                            "Name": "description",
                            "Value": ""
                        }
                    ]
                },
                {
                    "Type": "Folder",
                    "Fields": [
                        {
                            "Name": "path",
                            "Value": "Program:MainProgram"
                        },
                        {
                            "Name": "name",
                            "Value": "Program:MainProgram"
                        },
                        {
                            "Name": "description",
                            "Value": ""
                        }
                    ]
                },
                {
                    "Type": "Folder",
                    "Fields": [
                        {
                            "Name": "path",
                            "Value": "Program:Outputs"
                        },
                        {
                            "Name": "name",
                            "Value": "Program:Outputs"
                        },
                        {
                            "Name": "description",
                            "Value": ""
                        }
                    ]
                },
                {
                    "Type": "Folder",
                    "Fields": [
                        {
                            "Name": "path",
                            "Value": "Program:Periodic_Control"
                        },
                        {
                            "Name": "name",
                            "Value": "Program:Periodic_Control"
                        },
                        {
                            "Name": "description",
                            "Value": ""
                        }
                    ]
                },
                {
                    "Type": "Folder",
                    "Fields": [
                        {
                            "Name": "path",
                            "Value": "Program:trace"
                        },
                        {
                            "Name": "name",
                            "Value": "Program:trace"
                        },
                        {
                            "Name": "description",
                            "Value": ""
                        }
                    ]
                }
            ]
        }
    }
}
```

---

## Upload CSV

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

# Upload CSV (bulk tag import)

Imports tags from a CSV file. This is the multipart equivalent of the **Import Tags** flow in the LE UI. Use it to seed a fresh device from a spreadsheet, or to keep tag definitions under version control as a CSV and re-apply on each change.

Unlike the other endpoints in this folder, this is a **multipart `form-data`** request, not a regular GraphQL JSON body. The shape follows the [GraphQL multipart request spec](https://github.com/jaydenseric/graphql-multipart-request-spec).

## Endpoint

```http
POST {{edgeUrl}}/devicehub/v2
Content-Type: multipart/form-data
```

## 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`  | JSON envelope describing the GraphQL operation. Reference the file via `null` placeholders (see example).  |
| `map`         | `text`  | JSON mapping form file keys to placeholder locations within `operations`.                                  |
| `0`           | `file`  | The CSV payload. The form key (`0` here) must match what appears in `map`.                                 |

### `operations` value

```json
{
  "operationName": "UploadCsv",
  "variables": { "file": null },
  "query": "mutation UploadCsv($file: Upload!) { UploadCsv(file: $file) { Warnings { Error Line LineNumber } } }"
}
```

### `map` value

```json
{ "0": ["variables.file"] }
```

### CSV format

The CSV must match the schema exported by `Download Tags CSV`. The first row is a header. The columns are driver-dependent -- export a sample with `Download Tags CSV` from an existing populated device to discover the exact columns expected.

## Response

`200 OK` -- `application/json`. The mutation returns per-row outcomes in a `Warnings` array. **Despite the field name, this array carries both informational counts and error rows.**

| Field                                            | Type           | Description                                                                                |
|--------------------------------------------------|----------------|--------------------------------------------------------------------------------------------|
| `data.UploadCsv.Warnings`                        | `[Warning!]!`  | One entry per row that produced a warning, error, or summary line.                         |
| `data.UploadCsv.Warnings[].Error`                | `String`       | Error text or summary (e.g. `number of created tags 1, updated: 0, skipped tags: 0`).      |
| `data.UploadCsv.Warnings[].Line`                 | `String`       | Raw CSV line that produced the entry. Empty for the summary line.                          |
| `data.UploadCsv.Warnings[].LineNumber`           | `Int`          | 1-indexed CSV row number. `0` for the summary line.                                        |

### Example response

```json
{
  "data": {
    "UploadCsv": {
      "Warnings": [
        {
          "Error": "number of created tags 1, updated: 0, skipped tags: 0",
          "Line": "",
          "LineNumber": 0
        }
      ]
    }
  }
}
```

If the CSV is malformed at the top (bad header, unknown columns), the mutation fails with a `BAD_USER_INPUT` error before any rows are processed.

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

### Tips

- Round-trip first: `Download Tags CSV` -> edit -> `Upload CSV`. The exported CSV is always re-importable.
- For large files, increase your client's request timeout. The mutation is synchronous and can run for tens of seconds on thousands of rows.

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

```json
{
    "data": {
        "UploadCsv": {
            "Warnings": [
                {
                    "Error": "number of created tags 1, updated: 0, skipped tags: 0",
                    "Line": "",
                    "LineNumber": 0
                }
            ]
        }
    }
}
```

---

## Download Tags CSV

**GET** `{{edgeUrl}}/devicehub/registers/csv?deviceIDs=[Array of Strings]`

# Download Tags CSV (bulk tag export)

Exports tags belonging to one or more devices as a CSV file. The output format round-trips with `Upload CSV` -- export, edit, import, and the device returns to the new state.

## Endpoint

```http
GET {{edgeUrl}}/devicehub/registers/csv?deviceIDs=<array-of-uuids>
```

The `deviceIDs` query parameter is **repeatable**; encode multiple devices either as `?deviceIDs=A&deviceIDs=B` or as a JSON-style array depending on your client. Empty or missing means "export every device".

## Authentication

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

## Query parameters

| Parameter   | Type     | Required | Description                                                                                          |
|-------------|----------|----------|------------------------------------------------------------------------------------------------------|
| `deviceIDs` | string[] | No       | Device UUIDs to include. Omit to export every device. The Postman example uses the literal token `[Array of Strings]` -- replace it with your IDs. |

## Response

`200 OK` -- `text/csv` with a `Content-Disposition: attachment` header.

The first row is the header, columns are driver-dependent. Use the **Send and Download** button in Postman to save the response as a file (otherwise Postman renders the CSV in the body pane).

## Errors

| HTTP status         | When it happens                                                                |
|---------------------|--------------------------------------------------------------------------------|
| `400 Bad Request`   | Missing or malformed query/body parameter.                                     |
| `401 Unauthorized`  | Missing or invalid credentials.                                                |
| `403 Forbidden`     | Token lacks permission for this operation.                                     |
| `404 Not Found`     | Target entity does not exist.                                                  |
| `5xx`               | Service is unreachable, restarting, or internally errored. Inspect device logs under `System > Support`. |

> **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**: 200 OK

---

## Register/Tag Status

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

# Register/Tag Status (subscription)

Streams real-time per-register state updates (`OK`, `Failed`, ...). This is the tag-level analogue of `DeviceHub > Devices > Device Connection Status`, which only reports device-wide state. Useful for dashboards that need to flag individual stale or errored tags.

This is a GraphQL **subscription**, not a query. A one-shot `POST` will return at most the first event. For continuous updates, use a WebSocket transport (`subscriptions-transport-ws` or `graphql-ws`) or Server-Sent Events.

## Endpoint

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

WebSocket transport: `wss://{{edgeHost}}/devicehub/v2`.

## 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 RegisterStatusEvent($input: RegistersStatesRequest) {
  RegisterStatusEvent(input: $input) {
    ID
    State
  }
}
```

### Variables (`input: RegistersStatesRequest`)

| Field       | Type       | Required | Description                                                                                       |
|-------------|------------|----------|---------------------------------------------------------------------------------------------------|
| `DeviceID`  | `ID`       | Yes      | UUID of the device whose registers you want updates for.                                          |
| `IDs`       | `[ID!]`    | No       | Optional filter -- restrict events to these register UUIDs. Omit (or empty) to receive every register on the device. |

```json
{
  "input": {
    "DeviceID": "{{deviceID}}",
    "IDs": ["{{register_id}}"]
  }
}
```

## Event shape

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

```json
{
  "data": {
    "RegisterStatusEvent": [
      {
        "ID": "2C546FA7-C1CC-4AF5-B344-3877970A6A45",
        "State": "OK"
      }
    ]
  }
}
```

| Field                            | Type      | Description                                                                                |
|----------------------------------|-----------|--------------------------------------------------------------------------------------------|
| `RegisterStatusEvent[].ID`       | `ID`      | UUID of the register whose status changed.                                                 |
| `RegisterStatusEvent[].State`    | `String`  | New state. Common values: `OK`, `Failed`, `Stale`, `Disabled`, `Unknown`. Driver-defined.  |

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

### Subscription notes

- Reconnect with backoff on transport close. There is no replay buffer.
- On reconnect, also issue `List Registers from Single Device` to reconcile state.
- Filter aggressively with `IDs` -- a device with thousands of tags can produce a high event rate.

> **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 RegisterStatusEvent($input: RegistersStatesRequest) {
  RegisterStatusEvent(input: $input) {
    ID
    State
  }
}
```

**Variables**

```json
{
    "input": {
        "DeviceID": "{{deviceID}}",
        "IDs": [
            "{{register_id}}"
        ]
    }
}
```

### Response

**Status**: 200 OK

```json
{
    "data": {
        "RegisterStatusEvent": [
            {
                "ID": "2C546FA7-C1CC-4AF5-B344-3877970A6A45",
                "State": "OK"
            }
        ]
    }
}
```

---

## Server Search

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

# Server Search

Searches the **downloaded tag index** of a device for tags matching a name pattern. This is useful for protocols where the device exposes a very large native namespace (e.g. an OPC UA server with thousands of nodes) and you want fuzzy lookup before importing.

The search is **server-side**, so a slow or wide pattern does not pull the full index over the wire. Not every driver maintains a downloaded index; those that don't return an empty result.

## 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 SearchDownloadedTags($input: SearchTagsRequest) {
  SearchDownloadedTags(input: $input) {
    Items {
      Path
      TagName
      ID
    }
    Last
  }
}
```

### Variables (`input: SearchTagsRequest`)

| Field         | Type      | Required | Description                                                                                       |
|---------------|-----------|----------|---------------------------------------------------------------------------------------------------|
| `DevID`       | `ID`      | Yes      | UUID of the device to search within. Note the field name is `DevID`, not `DeviceID`.              |
| `Pattern`     | `String`  | Yes      | Tag-name pattern to match.                                                                        |
| `Similarity`  | `Float`   | No       | Threshold for fuzzy matching, `0.0..1.0`. Higher = stricter. Typical value: `0.7`.                |
| `Limit`       | `Int`     | No       | Page size. Combine with `Offset` for paging.                                                      |
| `Offset`      | `Int`     | No       | Number of results to skip. `0` is the first page.                                                 |

```json
{
  "input": {
    "DevID": "4C4B9CCC-F5E4-4C2E-A4C0-EAF5C32B0D1A",
    "Pattern": "cAM_BadVoltageAllPot",
    "Similarity": 0.7,
    "Limit": 5,
    "Offset": 0
  }
}
```

## Response

`200 OK` -- `application/json`

| Field                                     | Type        | Description                                                                |
|-------------------------------------------|-------------|----------------------------------------------------------------------------|
| `data.SearchDownloadedTags.Items`         | `[Item!]!`  | Matching index entries.                                                    |
| `data.SearchDownloadedTags.Items[].Path`  | `String`    | Native namespace path of the tag (e.g. OPC UA node path).                  |
| `data.SearchDownloadedTags.Items[].TagName`| `String`   | Suggested tag alias derived from the source name.                          |
| `data.SearchDownloadedTags.Items[].ID`    | `ID`        | Index entry ID. Pass through to `Create Tag` to materialize it as a tag.   |
| `data.SearchDownloadedTags.Last`          | `Boolean`   | `true` if this is the final page.                                          |

```json
{
  "data": {
    "SearchDownloadedTags": {
      "Items": [],
      "Last": true
    }
  }
}
```

An empty `Items` array with `Last: true` is also returned when the driver does not maintain a downloaded index or when no rows match.

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

```graphql
query SearchDownloadedTags($input: SearchTagsRequest) {
  SearchDownloadedTags(input: $input) {
    Items {
      Path
      TagName
      ID
    }
    Last
  }
}
```

**Variables**

```json
{
    "input":
    {
        "DevID": "4C4B9CCC-F5E4-4C2E-A4C0-EAF5C32B0D1A",
        "Limit": 5,
        "Offset": 0,
        "Pattern": "cAM_BadVoltageAllPot",
        "Similarity": 0.7
    }
}
```

### Response

**Status**: 200 OK

```json
{
    "data": {
        "SearchDownloadedTags": {
            "Items": [],
            "Last": true
        }
    }
}
```

---

## Validate Update Registers

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

# Validate Update Registers

Performs a **dry-run validation** of an `UpdateRegisters` (or `CreateRegisters`) payload without applying it. Use this for live form validation in custom UIs, or as a pre-flight check in automation pipelines that build register inputs programmatically.

The endpoint reuses the `UpdateRegistersRequest` input shape, so you can pass either a brand-new register (omit `ID`) or an existing one (include `ID`) and get the same per-field validation feedback.

## 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 ValidateUpdateRegisters($input: UpdateRegistersRequest!) {
  ValidateUpdateRegisters(input: $input) {
    ObjectID
    Errors   { FullName TextToDisplay }
    Warnings { FullName TextToDisplay }
  }
}
```

### Variables

Same shape as `Update a Tag` -- a `Registers` array of register inputs. Each entry may include or omit `ID` depending on whether you are validating a create or an update.

```json
{
  "input": {
    "Registers": [
      {
        "ID": "{{register_id}}",
        "DeviceID": "{{deviceID}}",
        "Name": "DB",
        "TagName": "FaultCode_Min_Value",
        "ValueType": "int",
        "Description": "",
        "PublishCoV": false,
        "TagFormula": null,
        "MetaData": [],
        "Properties": [
          { "Key": "valueType",       "Value": "int" },
          { "Key": "pollingInterval", "Value": "1000" },
          { "Key": "address",         "Value": "36" },
          { "Key": "count",           "Value": "1" },
          { "Key": "DB",              "Value": "11" }
        ]
      }
    ]
  }
}
```

## Response

`200 OK` -- `application/json`

| Field                                                | Type         | Description                                                                       |
|------------------------------------------------------|--------------|-----------------------------------------------------------------------------------|
| `data.ValidateUpdateRegisters`                       | `[Result!]` or `null` | Array of validation results, one per register input. `null` if every register passed without errors or warnings. |
| `data.ValidateUpdateRegisters[].ObjectID`            | `ID`         | UUID of the register the result applies to (matches the input's `ID`).            |
| `data.ValidateUpdateRegisters[].Errors`              | `[Issue!]!`  | Hard failures -- the actual `UpdateRegisters` mutation would reject these.        |
| `data.ValidateUpdateRegisters[].Errors[].FullName`   | `String`     | Dotted path to the offending input field (e.g. `Registers[0].Properties.address`). |
| `data.ValidateUpdateRegisters[].Errors[].TextToDisplay`| `String`   | Human-readable explanation suitable for inline form feedback.                     |
| `data.ValidateUpdateRegisters[].Warnings`            | `[Issue!]!`  | Soft warnings -- the mutation would succeed but the operator may want to review.  |

### Example response (no issues)

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

A `null` payload means every register input would be accepted as-is. Use this endpoint after every form change to keep the operator's feedback in sync without round-tripping the create/update mutation.

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

```graphql
query ValidateUpdateRegisters($input: UpdateRegistersRequest!) {
    ValidateUpdateRegisters(input: $input) {
        ObjectID
        Errors {
            FullName
            TextToDisplay
        }
        Warnings {
            FullName
            TextToDisplay
        }
    }
}
```

**Variables**

```json
{
    "input": {
        "Registers": [
            {
                "PublishCoV": false,
                "TagFormula": null,
                "MetaData": [],
                "Name": "DB",
                "Description": "",
                "DeviceID": "{{deviceID}}",
                "Properties": [
                {
                    "Key": "valueType",
                    "Value": "int"
                },
                {
                    "Key": "pollingInterval",
                    "Value": "1000"
                },
                {
                    "Key": "address",
                    "Value": "36"
                },
                {
                    "Key": "count",
                    "Value": "1"
                },
                {
                    "Key": "DB",
                    "Value": "11"
                }
                ],
                "TagName": "FaultCode_Min_Value",
                "ValueType": "int",
                "ID": "{{register_id}}"
            }
        ]
    }
}
```

### Response

**Status**: 200 OK

```Text
{
    "data": {
        "ValidateUpdateRegisters": null
    }
}
```

---

