Scroll down to learn more

Documentation Center

Welcome to Verity Documentation Center. You find here all the content you need to enjoy your data.

Search Results for

    Show / Hide Table of Contents

    Tagging

    What this guide covers. Everything you need to label ("tag") your measurement data in Verity, across all three ways data enters the platform: Standard Files, the Push API, and Calculated Variables. Read the Core concepts section once — it applies everywhere. Then jump to the ingestion path you use.

    Who this is for. Power users, integrators, account administrators, and anyone writing R for calculated variables whose data points need to carry free-form metadata.


    1. Why tagging exists

    A tag is a label attached to a data point to give it context: where the value comes from, what state it is in, which period it belongs to. Once applied, tags let you filter, visualize and compare your data consistently across the whole Data Hub.

    A tag always follows a strict key:value structure — for example validation:estimated, validation:raw, tariff:off_peak, version:V1.

    Use it whenever you want to distinguish subsets of your data:

    • Estimated values vs actual measurements
    • Off-peak vs peak hours
    • Source A vs source B
    • Validated vs raw data

    A tag is a semantic label. It does not define a timeframe, an aggregation rule, or a calendar — it qualifies the data, nothing more.


    2. Core concepts (read this once)

    Concept What it is Key property
    Tag A key:value pair attached to a data point. The key is the category, the value the state. Multiple tags per data point.
    Tag family A key + the set of values allowed under that key. Governed per account via the Tag Manager.
    Identifier The normalized, lowercase form (key / value). What data is ingested and matched on. Immutable.
    Display label The original casing (displayKey / displayValue). Cosmetic, editable. Never changes the identifier.
    version family Reserved, system-managed family. Auto-created from ingested version ids. Read-only.

    The golden rule

    Only the account's predefined and active tags are recognized at ingestion. A tag not registered in the Tag Manager (a typo, an unknown key) is silently dropped — the data point is still stored, just without that tag.

    One behaviour that surprises people

    • Case-insensitive. Production and production are the same value; the identifier is stored lowercase, and the first-seen casing becomes the display label.

    3. How submission paths compare

    You can send tagged data three ways. The concepts above are identical across all of them — only the syntax differs.

    Standard File Push API Calculated Variable
    Input CSV upload JSON POST /api/data R script output
    Tags field tags column (pipe \| separated) tags object (key/value) Tags column (JSON string per row)
    Setup needed None None None for tags
    Best for Manual or scheduled bulk loads System-to-system integration Derived values

    4. Managing tags: the Tag Manager

    Before they are recognized at ingestion, tags must be predefined at account level. That is the role of the Tag Manager, reached via Account settings → Tagging → Tag manager. Writes are restricted to the Manager or Admin roles; everyone else has read access.

    What you can do

    Action Effect
    Create a key New family, unique key + at least one initial value (in the UI).
    Add / remove a value On an existing key.
    Rename a label Of the key or a value — cosmetic, the identifier does not change.
    Deactivate / reactivate Hide or re-show the family from filters (reversible).
    Delete Permanent. No impact on data points already stored.

    Rules to know

    • Character set: A–Z a–z 0–9 _ . - only — no spaces.
    • Lengths: key ≤ 64 characters, value ≤ 256; neither may be empty.
    • Cap: max 20 values per key. The version family is exempt (unbounded).

    ⚠️ Renaming a label never reroutes data. Ingesting with the label (≠ identifier) would silently drop the tag. The UI always shows both to avoid confusion.


    5. Path A — Standard File

    A Standard File is a CSV you upload; the platform reads it and imports the values into the right sources and variables.

    The tags column (optional)

    • One or more key:value tags, pipe-separated with |.
    • Column name is case-insensitive; unknown columns are ignored.
    • The version column is a reserved key, picked up directly at ingestion.

    Example

    "date","value","variable_id","version","tags"
    "2026-01-01T00:00:00","10","1","V1","quality:estimated|period:high"
    "2026-01-01T00:00:00","10","1","V2","quality:consolidated|period:high"
    

    Common mistakes

    • A tag must follow the key:value form; multiple tags are separated by |, not by a comma.
    • One header row only — no sub-headers or units row.
    • A tag value not registered in the Tag Manager is dropped (see section 8).

    6. Path B — Push API

    Send data straight from an external system: POST /api/data with a JSON payload. Tags are declared as a key/value object, at two levels.

    Level Scope
    Batch (DataModel) Applies to every reading in that source/variable element.
    Individual reading Complements or refines the tags for one specific data point.

    ⚠️ Reading-level tags do not replace batch-level tags — the two levels are independent. Design your tagging strategy with that in mind.

    Example — batch level

    [
      {
        "variableId": 123456,
        "version": "V1",
        "tags": { "site": "plant-a", "import": "nightly" },
        "data": [
          { "date": "2026-05-27T08:00:00Z", "value": 42.5 }
        ]
      }
    ]
    

    Example — reading level

    {
      "sourceSerialNumber": "MTR-0098",
      "variableTypeId": 7,
      "data": [
        { "date": "2026-05-27T08:00:00Z", "value": 1024.0, "tags": { "quality": "measured" } },
        { "date": "2026-05-27T09:00:00Z", "value": 1031.5, "tags": { "quality": "estimated", "reason": "sensor-gap" } }
      ]
    }
    

    Never include accountId, userId or role in the body — they are read from your token.


    7. Path C — Calculated Variables

    A calculated variable can write tags onto its output data points through a Tags column in the data frame returned by the R script.

    • Each Tags cell is either "" or a valid JSON object with string values, e.g. {"source":"manual","unit":"kWh"}.
    • If present, the Tags column must be the same length as Dates/Values (otherwise the whole calculation fails).
    • Invalid JSON drops only that row's tags (a warning is logged) — the calculation does not fail.
    library(jsonlite)
    n <- length(inputVariables$input1$TimeSeries$Dates)
    tags <- vapply(seq_len(n), function(i) {
      toJSON(list(source = "qa-test", row = as.character(i)), auto_unbox = TRUE)
    }, character(1))
    # resulting cells: {"source":"qa-test","row":"3"}
    

    Wrap text columns in as.character(...) and set stringsAsFactors = FALSE to avoid factor conversion. The full R contract is documented in the Versioning guide.


    8. What happens at ingestion

    Ingestion is a hot path: it must stay fast and must never be blocked by tag bookkeeping.

    Situation Result
    Valid tags Validated against the account config, then stored on the data point as a key:value collection. Multiple tags are kept and independently queryable.
    No tags Ingestion proceeds without blocking. No default tag is applied.
    Unknown tag Data point is ingested without the unknown tag (never blocked); the unknown tag is reported in the ingestion webhook response.
    Tag service unavailable All tags are dropped rather than blocking; the data point is stored untagged.

    The version family is registered asynchronously: a brand-new version becomes queryable a moment later (a few seconds). This has no impact on the data point, which is never blocked.


    9. Filtering logic

    When you combine several tags, two rules apply — identical everywhere in the platform.

    Combination Logic Example
    Between keys AND validation:estimated AND tariff:off_peak
    Between values (same key) OR tariff:off_peak OR tariff:mid_peak

    In short: keys stack up (AND), while values within one key widen the match (OR).


    10. Filtering by tag across the platform

    Dashboards

    On a tile, filter a data source by tag (one or more values, one or more keys). Typical case: validation:validated in blue, validation:estimated in red.

    Source / Visualize tab

    Filter the displayed data points to inspect a specific version or quality subset. With no filter, the default behaviour is unchanged (latest version per timestamp).

    Calculated Variables (input)

    Restrict an input data source to data points carrying specific tags. The filter applies both to triggering (recalculate only if the data point matches) and to data fetch (only matching data points enter the computation).

    Alarms

    Optionally restrict an alarm's evaluation to data points carrying specific tags — avoids false alerts on unvalidated data.

    Reports

    Optionally apply a tag filter; the report's tag column then reflects the tags of the included data points.


    11. API impacts

    GetData — filter by tag

    • A tag filter returns only matching data points (OR between values, AND between keys).
    • With no filter, behaviour is unchanged (latest version per timestamp by default).
    • An unknown key/value in the filter returns an explicit error, not a silently empty result.

    REST data/tags — manage families

    Powers the Tag Manager screen. Send header Api-Version: 1.7. The account is implicit (never send an accountId). Reads are open to any authenticated user; writes are restricted to Manager/Admin.

    Method & path Purpose
    GET /data/tags List families (version excluded by default)
    GET /data/tags/status/active Active families (filter pickers)
    GET /data/tags/{key} One family with its values
    GET /data/tags/{key}/values Paged value search (never 404s)
    POST /data/tags Create a family
    PUT /data/tags/{key} Rename the label
    DELETE /data/tags/{key} Permanent delete
    POST …/deactivate · /reactivate Reversible (de)activation
    POST /data/tags/{key}/values Add a value
    DELETE …/values/{value} Remove a value
    PUT …/values/{value} Rename a value's label

    Error model

    Code When
    400 Invalid charset/length, duplicate key/value, 20-value cap reached, or a write on the reserved version family.
    403 Write attempted by a non Manager/Admin.
    404 Key or value not found (except the value-search endpoint).
    401 Not authenticated.

    Ingestion webhook

    When a batch contains unknown tags, the webhook response lists the unknown keys/values encountered. The data points are ingested without the unknown tag: no data is lost.


    12. Troubleshooting

    Symptom Cause Fix
    A tag doesn't appear after ingestion Tag not registered in the Tag Manager → silently dropped. Declare the key/value in the Tag Manager, then re-ingest.
    Version filter is empty Version just created (asynchronous registration). Wait a few seconds, then refresh.
    GetData returns an error on the filter Unknown key or value in the filter. Check the spelling of the identifier (≠ display label).
    A row's tags missing (calculated variable) Invalid JSON in the Tags cell. Emit valid JSON via jsonlite::toJSON(..., auto_unbox = TRUE).
    Write refused (403) Insufficient role. Ask your administrator for a Manager/Admin role.

    13. Cheat sheet

    • A tag = key:value. Multiple tags per data point.
    • Only predefined and active tags are recognized; unknown ones are dropped and reported to the webhook.
    • Standard File: tags column, pipe-separated with |.
    • Push API: tags key/value object; batch and reading levels are independent.
    • Calculated Variable: Tags JSON column, same length as Dates/Values.
    • Filtering: AND between keys, OR between values — everywhere the same.
    • version is a reserved, system-managed family, read-only.
    • Renaming a label never moves data (the identifier is immutable).
    DOCS 2026.08 Documentation changelog →

    Developer Center

    User manual API Swagger Github
    © 2025  -   www.verity.global

    Follow us

    Linkedin