Skip to main content
Version: Next

Tags

Tags are the gateway's data points: folders, driver-bound tags, memory tags, derived tags, expression tags and tag type instances. Configuration lives under /api/v1/tags; live values are read and written under /api/v1/runtime/tags. For what the fields mean, see Tags.

Every {id} below accepts the tag's id. The runtime routes also accept the tag's path (Plant.Line1.Setpoint) in place of the id.

Routes​

Method and pathRolePurpose
GET /api/v1/tagsviewerList tags, optionally one folder level or one connection's tags
GET /api/v1/tags/{id}viewerOne tag in full
GET /api/v1/tags/{id}/resolveviewerThe binding and source a tag resolves to (parameters substituted for instance members)
GET /api/v1/tags/bit-mapsviewerWhich derived tags read which bit of which word
GET /api/v1/tags/export?path=viewerExport a tag or folder with everything under it
POST /api/v1/tags/importengineerImport an export under a target path
POST /api/v1/tagsengineerCreate a tag or folder
PATCH /api/v1/tags/{id}engineerUpdate a tag
POST /api/v1/tags/{id}/moveengineerMove a tag or folder to another parent
POST /api/v1/tags/{id}/copyengineerDeep-copy a tag or folder under a new name
POST /api/v1/tags/{id}/restartoperatorMake the tag's source send its current value again
DELETE /api/v1/tags/{id}engineerDelete a tag, with everything under it
GET /api/v1/runtime/tags/{id}/currentviewerCurrent value and quality
GET /api/v1/runtime/tags/not-goodviewerEvery tag whose current quality is not good
POST /api/v1/runtime/tags/{id}/writeoperatorWrite a value through the tag's owner (driver or memory)
POST /api/v1/runtime/tags/{id}/valuesengineerInject a reading as if the source reported it
GET /api/v1/tags/{id}/alarmsviewerThe tag's alarm definitions (see Alarms)

Per-tag audit: there is no route under /tags; filter the admin audit log with GET /api/v1/admin/audit/tag-writes?tag= (see Administration).

Listing​

GET /api/v1/tags takes two optional query parameters:

ParameterMeaning
parentPathOnly the direct children of this folder. null (the literal string) returns the root level. Omitted returns every tag
producerInstanceIdOnly tags bound to this driver connection

The root listing always ends with the virtual @gateway folder. Listing under @gateway or @gateway.* returns the gateway's own tags (@gateway.performance, @gateway.connection.*, @gateway.clients, @gateway.browser.*), some of which are virtual (no database row) and some real. Gateway-managed tags cannot be changed, moved or deleted (400), but alarms can be added to them. See Gateway metrics as tags.

Each tag in the list has:

{
"id": "…", "path": "Plant.Line1.Setpoint", "name": "Setpoint", "parentPath": "Plant.Line1",
"enabled": true, "kind": "memory", "dataType": 4,
"udtInstanceId": null, "udtMemberKey": null, "overrides": null, "udtRootMemberKey": null, "udtDocumentKey": null,
"createdAt": "2026-11-08T12:00:00+00:00", "updatedAt": "…",
"binding": { "moduleId": "io.scadex.opcua", "instanceId": "…", "configuration": { } },
"derived": { }, "memory": { }, "expression": { }, "meta": { }, "numeric": { }
}

binding, derived, memory, expression, meta and numeric are null when the tag has no settings of that kind. GET /api/v1/tags/{id} returns the same shape; for a member of a tag type instance it returns a shorter record with udtInstance: { id, typeId, parameters, type: { id, path } }, because the type defines the member's fields.

Creating a tag​

POST /api/v1/tags with CreateTagDto. path and kind are required; everything else is optional and depends on the kind. name and parentPath are derived from path; if sent they are ignored.

FieldTypeApplies toMeaning
pathstringallFull dotted path. Names starting with @ are reserved (400)
kindstringallfolder, memory, derived, expression, udt (an instance root), or a driver tag with a binding
enabledbooleanallDefault true. Folders are always enabled
dataTypeintegerallData type code
producerModuleIdstringdriverThe driver module's id, for example io.scadex.opcua
producerInstanceIdstringdriverThe connection's id
bindingobjectdriverDriver-specific address, the shape the driver's descriptor defines
sourceTagstringderivedPath of the tag to follow
propertystringderivedWhich property of the source to follow
sourceBitintegerderivedFollow one bit of the source; 0 is the lowest
expressionstringexpressionThe expression; see Expression tags
memoryStartValuestringmemoryStart value, as text
scopestringmemorygateway (default), user or session
tooltip, documentationstringallNotes
engUnitstringallEngineering unit
formatStringstringallDisplay format
scaleModestringnumericlinear, squareRoot, bits or map; absent means no scaling
rawLow, rawHigh, engLowLim, engHighLimnumbernumericRaw and engineering ranges for linear/squareRoot
engLimitModestringnumericclamp to limit to the engineering range
scaleFactornumbernumericMultiplier
bitCountintegernumericFor bits
scaleMapstringnumericFor map: the value map, as text
deadbandModestringnumericabsolute or percent
deadbandnumbernumericDeadband amount

Scaling and deadband details are in Tags. An instance (kind: "udt") cannot be created inside another instance (400). A tag created under an instance's path becomes a tag added to that instance. History settings are not fields on the tag; the historian module decides what it stores (see History).

Response 200: the created tag in the list shape. A path already taken answers 409.

Updating a tag​

PATCH /api/v1/tags/{id} with UpdateTagDto: every field of the create model except path and scope-independent ones, plus:

FieldTypeMeaning
namestringRenames the tag (the last path segment); descendants are renamed along. An instance root keeps its name
kindstringChanges the kind
memoryValueanyMemory tags: sets the current value. An explicit null resets to the start value; absent leaves it alone
overridesobjectInstance members: a patch of member fields for this instance; replaces the stored overrides
sourceBitinteger0 to 63; -1 stops following a bit

Fields that are absent stay as they are. Response 200: the updated tag.

Moving, copying and deleting​

  • POST /api/v1/tags/{id}/move body {"newParentPath": "Plant.Line2"}; null moves to the root. Member tags of an instance and anything under @types cannot be moved (400). Response: the moved tag.
  • POST /api/v1/tags/{id}/copy body {"newName": "Setpoint2", "targetParentPath": "Plant.Line1"} (targetParentPath null means the root). Deep-copies the tag or folder with every descendant renamed along. Member tags cannot be copied (copy the instance), nor can types (400). Response: the copy.
  • DELETE /api/v1/tags/{id} answers 204.
  • POST /api/v1/tags/{id}/restart makes drivers re-create their subscriptions and rebuilds computed tags for the tag and everything under it (a folder, an instance, a type or a folder of types). Response: {"tags": 12, "driverTags": 8, "connections": 1}.

Export and import​

GET /api/v1/tags/export?path=Plant.Line1 returns:

{ "version": 1, "type": "…", "exportedPath": "Plant.Line1", "tags": [ { "relativePath": "Setpoint", "kind": "memory", "enabled": true, "producerModuleId": null, "producerInstanceId": null, "binding": null, "dataType": 4, "sourceTag": null, "property": null, "expression": null, "memoryStartValue": "0", "memoryValue": "42", "tooltip": null, "documentation": null, "engUnit": null, "formatString": null, "scope": null, "sourceBit": null } ] }

POST /api/v1/tags/import body {"targetPath": "Plant.Line2", "data": <the export>} creates the tags under targetPath. Answers 204. Types are exported and imported through their own routes (see Tag types).

Current value​

GET /api/v1/runtime/tags/{id}/current answers 404 {"message": "No current value is available for this tag."} until the tag has a value, otherwise:

{
"tag": { "id": 17, "publicId": "…", "path": "Plant.Line1.Setpoint", "kind": "memory", "enabled": true, "dataType": 4, "unit": null, "displayName": null, "description": null, "producerModuleId": null, "producerInstanceId": null },
"update": { "tagId": 17, "sequence": 120, "timelineSequence": 90311, "value": 42, "quality": { "code": "good", "detail": null }, "timestamp": "2026-11-08T12:00:00.123+00:00", "correlationId": null, "producerModuleId": null, "rawValue": null }
}

timestamp is ISO-8601. rawValue is the source's value when scaling changed it. For a user-scoped memory tag the caller's own value replaces the shared start value.

GET /api/v1/runtime/tags/not-good returns [{ "path": "…", "quality": "bad" | "uncertain" }] for every tag with a stored value whose quality is not good.

Writing a value​

POST /api/v1/runtime/tags/{id}/write (operator) body:

FieldTypeRequiredMeaning
valueany JSONyesThe value to write
correlationIdstringnoCarried into the resulting value update and the audit log

Response 202 when accepted, 400 when refused, both with the same body:

{ "accepted": true, "update": { "...": "a TagValueUpdate as above" }, "error": null }

Every attempt is recorded in the write audit log (see Audit log). Writes to @gateway.browser.* are not tag writes and are not logged.

Injecting a reading​

POST /api/v1/runtime/tags/{id}/values (engineer) publishes a value as if the tag's source had reported it, skipping the driver:

FieldTypeRequiredMeaning
valueany JSONyes
qualitystringnogood (default), uncertain or bad; anything else is 400
qualityDetailstringnoFree text attached to the quality
timestampISO-8601 stringnoDefaults to now
correlationIdstringno

Response 202 with the resulting TagValueUpdate. Refusals (unknown tag, disabled tag) answer 400 {"statusCode":400,"message":"…"}. Audit rows for this route carry operation publish.