# TagoIO Docs
> Documentation for TagoIO IoT platform, TagoDeploy, TagoCore, TagoTiP, and the TagoIO API.
This file contains all documentation content in a single document following the llmstxt.org standard.
## Create MQTT client
import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint";
import ParamsDetails from "@theme/ParamsDetails";
import RequestSchema from "@theme/RequestSchema";
import StatusCodes from "@theme/StatusCodes";
import OperationTabs from "@theme/OperationTabs";
import TabItem from "@theme/TabItem";
import Heading from "@theme/Heading";
Create a new MQTT client for a specific service
---
## Delete MQTT client
import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint";
import ParamsDetails from "@theme/ParamsDetails";
import RequestSchema from "@theme/RequestSchema";
import StatusCodes from "@theme/StatusCodes";
import OperationTabs from "@theme/OperationTabs";
import TabItem from "@theme/TabItem";
import Heading from "@theme/Heading";
Delete an existing MQTT client
---
## List MQTT clients
import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint";
import ParamsDetails from "@theme/ParamsDetails";
import RequestSchema from "@theme/RequestSchema";
import StatusCodes from "@theme/StatusCodes";
import OperationTabs from "@theme/OperationTabs";
import TabItem from "@theme/TabItem";
import Heading from "@theme/Heading";
Retrieve all MQTT clients for a specific service
---
## List project services
import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint";
import ParamsDetails from "@theme/ParamsDetails";
import RequestSchema from "@theme/RequestSchema";
import StatusCodes from "@theme/StatusCodes";
import OperationTabs from "@theme/OperationTabs";
import TabItem from "@theme/TabItem";
import Heading from "@theme/Heading";
Retrieve all services for a specific project
---
## TagoDeploy API
import ApiLogo from "@theme/ApiLogo";
import Heading from "@theme/Heading";
import SchemaTabs from "@theme/SchemaTabs";
import TabItem from "@theme/TabItem";
import Export from "@theme/ApiExplorer/Export";
This page explains how to authenticate to the TagoDeploy API and which base URL to use. All HTTP requests must include a valid token in the headers.
#### Authentication
TagoDeploy uses token-based authentication. Include a valid token in your HTTP headers for your requests.
- **Authorization**: Use Bearer token authentication for TagoDeploy API operations.
You should add the token to the request header using the Authorization header as documented in the API overview.
Send the Authorization header with every request.
Use for TagoDeploy API operations. Generate your token through TagoDeploy authentication.
```bash
curl -H "Authorization: Bearer " \
https://deploy.tagoio.com/status
```
:::info
**Rate limits** may apply to TagoDeploy API requests. Check the documentation for current limits and recommended usage.
:::
---
### API Endpoint
TagoDeploy API is available at:
- **Base URL**: `https://deploy.tagoio.com/`
Use the following pattern to construct your endpoint URLs:
```
https://deploy.tagoio.com/
```
deploy.tagoio.comdeploy.tagoio.com
---
## TagoIO Deploy API
import ApiLogo from "@theme/ApiLogo";
import Heading from "@theme/Heading";
import SchemaTabs from "@theme/SchemaTabs";
import TabItem from "@theme/TabItem";
import Export from "@theme/ApiExplorer/Export";
API for managing TagoIO Deploy projects.
All authenticated endpoints require a valid token.
A Project-Token that is unique to your device. Generate a token by accessing your Project's page.
Security Scheme Type:
apiKey
Header parameter name:
token
Contact
TagoIO Support:
URL: [https://tago.io](https://tago.io)
License
MIT
---
## Update MQTT client
import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint";
import ParamsDetails from "@theme/ParamsDetails";
import RequestSchema from "@theme/RequestSchema";
import StatusCodes from "@theme/StatusCodes";
import OperationTabs from "@theme/OperationTabs";
import TabItem from "@theme/TabItem";
import Heading from "@theme/Heading";
Update an existing MQTT client
---
## TagoIO Documentation
Welcome to the documentation for all TagoIO products and services.
## Products
### [TagoIO](/docs/tagoio/getting-started.md)
The complete IoT platform for device management, data visualization, and automation.
### [TagoRUN](/docs/tagoio/tagorun.md)
Edge computing solution for real-time data processing and analysis.
### [TagoCore](/docs/tagocore.md)
Core infrastructure components and services.
### [TagoDeploy](/docs/tagodeploy.md)
Deployment and management tools for IoT applications.
## Getting Started
If you're new to TagoIO, we recommend starting with:
1. [Quick Start with TagoIO](/docs/tagoio/getting-started.md) - Your first steps connecting a device
2. [Device Management](/docs/tagoio/devices.md) - Learn how to connect and manage devices
3. [Dashboard Creation](/docs/tagoio/dashboards.md) - Build beautiful data visualizations
## Need Help?
- Browse the documentation sections using the sidebar
- Visit the [main TagoIO website](https://tago.io)
- Join our [community forum](https://help.tago.io/portal/en/community)
- Open a [support ticket](https://help.tago.io)
---
## Download & Installation
TagoCore offers two installation methods to fit your needs: a quick Docker setup for immediate use, or a source code installation for development and customization.
## Method 1: Docker Installation (Recommended)
The fastest way to get TagoCore running is with Docker. This method requires no additional setup or dependencies.
### Prerequisites
- [Docker](https://docs.docker.com/get-docker/) installed on your system
### Quick Start
Run this single command to start TagoCore:
```shell
docker run -p 8888:8888 -p 8999:8999 tagoio/tagocore
```
TagoCore will be available at `http://localhost:8888` in your browser.
## Method 2: Source Code Installation
Install from source code when you need to customize TagoCore or contribute to its development.
### Prerequisites
- Node.js (version 20 or higher)
- [Just](https://github.com/casey/just) command runner
### Download
Download the latest release from the [GitHub releases page](https://github.com/tago-io/tagocore/releases/latest).
Choose your preferred format:
- **community_images.tar.gz** (Linux/macOS)
- **community_images.zip** (Windows/Cross-platform)
### Extract the Files
Navigate to your download folder and extract the archive:
**For .tar.gz files:**
```shell
tar -xvzf community_images.tar.gz
```
**For .zip files:**
```shell
unzip community_images.zip
```
### Install and Run
Navigate to the extracted folder and run these commands:
```shell
just install
just build-console
just server
```
TagoCore will start and display:
```shell
TagoCore is now available at http://localhost:8888
```
## Verification
Once TagoCore is running, open your browser and navigate to `http://localhost:8888`. You should see the TagoCore setup screen, confirming your installation was successful.
## Next Steps
With TagoCore installed, you're ready to start building IoT applications. Check out our [Core Concepts](/docs/tagocore/) to understand how Devices, Analyses, Actions, and Plugins work together.
---
## Welcome
# Welcome to TagoCore
## What is TagoCore?
TagoCore is a **free**, **fast**, and **open-source** IoT platform designed for edge computing and data management. Built for developers and businesses who need a reliable, self-hosted solution to collect, process, and analyze data from IoT devices.
### Key Features
- **Edge Computing Ready**: Process data locally without relying on cloud services
- **Real-time Data Processing**: Analyze and manipulate device data as it arrives
- **Extensible Architecture**: Customize functionality through plugins and integrations
- **Docker-based Deployment**: Simple installation and consistent runtime environment
- **Open Source**: Full access to source code and community-driven development
- **Cloud Integration**: Plugin support to connect with any cloud provider or server of your choice
### Why Choose TagoCore?
TagoCore bridges the gap between raw device data and actionable insights. Whether you're prototyping IoT solutions, managing industrial sensors, or building smart applications, TagoCore provides the foundation to:
- Connect and manage multiple devices from a single platform
- Execute custom logic on incoming data streams
- Trigger automated responses based on specific conditions
- Extend capabilities through a growing plugin library
### Quick Start
Get TagoCore running in seconds with Docker:
```shell
docker run -p 8888:8888 -p 8999:8999 tagoio/tagocore
```
## Core Concepts
Understanding these four fundamental concepts will help you get the most out of TagoCore:
### Devices
Devices represent your physical hardware within TagoCore. Each device you want to monitor or control must be registered in the platform. Once registered, devices can send sensor data, receive commands, and participate in automated workflows.
[Learn more about Devices →](/docs/tagocore/device)
### Analyses
Analyses are custom scripts that process your device data in real-time. Use them to transform raw sensor readings, detect patterns, calculate derived values, or implement business logic. Analyses can be triggered automatically when new data arrives or run on scheduled intervals.
[Learn more about Analyses →](/docs/tagocore/analysis)
### Actions
Actions are automated responses that execute when specific conditions are met. They enable TagoCore to react to your data by sending notifications, making HTTP requests, running analyses, or integrating with external systems. Think of them as the "then" part of "if-then" automation rules.
[Learn more about Actions →](/docs/tagocore/action)
### Plugins
Plugins extend TagoCore's capabilities beyond its core features. Install community-developed plugins or create your own to add new integrations, data sources, or functionality. The Plugin Store makes it easy to discover and install extensions that fit your specific needs.
[Learn more about Plugins →](/docs/tagocore/plugins)
---
## Plugin - Getting Started
# Getting Started
Plugins are programs or applications created by the community and TagoIO that extend the functionality of TagoCore.
## Prerequisites
Like TagoCore itself, Plugins are built using Node.js. You'll need an understanding of **JavaScript** or **TypeScript** and **Node.js** to
develop plugins. You should take a look at our [Prerequisites](/docs/tagocore/plugins/create/prerequisites.md) before starting.
## Lifecycle
Before jumping into development, it's important to understand how Plugins run in TagoCore, and how TagoCore manages
Plugins. Learn more about a [Plugin's Lifecycle](/docs/tagocore/plugin/create/lifecycle.md).
## Development
This tutorial will build a Plugin that allows the user to set a port for an HTTP server to start.
It will use many Plugin components to give an introductory demonstration of their relationships.
To begin, you need to create a new directory where your plugin will be located.
Now, we must start a new Node.js project. To do so, run the following command by opening up a terminal in the newly
created folder:
```bash
npm init --yes
```
### Manifest
Every Plugin needs a [manifest](/docs/tagocore/plugins/create/manifest.md). The Plugin manifest is defined within the package.json
of your project, so open the `package.json` file in your favorite text editor and add a `tcore` property to it.
Your Plugin's `package.json` should look something like this:
```json
{
"name": "Getting Started Example",
"version": "1.0.0",
"tcore": {
"name": "Getting Started Example",
"short_description": "Example of how to get started with Plugins"
}
}
```
### Installing the SDK
Now that you have your Plugin's manifest ready, it's time to add some functionality. The first step is to install
the [TagoCore Plugin SDK](https://npmjs.com/package/@tago-io/tcore-sdk) from the npm registry, so run the following
commands by opening up a terminal in your folder:
```bash
npm install @tago-io/tcore-sdk
```
### Adding Functionality
With the TagoCore Plugin SDK installed, we are free to start adding functionality to our Plugin. To do so, create a new
JavaScript file called `index.js`.
Inside of the `index.js` file, we will create our first [Module](/docs/tagocore/plugins/create/module.md). Modules are JavaScript
classes that allow you to add a specific functionality for your Plugin. Each **Plugin should have at least one module**.
Each Module should be instantiated with an `id` and `name` properties, which means our code should look something
like this:
```js
const { ServiceModule } = require("@tago-io/tcore-sdk");
const service = new ServiceModule({
id: "getting-started-service",
name: "Getting Started Service",
});
```
### Allow a Service Module to be created
Every Module type that you use in your code must be declared in the `types` property of your Plugin's manifest. In this
case, we are creating a Service Module so we must add a `service` type to the manifest.
[See the full list here](/docs/tagocore/plugins/create/manifest#types).
```json
{
"name": "Getting Started Example",
"version": "1.0.0",
"tcore": {
"name": "Getting Started Example",
"short_description": "Example of how to get started with Plugins",
// highlight-start
"types": ["service"]
// highlight-end
}
}
```
### Installing your Plugin
Your Plugin is ready. It doesn't do anything, but it can already be installed in TagoCore. Since you already have
created your Plugin you need to install it [Manually](/docs/tagocore/plugins/install/manually.md).
Once you do so, you should see something like this as soon as the application starts:
### Give users options
Our Plugin is loaded but it doesn't do anything. Our goal is to create an HTTP server, but we are not sure which
port is available in the system, so we will ask the user to set a port via the Plugin's UI.
Head over to the `index.js` file and add a `configs` field to the Module's
setup. We will add a single string number field for the user to set a port for us to start an HTTP server.
```js
const { ServiceModule } = require("@tago-io/tcore-sdk");
const service = new ServiceModule({
id: "getting-started-service",
name: "Getting Started Service",
configs: [
{
type: "number", // type of the field, there are many types
field: "port", // unique identifier for this field
name: "Port", // visual label for the field
required: true, // obligatory
},
],
});
```
:::info
Every time you modify your Plugin's code or manifest, you should restart TagoCore.
:::
After restarting TagoCore, you should see an input in the Plugin's Configuration page. The input will only accept
numbers because of its `type`, and it is required to have a value because of the `required` property.
### Using users values
Now that the user has informed a port and saved it, it's time for us to actually use the value informed by the user.
That's where the `onLoad` and `onDestroy` Module functions come in. These functions are a part of the
[Plugin's Lifecycle](/docs/tagocore/plugin/create/lifecycle.md).
- `onLoad` will always be the first function called for each Module. It is in here that you should add the starting
functionality for a single Module (starting an http server for example).
- `onDestroy` will always be the last function called for each Module. It is in here that you should add the stopping
functionality for a single Module (stopping an http server for example).
To start the HTTP server, we will use [express](https://npmjs.com/package/express), and our code will look something
like this:
```js
const { ServiceModule } = require("@tago-io/tcore-sdk");
const express = require("express");
const app = express();
let server = null;
const service = new ServiceModule({
id: "getting-started-service",
name: "Getting Started Service",
configs: [
{
type: "number",
field: "port",
name: "Port",
required: true,
},
],
});
service.onLoad = async (userValues) => {
if (!userValues.port) {
throw new Error("Invalid port");
}
app.get("/", (req, res) => res.send("Hello World"));
server = app.listen(userValues.port);
};
service.onDestroy = () => {
server.close();
};
```
After you restart TagoCore, you will see that if you access the address `http://localhost:3000` in your browser, a text
response of 'Hello World' will be sent back, indicating that the plugin is alive.
---
## Lifecycle
It's important to understand how Plugins run and how TagoCore manages Plugins.
In order for the Plugin system to be secure and stable, all Plugins run in a sandbox. All communication between a Plugin environment and TagoCore is done through messages. If a Plugin crashes, the crash will not affect TagoCore.
All incoming TagoCore messages are handled by [Modules](/docs/tagocore/plugins/create/module.md) Inside of each Plugin environment,
which means your **Plugin should have at least one module**.
## How Plugins are Started
```mermaid
sequenceDiagram
TagoCore->>Plugin Env: Start
Plugin Env->>Module1: onLoad
Plugin Env->>Module2: onLoad
Module2-->>Plugin Env: Success
Module1-->>Plugin Env: Success
Plugin Env-->>TagoCore: Success
```
After TagoCore is started, it will iterate through each Plugin environment and send a message for it to load. Once that
message is received, the Plugin environment will make a call to the `onLoad` function of each module.
Once all `onLoad` calls have been resolved, the Plugin environment sends a message to TagoCore, which in turn marks the
Plugin as `started`.
While the Plugin environment tries its best, it doesn't guarantee that the `onLoad` function of each module will be
called in the order that they were defined.
:::danger[Timeout]
Each plugin can take up to **30 seconds** to start. If after 30 seconds there are still pending `onLoad` calls, the plugin
will be terminated.
:::
## How Plugins are Stopped
```mermaid
sequenceDiagram
TagoCore->>Plugin Env: Stop
Plugin Env->>Module1: onDestroy
Plugin Env->>Module2: onDestroy
Module2-->>Plugin Env: Success
Module1-->>Plugin Env: Success
Plugin Env-->>TagoCore: Success
```
The Stop flow for each Plugin environment is very similar to the Start flow.
Whenever a Plugin needs to be terminated, the Plugin environment send a message for it to shutdown. Once that
message is received, the Plugin environment will make a call to the `onDestroy` function of each module.
Once all `onDestroy` calls have been resolved, the Plugin environment sends a message to TagoCore, which in turn marks the
Plugin as `stopped`.
While the Plugin environment tries its best, it doesn't guarantee that the `onDestroy` function of each module will be
called in the order that they were defined.
:::danger[Timeout]
Each plugin can take up to **60 seconds** to stop. If after 60 seconds there are still pending `onDestroy` calls, the plugin
will be terminated.
:::
## Errors during onLoad and onDestroy
Sometimes an error in your code may result in an Error being thrown in the `onLoad` or `onDestroy` functions of a
Module, or maybe it meant to throw one because it had not received the correct `configs` from your Plugin's UI.
In either case, errors may happen in these functions and that's acceptable. If an error occurs in these functions,
the plugin will still be loaded normally and its state will still be `started`.
However, the Module will have its state as `stopped` and the error will appear in the `Plugin Configuration`
page of your Plugin, under your Module's status:
This functionality is a great tool for you to indicate that something is not quite right in your Plugin, or that
some unexpected error happened while trying to start/stop your Module.
---
## Manifest
# Plugin Manifest
TagoCore Plugins need a manifest to be to loaded by TagoCore's Plugin Environment.
This manifest is defined within the package.json of your project.
## Package attributes
To generate a Plugin Manifest, you must add a `tagocore` property to it, this is a sample JSON showing all
attributes of the object:
```json
{
"tcore": {
"name": "My Plugin Name",
"short_description": "Explain what your Plugin does in a few words",
"full_description": "./README.md",
"icon": "./assets/icon.png",
"types": [
"service",
"action-type",
"action-trigger",
"database"
"encoder"
],
"permissions": [
"device",
"device-data",
"action",
"analysis"
]
},
}
```
### name
This property must contain the name of your Plugin. This is the same name that will appear in the Plugin Store when you
publish your plugin.
### short_description
Explain in up to 100 characters what your Plugin does.
### full_description
This property should contain a markdown file that will be rendered in the body of your Plugin Configuration. In the
markdown file you explain your Plugin and its API in-depth. Developers usually use the `README.md` file in the root
of the project.
:::info[Good to know]
TagoCore doesn't support html in the `full_description` file.
:::
### icon
Set the path of an image file to act as your Plugin's main icon. We recommend you use a `PNG` image for the icon.
### types
This is an array that should contain all [module](/docs/tagocore/plugins/create/module.md) types used by your Plugin. For instance,
if you are using a [Service Module](/docs/tagocore/plugins/create/service) and a [Payload Encoder Module](/docs/tagocore/plugins/create/encoder)
in your code, you should have an array like this:
```json
["service", "encoder"]
```
Here are all the types of modules:
- [Payload Encoder Module](/docs/tagocore/plugins/create/encoder) - `encoder`;
- [Service Module](/docs/tagocore/plugins/create/service) - `service`;
- [Action Trigger Module](/docs/tagocore/plugins/create/action-trigger) - `action-trigger`;
- Action Type Module - `action-type`;
- Database Module - `database`.
### permissions
This array specifies which API calls you will be able to make to TagoCore's API via the [core](/docs/tagocore/plugins/create/core.md) object. For instance, if you wish to create a new [Device](/docs/tagocore/device) via the `core.createDevice` function, you have need to add the `device` property to this permissions array.
These are all the possibilities for this array:
```json
["action", "analysis", "device", "device-data"]
```
---
## Prerequisites
TagoCore Plugins are built using Node.js and the [TagoCore Plugin SDK](https://npmjs.com/package/@tago-io/tcore-sdk).
You'll need an understanding of **JavaScript** and **Node.js** to develop plugins.
While it is not necessary, other tools may come in handy when developing more complex plugins. You don't need to learn
these tools before starting to write plugins, but they may be useful at some point.
- [TypeScript](https://npmjs.com/package/typescript), to make navigating the API easier and write reliable plugins;
- [Visual Studio Code](https://code.visualstudio.com/) as an IDE, to accelerate the development of your Plugin.
You do **not** need to have experience with front-end UI libraries such as React or Vue. TagoCore creates and manages
your Plugin's user interface via a JSON structure.
:::info[Important]
You should use **Node.js version 20** or greater to build plugins for TagoCore.
:::
---
## Core
The `core` object from the Plugin SDK allows your plugin to interact with TagoCore data.
## Device functions
All functions in this category need the `device` Permission in your manifest.
You cannot use any of these functions without the appropriate permissions.
### Get all devices
To retrieve the list of devices in TagoCore, use the `getDeviceList` function. You may pass additional parameters in the
function to filter the results of the list.
```js
const { core } = require("@tago-io/tcore-sdk");
async function getDeviceList() {
const list = await core.getDeviceList();
console.log("Retrieved", list.length, "devices"); // outputs 12 in my case
}
getDeviceList();
```
### getDeviceInfo
Retrieves all the information of a single device.
- Parameter: ID of the device;
### getDeviceByToken
Retrieves all the information of a single device via its token.
- Parameter: Token of the device;
### editDevice
Edits the information of a single device.
- Parameter: ID of the device;
- Parameter: Data to edit the device;
### deleteDevice
Deletes a device.
- Parameter: ID of the device;
### createDevice
Creates a new device.
- Parameter: Data to create the device;
### createDeviceToken
Creates a new device token.
- Parameter: ID of the device;
- Parameter: Data to create the token;
### getDeviceTokenList
Retrieves a list of device tokens.
- Parameter: ID of the device;
- Parameter: Query to filter and organize the list;
### deleteDeviceToken
Deletes a device's token.
- Parameter: Token of the device;
### getDeviceParamList
Gets all the parameters of a device.
- Parameter: ID of the device;
- Parameter: Sent status to filter the list;
### deleteDeviceParam
Deletes a device's param.
- Parameter: ID of the param;
### setDeviceParams
Overrides or edits device parameters.
- Parameter: ID of the device;
- Parameter: Data to override or edit the params;
## Action functions
All functions in this category need the `action` Permission in your manifest.
You cannot use any of these functions without the appropriate permissions.
### getActionTypes
Retrieves a list of all action types.
### getActionList
Retrieves a list of actions.
- Parameter: Query to filter and organize the list;
### getActionInfo
Retrieves all the information of a single action.
- Parameter: ID of the action;
### editAction
Edits the information of a single action.
- Parameter: ID of the action;
- Parameter: Data to edit the action;
### deleteAction
Deletes an action.
- Parameter: ID of the action;
### createAction
Creates a new action.
- Parameter: Data to create the action;
### triggerAction
Triggers an action.
- Parameter: ID of the action;
- Parameter: Data to pass to the action;
## Analysis functions
All functions in this category need the `analysis` Permission in your manifest. You cannot use any of these functions without the appropriate permissions.
### getAnalysisList
Retrieves a list of analyses.
- Parameter: Query to filter and organize the list;
### getAnalysisInfo
Retrieves all the information of a single analysis.
- Parameter: ID of the analysis;
### editAnalysis
Edits the information of a single analysis.
- Parameter: ID of the analysis;
- Parameter: Data to edit the analysis;
### deleteAnalysis
Deletes an analysis.
- Parameter: ID of the analysis;
### createAnalysis
Creates a new analysis.
- Parameter: Data to create the analysis;
## Device Data functions
All functions in this category need the `device-data` Permission in your manifest. You cannot use any of these functions without the appropriate permissions.
### getDeviceDataAmount
Retrieves the amount of data in a device.
- Parameter: ID of the device;
### addDeviceData
Adds a data item into a device.
- Parameter: ID of the device;
- Parameter: Data to be added;
### getDeviceData
Retrieves data from a device.
- Parameter: ID of the device;
- Parameter: Query to filter and organize the list;
## Other functions
These functions do not require any permissions.
### getSummary
Retrieves the summary information of TagoCore.
### getTagKeys
Retrieves the list of tags from a resource type.
---
## Helpers
The TagoCore Plugin SDK offers helpers functions that are useful when creating your Plugin.
## Write a file
To write a file, you can use the `writeFile` helper function. Similarly to the `fs` module from Node.js, this
function writes the desired contents in a file.
The main difference between using this helper function and the `fs` module directly is that this helper function
by default writes the file in the settings folder of your Plugin.
If you are writing a simple file, we recommend you use this helper function instead of using `fs` module directly.
```js
const { helpers } = require("@tago-io/tcore-sdk");
async function writeFile() {
const data = "Hello, my name is John";
await helpers.writeFile("folder/data.txt", data);
}
writeFile();
```
## Read a file
To read a file, you can use the `readFile` helper function. Similarly to the `fs` module from Node.js, this
function reads contents of a file.
The main difference between using this helper function and the `fs` module directly is that this helper function
tries to find the file in the settings folder of your Plugin.
We recommend you use this helper function instead of using `fs` module directly.
```js
const { helpers } = require("@tago-io/tcore-sdk");
async function readFile() {
const data = await helpers.readFile("folder/data.txt");
console.log(data); // outputs "Hello, my name is John"
}
readFile();
```
---
## Module - Action Trigger
# Action Trigger Module
This Module allows you to create a custom Action trigger. You decide when the action must be triggered.
An `ActionTriggerModule` will never be triggered by an external event, you must manually analyze the configuration and
trigger the action. You are responsible for watching external events and triggering events.
For instance, if you were to develop an MQTT Plugin, you would need to manually watch all topics in your
`ActionTriggerModule` configuration and decide if your action should be triggered or not.
## Sample code
```js
const { ActionTriggerModule } = require("@tago-io/tcore-sdk");
const plugin = new ActionTriggerModule({
id: "value-list",
name: "Value list Plugin",
option: {
description: "Triggers the action if value matches a string",
name: "Value list Trigger",
configs: [
{
description: "Triggers the action when the value of the device matches any value in the list.",
field: "list",
icon: "hashtag",
name: "Value list",
placeholder: "You may use wildcards (**_temperature)",
required: true,
title: "Value Subscription",
type: "string-list",
},
],
},
});
// You should validate if your action should be triggered here.
plugin.onCall = async (actionID, actionValues, deviceData) => {
// ...
};
```
---
## Module - Payload Encoder
# Payload Encoder Module
This Module allows you to encode data before it reaches a device.
```mermaid
graph LR
A[Device sends data] --> B[Payload Encoder]
B[Payload Encoder] --> C[Payload Parser]
C[Payload Parser] --> D[Data added to Device]
classDef default fill:#333,stroke:#333,stroke-width:2px,color:#fff,font-weight:bold
```
Once a Device sends data, the data is immediately forwarded to the first `PayloadEncoderModule` found. The Payload
Encoder order is defined by the `encoder_stack` field of the Device.
After the first `PayloadEncoderModule` resolves a value it is passed sequentially through all the remaining
`PayloadEncoderModule` modules.
:::tip[Keep in Mind]
The value received in your `PayloadEncoderModule` class may have already been parsed by another `PayloadEncoderModule`
from a different Plugin.
:::
## Encoding the value
To encode the value, you should override the Module's `onCall` function. This function receives a single value, which
is the data sent by a Device or parsed by other encoders.
You should **always** return a value from the `onCall` function.
## Sample code
```js
const { PayloadEncoderModule } = require("@tago-io/tcore-sdk");
const encoder = new PayloadEncoderModule({
id: "number-to-hex-encoder",
name: "Number to Hex encoder",
});
// You should encode your data here.
// This example encodes number values into a hex format.
encoder.onCall = async (data) => {
if (data) {
const isNumber = !isNaN(data.value);
if (isNumber) {
// encodes data.value to a hex format
data.value = data.value.toString(16);
}
}
return data;
};
```
---
## Module - Service
# Service Module
This Module allows you to run any kind of code once your plugin is loaded.
## How it works
As soon the Plugin is loaded, the `onLoad` function of the Module will be invoked. You can override this function to
execute your code, such as to start an http server or to run analytics on some data you have stored.
Just like all other Modules, the `onDestroy` function will be invoked just before the Module is stopped. You can
override this function if you wish to clean up your code before the Module is destroyed.
## Sample code
This sample starts an http server with [express](https://npmjs.com/package/express). When a `GET /` request is made to the server, the string `Hello World` will be sent back.
```js
const { ServiceModule } = require("@tago-io/tcore-sdk");
const express = require("express");
const app = express();
let server = null;
const myService = new ServiceModule({
id: "hello-world-service",
name: "Hello World service",
});
// `onLoad` is used to run your code.
// This function will be called once when your plugin gets loaded.
myService.onLoad = async () => {
app.get("/", function (req, res) {
res.send("Hello World");
});
server = app.listen(3000);
};
// `onDestroy` is used to clean up your code.
// This function will never be called before `onLoad`.
myService.onDestroy = async () => {
server.close();
};
```
---
## Module
Modules are JavaScript classes that allow you to add a specific functionality for your Plugin.
Your **Plugin should have at least one module**.
We currently offer these types of Modules for you to use in your Plugin:
- [Payload Encoder Module](/docs/tagocore/plugins/create/encoder) - To encode data before reaching a Device;
- [Service Module](/docs/tagocore/plugins/create/service) - To create a service that runs code;
- [Action Trigger Module](/docs/tagocore/plugins/create/action-trigger) - To create a new trigger for Actions;
- Action Type Module - To create a new type for Actions;
- Database Module - To create a database that will save TagoCore data.
## Setup
Setup is the name of object passed to the constructor of each Module class.
The setup object **must have** an `id` and a `name` property. The setup object may also contain a `configs`
property to request configuration parameters to the user.
```js
const { ServiceModule } = require("@tago-io/tcore-sdk");
const setup = {
id: "my-service",
name: "Pretty name of service",
};
new ServiceModule(setup);
```
## Payload Encoder Module
This Module allows you to encode data before it reaches a device. Learn more about it [here](/docs/tagocore/plugins/create/encoder).
```mermaid
graph LR
A[Device sends data] --> B[Payload Encoder]
B[Payload Encoder] --> C[Payload Parser]
C[Payload Parser] --> D[Data added to Device]
classDef default fill:#333,stroke:#333,stroke-width:2px,color:#fff,font-weight:bold
```
## Service Module
This Module allows you to create a service that will run in your Plugin. Learn more about it [here](/docs/tagocore/plugins/create/service).
## Action Trigger Module
This Module allows you to create a new trigger for Actions. Learn more about it [here](/docs/tagocore/plugins/create/action-trigger).
## The `configs` property
If your Module has a `configs` property in the `setup` object, you can access the current values of those configurations
using the `.configs` getter.
The `.configs` getter retrieves the latest configuration values from TagoCore, so you should have the latest values
every time. Below is a sample code of how to use it:
```js
const { ServiceModule } = require("@tago-io/tcore-sdk");
const service = new ServiceModule({
id: "my-service",
name: "Service",
configs: [
{
name: "Token",
field: "token",
type: "string",
required: true,
},
],
});
service.onLoad = async () => {
// Retrieves the latest config values from TagoCore
const configs = await service.configs;
const token = configs.token;
};
```
---
## Plugin Storage
Each plugin has its own key-value database, and you can store any kind of information there by using the `pluginStorage`
object from the TagoCore Plugin SDK.
:::info[Important]
A Plugin **cannot** access the key-value pairs from another Plugin. Everything you store in this database is only
accessible by your plugin.
:::
## Storing data
To store a key-value pair in the database, use the `set` function. If the key already exists in the database,
the value will be overridden, otherwise the key-value pair will be added. Here is a code sample using that function:
```js
const { pluginStorage } = require("@tago-io/tcore-sdk");
async function addKeyValuePair() {
const key = "name";
const value = "John";
await pluginStorage.set(key, value);
}
addKeyValuePair();
```
## Retrieving a single value
To retrieve a value associated to a key, use the `get` function. If the key doesn't exist, `undefined` will be
returned as a result.
Here is a code sample using that function:
```js
const { pluginStorage } = require("@tago-io/tcore-sdk");
async function getKeyValuePair() {
const key = "name";
const value = await pluginStorage.get(key);
console.log(value); // outputs "John"
}
getKeyValuePair();
```
## Retrieving all key-value pairs
At some point you may want to retrieve all key-value pairs in the database. If that's the case, use the
`getAllItems` function. This function will return a key-value array containing all pairs in your Plugin's database.
Here is a code sample using that function:
```js
const { pluginStorage } = require("@tago-io/tcore-sdk");
async function getAllKeyValuePairs() {
const pairs = await pluginStorage.getAllItems();
console.log(pairs[0]); // outputs { key: "name", value: "John" }
console.log(pairs[1]); // outputs { key: "location", value: "NC" }
console.log(pairs[2]); // outputs { key: "hobby", value: "IoT" }
}
getAllKeyValuePairs();
```
## Removing data
To remove a key-value pair from the database, use the `delete` function. If the key doesn't exist, nothing will happen.
Here is a code sample using that function:
```js
const { pluginStorage } = require("@tago-io/tcore-sdk");
async function removeKeyValuePair() {
const key = "name";
await pluginStorage.delete(key);
}
removeKeyValuePair();
```
---
## Finish
# You're Ready to Build Amazing IoT Solutions!
Congratulations! You've completed the TagoCore documentation and now have all the knowledge you need to build IoT applications from the ground up.
## What You've Mastered
You now understand the complete TagoCore platform and can:
**Platform Fundamentals:**
- Install and configure TagoCore using Docker or source code
- Navigate the dashboard and monitor system health
- Configure network settings, databases, and system preferences
**Device Management:**
- Create and configure IoT devices with secure authentication
- Transform raw device data using payload parsers
- Monitor device communication with the live inspector
- Manage data retention policies
**Data Processing:**
- Write custom Analysis scripts in any programming language
- Process device data in real-time with complex logic
- Monitor script execution through the console
**Automation & Actions:**
- Create automated responses using Actions
- Set up variable-based and schedule-based triggers
- Send data to external systems via HTTP requests
**System Management:**
- Monitor application logs for troubleshooting
- Use different log channels for plugins and core system
**Plugin library:**
- Browse and activate plugins from the Plugin Store
- Install custom plugins manually
- Extend TagoCore functionality with community-developed tools
## Get Involved
TagoCore is an open-source project, and your contributions can help make it even better! The complete source code is available under the [Apache License 2.0](https://github.com/tago-io/tagocore/blob/main/LICENSE.md), which means you can:
- Report bugs and suggest improvements
- Contribute code and documentation
- Create and share your own plugins with the community
## Need Help?
Have questions or running into issues? The TagoCore community is here to help! Visit our [Community Forum](https://community.tago.io/c/tagocore/16) to:
- Ask questions and get answers from experienced users
- Share your projects and get feedback
- Connect with other TagoCore developers
- Stay updated on the latest features and updates
---
## Manually
# Manual Plugin Installation
Need to install a custom plugin that's not available in the Plugin Store? This happens when you're developing your own plugin or someone has shared a custom plugin with you. TagoCore offers two convenient ways to manually install plugins:
## Method 1: Quick Install via Sidebar
The fastest way to install a custom plugin is through TagoCore's interface:
1. Look for the puzzle piece icon in the sidebar
2. Click the icon to open the plugin installer
3. Browse and select the folder containing your plugin (the folder should have a `package.json` file)
4. TagoCore will automatically install and activate the plugin
This method is perfect for testing plugins during development or quickly adding plugins shared by others.
## Method 2: Configure via Settings File
For permanent installation or when managing multiple plugins, you can add them directly to TagoCore's configuration:
### Locate Your Configuration File
Find the `tagocore.yml` file in your TagoCore settings folder:
- **Windows**: `TagoCore` folder in your user directory
- **Mac/Linux**: `.tagocore` folder in your user directory (hidden folder)
### Add Plugin Paths
Open the `tagocore.yml` file and add your plugin paths to the `custom_plugins` section:
```yml
custom_plugins:
- /path_to_your_plugin/folder_plugin_one
- /path_to_your_plugin/folder_plugin_two
```
Replace the example paths with the actual folder paths where your plugins are located. After saving the file, restart TagoCore to load the new plugins.
---
## Plugin Store
# Using the Plugin Store
The Plugin Store is your one-stop shop for discovering and enabling new TagoCore functionality. All available plugins are already built into TagoCore - you just need to activate the ones you want to use.
Think of it like enabling features rather than installing new software. All plugin code is already included with your TagoCore installation, so activation is instant and doesn't require downloading anything.
You can browse all available plugins in the [TagoCore plugins repository](https://github.com/tago-io/tagocore/tree/main/plugins).
## Activating a Plugin
Ready to add new functionality to your TagoCore? Here's how to activate a plugin:
1. **Open Plugin Store**: Click the `Plugins` button in TagoCore's sidebar
2. **Choose Your Plugin**: Browse the available plugins and select one that meets your needs
3. **Activate**: Click the `Activate` button on the right side of the page
Once activated, the plugin will immediately be available for use in your TagoCore system.
:::info[Compatibility Check]
TagoCore automatically prevents you from activating plugins that aren't compatible with your current platform or system configuration.
:::
---
## Plugin Overview
# Plugins
Plugins are add-on components that expand TagoCore's capabilities beyond its core features. Think of them as apps for your IoT platform - they add new integrations, data sources, storage options, and specialized functionality created by both the TagoCore community and TagoIO team.
Plugins help you customize TagoCore to fit your specific needs without modifying the core system.
## Creating Your Own Plugin
Want to build custom functionality for TagoCore? All plugins are developed using Node.js, making them accessible to JavaScript developers.
Once you've built a plugin, you'll soon be able to share it with the community through the upcoming Plugin Store.
Ready to start? Check out our [Creating Plugins](/docs/tagocore/plugins/create) guide for step-by-step instructions.
## Installing Plugins
**For your own plugins**: If you've developed a custom plugin, use our [Manual Installation](/docs/tagocore/plugins/install/manually.md) guide to add it to your TagoCore instance.
**For community plugins**: The easiest way to discover and install plugins from other developers is through the [Plugin Store](/docs/tagocore/plugins/install/pluginstore.md), where you can browse available plugins and install them with just a few clicks.
---
## Action Overview
# Action
An Action is an automated response that executes when specific events happen in your TagoCore system. Think of Actions as the "then" part of "if-then" rules - when something occurs (like a device sending data), TagoCore can automatically take action based on the rules you set up.
Actions give you extensive automation capabilities for your [Devices](/docs/tagocore/device) and IoT workflows.
## What Actions Can Do
With Actions, you can automatically:
- **Run custom scripts** using an [Analysis](/docs/tagocore/analysis) to process or analyze data
- **Send HTTP requests** to external APIs or webhooks to integrate with other systems
- **Trigger plugin functionality** using specialized [triggers](/docs/tagocore/action/trigger.md) and [types](/docs/tagocore/action/type.md)
- **Extend capabilities** through the growing plugin library
## Creating an Action
To create a new Action, click the button on the **Actions** page. This opens the Action creation modal:
You'll need to configure three essential components:
1. **Name**: A descriptive name for your Action (e.g., "High Temperature Alert")
2. **[Trigger](/docs/tagocore/action/trigger.md)**: What event should activate this Action (e.g., when device data arrives)
3. **[Type](/docs/tagocore/action/type.md)**: What should happen when the Action runs (e.g., run an analysis, post to data to HTTP endpoint)
---
## Trigger
# Action Trigger
A trigger defines when your Action should run. Think of it as the "when" part of your automation rule. TagoCore offers two main types of triggers to cover different automation scenarios:
- **Variable Trigger**: Executes when device data meets specific conditions (e.g., temperature > 30°C)
- **Schedule Trigger**: Executes at specific times or intervals (e.g., every day at 9 AM, every 5 minutes)
## Variable Trigger
This trigger watches your device data and executes Actions when certain conditions are met. Perfect for responding to sensor readings, alerts, or changes in device status.
### Device Scope Options
**Single Device**: Monitor one specific [Device](/docs/tagocore/device) in your system. When that device sends data matching your conditions, the Action triggers.
**Multiple Devices**: Monitor several [Devices](/docs/tagocore/device) at once by using device tags. Any device with matching tags that sends data meeting your conditions will trigger the Action.
### Setting Up Conditions
After choosing your device scope, define the conditions that will trigger your Action:
1. **Choose a variable** to monitor (e.g., "temperature", "humidity", "battery")
2. **Select a condition type**:
- **Less than**: Triggers when the value is below your threshold
- **Greater than**: Triggers when the value exceeds your threshold
- **Equal to**: Triggers when the value exactly matches
- **Different from**: Triggers when the value changes from a specific value
- **Any**: Triggers whenever new data arrives (regardless of value)
- **Between**: Triggers when the value falls within a specific range
3. **Set the comparison value** (e.g., 15 for "temperature > 15")
## Schedule Trigger
This trigger runs Actions based on time schedules rather than device data. Use it for regular maintenance tasks, periodic reports, or time-based automation.
Schedule triggers support flexible timing options:
- **Specific times**: Daily at 9:00 AM, weekly on Mondays
- **Regular intervals**: Every 5 minutes, every hour, every month, every year
Perfect for tasks like daily data backups, weekly reports, or periodic system health checks.
---
## Type
# Action Type
Action Types determine what happens when your Action is triggered. Think of them as the "do this" part of your automation rule - after a trigger condition is met, the Action Type defines the specific task that will be executed.
TagoCore offers several built-in Action Types to handle common automation needs:
## Run Analysis Script
This type executes custom [Analysis](/docs/tagocore/analysis) scripts when your Action triggers. Perfect for complex data processing, calculations, or custom business logic that goes beyond simple data forwarding.
**Common use cases:**
- Process sensor data and calculate averages or trends
- Apply complex filtering or validation rules
- Transform data formats before sending to external systems
- Implement custom alert logic based on multiple variables
## Send HTTP Request
This type sends data to external web services or APIs via HTTP POST requests. When your Action triggers, it automatically sends the device data (in JSON format) to any web endpoint you specify.
**Common use cases:**
- Forward device data to cloud services or databases
- Send notifications to webhook services (like Slack or Discord)
- Integrate with third-party IoT platforms or analytics tools
- Trigger actions in external systems based on your device data
---
## Code
# Analysis Code
You have complete freedom in how you write your Analysis code - use any programming language, framework, or coding style you prefer. This guide covers the essentials of accessing device data and structuring your Analysis code effectively.
## Accessing Device Data
When your Analysis is triggered by an [Action](/docs/tagocore/action) (for example, when a device sends new data), you can access that triggering data through a special environment variable called `T_ANALYSIS_DATA`.
This environment variable contains the device data that triggered your Analysis. The data might be a single data point or multiple data points, depending on what your device sent. Here's what a typical data point looks like:
```json
{
"origin": "61be276db205c10019e6a218",
"variable": "temperature",
"value": 100,
"unit": "F"
}
```
**Key fields explained:**
- `variable`: The type of data (e.g., "temperature", "humidity", "pressure")
- `value`: The actual measurement or reading
- `unit`: The measurement unit (e.g., "°F", "°C", "%")
- `origin`: The unique ID of the device that sent this data
## Writing Your Code
TagoCore supports any programming language through the `Binary Executable path` and `File path` configuration. For details on setting this up, see the [Analysis Overview](/docs/tagocore/analysis) guide.
:::tip[Keep It Simple]
- Write your code directly - no need to wrap it in functions
- Skip `return` statements - just execute your logic
- Focus on processing the data and producing results
:::
## Example: Temperature Monitor
This Node.js example shows how to access device data and log temperature readings to the [Analysis Console](/docs/tagocore/analysis/console.md):
```js
const data = process.env.T_ANALYSIS_DATA;
const temperatureItem = data.find((i) => i.variable === "temperature");
console.log("Temperature is at:", temperatureItem.value);
```
This simple Analysis reads the device data, finds the temperature variable, and outputs the current temperature reading to the console where you can monitor it.
---
## Console
# Analysis Console
The Analysis Console is your window into what your Analysis is doing. It displays all output from your code, including debug messages, results, and any errors that occur during execution. Think of it as a real-time log of your Analysis activity.
The console shows the most recent messages at the top, with each entry timestamped so you know exactly when something happened. There's no limit to how many log entries you can have.
## How to Add Log Messages
TagoCore automatically captures anything your code prints out and displays it in the console. This works with standard output methods in any programming language:
**For normal messages** (information, debug output):
- **Node.js**: Use `console.log("Your message")`
- **Python**: Use `print("Your message")`
- **Any language**: Write to standard output (stdout)
**For error messages** (warnings, errors):
- **Node.js**: Use `console.error("Error message")`
- **Python**: Use `print("Error message", file=sys.stderr)`
- **Any language**: Write to standard error (stderr)
## Understanding Validation Errors
Before your Analysis runs, TagoCore checks that everything is set up correctly:
- Does the executable file exist and can it be run?
- Does your script file exist and can it be accessed?
- Are the file permissions correct?
If any of these checks fail, you'll see a validation error in the console explaining what went wrong. Your Analysis won't run until these issues are fixed.
## Managing Console Output
**Clear the Console**: When the console gets too cluttered, click the button to hide current messages and start fresh.
Note that clearing is temporary - if you navigate away and come back to your Analysis page, all the original log messages will reappear.
**Download Logs**: Save your console output as a text file by clicking the button in the console header.
:::info[Download Behavior]
Only currently visible messages are included in the download. If you've cleared the console, the downloaded file will be empty.
:::
---
## Analysis Overview
# Analysis
An Analysis is a custom script that processes your device data in real-time. Think of it as your personal data processor - when your devices send information to TagoCore, an Analysis can automatically transform that data, calculate new values, detect patterns, or trigger other actions based on what it finds.
## Creating an Analysis
To create a new Analysis, click the button on the **Analyses** page. This opens the creation modal:
Simply give your Analysis a descriptive name (like "Temperature Alert" or "Battery Monitor") to get started.
## Programming Language Support
TagoCore supports any programming language you want to use for your Analysis. This flexibility is made possible through two key settings:
### Binary Executable Path
This tells TagoCore which program should run your code. For example:
- **Node.js**: Point to your `node` executable to run JavaScript
- **Python**: Point to your `python` executable to run Python scripts
- **Rust**: Point to your Rust compiler to run Rust programs
- **Any other language**: Point to the appropriate executable
### File Path
This is your actual script file that contains the Analysis logic:
- **Node.js**: Select your `.js` file
- **Python**: Select your `.py` file
- **Rust**: Select your `.rs` file
- **Compiled programs**: Leave this empty if you're running a standalone executable
### Example Setup
Here's how to set up a Node.js Analysis. If you have Node.js installed (perhaps through [nvm](https://github.com/nvm-sh/nvm)), you would configure it like this:
Behind the scenes, TagoCore runs your Analysis using a command like:
```shell
/Users/tagocore/.nvm/versions/node/v14.17.3/bin/node /Users/tagocore/project-code/index.js
```
## Running Your Analysis
You have three options for executing your Analysis:
1. **Automated**: Set up an [Action](/docs/tagocore/action) to run your Analysis automatically when specific events occur (like when new device data arrives)
2. **Manual**: Click the **Run** button on your Analysis page to test it immediately
3. **API**: Trigger it programmatically by making a POST request to `/analysis/:id/run`
## Writing Analysis Code
You can write your Analysis code in any style or framework you prefer. TagoCore provides complete flexibility in how you structure and implement your data processing logic.
For detailed guidance on coding conventions, patterns, and how to access [Device data](/docs/tagocore/device/data.md) within your Analysis, see the [Analysis Code](/docs/tagocore/analysis/code.md) guide.
---
## Configuration Parameters
Configuration Parameters are settings you can send to your devices to control how they behave. Think of them as remote controls for your IoT hardware - you can change device settings from TagoCore without physically accessing the equipment.
These parameters work as simple key-value pairs, like:
- `update_interval: 30` (how often to send data, in seconds)
- `sensor_threshold: 75` (when to trigger an alert)
- `wifi_ssid: MyNetwork` (which network to connect to)
When your device requests its configuration from TagoCore, it receives these settings and can adjust its behavior accordingly.
## Managing Parameters
You can mark parameters as `read` to organize them and filter which ones are shown when your device retrieves its configuration. This helps keep the configuration clean and only shows relevant settings to your device.
---
## Data Retention
Data Retention helps you manage storage space by automatically deleting old device data after a specified time period. This feature is useful when you don't need to keep historical data forever and want to prevent your database from growing too large with unnecessary information.
You can configure how long to keep data in two ways:
- **Forever**: Data is never automatically deleted (this is the default setting)
- **Time-based**: Automatically delete data after a specific number of days or months
Each device can have its own retention settings, giving you flexibility to keep important data longer while cleaning up less critical information more frequently.
:::tip[How It Works]
TagoCore checks the `created_at` timestamp of each data point to determine when it should be deleted based on your retention settings.
:::
:::info[Deletion Schedule]
Data cleanup runs once daily after midnight UTC time. This means data might remain visible for several hours past its retention period, depending on your local timezone.
:::
---
## Data
# Device Data
Device data represents the information your IoT devices send to TagoCore. Each
piece of information is called a "data point" and follows a specific JSON
structure that TagoCore can understand and process.
When your device sends data to TagoCore, the platform automatically validates
each data point to ensure it follows the correct format. This validation helps
prevent errors and ensures your data is properly stored.
All data points use the same JSON structure as the
[TagoIO Cloud](/docs/api/send-data) platform:
```json
{
"origin": "61be276db205c10019e6a218",
"variable": "temperature",
"value": 100,
"time": "2022-01-01T12:00:00Z",
"unit": "F",
"group": "123456",
"metadata": {}
}
```
**Key fields explained:**
- `variable`: What type of data this is (e.g., "temperature", "humidity")
- `value`: The actual measurement or reading
- `time`: When this data was recorded
- `unit`: The measurement unit (e.g., "°C", "%", "ppm")
For detailed specifications, see the
[API Overview](https://help.tago.io/portal/en/kb/articles/31-api-overview#Security).
## Sending Data
TagoCore uses the same communication standards as
[TagoIO Cloud](https://admin.tago.io), making it easy to migrate or integrate
existing solutions.
To send data from your device to TagoCore:
1. Make an HTTP `POST` request to the `/data` endpoint
2. Include your [Device Token](/docs/tagocore/device#token--serial-number) in
the `token` header for authentication
3. Send your data in the JSON format shown above
For complete parameter details and examples, check the
[API documentation](/docs/api/send-data).
## Retrieving Data
You can access your device data in two ways:
**Programmatic Access**: Send a `GET` request to the `/data` endpoint using your
[Device Token](/docs/tagocore/device#token--serial-number) in the header. This
method gives you complete access to all data points with their full details.
**Visual Overview**: Visit the `Data` tab on your device's page in the TagoCore
interface. This provides a quick overview of all variables and their current
values without needing to make API calls.
---
## Live Inspector
The Live Inspector is a real-time monitoring tool that shows you exactly what data is flowing between TagoCore and your [Devices](/docs/tagocore/device). It's perfect for troubleshooting connection issues, testing [Payload Parser scripts](/docs/tagocore/device/payload-parser.md), and watching your device communication in action.
Think of it as a window into your device's communication - you can see every message sent and received, helping you understand what's happening behind the scenes.
## Starting Live Inspector
To start monitoring your device traffic, click the button on your device page.
Once activated, the Live Inspector will display all communication in real-time. Every data packet, request, and response will appear on screen as it happens, giving you instant visibility into your device's activity.
:::info[Session-Based Monitoring]
Live Inspector only works while you have the device page open in your browser. It automatically stops when you close or navigate away from the page.
:::
## Stopping Live Inspector
To stop monitoring and clear the display, click the button.
---
## Device Overview
# Device
A Device represents the connection between your physical hardware and TagoCore. Think of it as a digital identity for your sensors, controllers, or any IoT equipment. You need to create a device in TagoCore before any hardware can send or receive data.
TagoCore communicates with your devices using standard HTTP requests with JSON data format - the same technology that powers websites, making it easy to integrate with any hardware platform.
## Adding a Device
To add a new device, click the button on the **Devices** page. This will open the device creation form:
Fill in the basic information like device name and description, then click create. TagoCore will automatically generate the security credentials your device needs to communicate.
## Token & Serial Number
Every device gets a unique **Device Token** - this works like a secure password that allows your hardware to send data to TagoCore or receive commands from it. Only devices with a valid token can communicate with your platform.
:::danger[Be careful]
Device tokens should be kept secret and only shared with trusted team members. Treat them like passwords - if compromised, you can regenerate them in the device settings.
:::
## Payload Parser
Payload Parsers help you transform raw data from your devices into useful information. For example, if your device sends hex data like `41BC7E`, a payload parser can convert it into readable values like `{"temperature": 23.5, "humidity": 67}`.
[Learn more about Payload parsers](/docs/tagocore/device/payload-parser.md)
---
## Payload Parser
A Payload Parser transforms raw data from your devices into a format that's easier to work with. For example, it can convert cryptic hex data like `41BC7E` into meaningful values like temperature and battery readings that you can actually understand and use.
## Setting Up a Payload Parser
To use a payload parser with your device:
1. Go to your [Device](/docs/tagocore/device) settings
2. In the **Payload parser** field, select your JavaScript file
3. Save your changes
Each device can have only **one** payload parser, but you can create different parsers for different types of devices.
## Writing Your Parser Code
:::info[JavaScript Required]
Payload parsers must be written in JavaScript - no other programming languages are supported.
:::
Your JavaScript code has access to a special variable called `payload` that contains the raw data sent by your device. You can read from this variable and modify it to transform the data before TagoCore saves it to your [Device Data](/docs/tagocore/device/data.md).
The `payload` variable can contain different types of data depending on what your device sends:
- An **array** of data points
- An **object** with multiple properties
- A simple **string** or **number**
:::tip[Writing Tips]
- Write your code directly - no need to wrap it in a function
- Don't use `return` statements - just modify the `payload` variable
- Keep it simple - avoid `require`, `import`, or `window` statements
:::
Keep your parser code fast and efficient to avoid slowing down data processing for your entire application.
## Example: Temperature Conversion
This example shows how to convert temperature from Fahrenheit to Celsius and add proper units:
```js
// This code:
// 1. Finds the temperature inside the payload sent by the device;
// 2. Converts the value from Fahrenheit to Celsius;
// 3. Adds the unit.
// To test this code, make sure your device sends a POST request
// with a variable named "temperature".
// First, we find the temperature variable inside the payload (array)
const temperatureItem = payload.find((i) => i.variable === "temperature");
if (temperatureItem) {
// If we find the variable, we convert the value
const actualTemperatureInFahrenheit = temperatureItem.value;
const celsius = (5 / 9) * (actualTemperatureInFahrenheit - 32);
// Set the value and unit
temperatureItem.value = celsius;
temperatureItem.unit = "C";
}
```
---
## Home Dashboard
The TagoCore Home Dashboard is your central command center, providing an overview of your IoT platform's health, performance, and resources. This dashboard gives you instant visibility into both your TagoCore application and the underlying system infrastructure.
## Platform Overview
### TagoCore Summary
Monitor your platform's core resources at a glance. This card displays:
- **Devices** - Total number of connected IoT devices
- **Analyses** - Active data processing scripts
- **Actions** - Configured automation rules
- **Connectors** - External system integrations
- **Version** - Current TagoCore release information
This summary helps you quickly assess your platform's scale and ensure all components are properly configured.
### Operating System Information
View essential details about your host environment:
- Operating system name and version
- System architecture and platform type
- OS-specific visual indicators
This information is essential for troubleshooting compatibility issues and understanding your deployment environment.
## Infrastructure Monitoring
### Network Overview
Track your network connectivity and accessibility:
- **Local IP addresses** - Private network interfaces
- **Public IP** - External network visibility (when available)
- **Network interfaces** - All available network connections
Use this information to verify network configuration and troubleshoot connectivity issues.
### System Performance
Monitor real-time hardware utilization with dynamic metrics that adapt to your system:
- **RAM Usage** - Memory consumption with percentage and formatted values
- **CPU Usage** - Processor utilization and model information
- **Disk Storage** - Available storage across all mounted drives with names, locations, and capacity
- **Swap Memory** - Virtual memory usage (Linux/Unix systems)
- **Battery Status** - Power level for mobile/laptop deployments
:::info[Dynamic Metrics]
Performance metrics are system-dependent. Some metrics may not appear based on your operating system, hardware configuration, or system permissions.
:::
## Data Flow Analytics
### Real-time Data Monitoring
Track your IoT data streams with live visualization:
**Data Input Analytics**
- 15-minute sliding window of incoming data
- Real-time updates as devices send information
- Visual trends to identify data flow patterns
**Data Output Analytics**
- 15-minute sliding window of outgoing data
- Updates when devices or external systems retrieve data
- Helps monitor API usage and data consumption
These charts provide immediate feedback on your platform's activity and can help identify unusual patterns or connectivity issues.
## Customization
### Plugin-Enhanced Cards
Extend your dashboard with custom functionality through plugins:
- Create specialized monitoring widgets
- Add business-specific metrics
- Integrate external data sources
- Build custom visualization components
Plugin-based cards appear alongside the standard dashboard components, allowing you to tailor the interface to your specific operational needs.
:::tip[Coming Soon]
Enhanced plugin documentation and examples for custom dashboard cards will be available in future releases.
:::
---
## Logs
# Application Logs
Application Logs provide a real-time view of what's happening inside TagoCore and its plugins. Think of them as a detailed activity feed that helps you monitor system health, troubleshoot issues, and understand how your platform is performing.
## Choosing Log Sources (Channels)
You can filter logs by their source using the Channel selector. This helps you focus on specific parts of your system:
**Application Channel** (default): Shows logs from the core TagoCore system - startup messages, device connections, data processing activities, and system-level events.
**Plugin Channels**: Each installed plugin has its own dedicated channel. Select a plugin's channel to see only logs related to that specific plugin's activities, making it easier to debug plugin-specific issues.
## Understanding Log Types
TagoCore displays two types of log messages to help you quickly identify their importance:
### Error Logs
These appear in **red** and indicate problems that need your attention. Error logs help you identify:
- Connection failures with devices or external services
- Configuration issues that prevent proper operation
- Plugin crashes or malfunctions
- Data processing errors
Some error logs may include technical details (called a stack trace) to help with troubleshooting.
### Verbose Logs
These are **informational messages** that show normal system activity. Verbose logs are more common and indicate healthy operation:
- Successful device data reception
- Plugin startup and shutdown events
- Regular system maintenance activities
- Data processing confirmations
These logs help you confirm that everything is working as expected and provide visibility into your system's daily operations.
---
## Resources Overview
Welcome to TagoCore's Resources section! This guide covers all the essential components and features that make up your TagoCore platform. Whether you're setting up your first device or building complex automation workflows, these resources will help you master every aspect of TagoCore.
## What You'll Find Here
The Resources section is organized into key areas that cover TagoCore's core functionality and system management:
### Core Components
**[Devices](/docs/tagocore/device)**
Learn how to connect, configure, and manage your IoT devices. Devices are the foundation of your IoT platform: they collect data from sensors, receive commands, and act as the bridge between your physical hardware and TagoCore's digital environment.
**[Analyses](/docs/tagocore/analysis)**
Discover how to create and manage custom scripts that process your device data in real-time. Analyses enable you to transform raw sensor data, implement business logic, detect patterns, and trigger automated responses based on your specific requirements.
**[Actions](/docs/tagocore/action)**
Master the automation capabilities built into TagoCore. Actions respond to events in your system by sending notifications, making API calls, running analyses, or integrating with external services. Actions are the "then" part of your "if-then" automation rules.
### System Management
**[Home Dashboard](/docs/tagocore/home)**
Understand your TagoCore dashboard - your central command center that provides system overview, resource summaries, network information, hardware usage metrics, and real-time data flow visualization.
**[Settings](/docs/tagocore/settings)**
Configure and customize your TagoCore instance. Modify port settings, database plugins, queue plugins, and filesystem plugins to tailor TagoCore to your specific environment and requirements.
**[Logs](/docs/tagocore/log)**
Monitor and troubleshoot your TagoCore platform. The logging system helps you track system events, debug issues, monitor performance, and maintain operational visibility across all components.
## Getting Started with Resources
If you're new to TagoCore, we recommend following this learning path:
1. **Start with [Devices](/docs/tagocore/device)** - Connect your first device and understand how data flows into TagoCore
2. **Explore [Analyses](/docs/tagocore/analysis)** - Learn to process and transform your device data
3. **Set up [Actions](/docs/tagocore/action)** - Create automated responses to your data
4. **Customize your [Settings](/docs/tagocore/settings)** - Configure TagoCore for your specific environment
5. **Monitor with [Logs](/docs/tagocore/log)** - Keep track of your system's health and performance
## Advanced Topics
Each resource section includes both beginner-friendly introductions and advanced configuration options. Look for:
- **Overview pages** - High-level concepts and getting started guides
- **Configuration guides** - Detailed setup and customization instructions
- **Code examples** - Practical implementations and sample scripts
- **Troubleshooting tips** - Common issues and their solutions
## Need Help?
Each resource page includes practical examples, step-by-step instructions, and troubleshooting guidance. If you need additional support, check out our community forums or technical documentation for more detailed information.
Ready to dive in? Choose the resource that matches your current needs, or start with [Devices](/docs/tagocore/device) if you're just getting started with TagoCore.
---
## Settings
The Settings page is your control panel for configuring how TagoCore operates. Here you can customize core system behavior, choose which plugins to use, and adjust network settings to match your environment.
Access Settings by clicking the gear icon in the sidebar.
All settings are automatically saved to a configuration file (`tagocore.yml`) in your system's home directory, so your preferences persist between restarts.
## Settings Location
**Settings Folder** shows you exactly where your configuration file is stored on your computer. This read-only field helps you locate the file if you need to back it up or troubleshoot configuration issues.
## Network Configuration
### Port Setting
Controls which network port TagoCore uses to communicate. The default port is `8888`, but you can change this if:
- Another application is already using port 8888
- Your network requires a specific port number
- You want to run multiple TagoCore instances
**What changes when you modify the port:**
- The web address you use to access TagoCore (e.g., `http://localhost:9000`)
- The API endpoint your devices use to send data
:::tip[Restart Required]
TagoCore must restart after changing the port. Your devices will need the updated port number to continue sending data.
:::
:::info[Plugin Independence]
This setting only affects TagoCore itself. Plugins that start their own web servers use separate port configurations.
:::
## Plugin Configuration
### Database Plugin
Determines where TagoCore stores your device data and system information. If you have multiple database plugins installed, you can switch between them here.
**Default behavior:** TagoCore automatically uses the first available database plugin it finds.
**When to change:** You have specific database requirements (like PostgreSQL instead of SQLite) or want to migrate to a different storage system.
:::tip[Restart Required]
TagoCore must restart when switching database plugins.
:::
:::info[Data Migration]
Switching database plugins doesn't automatically transfer your existing data. You'll need to manually migrate data between different database systems.
:::
### Queue Plugin
Manages how TagoCore handles background tasks and message processing. Queues help TagoCore process multiple operations efficiently without blocking other activities.
:::info[Restart Required]
TagoCore must restart when changing queue plugins.
:::
### Filesystem Plugin
Controls how TagoCore accesses and stores files on your system. The default Local Disk plugin works for most installations.
**When to change:** You need specialized file storage (like network drives or cloud storage) through custom plugins.
:::info[Restart Required]
TagoCore must restart when changing filesystem plugins.
:::
---
## TagoDeploy
TagoDeploy is a single-tenant IoT platform built on the TagoIO architecture. Each
deployment runs on its own infrastructure, so your team controls the resources
without sharing them with other tenants.
You switch between projects from the project switcher in the top navigation, and
you add new projects and services from the App Catalog, which you open from
**Apps** in the top navigation.

## Architecture overview
The diagram below shows how a TagoDeploy environment isolates infrastructure for
each deployment. Every deployment gets its own compute, storage, and networking.
TagoDeploy is a dedicated instance of the TagoIO platform. You can deploy it
across 12+ AWS regions, so you can place your infrastructure close to your users
or inside a region that satisfies your data residency rules.
The deployment includes API endpoints and admin interfaces with configurable
URLs, which lets you white-label the platform. It also includes TagoRUN for
custom application deployment and user management.
## Core capabilities
### Dedicated infrastructure
Each TagoDeploy instance runs on its own infrastructure. You get your own compute
instances, storage, and network configuration, with no resources shared across
tenants. Performance stays predictable because nothing else competes for the same
capacity.

### Scalable resource configuration
You can scale resources to match your workload. Configure CPU allocation, instance
counts, and autoscaling rules to handle changing traffic. The infrastructure
scales horizontally and vertically, and you can change resource allocation while
the platform runs.

Resource limits are configurable rather than fixed. You set your own constraints
for:
- API request rates per minute
- Maximum device connections
- Data storage allocation
- Dashboard quantities
- Tag limit

### Data protection and recovery
Automated backups protect your data and configuration. They cover device
registrations, historical data, dashboard configurations, analysis scripts, and
platform settings. You configure the backup schedule and retention policy to meet
your recovery time objectives and compliance requirements.

### Custom domain management
You can set custom domains for both the admin interfaces and TagoRUN portals. This
covers SSL certificate management, DNS configuration, and domain routing. With
custom domains you brand the platform under your own name and keep the same
experience across every entry point.

### Version control and updates
Platform updates ship through controlled versioning, and you decide when to apply
them. Test and validate a new version in a non-production environment before you
roll it out to production. This keeps production stable while you adopt new
features on your own schedule.
## Apps
Apps install from the App Catalog, which you open from **Apps** in the top
navigation, the same place you create a project. The catalog groups items by category: Platform, Middleware,
MQTT, and Others.
### MQTT Broker
You can deploy a dedicated MQTT broker inside your TagoDeploy environment and
control how devices communicate. The broker supports custom authentication,
message routing rules, and protocol configuration for your deployment.

### Middleware Services
Middleware connects external networks and systems to your project. It runs inside
the isolated environment and has full access to platform APIs and data streams.
Available middleware types are AWS IoT, Chirpstack, Everynet, Generic HTTPS,
Loriot, MachineQ, Myriota, Senet, Sigfox, Tektelic, and TTN.

## Technical specifications
- Available across 12+ AWS regions
- Configurable API and admin interface endpoints
- CPU, memory, and instance scaling
- Tenant separation at the infrastructure level
- Automated backups and recovery
- Custom SSL and DNS configuration
- Managed updates and rollbacks
## Implementation considerations
Before you choose TagoDeploy, check it against your requirements for data
isolation, regulatory compliance, and resource scaling. It fits teams that need
dedicated IoT infrastructure and want to control resource allocation and platform
behavior.
For pricing and deployment details, contact the TagoIO team at
[contact@tago.io](mailto:contact@tago.io).
---
## Creating a New Project
You create a project in TagoDeploy by installing the **TagoIO Platform** app from
the App Catalog. The catalog provisions the project's infrastructure and gives you
a single-tenant TagoIO environment.
## Open the App Catalog
You can open the App Catalog from the project switcher or the Apps nav item:
1. Open the project switcher in the top navigation and click **New project** at the
bottom of the dropdown.
2. Open **Apps** in the top navigation to reach the App Catalog and
select the **TagoIO Platform** card.
Both routes open the same install flow.
## Install the TagoIO Platform
Selecting the TagoIO Platform opens a detail dialog with its category, dates, and
overview. Click **Next** to open the install dialog.
In the install dialog, set the following:
- **Installation:** For the TagoIO Platform this is fixed to **New Project**. The
"In a project" option is reserved for services and is shown as coming soon here.
- **Region:** The AWS region the project runs in. Choose the one closest to your
devices or required by your data residency rules.
- **Version:** Defaults to the latest platform version. Pick an older version only
when you need compatibility with a specific stack.
- **Name:** The display name for your project, under **Settings**.
Click **Next** to continue to review, then confirm to install. The project is
provisioned and appears in the project switcher once ready.
## Add services later
If you need a middleware or an MQTT broker to receive data from your devices, you
install them from the same App Catalog after the project exists. See
[Services](/docs/tagodeploy/services/overview.md) for the available service types.
## Help and support
For help or to talk with other users, visit the
[TagoIO Community](https://community.tago.io).
---
## TagoIO Admin Accounts
# Accounts
The **Accounts** page manages developer accounts in your project's TagoIO Admin
console. You find it in the TagoIO & API section, under "Accounts & Profiles"
in the sidebar. From here you can browse
the accounts in the project and the profiles each one owns, and you can create
new accounts.
Each account represents an individual user with access to the TagoIO Admin
environment. There is no limit on the number of accounts you can add to a
project.
To learn more about what TagoIO developers can access through the Admin console,
visit the
[TagoIO Help Center](https://help.tago.io/portal/en/kb/articles/211-editing-accounts-details).
Developer accounts are separate from TagoDeploy accounts. Access to the
TagoDeploy console is managed independently. To grant TagoDeploy access, add
collaborators to your project. See
[Collaborators](/docs/tagodeploy/project/management/collaborators.md).
If a user needs to change their password, they click the "Forgot password"
button on the sign-in page of the TagoIO Admin console and follow the reset
flow.
## Browsing accounts
The page lists each account along with the profiles it owns. Use the search box
to filter accounts by name, and use Expand All or Collapse All to show or hide
the profiles under each account. When a project has no accounts, the page shows
"No accounts found for this project."
## Creating a new account
1. Click **New Account**.
2. In the "New Account" dialog, fill in the fields:
- **Name**: the user's full name.
- **Email**: the account email address.
- **Password**: the initial password.
- **Company**: the company name.
- **Country**: select a country.
3. Click **Confirm** to create the account, or **Cancel** to discard.
The new account is added to the list, and the user can access the project's
TagoIO Admin console.
## Profile sharing
Each new account gets its own profile, which sets the user's access scope and
available resources. To let several users share one profile, use the
[TagoIO Teams](https://help.tago.io/portal/en/kb/articles/106-sharing-your-profile)
feature. Teams keeps shared access under access controls and an audit trail.
---
## Integrations
Use Integrations to connect your project to email and SMS providers for outbound
notifications. These settings control how the platform sends 2FA codes, password
resets, invitations, and other system messages.
The Integrations page lives in the TagoIO & API sidebar under **Integrations**.
The page header reads "Integrations" and the card is titled "Integrations
Management", with the subtitle "Connect email and SMS providers for outbound
notifications." A **Save** action stores your changes.
## What you can configure
- Email delivery
- SMS delivery
Each channel is optional and can be enabled independently. When a channel is
disabled, the platform does not send messages through it.
## Email settings
Email is used for 2FA, password recovery, user invitations, and any workflow in
your apps that sends email through the platform.
Fields:
- **Enable emails**: master toggle for the email channel. Its description reads
"Allow the system to send transactional and notification emails."
- **From address**: the address shown in the From field of outgoing messages.
Use a verified sender for your provider.
- **Provider**: the email vendor. The default is SendGrid. Contact TagoIO support
to enable additional providers for your instance.
- **API key**: the secret credential for the selected provider, stored encrypted.
### Provider notes: SendGrid
- Create an API key with Mail Send permissions in your SendGrid account.
- Verify the From address, or set up a domain authenticated sender, to improve
deliverability.
- Rate limits and message classification are enforced by your SendGrid plan.
### Common errors and fixes
- **401 or 403 from provider**: the API key is invalid or lacks permissions.
Create a new key and update the setting.
- **Emails not delivered**: the From address is not verified or domain
authentication is missing. Verify the sender or domain in the provider.
- **High spam rate**: use a dedicated IP or an authenticated domain, and keep
content transactional.
## SMS settings
SMS is used for 2FA and platform notifications that require text messages.
Fields:
- **Enable SMS**: master toggle for the SMS channel. Its description reads "Allow
the system to send SMS notifications and alerts."
- **From**: the sender identifier used for SMS.
- In the US and Canada, use a 10DLC long code or a toll-free number that is
registered and verified with your provider.
- In other countries, an alphanumeric sender ID may be supported, depending on
the provider.
- **Provider**: the SMS vendor. The default is Twilio. Others may be enabled per
instance.
- **Account SID**: your provider account identifier (Twilio: Account SID).
- **Authorization token**: the secret token for API access (Twilio: Auth Token),
stored encrypted.
### Provider notes: Twilio
- Make sure the From number is purchased in Twilio and enabled for SMS.
- For the US and Canada, complete 10DLC or toll-free verification to avoid
filtering.
- Check country permissions in Twilio's Messaging Geographic Permissions.
### Common errors and fixes
- **400 from provider**: the From number is not SMS-enabled or is not allowed for
the destination country. Enable SMS or buy a supported number or sender ID.
- **Message filtering or no delivery**: carrier registration is incomplete
(10DLC or toll-free). Complete the registration.
- **Rate limiting**: add a messaging service with queueing, or request higher
throughput from the provider.
## Security and operations
- **Secrets management**: API keys and tokens are encrypted at rest. Rotating
keys requires admin access.
- **Key rotation**: update the key on the Integrations page.
- **Audit**: TagoDeploy does not record email or SMS activity. Use your provider's
audit logs to track delivery.
## Recommendations
- Use dedicated sender identities per environment to avoid cross-environment
confusion.
- Avoid personal mailboxes as From addresses. Use a domain your organization
controls.
- For production SMS in the US and Canada, complete 10DLC or toll-free
verification before go-live to prevent blocking.
For help enabling additional providers or configuring private connectivity to
providers, contact the TagoIO team at contact@tago.io.
---
## Resource Limits
# Limits
The **Limits** page configures resource quotas and rate limits for a profile.
You find it in the TagoIO & API section, under "Limits" in the sidebar.
Limits set the maximum amount of a resource a profile can consume. They keep
resource use under control and keep usage fair across tenants on shared
infrastructure, so one profile cannot degrade service for the rest or overwhelm
backend components.
Raising a limit, such as the number of API requests per minute or the maximum
data volume per request, changes what your API service has to handle. Higher
limits may need more resources, either through vertical scaling (more CPU and
memory per instance) or horizontal scaling (more API instances). For guidance on
scaling, see the
[API service](/docs/tagodeploy/project/api.md) page.
Before raising request limits, make sure the profile's Analysis scripts are
optimized. Refactor Analysis code to remove unnecessary API calls and use
parallel queues to reduce strain on the API. Fixing inefficiencies at the script
level can cut peak resource use and reduce the need for extra resources.
## Editing limits
1. Select an **Account** from the combobox.
2. Select a **Profile** from the combobox.
3. Once a profile is chosen, the limit groups appear. Use the "Search limits"
box to find a specific limit, then enter the new value. Keep its effect on
system performance and infrastructure in mind.
4. Click **Save** to apply the changes.
**Important:** After raising limits, monitor API performance and system metrics
to confirm your infrastructure keeps up with the workload. Adjust scaling
settings as needed to maintain availability and responsiveness.
---
## Open Connectors
[Open Connectors](https://github.com/tago-io/decoders) is a library of public
device and network connectors maintained by TagoIO. These
[connectors](https://help.tago.io/portal/en/kb/articles/466-connector-overview)
route external data into your TagoIO instance, so you can add support for new
device models without writing your own decoders.
Open Connectors is the second section on the **System** page, below the Version
Timeline, reached from the TagoIO & API sidebar under **System**. The section is
titled "Open Connectors" and describes itself as "External connectors routing
data into your TagoIO instance." It includes a "Learn more about open connectors"
link.
## Syncing connectors
To bring the latest connectors into your instance, use the **Sync connectors**
action. This pulls the current versions published in the Decoders library into
your project.
Before any connectors are synced, the section shows the empty state "No
connectors synced yet." After a sync, the device and network connectors from the
repository are available in your project.
## Impact on existing devices
Syncing connectors can change existing devices in your project that already use a
connector from the repository. If a device runs older firmware, changes to a
connector's decoding logic or protocol handling could affect its compatibility
or behavior. Before syncing, review the release notes for the updated connectors
and test changes in a staging environment, especially for devices with legacy
firmware or custom configurations.
## Network connectors and middleware
Syncing adds network connectors so device data can be decoded. To start receiving
data through a network connector, you also need the matching middleware enabled.
Middlewares are added from the App Catalog under the **Apps** top-nav. Without the
required middleware, data from the network server does not reach your devices and
the connector has nothing to parse.
## Recommended practices
- Review connector release notes before syncing to assess the impact of updates.
- Test connector and network updates in a development or staging environment,
particularly for devices on older firmware.
- After syncing, confirm the required network middleware is enabled so data flows
from the server to your devices.
- Check device and network behavior after a sync to confirm data still flows.
---
## Version Timeline
The Version Timeline controls which platform version your project runs on. You
can move to a newer release when you are ready, or roll back to an older one. You
decide when to change versions, so the timing fits your own release schedule.
The Version Timeline is one of the two sections on the **System** page, found in
the TagoIO & API sidebar under **System**. The page header reads "System"; the
card is titled "Version Timeline" with the subtitle "Pick the platform version
this project runs." A **Refresh** button reloads the list.
## Reading the timeline
Versions are listed by date and grouped by where they sit relative to the
version you run today:
- **Newer Versions**: releases ahead of your current version. A count badge
shows how many are available, the most recent one is tagged "Latest", and each
row has an **Update** action to move to it.
- **Current Version**: the version your project runs now, tagged "Current".
- **Older Versions**: releases behind your current version, each with a
**Rollback** action to return to it.
Each release comes with notes describing what changed, so you can decide when to
update or roll back.
## Updating and rolling back
Version changes are manual. There is no automated or scheduled mechanism, so an
administrator triggers an **Update** to move forward or a **Rollback** to move
back. Your team decides when version changes happen.
Before changing versions, keep a current backup so you can recover if an upgrade
or rollback surfaces an issue. See the
[backup](/docs/tagodeploy/project/backups.md) documentation.
## Components affected by version changes
Changing versions may affect the following infrastructure components:
- **Admin**: administrative user interface and management tools.
- **TagoRUN**: the runtime environment that executes project workflows.
- **API**: backend APIs that provide data access and integration points.
- **Features (Microservices)**: the microservices that run specific project
functionality.
---
## Project Overview
The **Overview** page is the first page in a project's Management section. It
shows project status, resource usage, spend, and recent activity at a glance.
Open it from the Management sidebar.
## Project status
The project card shows the project avatar, name, and a status badge (for
example, "Deploying"). It also lists:
- **Version**: the platform version the project runs, shown as a date.
- **Created**: the date the project was created.
- **Region**: the region the project runs in, for example `us-east-1`.
## Domains
The Domains panel lists the project's service URLs, such as TagoIO API, TagoIO
Admin, and TagoIO SSE, each in the form
`https://{service}.{id}.tagoio.net`. Every row has a Copy URL action. Use the
"View all domains" link to open the full
[Domains](/docs/tagodeploy/project/domains.md) page.
## Spend
The Month-to-Date Spend card shows the current dollar amount, a comparison
against last month's pace, and a daily spend chart with a month toggle. For the
full cost breakdown, open the
[Bills](/docs/tagodeploy/project/bills/billing.md) page.
## Monitoring
Two charts track resource usage:
- **CPU Utilization - TagoIO**: series for API, Main Database, and In-Memory
Database.
- **CPU & Memory Utilization - MQTT**: CPU and Memory series.
Both charts support time ranges of 1h, 6h, 12h, 1d, 3d, 7d, and 30d. When no
metrics exist yet, the chart shows "No data available".
## Recent activity
The History and Logs sections show recent project activity. Until a project
produces output, the Logs section shows an empty state.
When you have pending changes, a floating "Review & Deploy" button appears so
you can review and apply the queued deployment.
---
## Backups
The **Backups** page lists the snapshots of your project's database. You find it
in the TagoIO & API section, under "Backups" in the sidebar. The page is read-only: backups are created on
a schedule, not by hand, so there is no create action here. When the project has
no snapshots yet, the page shows "Backup snapshots will appear here once they are
created."
Backup management is restricted to project owners. Collaborators cannot access
backup features.
## How backups are scheduled
Backups are created automatically on a daily schedule. The schedule and how long
snapshots are kept are configured on the
[Main Database](/docs/tagodeploy/project/main-database.md) page,
using the "Daily backup schedule" and "Backup retention period" settings.
## What is excluded from backups
The following data is not included in backups:
- **Analysis**: Analysis console logs and code.
- **Files**: uploaded files and file attachments.
## Restore behavior
Restoring a snapshot provisions a new, isolated project instance initialized
from that snapshot. The platform does not overwrite an existing project's
database in place.
**Note:** Standard project creation fees apply when a new project instance is
created from a backup. Restored instances continue to incur costs for as long as
they stay active.
Once a backup project is running, project owners can move data into a target
project by:
- Manually transferring data between the backup project and the target project.
- Running analysis scripts or the
[TagoIO API](/docs/api/sidebar/tagoio-api-intro) to automate the migration.
- Using the [TagoIO CLI](https://github.com/tago-io/tago-cli) for bulk transfer.
### Restore limitations
When data is restored from a backup, the platform does not preserve variable
unique IDs. The system assigns a new unique identifier to each variable during
ingestion, so a variable's ID in the restored project differs from its ID in the
backup.
The platform also does not restore resource tokens. Each time a resource is
created, the system generates a new token for it, so a resource's token after a
restore differs from its token in the backup. Update any integrations or
automation that reference resource tokens after a restore.
---
## Bills
The **Bills** page shows the monthly cost breakdown, charges, and payment status
for a project. Open it from the Management sidebar. Use the "Filter by app" and "Period"
comboboxes at the top to scope what the page shows.
TagoIO uses a post-paid consumption model with a flat monthly platform license.
You are charged for actual resource use, so the bill follows your device
activity and data volumes as they change.
## KPI cards
The cards at the top summarize current spend:
- **Month-to-date**: spend so far this period and the change against last month.
- **Avg / Day**: the daily average, based on the number of days in the period.
- **Last Month**: the previous month's total.
## Bill summary
The Bill Summary shows the period total along with **Status** (the current
cycle), **Payment**, and **Due** (the estimated due date).
## Cost history
The Cost History chart breaks daily spend down by service in a stacked chart.
Each service has a toggle so you can show or hide it. The services are:
- **Network**
- **Files**
- **Platform License**
- **Infrastructure**
- **Main DB**
- **Queue**
- **Analysis**
- **In-Memory DB**
- **API**
- **Apps**
The Platform License is your flat subscription fee for access to the platform.
It stays constant across periods. Infrastructure and Main DB usually show the
most variation, since they track application usage directly. Spikes there often
line up with increased device activity, data ingestion bursts, or new
deployments.
## Daily spend and top services
The Daily Spend chart covers the selected period with a 7d, 14d, or MTD range
toggle. The By Service chart ranks the top services by cost.
## Charges by service
The Charges by Service section lists each service with its amount for the period
and a Total row. Use it to see exactly how much each service contributes to the
bill. If a service is growing faster than your business metrics, it can point to
inefficient resource allocation. For example, high API and Analysis costs
together can indicate room to batch operations or add caching.
## Billing periods
TagoIO bills monthly, with each period running from the first to the last day of
the month. The bill summary is an estimate based on current usage, so the final
amount can change as usage shifts through the month.
---
## Collaborators
Collaborators are the people who can view and change your TagoDeploy project's
resources. Open the page from the Management sidebar. When the project has none, it shows
"Invite team members to collaborate on this project."
Collaborators are different from TagoIO Admin developers, which you manage
separately under
[TagoIO Admin Accounts](/docs/tagodeploy/project/accounts.md).
A collaborator must have their own TagoDeploy account to accept an invitation.
## Permissions and limits
- Can view and edit project resources
- Cannot delete the project
- Cannot manage billing
- Cannot access your Admin console unless they have an Admin account there
Project owners can remove collaborators at any time.
## Inviting collaborators
1. Click **Invite**.
2. In the "Invite Collaborator" dialog, enter the **Email address** of the user.
It must match an existing TagoDeploy account.
3. Select a **Role**:
- **Admin**: full access to the project's resources.
- **Contractor**: scoped access for outside contributors.
4. Click **Send Invite**, or **Cancel** to discard.
Invitation states:
- **Pending**: invitation sent, not yet accepted.
- **Accepted**: the user joined the project.
---
## Deployments
The Deployments page keeps a chronological record of every deployment in your
project, so you can review what changed and when.
Open it from the Management sidebar under **Deployments**. The page header reads
"Deployments" and the card is titled "Manage Deployments", with the subtitle
"Complete history of every deployment in this project."
While a deployment is in progress you can not make further changes to the
project. You keep accessing the previous version until the deployment finishes.
## When do deployments happen?
A deployment occurs whenever you change the project using the TagoDeploy
console. The following changes do not trigger a deployment:
- Editing MQTT broker clients, groups, group rules, or pipelines
- Adding or removing collaborators
- Adding or removing developer accounts
- Adding or editing domains
When a change needs a deployment, you are taken to the deployment view where you
can watch its status.
## Browsing the history
The "History" section lists each deployment, and a refresh button reloads the
list. To find a specific entry, use the controls at the top of the page:
- A search box (placeholder "Search a deployment...") to match by token or
description.
- A status filter that defaults to "All".
- A "Select Date Range" button to narrow the list to a time window.
Each entry records the deployment token or description, the status, and the date
and time of the event. The token is a unique identifier for the deployment. The
status tells you whether it succeeded, is still running, or failed.
Deployment statuses are:
- **Success**: the deployment finished and the resources are running.
- **In Progress**: the deployment is still running.
- **Error**: the deployment failed and did not finish.
When a project has no deployments yet, the page shows "No deployments yet" with
the note that deployment history appears here once deployments are made.
**Note:** if a deployment fails, contact TagoIO support and provide the
deployment ID along with the date and time of the deployment.
---
## Logs(Deployments)
The Logs page shows runtime output from the services running in your project.
Open it from the Management sidebar under **Logs**. The page header reads "Logs"
and the card is titled "View Logs", with the subtitle "Runtime output from
services running in this project."
## Filtering and following logs
Use the controls at the top of the page to focus on what you need:
- A search box (placeholder "Search logs...") to match entries by text.
- A "Type" filter to limit the output to a specific log type.
- A "Date" filter to narrow logs to a time window.
- A "Refresh" button to reload the current view.
- A "Live" toggle to stream new entries as they arrive.
When there is no output to show, the page displays "No logs yet" with the note
that logs from your project will appear here.
## Change management behavior
Any change to a service triggers a new deployment. Changes apply only after the
deployment finishes and the previous deployment has been removed. This avoids
partial updates.
If the most recent deployment fails, a warning appears at the top of every page
in the TagoDeploy console so you can fix the issue.
---
## Domain Management
# Domains
The **Domains** page manages the domains that route to your project's services,
including TagoIO Admin, TagoRUN applications, the API, middlewares, and the MQTT
broker. Open it from the Management sidebar.
The page splits domains into two sections: Custom Domains and Default Domains.
**Important:** Only project owners can see and configure middleware domains.
Collaborators cannot view or change them.
## Custom Domains
Custom domains are external domains that let you use your own branding for the
project's services. When the project has none, the section shows "No custom
domains yet". Use **New Domain** to add one.
## Default Domains
Default domains are the standard domains assigned to your project for immediate
access. They are listed in a sortable table with these columns:
- **URL**: the full address used to reach the service.
- **Application**: the service the domain serves, such as TagoIO API, TagoIO
Admin, TagoIO SSE, a middleware, a container, or the MQTT broker.
- **Endpoint**: the endpoint type, for example "API". The MQTT broker row uses
an `mqtts://` URL.
## Adding a custom domain
Click **New Domain** to open the wizard. The wizard steps are **Form**,
**Review**, and **Records**.
1. On the **Form** step, fill in the fields:
- **Subdomain**: the prefix to use, for example `api`, `portal`, or `iot`.
- **Domain**: your primary domain, for example `mycompany.com`.
- **Target**: the service the domain should point to.
Click **Next**.
2. On the **Review** step, confirm the details.
3. On the **Records** step, copy the DNS records shown and add them to your DNS
provider's settings.
After DNS propagation, the custom domain becomes available for the target
service.
---
## Features
Features in TagoIO are native platform functionalities you can enable or disable
for your project. They add services such as data processing, integrations, and
notification handling. You can manage most features, while some are always on
because the platform needs them to run.
The Features page is the entry point of the TagoIO & API section. It is reached
from the "TagoIO & API" breadcrumb tab and from the **Features** item in that
sidebar. The page header reads "Features".
:::note
On the survey project the Features page rendered without any field content, so
the feature-toggle controls could not be captured directly. The descriptions
below cover the intended behavior. The exact controls, including any
simplified and advanced view switch suggested by the `?view=simplified` URL,
should be confirmed against a project that has a populated feature set.
:::
## Instance configuration
When the advanced configuration is available, each feature can be given a
minimum and maximum number of instances. Each instance adds capacity for
concurrent requests, which helps when traffic is high. Within the limits you
set, TagoIO scales the feature automatically.
Every enabled feature consumes computational resources from your allocated
infrastructure. You can enable or disable most features on their own, which lets
you trade off performance against cost.
## Available features
TagoIO groups features into those that are always on and those you can turn on
as needed.
Obligatory features:
- **Secrets**: manages sensitive information such as credentials for analyses and
integrations using
[TagoIO Secrets](https://help.tago.io/portal/en/kb/articles/secrets).
- **Payload Parser**: processes and parses incoming data from devices,
[connectors](https://help.tago.io/portal/en/kb/articles/466-connector-overview),
and other sources, so you can
[parse payloads](https://help.tago.io/portal/en/kb/articles/147-payload-parser)
as they arrive.
- **Action Handler**: coordinates and executes internal platform
[actions](https://help.tago.io/portal/en/kb/articles/30-actions) in response to
system events and user-defined triggers.
- **Action - Post Data to Endpoint**: sends data to external endpoints when
specific actions are triggered.
- **Action - Notification**: delivers notifications as part of automated
workflows.
- **Action - TagoRUN Notifications**: sends
[targeted notifications](https://help.tago.io/portal/en/kb/articles/223-notifications-for-users)
to TagoRUN users.
Optional features:
- **Action - SMS (Twilio)**: sends SMS messages using
[Twilio](https://www.twilio.com/).
- **Action - Email (Sendgrid)**: sends emails via
[Sendgrid](https://sendgrid.com/).
- **Action - Email (SMTP)**: sends emails using standard SMTP servers.
- **Action - Queue (AWS SQS)**: sends messages to
[AWS SQS](https://docs.aws.amazon.com/sqs/) queues for asynchronous processing.
- **Action - Whatsapp (Twilio)**: sends WhatsApp messages using Twilio.
- **Action - SMS (AWS SNS)**: sends SMS messages using
[AWS Simple Notification Service](https://docs.aws.amazon.com/sns/).
- **Action - SES (AWS SES)**: sends emails using
[AWS Simple Email Service](https://docs.aws.amazon.com/ses/).
The optional features map to optional action types. For more on actions, see the
[Defining Actions](https://help.tago.io/portal/en/kb/articles/122-defining-actions#Push_Notification_to_myself)
article.
## Resource considerations
Keep enough computational resources available for everything you enable. Watch
how your combination of features affects performance, and review resource
consumption regularly to control costs as your project grows.
---
## Project Tokens
Project tokens give scripts and services programmatic access to a TagoDeploy
project. Open the page from the Management sidebar. It lists the tokens that exist for the
project and lets you create new ones or revoke the ones you no longer need. When
the project has none, it shows "Create an API token to enable programmatic
access to this project."
## How tokens work
A token authenticates API requests to the project without a browser session. A
token has full access to the project and can be used with the API.
The list shows each token by its last four characters, who created it, and when.
The full secret is never shown after creation, so the last four characters are
how you tell tokens apart. A project can hold up to 10 tokens. Once you reach
that limit, the New Token button is disabled and the page shows a warning.
Delete a token to free a slot.
## Creating a token
Click **New Token** and confirm in the dialog. The dialog has no input fields:
the token is created with full access to the project for use with the API. The
secret is shown once, right after creation, with a Copy button. Copy it and
store it somewhere safe before you close the dialog, because it cannot be shown
again. If you lose it, delete the token and create a new one.
## Revoking a token
Use the Delete action on a token row, then confirm in the dialog, which
identifies the token by its last four characters. Revoking takes effect
immediately, and any request that uses the token after that fails. This cannot
be undone, so revoke a token whenever its secret may have leaked or it is no
longer in use.
---
## Analysis Runtime
The Analysis Runtime page sets the memory available to the Node.js and Python
scripts that run your
[analyses](https://help.tago.io/portal/en/kb/articles/29-analysis-overview).
Analyses are billed mainly on execution duration and the memory used during a
run, so the memory you set here affects both performance and cost.
Open the page from the **TagoIO & API** area, under **SERVICES** in the sidebar.
## Memory Configuration
The page has two configuration groups, one per runtime:
- **Node.js Runtime**: memory allocation for Node.js analysis scripts. The
**Memory (MB)** field defaults to 512.
- **Python Runtime**: memory allocation for Python analysis scripts. The
**Memory (MB)** field defaults to 512.
The default of 512 MB is enough for standard operations that do not run
intensive data processing. Raise it when an analysis works over large datasets
or performs heavy in-memory calculations. Higher memory increases cost, so set
only what the script needs.
Click **Save** to apply changes.
## Monitoring
The **Monitoring** section reports runtime activity. A range toggle switches the
window across 1h, 6h, 12h, 1d, 3d, 7d, and 30d. When there is no data for the
selected window, the chart shows "No data available".
### Invocations
Counts the analysis executions started in the selected window (COUNT). The trend
shows your usage pattern, which helps when you tune scheduling or plan for
scaling.
### Duration
Reports how long analyses take to run, in milliseconds (MS). Use it to find
slow scripts and the bottlenecks worth refactoring.
## Cost and Performance
Billing follows execution duration and memory use, so:
- Review the Invocations and Duration charts to find inefficiencies or
unexpected usage.
- Set memory per runtime to the actual needs of your scripts.
- Refactor scripts to cut execution time and avoid unnecessary data retrieval.
---
## API Service
The [TagoIO API](https://help.tago.io/portal/en/kb/articles/31-api-overview)
service is how services, applications, and devices reach your project's data and
features. Every operation depends on it, so the rest of the platform needs an
active API instance to work.
This page sets the machine size and autoscaling for incoming API requests. API
usage is tied to your billing costs, since compute, memory, and network use
drive your charges. See the
[Bills](/docs/tagodeploy/project/bills/billing.md) page for how that adds
up.
Open the page from the **TagoIO & API** area, under **SERVICES** in the sidebar.
## Instance Settings
The **Instance settings** section configures machine type, scaling, and cooldown
parameters:
- **Machine**: the machine type for each instance. Default is 1 vCPU / 2GB RAM.
- **Minimum instances**: the lowest number of instances kept running. Default
is 1.
- **Maximum instances**: the highest number autoscaling can reach. Default is 1.
- **Scale on CPU utilization**: the CPU percentage that triggers scaling.
Default is 60.
- **Cooldown for scaling up**: seconds to wait before adding instances again.
Default is 200.
- **Cooldown for scaling down**: seconds to wait before removing instances.
Default is 300.
Click **Save** to apply changes.
## Scaling Strategies
As the project grows with more devices, users, and automated workflows, the API
service has to handle more load. You scale it by adjusting the vertical config, the horizontal config, or both:
- **Vertical scaling (Machine)**: raise the CPU and RAM of each instance with a
larger machine type. This helps with intensive data processing or large
retrievals, such as dashboard queries returning more than 10,000 records in a
single request. For machine types beyond the listed options, contact TagoIO
support.
- **Horizontal scaling (instances)**: raise the minimum and maximum instance
counts so requests spread across more instances. This is the better fit for
high concurrency and traffic bursts, and it improves fault tolerance.
Autoscaling adds and removes instances between the minimum and maximum based on
the CPU threshold and the two cooldown windows.
## Monitoring
The **Monitoring** section has a range toggle across 1h, 6h, 12h, 1d, 3d, 7d,
and 30d. Charts show "No data available" until there is data for the window.
- **CPU Utilization (%)**: how intensively the API uses processing resources.
Use it to spot peak periods and the operations driving the highest load.
- **Memory Utilization (%)**: how much data the API holds in memory while
serving requests. Use it to catch requests or concurrent operations that push
memory high, then tune data handling.
## Billing Considerations
The API runs on AWS, and resource use drives billing, so review the instance
settings and the monitoring charts regularly. Tighter scaling settings cut
wasted cost while keeping the project responsive.
For billing models, usage analysis, and cost details, see the
[Bills](/docs/tagodeploy/project/bills/billing.md) page.
---
## In-Memory Database
The in-memory database aggregates in-transit memory across active API instances.
It handles real-time data processing, high-frequency caching, and message
queuing between APIs and microservices. Its distributed queues hold and process
data through failures or traffic spikes, so delivery stays low-latency and the
service stays available.
This page sets the cache machine size and read replicas. Open it from the
**TagoIO & API** area, under **SERVICES** in the sidebar.
## Cache Settings
The **Cache settings** section configures machine type and read replicas:
- **Machine**: the machine type for the cache. Default is 2 vCPU / 1GB RAM.
- **Additional reader replicas**: read-only instances added to the cluster.
Default is 0.
Click **Save** to apply changes.
## Key Use Cases
### Real-Time Data Transmission
The in-memory database handles real-time data delivery. For example, it updates
dashboards as soon as new information arrives, so users see current data without
delay.
### Caching System
TagoIO caches high-frequency requests through the in-memory database. Common
cases include token authentication and other repeated queries, where cached
responses cut latency and backend load.
## Scaling the In-Memory Database
You scale the in-memory database by adjusting the vertical config, the horizontal config, or both, for performance and reliability:
- **Vertical scaling (Machine)**: move to a machine type with more CPU and
memory. This helps with data ingestion peaks or dashboards that display large
volumes of real-time data.
- **Horizontal scaling (reader replicas)**: add read-only instances to the
cluster. This spreads read operations, eases load on the primary instance, and
improves performance for read-heavy access patterns.
## Recommended Production Configuration
For most production environments, this baseline is a good starting point:
- **Machine**: 2 vCPU / 4GB RAM
- **Additional reader replicas**: 1
## Monitoring
The **Monitoring** section has a range toggle across 1h, 6h, 12h, 1d, 3d, 7d,
and 30d. Charts show "No data available" until there is data for the window. The
following metrics are available:
- **CPU Utilization (%)**: how intensively the cache uses processing resources.
- **Freeable Memory (BYTES)**: the memory currently available to the cache
instance.
- **Cache Hits (COUNT)**: successful cache retrievals over time, a read on how
well the cache serves repeated requests and reduces backend load.
- **Network Inbound (B/S)** and **Network Outbound (B/S)**: the volume of data,
in bytes per second, transmitted to and from the in-memory database. Use these
to find bottlenecks and check the instance is sized for your throughput.
---
## Main Database
The Main Database stores and maintains all your TagoIO data, including profile
configurations and device data. If it becomes unavailable, the API cannot
retrieve stored data, which affects every project feature.
This page configures the primary database. Open it from the **TagoIO & API**
area, under **SERVICES** in the sidebar.
## Database Settings
The **Database settings** section configures machine type, read replicas, and
backup parameters:
- **Machine**: the machine type for the database. Default is 2 vCPU / 4GB RAM.
- **Additional reader instances**: read-only instances added alongside the
writer. Default is 0.
- **Backup retention period (days)**: how many days each backup is kept before
deletion. Default is 7.
- **Daily backup schedule**: the time of day, in UTC, when the daily backup
runs. Default is 04:30.
Click **Save** to apply changes.
## Backups
Backups run automatically once a day at the scheduled time and are kept for the
retention period, then deleted to manage storage. The schedule and retention are
set here; existing snapshots are listed on the
[Backups](/docs/tagodeploy/project/backups.md) page.
## Scaling Your Database
As demand grows, you may need to scale the database to handle more read and
write operations:
- **Vertical scaling (Machine)**: move to a machine type with more CPU and RAM.
This helps with large data submissions or high write and update activity.
- **Horizontal scaling (reader instances)**: add read-only instances to spread
read-heavy operations and improve availability.
## Monitoring
The **Monitoring** section has a range toggle across 1h, 6h, 12h, 1d, 3d, 7d,
and 30d. Charts show "No data available" until there is data for the window. The
following metrics are available:
**CPU Utilization (%)**: Reads and writes consume CPU to process the data and
run the database pipeline operations around them, such as validation,
transaction management, and integrity checks. CPU can spike during heavy
queries, like aggregates that calculate daily averages over a full month, and
during updates that touch large entity or device datasets.
**Freeable Memory (BYTES)**: the memory currently available to the database
instance. By default the database caches frequently used queries, using around
90% of freeable memory, and evicts older entries to make room for newer ones.
Consistently consuming all available memory usually means request volume or
complexity exceeds what the cache can manage, and performance may degrade.
**Database Connections (COUNT)**: the number of active connections to the
database, including connections from API instances and other services.
**Read IOPS** and **Write IOPS**: the number of input and output operations per
second. When investigating CPU or memory spikes, lining these up against IOPS at
the same timestamps helps tell whether disk activity is driving the issue.
**Network Throughput (B/S)**: the volume of data, in bytes per second,
transmitted to and from the database.
## Recommended Production Configuration
For environments supporting up to **10,000 devices**, this baseline is a good
starting point:
- **Machine**: 2 vCPU / 4GB RAM
- **Additional reader instances**: 1
- **Backup retention period**: 7 days
---
## Docker Image Configuration
The **Docker Image** and **Registry Authentication** sections are at the top of
the container **Docker Settings** page. They set which image the container pulls
and how it signs in to a private registry. Edit the fields and use **Save** to
stage the change, which is applied through the project deploy flow.
## Docker Image
This section sets the image the container runs:
- **Image Name**: the name of the Docker image to use, including the registry
and repository path when it is not on Docker Hub (for example
`nginx`, `docker.io/library/postgres`, `ghcr.io/company/app`).
- **Image Tag**: the tag of the Docker image to use (for example `latest`,
`v1.2.0`, `main`).
Pin a specific tag instead of `latest` for production so deploys stay
reproducible.
## Registry Authentication
Use this section to provide credentials for private Docker registries. Public
images from Docker Hub do not need credentials.
- **Registry Username**: the username for the Docker registry.
- **Registry Access Password**: the access password or token for the Docker
registry. The field has a reveal toggle and is treated as a secret, so it is
only sent when you edit it.
### Docker Hub
For a private Docker Hub repository, create a personal access token rather than
your account password:
1. Sign in to [Docker Hub](https://hub.docker.com).
2. Open **Account Settings** then **Security**.
3. Click **New Access Token**, give it a description, and pick **Read** access
for pulling images.
4. Generate the token, then enter your Docker Hub username as the Registry
Username and the token as the Registry Access Password.
### GitHub Container Registry
For images on `ghcr.io`, create a GitHub personal access token:
1. Open [GitHub token settings](https://github.com/settings/tokens).
2. Generate a classic token with `read:packages` (and `repo` if the image lives
in a private repository).
3. Enter your GitHub username as the Registry Username and the token as the
Registry Access Password.
### Other registries
The same two fields work for AWS ECR, Google GCR, Azure ACR, and other private
registries. Use the registry's username or access key as the Registry Username
and its password, token, or secret key as the Registry Access Password.
---
## Container Environment Variables
# Environment Variables
The **Environment Variables** section on the container **Docker Settings** page
holds the key/value pairs passed to your container at runtime. Use them for
anything the container reads from its environment, like connection strings,
feature flags, or API keys.
## Managing variables
The section starts empty, with "No environment variables configured". Use
**Add variable** to add a key/value row, and repeat for each variable. Edit the
values and use **Save** to stage the change, which is applied through the
project deploy flow.
## Recommended practices
Name each variable so its purpose is obvious, and prefix related ones so they
group together (for example `DB_`, `SMTP_`). Keep credentials out of the image:
pass them in here instead of baking them into the build. In your app, fall back
to sensible defaults for optional variables and check the critical ones at
startup.
---
## Container Instances
The Instances page controls how a container application scales. You pick the
machine size, set the instance count range and the autoscaling thresholds, then
watch CPU and memory charts for the running service.
## Instance settings
The settings form holds the scaling configuration:
- **Machine** is the vCPU and RAM tier each instance runs on. It defaults to
1 vCPU / 2GB RAM. Your project plan limits the list of tiers, so larger sizes
appear only when your plan allows them.
- **Minimum Instances** and **Maximum Instances** set the range the service can
scale between. They default to 1 and 2. The minimum cannot be higher than the
maximum, and your plan caps the highest selectable count.
- **Scale on CPU Utilization** is the CPU percentage that triggers autoscaling.
It defaults to 60.
- **Cooldown for Scaling Up** and **Cooldown for Scaling Down** are the wait
periods in seconds before another scaling step. They default to 200 and 300.
Edit the values and use **Save** to stage the change. Only the fields you
changed are sent. Save is disabled until you make a change, and staged changes
are applied through the project deploy flow.
## Monitoring
The Monitoring section charts CPU Utilization and Memory Utilization for the
service as percentages over time. The time range control switches the window
between 1h, 6h, 12h, 1d, 3d, 7d, and 30d. Compare these charts against your
Scale on CPU Utilization threshold to check whether the instance range and
machine size fit the load.
---
## Container Network Configuration
# Network Configuration
The **Network** section on the container **Docker Settings** page sets the
network protocol and port mappings for the load balancer that fronts your
container. Edit the fields and use **Save** to stage the change, which is
applied through the project deploy flow.
## Network protocol
**Network protocol** is a dropdown that defaults to **TCP**. It sets the
protocol the load balancer uses to reach the container.
## Port mappings
**Port mappings** route external traffic to the port your container listens on.
The list starts empty, with "No port mappings configured". Use **Add port** to
add a mapping row, and repeat for each port your container exposes.
Only expose the ports that need external access. Check that your application
actually listens on the port you map. If it does not, the load balancer has
nothing to connect to.
---
## Custom Docker
The **Custom Docker** app runs your own container image inside your project. You
point it at an image, set how it scales, and decide how it gets built and run.
The service gets its own subdomain under your project.
The sidebar has these pages:
- **Overview**: service name, URL, and product details (this page).
- **Instances**: machine size and autoscaling range. See
[Container Instances](/docs/tagodeploy/project/container/instances.md).
- **Settings**: image, registry credentials, environment variables, network,
and runtime, grouped on one **Docker Settings** page. See the sections below.
## Overview page
The Overview page shows the service details and a few controls.
### Service information
- **Display Name**: the name shown for this service in your project.
- **App**: the product type, shown as "Custom Docker". Read-only.
- **Service URL**: the address the container is served on,
`https://{serviceId}.{projectId}.tagoio.net`. Read-only, with a copy button.
Edit the Display Name and use **Save** to stage the change. Save is disabled
until you make a change.
### Danger Zone
A collapsible section with irreversible actions for this service.
## Docker Settings
The **Docker Settings** page (the Settings sidebar entry) is where you build and
run the container. It is one page with collapsible sections, in this order:
- **Docker Image** and **Registry Authentication**: the image to pull and
credentials for private registries. See
[Docker Image Configuration](/docs/tagodeploy/project/container/docker-image.md).
- **Environment Variables**: key/value pairs passed to the container. See
[Environment Variables](/docs/tagodeploy/project/container/environment-variables.md).
- **Network**: protocol and port mappings for the load balancer. See
[Network Configuration](/docs/tagodeploy/project/container/network.md).
- **Runtime**: working directory and command overrides. See
[Runtime Configuration](/docs/tagodeploy/project/container/runtime.md).
Changes on the Docker Settings page are staged with **Save** and applied through
the project deploy flow.
> [!NOTE]
>
> The container service has no Logs page of its own. Runtime output appears on
> the project's **Logs** page under Management.
---
## Container Runtime
# Runtime Configuration
The **Runtime** section on the container **Docker Settings** page overrides the
default work directory and command from the Docker image. Leave a field empty to
keep the image default. Edit the fields and use **Save** to stage the change,
which is applied through the project deploy flow.
## Work Directory
**Work Directory** is the directory the container runs in. Leave it empty to use
the working directory defined in the image, or set an absolute path (starting
with `/`) to override it. Common values are `/app` and `/usr/src/app`.
## Command
**Command** is what runs when the container starts. Leave it empty to fall back
to the `CMD` or `ENTRYPOINT` from the image, or type your own command with any
arguments, such as `node server.js`, `python app.py`, or `java -jar app.jar`.
Make sure the command runs in the foreground so the container does not exit
right after it starts.
---
## Services
Services add capabilities to your projects beyond the base TagoIO Platform. You
add them from the **App Catalog**, which you open from **Apps** in the top
navigation, the same place you create a project.
## Overview of services
Services are modular components that you install into a project. The catalog
groups items by category: Platform, Middleware, MQTT, and Others. The service
types available today are:
- **Middleware**: Connect your project with external networks, such as LoRaWAN
LNS providers, to move data between TagoIO and those systems.
- **MQTT Broker**: Deploy an MQTT broker within your project so devices can
connect and communicate over the MQTT protocol.
## Service categories
### Middleware
Middleware sits between your project and external networks and passes data in both
directions. The available middleware types are AWS IoT, Chirpstack, Everynet,
Generic HTTPS, Loriot, MachineQ, Myriota, Senet, Sigfox, Tektelic, and TTN.
### MQTT Broker
The MQTT Broker service provisions an MQTT broker instance in your project. It
connects IoT devices and handles data transmission over the MQTT protocol, for
both ingestion and distribution.
## Installing a service
Open **Apps** in the top navigation to reach the App Catalog. Use the search box
or the category filters on the left to find a service, then click its card to
open the detail dialog and review the overview and features. Click **Next** to
open the install dialog.
The install dialog asks where the service should run:
- **In a project**: Add the service to an existing project. You pick the target
**Project**, then the **Region** and **Version**.
- **New Project**: Provision the service alongside a new project. You pick the
**Region** and **Version** only.
Under **Settings** you set the service **Name** and any required credentials.
Confirm to install. The service provisions networking, compute, and a default
autoscaling policy, then shows up as a resource tab in the project.
---
## Network Middlewares
Middleware adds managed endpoints that connect external networks and systems to
TagoIO. You install it from the App Catalog, which you open from **Apps** in the
top navigation. Inside a project, a middleware's app label reads
`Middleware {Type}`, for example "Middleware Chirpstack".
> The catalog changes over time, so this guide covers how to deploy and operate
> any middleware. Available types today are AWS IoT, Chirpstack, Everynet, Generic
> HTTPS, Loriot, MachineQ, Myriota, Senet, Sigfox, Tektelic, and TTN.
An active middleware instance runs inside your single-tenant environment and uses
your project's resources. You can scale instances, attach custom domains, and
point external networks to the middleware's endpoint.
## What is a middleware?
A middleware bridges data between an external network or platform and your
TagoDeploy project. It receives uplinks and forwards data to TagoIO, and most
types also handle downlinks or callbacks. Each middleware runs as an isolated
service with its own configuration, token, and scaling policy.
## Installing a middleware
You install a middleware from the App Catalog. Open **Apps** in the top
navigation, find the middleware type, and click its card to open the detail
dialog. Click **Next** to open the install dialog.
The install dialog first asks where the middleware should run:
- **In a project:** Add it to an existing project. Action button "Add to
Project".
- **New Project:** Provision it alongside a new project. Action button "Review
and Install".
You then set these fields:
- **Project:** The target project (only when installing into an existing
project).
- **Region:** The AWS region the service runs in.
- **Version:** Defaults to the latest unless you need compatibility with an older
stack.
- **Name:** Display name for the service instance, under Settings.
- **Network Token:** The token the middleware uses to write and read in your
project.
- **TagoIO API URL:** Pre-filled with your project's API endpoint.
Confirm to deploy. The service provisions networking, compute, and a default
autoscaling policy.
### Domains
You can attach a custom DNS domain to a middleware after you deploy it. See Domain
Registration at
[TagoDeploy Domains Management](/docs/tagodeploy/project/domains.md).
## Using the middleware
After it deploys, point your external network or platform at the middleware's
public endpoint. The endpoint URL is on the middleware's Overview page in the
project. Follow the integration steps for your network in the TagoIO
documentation.
The middleware authenticates with the Network Token you selected at install time.
That token scopes which devices the middleware can write and read in your project.
## Billing considerations
Middleware instances use compute, memory, and network resources in your TagoDeploy
environment, so they add to project costs. Size your machine tier and autoscaling
limits to the load, and check the utilization graphs now and then so you don't
over-provision.
---
## Managing a Deployed Middleware
After you install a middleware from the App Catalog, it shows up as a resource tab
in the project breadcrumb, labeled with its display name. Open that tab to view
and change settings. The middleware sub-navigation has Overview, Instances, and
Settings. Most updates apply without downtime, though scaling actions can briefly
recycle instances.
### Settings
- Name: Change the display name shown in the project.
- Network Token: Locked by default to prevent accidental changes. Unlock to
switch the token if you must move the middleware to a different network
context.
- TagoIO API URL: Your project's internal API endpoint. Normally you don't
change this.
### Instance Settings and Scaling
Middleware services support vertical and horizontal scaling. Choose a
configuration that matches your traffic pattern: message rate, payload size, and
downlink volume.
Recommended baseline for typical production projects up to ~10,000 devices
connected through a single middleware:
- **Machine:** 1 vCPU / 2 GB RAM
- **Minimum instances:** 1 to 2 (use 2 for higher availability)
- **Maximum instances:** 3 to 5
- **Scale on CPU utilization:** 60%
- **Cooldown for scaling up:** 200 seconds
- **Cooldown for scaling down:** 300 seconds
When to scale vertically:
- Payload processing or transformations push memory usage near limits.
- Single requests involve large batches or heavy encryption/decryption.
When to scale horizontally:
- You see spikes in concurrent uplinks.
- You need better fault tolerance and rolling updates with no single point of
failure.
You can adjust thresholds based on load testing. If CPU stays above the
threshold for several minutes during normal traffic, increase max instances or
choose a larger machine. If memory pressure is the main limiter, move up one
machine tier before increasing instance count.
### Monitoring
The service detail page shows time-series graphs for CPU and memory utilization.
- **CPU Utilization:** Tracks processing demand from inbound messages and downlink
handling. Sustained peaks mean the middleware is saturated or bursty traffic
exceeds current capacity.
- **Memory Utilization:** Spikes often line up with buffering, message
aggregation, or large payload parsing. Watch for plateaus near the machine
limit.
Line these metrics up with message timestamps from your external network to see
whether bursts or steady load is the cause.
### Updating and Versioning
Platform and service versions are managed from the project's System page, under
Version Timeline. Newer versions offer an Update action, and older versions offer
Rollback. For non-breaking updates, rolling replacement keeps the endpoint
available. For major changes, validate in a non-production project first.
## Troubleshooting
- **401/403 from TagoIO API:** The Network Token is missing required scopes,
expired, or bound to the wrong network. Recreate a Network Token for the
correct network and update the service.
- **High CPU with low throughput:** Look for repeated retries from the external
network due to misconfigured callback URLs or signatures. Confirm the endpoint
and credentials in the external platform.
- **Downlink delays:** Increase max instances or lower the CPU threshold so the
service scales sooner during bursts.
---
## Monitoring Actions
# Actions
An action is how monitoring reaches out when an alert fires. An alert on its own
only detects a condition; the action turns that into an email, a text message,
or a request to another system. An action is reusable: define a response once
and point any number of alerts at it. This page lists your actions and is where
you create and edit them.
Existing actions are shown in a table, one row per action, with its name, type,
and destination. The destination column shows where each action delivers, the
webhook URL or the recipient address or number, so you can confirm at a glance
that an action points where you expect without opening it.
An action's type is chosen when you create it. To deliver the same alert a
different way, such as by SMS instead of email, create a second action and add
it alongside the first on the alert.
## Action types
Every action is one of three types, chosen when you create it. They differ in
what they can reach and what you need to set up.
### Email
Best for routing an alert to a person or a shared team inbox. Email goes through
SendGrid, so you need a SendGrid account and its API key. The **From Address**
must be a sender verified in SendGrid, or the message will not deliver, and **To
Addresses** takes one or more recipients. The **Subject** and **Body** accept
the variables described below.
### Webhook
The most flexible type, and the one to reach for when the response is automation
rather than a person. It sends an HTTP request to any **URL**, with the
**Method** and optional **Authentication** (Basic, Bearer token, or None) you
choose, and a **Body** you define. A **Body format** toggle selects JSON or plain
text. The body accepts the same variables as the other types, so the receiving
system gets the full context of what fired:
```json
{
"service": "{serviceName}",
"metric": "{metric}",
"value": "{metricValue}",
"threshold": "{thresholdName}",
"severity": "{severity}",
"time": "{timestamp}"
}
```
Because the target is any URL, the webhook is also how you drive automation.
Point it at a TagoIO device or endpoint, and the incoming data can run an
Analysis that reacts on its own, such as disabling devices or generating a
report.
### SMS
For alerts that need to reach someone away from a screen, such as your highest
thresholds. Text messages go through Twilio, so you need a Twilio account (its
**Account SID** and **Auth Token**) and a Twilio **From Number** to send from.
The recipients in **To Numbers** receive the **Message Template** with its
variables filled in.
## Message content
The subject, body, and message fields accept variables that the monitor fills in
when an alert fires, so each notification arrives with the context of what
happened:
- `{serviceName}`: the service that triggered the alert.
- `{serviceType}`: the service type.
- `{metric}`: the metric that crossed the threshold, with its threshold name.
- `{metricName}`: the raw metric name on its own.
- `{metricValue}`: the metric's value at trigger time.
- `{thresholdName}`: the threshold that fired.
- `{severity}`: the threshold's severity (info, warning, critical, ...).
- `{timestamp}`: when the alert fired.
For example, an email action for a database CPU alert might use:
- **Subject**: `[{thresholdName}] {serviceName} - {metric} at {metricValue}`
- **Body**: `{metric} on {serviceName} crossed the {thresholdName} threshold.
Current value: {metricValue}. Severity: {severity}. Time: {timestamp}.`
When that alert fires at 92% CPU, the email arrives with a subject like
`[Critical] Main Database - CPU Utilization at 92%`.
The email and SMS types use SendGrid and Twilio, the same providers the project
uses for system notifications. For provider account setup and deliverability
notes, see
[Integrations](/docs/tagodeploy/project/configuration/integrations.md).
## Connecting actions to alerts
Actions do not fire on their own. An
[alert](/docs/tagodeploy/project/monitoring/alerts.md) points at them, as the
rule's default actions or per threshold, and runs them when a threshold is
crossed. Define the action first, then select it on the alert.
---
## Monitoring Alerts
# Alerts
An alert rule is the core of monitoring: it watches one metric on one resource
and triggers a response when that metric crosses a threshold you set. Alerts are
where you decide what counts as a problem worth knowing about. This page lists
your rules and is where you create, edit, and enable them.
Existing rules are shown in a table, one row per rule. The columns name the rule
and the **Product**, **Resource**, and **Metric** it watches, and preview its
**Thresholds**:
- The **Thresholds** preview lets you confirm where a rule triggers without
opening it, so you can scan the table and spot a threshold that drifted out of
date.
## Creating an alert
A rule is organized into three parts: General sets its identity and timing, Data
Source picks what it watches, and Thresholds define when it triggers.
### General
Identity and timing for the rule. Past the **Name** and **Enabled** fields, two
settings shape how the rule behaves when it fires:
- **Default actions** are the actions a threshold runs when it sets none of its
own. Setting them here lets you reuse one response across every threshold
instead of wiring each one up by hand, so a rule with five thresholds can
share a single notification. Actions come from the
[Actions](/docs/tagodeploy/project/monitoring/actions.md) page.
- **Cooldown (seconds)** is how long to wait after firing before the rule can
fire again, so a metric sitting over the line does not bury you in
notifications. A cooldown of 300 means a rule that fires at 10:00 stays quiet
until 10:05 even if the metric never drops. Set it too high and you miss a
metric that recovers and spikes again inside the window; too low and a flapping
metric notifies on every check.
### Data Source
The data source ties the rule to one resource and one metric. You pick them in
order: choosing a service narrows the resources, and choosing a resource narrows
the metrics.
- **Service**: a service in the project, such as TagoIO Platform, an installed
middleware, or an MQTT broker, plus **Account** for billing.
- **Resource**: the specific resource within that service, for example the Main
Database under TagoIO Platform, or Billing under Account.
- **Metric**: the measurement to evaluate, such as CPU or memory utilization.
#### Budget alerts
When the service is **Account** and the metric is **Budget**, a **Budget (USD)**
field appears. You set a budget amount in dollars, and the rule's thresholds are
read as a percentage of it. A threshold at 50% of a 100 USD budget fires once
month-to-date spend reaches 50 USD, which gives you an early warning before
spend reaches the limit.
### Thresholds
A threshold is a level on the metric that should trigger a response, set on a
slider. Percentage metrics such as CPU and budget run from 0 to 100%; other
metrics, like counts, seconds, or bytes, scale the slider to a range above their
highest threshold instead. Beyond its name and color, two parts decide its
behavior:
- The **comparison** can be greater than or equal (≥) or less than or equal (≤).
Greater than fits metrics you want to cap, such as CPU; less than fits metrics
where a drop is the problem, such as available disk or a connection count that
should stay up.
- The **actions** run when the metric crosses the threshold. Left empty, the
threshold falls back to the rule's default actions, so set them here only when
this level needs a different response. Either way a threshold must resolve to
at least one action.
A rule can hold more than one threshold, so the same metric can warn at one level
and escalate at a higher one. A Main Database CPU rule might warn over 70% with
an email and escalate over 90% with a webhook. When a value crosses more than one
threshold at once, only the one nearest the current value runs its actions: a
jump straight to 95% fires the 90% threshold, not the 70% one, so you get the
most severe alert without the lower levels adding noise. The thresholds that were
crossed but skipped are still recorded in the
[Logs](/docs/tagodeploy/project/monitoring/logs.md).
## How alerts run
The monitor reads the rule's metric regularly and compares it against each
threshold. When a threshold is crossed, it runs that threshold's actions and
records the event in the [Logs](/docs/tagodeploy/project/monitoring/logs.md).
The cooldown then holds off repeat notifications while the condition lasts. A
rule starts being evaluated as soon as it is enabled, and changes take effect as
soon as you save them.
## Use cases
- Catch a database or service running hot before it slows your application.
- Get an early warning as monthly spend approaches your budget.
- Warn at one threshold and run an automated response at a higher one.
- Trigger a webhook that runs an Analysis or calls an external system, so the
response happens without anyone watching.
---
## TagoDeploy Monitoring
The TagoDeploy Monitoring app watches the infrastructure your TagoDeploy project
runs on and lets you react when a resource crosses a limit you set, by
notification or by automation.
It works one level below your TagoIO application. It does not watch your
dashboards, devices, or data. It watches the services and resources underneath
them, such as databases, middlewares, MQTT brokers, and your account spend.
## Why use it
Infrastructure problems are quiet until they reach your users. A database
running hot, or a month's spend climbing past budget, is something you want to
know about early, before it becomes a slow dashboard or an unexpected bill.
Monitoring catches those conditions at the resource level and lets you act on
them, by alerting someone or by running an automated response.
## What you can monitor
Each rule reads one metric from one resource. Resources come from the project's
services:
- TagoIO Platform services, such as the Main Database.
- Installed middlewares.
- MQTT brokers.
- Account billing, to track month-to-date spend against a budget you set.
The available metrics depend on the resource, for example CPU utilization for a
service, or the budget for billing.
## How it works
Monitoring connects alerts, actions, and logs:
- **Alerts** are the rules. Each watches a resource metric and defines the
thresholds that should trigger.
- **Actions** are the response: an email, an SMS, or a webhook to any endpoint.
- **Logs** are the record of every check and trigger, for confirmation and
troubleshooting.
The monitor checks each enabled rule regularly. When a metric crosses a
threshold, it runs that threshold's actions and writes the result to the log. A
cooldown on the rule keeps a sustained condition from notifying you over and
over.
## Typical setup
- Create an action for how you want to be notified, or what you want to run.
- Create an alert: pick the resource and metric, set the thresholds, and point
them at your action.
- Watch the Logs to confirm checks run and alerts fire as expected.
---
## Monitoring Logs
# Logs
The log is the record of everything the monitor does: each check it runs against
a resource, each threshold it crosses, and any error it hits along the way. It
is where you confirm that rules are running, and where you look first when an
expected alert did not fire.
Each entry is timestamped and tagged with a **type**, the kind of event it
records. When it relates to a specific resource, the entry also names the
**service** and **metric** it read from. Alongside a short summary, every entry
keeps its full payload, including any error message, so a check that failed or a
notification that did not send shows exactly what went wrong.
## Entry types
- **Check Completed**: the monitor finished evaluating a resource. Each enabled
rule is checked on its own interval (one minute by default), so a healthy rule
leaves a Check Completed entry on that cadence, and a gap in them is itself a
signal something is wrong. The entry records the metric value that was read. If
the metric could not be read, for example because the service was unreachable,
the error is recorded on the entry instead.
- **Threshold Crossed**: a threshold was crossed during a check. The expanded
row shows the metric value at the time, the threshold name and comparison that
fired, and which actions ran. When a value crosses several thresholds at once,
only the nearest one runs its actions; the others are recorded here as crossed
but skipped, with the reason.
- **Action Triggered**: an action ran in response to a crossed threshold. The
entry names the action and records its outcome, so you can confirm a
notification was delivered or see why it failed, such as an invalid SendGrid
key or an unreachable webhook URL.
- **Alert Resolved**: a metric that was over a threshold returned to the normal
side of the line, clearing the active alert. This is what drops a rule out of
the Overview's Active alerts count.
## Finding entries
Use the search box to filter the list, and the time-range buttons to scope it to
a window: 1h, 6h, 24h, 7d, or 30d. Narrow the window to focus on a recent
incident, or widen it to confirm a rule has been running over time.
## Troubleshooting
**A rule is enabled but no Check Completed entries appear.** Check the system
entries for a validation error on that rule. A threshold with no action
configured is a common cause: the rule fails at load time and never starts
evaluating.
**An alert fired but no notification arrived.** Find the Action Triggered entry
for that event and expand it. Its outcome shows whether the action ran and any
delivery error. Verify the action's credentials (SendGrid key, Twilio SID,
webhook URL) on the
[Actions](/docs/tagodeploy/project/monitoring/actions.md) page.
**An alert is not firing even though the metric looks high.** Open a Check
Completed entry for that rule and check the metric value the monitor recorded.
If the value is lower than expected, the alert is working correctly: the metric
value at check time may differ from what you see in another tool. If no Check
Completed entries exist for the rule, see the first point above.
## Use cases
- Confirm a new alert is being checked as expected.
- Find out why an alert did not fire, for example a metric that could not be
read.
- Trace what happened around an incident, using the time and detail of each
entry.
---
## MQTT ACL Permissions
# ACL Permissions
ACL (Access Control List) permissions are the rules that decide which MQTT
topics a client can access and what it can do on them. There is no separate ACL
Permissions page in the broker. ACL permissions live inside [Groups](/docs/tagodeploy/project/mqtt/groups.md):
each group holds a list of permission rows, and a client inherits them through
the groups it is assigned to by [Group Rules](/docs/tagodeploy/project/mqtt/group-rules.md).
## What an ACL permission is
Each permission is a single rule with three parts:
- **Permission**: **Allow** or **Deny** the matching access.
- **Action**: **Subscribe** or **Publish**.
- **Topic**: the topic the rule applies to, with MQTT wildcards supported.
## How ACL permissions work
When a device tries to publish to a topic or subscribe to one, the broker checks
the permissions in the device's groups. It matches the topic against the rules,
checks the action (subscribe or publish), and grants or denies access based on
the matching rule.
## Topic patterns and wildcards
ACL permissions support MQTT topic wildcards:
- Single-level wildcard (+) matches any single topic level. For example,
`sensors/+/temperature` matches `sensors/device1/temperature` and
`sensors/device2/temperature`.
- Multi-level wildcard (#) matches multiple topic levels. For example,
`sensors/#` matches all topics under the `sensors` hierarchy.
## Use cases
Use ACL permissions to:
- Keep devices to the topics that belong to their function.
- Block access to sensitive data topics.
- Draw boundaries between device types or applications.
- Meet security standards that require access control.
---
## MQTT Certificates
# Certificates
Provide TLS certificates to encrypt broker traffic and verify clients. The
Certificates page is a form on the MQTT service. Edit the fields and use the
Save button in the page header to stage your changes, which take effect after
you deploy.
## What certificates do
Certificates secure the traffic between the MQTT broker and your devices. They
encrypt data in transit, establish the connection over TLS, and, when MTLS is
on, verify each connecting device by its certificate.
## Enabling a custom certificate
Turn on **Enable Custom Certificate** to provide your own certificate material.
The form then accepts three values, each in its own text area with a copy and
expand control:
- **CA Certificate**: the Certificate Authority certificate
- **Server Certificate**: the server certificate
- **Server Key**: the private key for the server certificate
## Mutual TLS
Turn on **Enable MTLS** to require mutual TLS. With MTLS the broker requires a
custom certificate, and both the client and the broker authenticate each other
using certificates.
---
## MQTT Clients
# Clients
Clients are the credentials that devices use to authenticate with the broker.
This page lists, creates, edits, and deletes them. Existing clients show in a
table with these columns:
- **Client Name** (sortable)
- **Authentication**
- **Actions** (a per-row menu)
Use the search box to filter by client name. The table pages through results
with the count and page controls at the bottom.
## What are clients?
A client is the set of credentials a device uses to connect to the MQTT broker,
a username and a password. Clients are the first layer of access control: they
verify that a device is who it claims to be, give each device an identity, and
work with Groups and Group Rules to decide what the device can do.
## How clients work
When a device connects to the MQTT broker:
1. Connection request: the device presents its username and password.
2. Authentication: the broker checks them against the registered clients.
3. Group assignment: the broker uses Group Rules to find which groups the
client belongs to.
4. Permissions: the client inherits the ACL permissions of its assigned groups.
## Authentication
A client authenticates with one of two methods, shown in the Authentication
column as either **Credentials** or **Certificate**.
### Credentials
Username and password authentication. The device connects using:
- **Username**: a unique identifier for the client
- **Password**: a secret that validates the client's identity
Credentials can be paired with a certificate for an extra layer of trust.
### Certificate
Authentication backed by a client certificate, verified by the broker when
custom certificates and MTLS are enabled on the Certificates page.
## Managing clients
### Creating a new client
Click **New Client** to open the dialog. Set:
- **Name**: a descriptive name to identify the client
- **Authentication**: choose Credentials and fill in the **Username** and
**Password**
Click **Create client** to add it.
### Editing a client
Open the client's row menu and select Edit to change its name and
authentication.
### Deleting a client
Open the client's row menu and select Delete.
## When changes apply
Editing or deleting a client does not affect devices that are already connected.
The change applies the next time a device disconnects and reconnects.
---
## MQTT Connections
# Connections
This page is a live view of the devices currently connected to the broker. It
reads live broker state, so it shows who is connected right now rather than the
clients you have defined. There is no create action, the page is read-only.
## What a connection is
A connection is a live session on the broker. It is not the same as a client or
as topic access:
- A connection is one client connected to the broker right now. When the client
disconnects, its row drops off the list.
- A client is the set of credentials that authenticate a device. Clients are
created on the Clients page and exist whether or not anyone is connected.
- Topic access is who may publish or subscribe to which topics. Groups and Group
Rules control that, not this page.
One client can open several connections at once, and each shows up as its own
row.
## Reading the table
Each row describes one live session:
- **MQTT Client ID** is the client identifier sent in the connect packet.
- **IP Address** is the source address the broker sees for the session.
- **Protocol** is the connection protocol, shown as MQTT.
- **Keep-Alive** is the negotiated keep-alive interval in seconds.
- **Connected At** is when the session opened, in UTC. This column is sortable.
- **Group** is the group the client is assigned to.
## Finding and paging through sessions
Use the search box to filter by MQTT Client ID. The table pages through results
with the count and the previous and next controls at the bottom.
When no device is connected, the page shows an empty state instead of the table.
---
## MQTT Credentials
# Credentials
Credentials are one of the two authentication types a client can use to connect
to the MQTT broker, the other being a certificate. There is no separate
Credentials page. You set credentials when you create or edit a client on the
[Clients](/docs/tagodeploy/project/mqtt/clients.md) page.
Credentials are a username and password:
- **Username**: a unique identifier for the client.
- **Password**: the secret that validates the client's identity.
Credentials can be paired with a certificate for an extra layer of trust.
---
## MQTT Group Rules
# Group Rules
Group rules assign clients to groups automatically based on matching
conditions. The rules are edited inline on this page, with each rule as a row.
Use **New rule** to add a row and the **Save** button in the page header to
stage your changes, which take effect after you deploy. Each row has these
columns:
- **Match By**
- **Value**
- **Groups**
- a remove control
The footer shows how many rules are configured.
## What are group rules?
Group rules connect clients to the groups that grant their permissions. Each
rule matches a connecting client by a condition and assigns it to one or more
groups. A matched client inherits the ACL permissions of every group the rule
assigns.
## Match By
Each rule matches on one of three conditions, set in the **Match By** column.
The **Value** field changes to suit the choice:
### Certificate Fingerprint
Match a client by the fingerprint of its certificate. The Value is the
fingerprint text, for example `SHA256:...`.
### MQTT Client ID
Match a client by the MQTT client identifier it sends during connection. The
Value is the identifier to match. Use this for clients that connect with their
own identifiers.
### Client
Match a named client created on the Clients page. The Value becomes a picker
where you select the client by name.
## Groups
The **Groups** column is a multi-select. Pick one or more groups, shown as
chips, for the matched client to join.
## How group rules work
When a device connects to the MQTT broker:
1. Identification: the broker identifies the client by its certificate
fingerprint, its MQTT client ID, or its named client.
2. Rule evaluation: the broker checks the group rules to find which groups the
client should join.
3. Permissions: the client inherits the ACL permissions of the assigned groups.
4. Access control: the client can only access topics and run operations that the
assigned groups allow.
## Managing group rules
Unlike most sections in the MQTT service, group rules are edited directly on the
page with no dialog or row menu:
### Creating a group rule
Click **New rule** to add a row, then:
- Choose the **Match By** condition (Certificate Fingerprint, MQTT Client ID, or
Client)
- Enter or pick the **Value** to match
- Select the **Groups** to assign
### Editing a group rule
Change the Match By, Value, or Groups directly on the row.
### Deleting a group rule
Use the remove control on the row to drop the rule, which revokes those group
assignments. In both cases, use **Save** to stage the change.
## When changes apply
Changes to group rules do not affect devices that are already connected. The
change applies the next time a device disconnects and reconnects.
## Recommended practices
- Group devices by what they do or what they need to reach.
- Keep MQTT ID matches specific so a rule does not grant access you did not
intend.
- Review your rules now and then against your current devices and security
needs.
- Test rule changes in a controlled environment before you apply them to
production devices.
---
## MQTT Groups
# Groups
A group bundles ACL permissions that allow or deny access to topics. This page
lists, creates, edits, and deletes groups. Existing groups show in a table with
these columns:
- **Name**
- **Description**
- **ACL Permissions** (the number of permission rows in the group)
Use the search box to filter by group name.
## What are groups?
Groups are collections of ACL permissions that define which topics clients can
publish to and subscribe to. Each group has a name and a description. Group
Rules assign clients to groups, and a client inherits the combined permissions
of every group it belongs to.
## ACL permissions
Each group holds a list of ACL (Access Control List) permissions. A permission
is a single rule with three parts:
- **Permission**: **Allow** or **Deny** the matching access.
- **Action**: **Subscribe** or **Publish**.
- **Topic**: the topic the rule applies to. MQTT wildcards are supported, where
`+` matches a single level (`sensors/+/temperature`) and `#` matches every
level below a point (`sensors/#`).
When a device publishes or subscribes, the broker checks the device's groups
and grants or denies the operation based on the matching rules.
### Creating a new group
Click **New Group** to open the dialog and set the **Name** and
**Description**. Under **ACL Permissions**, use **Add permission** to add the
first rule, then **New permission** to add more. Each row sets a Permission, an
Action, and a Topic, with a remove control to drop it. Click **Create group**
to save.
### Editing a group
Open the group's row menu and select Edit to change its name, description, and
permission rows.
### Deleting a group
Open the group's row menu and select Delete.
## When changes apply
Editing or deleting a group does not affect devices that are already connected.
The change applies the next time a device disconnects and reconnects.
---
## MQTT Broker
The TagoDeploy MQTT Broker is a managed MQTT service that runs inside your
private TagoDeploy instance. It is single-tenant infrastructure, separate from
the public TagoIO MQTT broker, with its own security, access control, and
routing to TagoIO.
Use it to deploy one or more private brokers, authenticate devices, set
topic-level permissions, and route messages to your projects. You keep full
administrative control.
## What you get
- Private MQTT infrastructure inside your TagoDeploy environment
- TLS encryption with custom certificate management
- Per-client authentication and topic access control through Groups
- A live view of connected devices
- More than one broker in the same TagoDeploy instance
- Pipelines that forward MQTT messages to your API instance
## How it works (high level)
The broker processes data in three stages:
1. Authenticate: devices connect over TLS using clients you define.
2. Authorize: Groups and their ACL permissions control publish and subscribe
access per topic, and Group Rules assign clients to those groups.
3. Route: Pipelines forward mapped topics to your API instance with the
required authorization and network tokens.
## MQTT sub-pages
The broker service splits its configuration across these pages:
- Overview: broker name, service URL, and service controls.
- Instances: machine size and autoscaling for the broker service.
- Settings: broker-level configuration.
- Clients: credentials that devices use to authenticate.
- Groups: ACL permissions that allow or deny topic access.
- Group Rules: rules that assign clients to groups automatically.
- Connections: live view of devices currently connected.
- Pipelines: forward incoming MQTT messages to external services. Topic
mappings live on the same page.
- Certificates: TLS certificates that encrypt broker traffic and verify
clients.
## Typical setup
- Add the Broker from the App Catalog and track its deployment.
- Get the broker endpoint from Domains.
- Create clients with credentials or certificate authentication.
- Define Groups with ACL permissions for publish and subscribe access.
- Use Group Rules to assign clients to groups.
- Configure a Pipeline with the target API URL and tokens, then map topics
to it.
- Connect your devices using their credentials and authorized topics.
---
## MQTT Instances
# Instances
The Instances page controls how the MQTT broker service scales. From here you
pick the machine size, set the autoscaling range, tune the thresholds, and read
CPU and memory charts for the service.
## Instance settings
The settings are staged and applied through the project deploy flow, so a change
here takes effect only after you deploy it. Use the **Save** button in the page
header to stage your edits. The button stays disabled until you change
something.
- **Machine** is the vCPU and RAM tier each instance runs on, for example
1 vCPU / 2GB RAM. The list of tiers is capped by your project plan.
- **Minimum Instances** and **Maximum Instances** set the floor and ceiling for
how many copies of the broker run. The minimum cannot be larger than the
maximum.
- **Scale on CPU Utilization** is the CPU percentage that triggers autoscaling.
When average CPU crosses this value, more instances are added up to the
maximum.
- **Cooldown for Scaling Up** and **Cooldown for Scaling Down** are wait periods
in seconds that the service holds before adding or removing instances again,
so it does not react to short spikes.
## Monitoring
Below the settings, two charts plot CPU Utilization and Memory Utilization as
percentages over time. Pick a time range from 1h to 30d for both charts at once.
Use these to judge whether the current machine size and instance range match the
load your devices put on the broker.
---
## MQTT Pipelines
# Pipelines
Pipelines forward incoming MQTT messages to external services. This page lists,
creates, edits, and deletes them, and it also holds the Topic Mappings that
route topics to pipelines. Existing pipelines show in a table with these
columns:
- **NAME** (sortable)
- **DESCRIPTION**
- **STATUS** (for example, Active)
Use the search box to filter by pipeline name.
## What are pipelines?
A pipeline connects your MQTT devices to an external service such as a TagoIO
instance. It takes the messages from its mapped topics and forwards them to the
configured API URL with the network and authorization tokens.
## How pipelines work
When a message is published to a topic mapped to a pipeline:
1. The broker receives the message on the mapped topic.
2. The topic mapping forwards it to the pipeline.
3. The pipeline sends it to its API URL with the network and authorization
tokens.
## Managing pipelines
### Creating a pipeline
Click **New pipeline** to open the dialog. The API URL is generated from your
project settings by default. Set:
- **NAME**: a descriptive name
- **DESCRIPTION**: what the pipeline is for
- **API URL**: the endpoint to forward messages to (default
`https://api.tagoio.net`)
- **NETWORK TOKEN**: the token identifying the network
- **AUTHORIZATION TOKEN**: the token that authorizes the request
Click **Create pipeline** to save.
### Editing a pipeline
Open the pipeline's row menu and use Edit to change its name, description, API
URL, and tokens.
### Deleting a pipeline
Open the pipeline's row menu and use Delete.
## Topic Mappings
Topic Mappings route MQTT topics to specific pipelines. They live in a section
on this same Pipelines page, edited inline with no dialog. Use **New mapping**
to add a row and the **Save** button to stage your changes, which take effect
after you deploy. Each row has:
- **TOPIC**: the topic to match, for example `devices/+/telemetry`. MQTT
wildcards are supported, where `+` matches a single level and `#` matches
every level below a point.
- **PIPELINE**: the pipeline to forward matching messages to, picked by name.
- a remove control.
A single topic can map to more than one pipeline, and the same pipeline can
serve many topics.
---
## MQTT Topic Mappings
# Topic Mappings
Topic Mappings route MQTT topics to specific pipelines. When a message is
published to a mapped topic, it is forwarded to the pipeline. There is no
separate Topic Mappings page. They live in a section on the
[Pipelines](/docs/tagodeploy/project/mqtt/pipelines.md) page, edited inline with no dialog.
## Editing topic mappings
Use **New mapping** to add a row, and the **Save** button to stage your changes,
which take effect after you deploy. Each row has:
- **TOPIC**: the topic to match, for example `devices/+/telemetry`.
- **PIPELINE**: the pipeline to forward matching messages to, picked by name.
- a remove control.
A single topic can map to more than one pipeline, so the same message can reach
several pipelines.
## MQTT topic patterns
Topic mappings support MQTT wildcards:
- Single-level wildcard (+): `sensors/+/temperature` matches
`sensors/device1/temperature`.
- Multi-level wildcard (#): `sensors/#` matches all topics under `sensors/`.
## Use cases
Use topic mappings to:
- Route different device types to different pipelines.
- Send the same data to more than one TagoIO instance.
- Forward only the topics you care about and ignore the rest.
---
## Web Service Instances
A web service is a middleware connector. It connects an external IoT network or
protocol to TagoIO over webhooks. The Instances page controls how that connector
scales, so it keeps up when the external network sends a burst of uplinks. Here
you pick the machine size, set the instance count range and the autoscaling
thresholds, and read CPU and memory charts for the running connector.
## Instance settings
The settings form holds the machine size, scaling range, and autoscaling
thresholds for the connector.
- **Machine** is the vCPU and RAM tier each instance runs on, for example
"1 vCPU / 2GB RAM". The list of tiers is limited by your project plan, so
larger sizes appear only when your plan allows them.
- **Minimum Instances** and **Maximum Instances** set the range the connector
can scale between. The minimum has to stay at or below the maximum. The
highest selectable count is capped by your plan.
- **Scale on CPU Utilization** is the CPU percentage that triggers autoscaling.
A burst of uplinks raises CPU, and crossing this threshold adds instances to
keep up.
- **Cooldown for Scaling Up** and **Cooldown for Scaling Down** are the wait
periods in seconds before another scaling step.
Edit the values and use Save to stage the change. Only the fields you changed
are sent. Save is disabled when you lack edit permission or while a project
deploy is in progress, and staged changes are applied through the project
deploy flow.
## Monitoring
The Monitoring section charts CPU Utilization and Memory Utilization for the
connector as percentages over time. Use the time range control to change the
window, with options from 1h to 30d. Read these charts alongside the
Scale on CPU Utilization threshold to judge whether the instance range and
machine size fit the connector's uplink load.
---
## Web Service Overview
A web service is a middleware connector. It connects an external IoT
network or protocol to TagoIO over webhooks. The Overview page shows
the connector's core details and lets you set a display name you can
recognize across the project.
## Service information
This section holds the identity of the connector. It is open by
default and has three fields.
- **Display Name** is the only editable field. Use it to label the
connector so you can tell it apart from others in the same project,
for example "TTN US915" or "Sigfox EU". The name is required and can
be up to 100 characters. The Save button stays disabled until you
change the value.
- **App** is read-only and shows the connector type this service was
created from. It reads `Middleware {Type}`, for example "Middleware
Chirpstack". Connector types include AWS IoT, Chirpstack, Everynet,
Generic HTTPS, Loriot, MachineQ, Myriota, Senet, Sigfox, Tektelic,
and TTN.
- **Service URL** is read-only. It is the webhook endpoint your network
posts uplinks to, and it follows the pattern
`https://..tagoio.net`. A copy button next to
the field puts the URL on your clipboard.
The display name is cosmetic. It does not change the connector type,
the Service URL, or how messages flow between the device and TagoIO.
## Related pages
The Overview page covers identity only. Two sibling pages handle the
rest of the connector. The Settings page holds the connector
configuration, where the network token and TagoIO API URL are set. The
Instances page controls scaling, where you adjust how much capacity the
connector runs with.
## Danger Zone
This section is collapsed by default and holds one irreversible action.
**Remove service** deletes the connector from the project. Removing a
service cannot be undone, so a confirmation dialog appears before it
runs. After removal you are returned to the project management
overview. The button is only shown if you have permission to disable
apps.
---
## Web Service Settings
A web service is a middleware connector. It relays uplink messages from an
external network or protocol to TagoIO over webhooks, and often sends downlink
messages back to devices. This page holds the connector's configuration: the
credentials and endpoints it uses to talk to both sides.
## Configuration
The Configuration section holds the credentials and endpoints used by this
middleware.
- **Network Token** is selected from a network combobox. Pick the network you
want the connector to use, and use the "Refresh networks" button to reload the
list. A "Use custom token (advanced)" toggle reveals a manual token text field
if you need to supply a token by hand instead of picking from the list.
- **TagoIO Api URL** is the API endpoint the connector posts to. It defaults to
`https://api..tagoio.net`.
## Saving changes
Save is available once you edit a field. The button stays disabled while the
form is unchanged, and the whole form locks while a deployment for the project
is in flight, with an alert at the top of the page. Saving does not apply the
change to the running connector right away. It stages the change through the
project deploy flow, where it is applied together with the project's other
pending changes on the next deploy.
## Related pages
The Overview page shows the service identity, such as its display name and
service URL, and the action to remove it from the project. The Instances page
controls how the connector scales. Use this Settings page for the connector's
network token and API endpoint.
---
## Action Cron
A cron is a command for scheduling a task to be executed periodically at a specified time in the future.
Inside TagoIO, cron is used to define when a Schedule Action will be triggered. See [Schedule Action](/docs/tagoio/actions/trigger-by-schedule.md).
To modify the cron of the Action, select the Advanced property in the top-right of the Recurrence options panel.

## Cron example and visual preview
The example cron shown in the Recurrence options panel:
```cron
00 00 */1 * Mon
```
The preview below the cron input shows:
- "This action will run at 00:00, only on Monday."
- Note: "The action can take up to 1 minute to be executed."
## Cron fields in the Recurrence options panel
1. **Timezone selector**
- This field allows you to change the timezone where your cron will run.
2. **Cron input field**
- This field contains the cron data. You must insert a correctly formatted cron expression here; otherwise you won't be able to save your Action. Learn more about [cron](https://en.wikipedia.org/wiki/Cron) and its format.
3. **Visual representation / preview**
- This field displays a visual representation explaining when your action will run based on the cron. If your cron is not properly formatted, this field will disappear.
## Notes about formatting
- Ensure the cron expression follows the expected format (minute hour day (month) month day (week) or the format required by your environment).
- When using the Advanced option, confirm the timezone is correct to avoid unexpected run times.
---
## Defining Actions
Actions are initiated based on specific conditions and can execute many tasks. The first step in configuring an Action is to specify the desired action. Below are the different types of actions available:
## Push Notification to myself
This action can notify you on your account via the
[Admin](https://admin.tago.io/) in your browser or through the TagoIO app. You
can customize the title and content of the notification. In the message field,
you can use variables to dynamically include information in your text. For more
details on how to use message variables, refer to the "Message Variables"
section at the end of this page.
Please note that the notification is only sent to the account where it was
created.

## Push Notifications to Run Users
Send notifications to designated **Run Users** within your **Run**. You must
specify the particular Run User you wish to notify. For notifying multiple
users, please refer to our documentation on
[notifications using analysis](/docs/tagoio/tagorun/getting-started/notifications-for-users.md).
In the message field, you can use variables to dynamically include information
in your text. For more details on how to use message variables, refer to the
"Message Variables" section at the end of this page.

## Send Email
Sends an email to a specified address when certain conditions are met. You can
specify one or multiple recipients by separating email addresses with a
semicolon (;).
In the message field, you can use variables to dynamically include information
in your text. For more details on how to use message variables, refer to the
"Message Variables" section at the end of this page.

:::warning
Starting **January 1st, 2025**, TagoIO will discontinue its email
services. We recommend integrating with external providers to send emails using
the "Send Email using SMTP/SendGrid" action.
:::
## Send Email using SMTP
An email will be sent via SMTP when a specific condition is met. This action
allows you to send emails via external providers. To configure this action, you
need to create a **Secret** of type **SMTP** to authorize TagoIO to connect to
your email provider and send emails; you will need to supply the server address,
port number, username, and password. This information is usually provided by
your email hosting service.
Learn here how to create this secret:
[Secrets](/docs/tagoio/security/secrets.md).
In the message field, you can use variables to dynamically include information
in your text. For more details on how to use message variables, refer to the
"Message Variables" section at the end of this page.

## Send Email with SendGrid
An email will be sent via SendGrid when a specific condition is met. To
configure this action, you need to create a Secret of type **Text** containing
the _SendGrid API Key_. Ensure that the SendGrid API Key is set in TagoIO
Secrets beforehand so it appears in the dropdown menu.
Learn here how to create this secret:
[Secrets](/docs/tagoio/security/secrets.md).

## Send Message to AWS SQS
This action sends messages to Amazon SQS when triggered. It requires
authentication via Secrets. You will need to provide the region, queue URL, and
access key id and secret access key.
Learn how to create this secret:
[Secrets](/docs/tagoio/security/secrets.md).

## Send SMS
Sends SMS messages when certain conditions are met. You can specify one or
multiple recipients by separating phone numbers with a semicolon (;). You need
to include the country code with each phone number; if omitted, the system
assumes the USA code (+1).
In the message field, you can use variables to dynamically include information
in your text. For more details on how to use message variables, refer to the
"Message Variables" section at the end of this page.

Some costs may occur when using the SMS service, which vary based on the
country of operation. Check the [pricing](https://tago.io/pricing/),
[terms of use](https://tago.io/terms-of-support/), and your plan before using
the SMS service.
**Attention:** starting on **November 1st, 2024**, TagoIO will no longer offer
SMS services. We recommend integrating with external providers to send SMS using
the "Send SMS with Twilio" action.
## Send SMS with Twilio
Use this action to send an SMS message via Twilio when a specified condition is
met. You will need to provide the recipient's phone number, the sender's "from"
number, and the message content. The message field supports variables, allowing
you to dynamically include information in your text.
Ensure that the _Twilio SID_ and _Twilio Auth Token_ are set in TagoIO Secrets
beforehand for them to appear in the dropdown menu. Use a Secret of type
**Text**. Learn how to create this secret:
[Secrets](/docs/tagoio/security/secrets.md).

If you're using Twilio Long Code for messaging, be aware that it requires a
Toll‑Free verification process. Please consult Twilio's documentation for
instructions on how to verify your phone number before proceeding. Depending on
Twilio's rules for your account, the Toll‑Free verification may be skipped.
You can send SMS directly from scripts within the
[Analysis](/docs/tagoio/analysis/creating-analysis.md) using the Twilio service.
Before proceeding, ensure you review Twilio's terms of use and confirm your plan
details. You must register your "From" number in the Twilio console before
integrating it. The Twilio console also allows you to track SMS delivery status
and view message content. Note that TagoIO does not charge for using this
third-party Twilio service.
## Send WhatsApp message with Twilio
Use this action to send a WhatsApp message via Twilio when a specified condition
is met. You will need to provide the recipient's phone number, the sender's
"from" number, and the message content. The message field supports variables,
allowing you to dynamically include information in your text.
Ensure that the _Twilio SID_ and _Twilio Auth Token_ are set in TagoIO Secrets
beforehand for them to appear in the dropdown menu. Use a Secret of type
**Text**. Learn how to create this secret:
[Secrets](/docs/tagoio/security/secrets.md).

You can send WhatsApp messages directly from scripts within the
[Analysis](/docs/tagoio/analysis/creating-analysis.md) using the Twilio service.
Before proceeding, ensure you review Twilio's terms of use and confirm your plan
details. You must register your "From" number in the Twilio console before
integrating it. The Twilio console also allows you to track the delivery status
and view message content. Note that TagoIO does not charge for using this
third-party Twilio service.
You can also send WhatsApp messages using Twilio Templates. To do this, you need to set a Twilio Template SID in TagoIO Secrets and configure the Template variables. Template variables also support dynamic content, allowing you to include information dynamically within them.
## Run Analysis Script
Actions can initiate scripts that run in the
[Analysis](/docs/tagoio/analysis/). This is a very flexible feature, as you can
define exactly when your code will run.
Learn more about
[Running Analysis via Action](/docs/tagoio/actions/running-analysis-via-action.md).

## Publish to TagoIO MQTT Broker
This action can publish to an MQTT topic defined by the developer. This means
that every time your trigger criteria are met, TagoIO will publish data to that
predefined MQTT topic, which is very useful if you need to forward data to
devices and/or applications.
To set the MQTT action up, select the option **Publish to MQTT**, select the
device which will receive that topic and type in the topic. The content of the
MQTT payload that subscribed devices will receive can be defined as well.

TagoIO [MQTT Broker](/docs/tagoio/integrations/networks/mqtt.md) is available
exclusively for **Starter** and **Scale** accounts in the **US database
region**. Free accounts and European database region accounts may use
third-party MQTT services with TagoIO via the
[MQTT Relay](/docs/tagoio/integrations/networks/third-party-mqtt-broker)
feature.
## Post data to HTTP End-Point
This action can post data directly to an endpoint defined by you. When the
action is triggered, it will POST the entire data payload received from the
device to the configured HTTP endpoint address using the
[TagoIO Standard Data Format](/docs/tagoio/devices/sending-data-to-device.md).

> You can use payload fields in the HTTP endpoint, like the example above but
> with a variable (`https://myapi.io/endpoint/$VALUE$`), or even in the HTTP
> Headers like in the example below:

The `TagoIO-Retries` header is managed by the TagoIO API and sent in every Post
request for this Action type, and indicates the number of attempts to post the
data to your endpoint.
It will try to post the data 10 times, and if it's unsuccessful (any response
from your endpoint other than
[HTTP 20X status codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#successful_responses))
it will require a fallback device to be configured to store the data in TagoIO
so you can retrieve it later without losing your data in case your endpoint
fails to receive it.
To set up a fallback device, enable it in the bottom part of the HTTP Post
action settings and put the token for the device you intend to hold the data.

After 10 unsuccessful attempts, the data will be sent to the fallback device
with some information added to the `metadata` in the `_http_*` properties
(original values in `metadata` will be kept), like shown below:
```json
[
{
"variable": "image",
"group": "465c78a000c39bee89461786",
"time": "2025-07-11T19:23:04.027Z",
"created_at": "2025-07-11T19:23:04.027Z",
"value": 71,
"id": "68716498eeb93c000a87c565",
"device": "66f16881c903480009ff1042",
"bucket": "66f16881c903480009ff1042",
"metadata": {
"_http_error": "getaddrinfo ENOTFOUND [example.com](http://example.com/)",
"_http_url": "[http://example.com/aaa](http://example.com/aaa)",
"_http_attempts": 10
}
}
]
```
## Insert to Device's data storage
This option is only available if your Action is of the type **MQTT Topic**. This
action will insert the data directly into the device's data storage.

## Message Variables
In some action types, the message body can be as simple as a text like _Hi, your
car is over the speed limit_ or you can specify fields in the message to
personalize it with real‑time data from your devices.
You can use most of the payload fields from our API:
| Variable | Description |
| ---------------- | ------------------------------------------------------------------------------------------ |
| `$VARIABLE$` | Variable name |
| `$VALUE$` | Variable value |
| `$UNIT$` | Unit of your variable |
| `$TIME$` | Time the variable was received |
| `$LOCATION$` | Location value |
| `$DEVICE$` | Device ID |
| `$DEVICE.NAME$` | Name of your device |
| `$ACTION.STATE$` | Current state of your [Action](/docs/tagoio/actions/trigger-unlock.md): locked or unlocked |
For example, a personalized message like this
- Hi, your device reached **$VALUE$ $UNIT$** at the coordinates **$LOCATION$**
at **$TIME$**.
This would result in the message being:
- Hi, your device reached **73 mph** at the coordinates **43.05334,-86.45340**.
And a message like this
- **$TIME$**: Warning, device with ID **$DEVICE$** is **$VARIABLE$ $UNIT$**.
Would result in the following message:
- **2021‑01‑02T22:51:48.005Z**: Warning, device with ID
**547e353d7dbf3af122c0257d** is **90 °F**.
---
## Actions
Actions is a feature that gives you total control over your devices by executing operations based on events you define.
With Actions you can:
- Send an SMS based on certain conditions.
- Send an email based on certain conditions.
- Run scripts using [Analysis](/docs/tagoio/analysis/).
- Make HTTP POST requests.
- Send notifications to your account or to the account of your [RunUsers](/docs/tagoio/tagorun/).
- Publish to an MQTT topic based on certain conditions.
Learn about [Defining Actions](/docs/tagoio/actions/defining-actions.md).
## Creating Actions
To create a new Action, click the "Action" button in the sidebar, then click the + button in the top right.

### Type of trigger
There are 6 types of triggers you can use:
- **Variable**: The action will be triggered when one variable meets certain conditions. If your device has sent data and the variables are meeting those conditions, this action will be triggered. Learn more about [Trigger by Variable](/docs/tagoio/actions/trigger-by-variable.md).
- **Resource**: This action will be triggered when the selected resources, for example, analysis or devices, have been modified, deleted, or created. This type keeps _watching_ resources and triggers the action when the resources have met certain conditions. Learn more about [Trigger by Resource](/docs/tagoio/actions/trigger-by-resource.md).
- **Schedule**: This action will be triggered automatically from time to time. You can specify a custom range (every second Wednesday of September), or you can specify a fixed time range (every 2 hours). Learn more about [Trigger by Schedule](/docs/tagoio/actions/trigger-by-schedule.md).
- **Usage Alert**: This action will be triggered upon a Services and/or Resources usage percentage set by the user. This type keeps watching Services and Resources and triggers the Action when the conditions are met. Learn more about [Trigger by Usage Alert](/docs/tagoio/actions/trigger-by-usage-alert.md).
- **Geofence**: This action will be triggered whenever a variable containing location data meets specific geofence conditions, either inside or outside a predefined area. Learn more about [Trigger by Geofence](/docs/tagoio/actions/trigger-by-geofence.md).
* **MQTT Topic**: This action will be triggered when a message is received on an MQTT topic. Learn more about [Trigger by MQTT Topic](/docs/tagoio/actions/trigger-by-mqtt-topic.md).
:::warning
Note that the **TagoIO MQTT Broker** is available exclusively for Starter and Scale accounts in the US database region. Free accounts and accounts hosted in the European database region may use third-party MQTT services with TagoIO via the [MQTT Relay](/docs/tagoio/integrations/networks/third-party-mqtt-broker) feature.
:::
To prevent your action from continuously activating when a trigger condition is met, it is advisable to define a reset trigger condition. Ensure you check the "[Trigger Unlock]" option to avoid potential issues with your logic and account:
[Trigger Unlock](/docs/tagoio/actions/trigger-unlock.md).
---
## Running Analysis via Action
Actions can initiate scripts that run in the [Analysis](/docs/tagoio/analysis/). This lets you define exactly when your code will execute.
- For example, you can run an Analysis every time data is sent from a device or when data meets a certain condition. Learn more in [Trigger by Variable](/docs/tagoio/actions/trigger-by-variable.md).
- You can trigger the Action when a resource change happens, for example, when a new Device is created or when a new RunUser is registered. Learn more in [Trigger by Resource](/docs/tagoio/actions/trigger-by-resource.md).
- You can also create a Schedule Action to trigger the Analysis at fixed time intervals.
## Setting up the Action
To run an Analysis from an Action:
1. Create the Action.
2. Set the field "Type of action" to "Run Analysis".
3. Select one or more Analyses to run.

Notes about the UI shown above:
- The "Type of action" field should be set to "Run Analysis".
- Use the "Run one or more analyses" area to add the Analyses you want to execute (e.g., Analysis 1, Analysis 2).
- Use the plus and trash can icons to add or remove Analyses from the Action's list.
- You can specify up to **10 Analyses** that will run in parallel when the Action is triggered.
## Action Context Variables
When an Action triggers an Analysis, it automatically adds context variables that provide information about how the Analysis was initiated. These variables can be essential for the Analysis to execute correctly and respond appropriately to the specific trigger.
| Field Name | Description |
| --------------- | -------------------------------------------------------------------------------------- |
| `_action_id` | The unique identifier for the Action. |
| `_action_type` | The category or type of Action that triggered the Analysis. |
| `_action_state` | Indicates whether the Action was locked or unlocked at the time this Analysis was run. |
| `_device_id` | The identifier of the device that triggered the Action, if triggered by a device. |
---
## Trigger by Geofence
The "Trigger by Geofence" feature allows you to execute an [Action](/docs/tagoio/actions/) whenever a variable containing location data meets specific geofence conditions, either inside or outside a predefined area. For example, you can configure an action to trigger when a device enters or exits a designated zone.

## Setting the Trigger Conditions
To set up this trigger, you need to draw geofences on the map. Geofences can be either polygons or circles, and you must select a variable that contains the location data.
For each condition, you can only set one geofence, so if you need multiple geofences, you will have to create additional conditions, up to 10 in total.
You must define whether the trigger should activate when the chosen variable is **inside** or **outside** the geofence. When the selected variable receives new location data, the system checks if this location meets the specified geofence condition.

### Editing Geofence
To edit a geofence condition, simply select the condition and click on the pencil icon to resize or move it.
To delete a geofence in your condition, you can either draw another one, which will replace the previous geofence, or click on the trash icon to remove it.
---
## Trigger by MQTT Topic
The trigger type **"Trigger by Variable"** allows you to execute an
[Action](/docs/tagoio/actions/) when data is sent to an MQTT topic.
TagoIO provides its own MQTT broker that pushes data to clients when new
messages are published to the topics they are subscribed to. To learn more about
the MQTT infrastructure used by TagoIO, see
[MQTT](/docs/tagoio/integrations/networks/mqtt.md).
:::warning[Deprecation Notice]
**Legacy MQTT is deprecated.** The TagoIO MQTT Broker was available exclusively
for Starter and Scale accounts in the US database region that upgraded before
April 15, 2026. Accounts created or upgraded after that date do not have access
to this feature.
Please migrate to [TagoTiP MQTT](/docs/tagotip/transports/mqtt), which supports
all account types and regions (US and EU).
Existing devices and actions will continue to function during the deprecation
period, but no new features or bug fixes will be provided.
:::
## Trigger categories
There are 2 categories for this trigger type:
1. **Single device**\
Allows you to watch a specific device from your device list. Any data sent by
that device will be tested against your topics to determine whether this
action should be triggered.

2. **Multiple devices**\
Allows you to watch multiple devices. Any data sent by those devices will be
tested against your topics to determine whether the action should be
triggered. For this type, you must supply the tag keys and tag values of the
devices you want to watch.

## Trigger Conditions
After setting up the device, you should set a condition for your action to be
executed. To do so, you must head over to the **Trigger** section.

For each row, you need to set the topic that will be listened to. You may use
MQTT [wildcards](/docs/tagoio/integrations/networks/mqtt.md) for the topic if
you prefer.
You can set multiple conditions in an action; if at least one of them results in
a match, the action will be executed.
:::info
When you set up multiple conditions, the action will execute only once per MQTT
message, even if that message matches multiple conditions. For example, if you
have two conditions:
- Condition 1: Topic `sensors/+/temperature`
- Condition 2: Topic `sensors/+/+`
And you receive a message on topic `sensors/device1/temperature`, it matches
both conditions, but the action will trigger only once for that single message.
:::
---
## Trigger by Resource
The trigger type "Trigger by Resource" allows you to execute an Action when a resource meets certain conditions. A resource can be one of the following:
- [Access Management](/docs/tagoio/tagorun/access-management/)
- [Actions](/docs/tagoio/actions/)
- [Analysis](/docs/tagoio/analysis/)
- [Device](/docs/tagoio/devices/)
- [RunUser](/docs/tagoio/tagorun/)
For example, you can trigger an Action when a new user signs up in your RUN or when a new Device is added. That Action could, for example, push a notification to yourself.
## Trigger Conditions
After setting up the device, you should set a condition that causes your Action to be executed. To do this, open the Trigger section of the Action.
:::tip
You can set multiple conditions for different resource types in a single Action. If at least one condition matches, the Action will be executed.
:::

1. **Resource**: Select a resource to be tested.
2. **Tag key**: The tag key of the resource to be tested.
3. **Tag value**: The tag value of the resource to be tested.
4. **Condition**: The condition to be watched.
The example above means that the action will be triggered when a Device with a tag key of `tag_key` and a tag value of `tag_value` is created.
---
## Trigger by Schedule
The trigger type Trigger by Schedule lets you define a time interval to execute your Actions automatically.
There are two categories within this trigger type:
1. By Interval
Allows you to specify a time interval to trigger the Action automatically. Choose the time interval on the right side of the interface; the minimum allowed time is 1 minute.

2. By Date
Allows you to specify a date or a recurring date rule to trigger the Action. For example, you can choose to run an Action on the second Wednesday of each month.

## Customizing By Date
If you pick the By Date type, you can customize it in either a basic or an advanced way:
- Basic customization: Use the interface fields to define the date or recurring date rules.
- Advanced customization: Write a cron expression to define the date/time more precisely.
To learn more about advanced (cron) customization, see [this article](/docs/tagoio/actions/action-cron.md).
### 1. Basic customization details
The basic mode provides several interface fields that let you fine‑tune when your Action runs:
- **Repeat Every**: Choose the interval between executions. Options include:
- Day
- Week
- Month
- **Repeat at**: Specify the exact time of day the Action should trigger.
- **Repeat on**: Select one or more weekdays for the Action to run.
- **Timezone**: Set the timezone for the scheduled Action; by default it uses the account's timezone.
- **Customization**: Toggle between Basic and Advanced modes. The basic mode is enabled by default, but you can switch to advanced if needed.
- **Explanation**: A helpful field that displays a human-readable summary of when the Action will execute.
### 2. Advanced customization details
When you need more granular control, switch to the advanced mode and enter a cron expression. You can specify exact dates, times, and recurrence patterns beyond what the basic interface offers. For guidance on writing cron expressions, refer to [this article](/docs/tagoio/actions/action-cron.md).
---
## Trigger by Usage Alert
The "Trigger by Usage Alert" feature lets you execute an Action when a specific service or resource meets predefined usage conditions.
## Resources and Services
The following resources and services can be used to trigger an Action:
### Resources
- [Access Management](/docs/tagoio/tagorun/access-management/)
- [Actions](/docs/tagoio/actions/)
- [TCore](/docs/tagocore/)
- [Device](/docs/tagoio/devices/)
- [Team Members](/docs/tagoio/profiles/team-management-sharing-your-profile.md)
- [Dashboards](/docs/tagoio/dashboards/)
- [Analysis](/docs/tagoio/analysis/)
### Services
- [Input](/docs/tagoio/profiles/services/data-input-service.md)
- [Output](/docs/tagoio/profiles/services/data-output-service.md)
- [Analysis](/docs/tagoio/analysis/)
- [Data Records](/docs/tagoio/profiles/services/data-records.md)
- [SMS](/docs/tagoio/profiles/services/sms-service.md)
- [Emails](/docs/tagoio/profiles/services/e-mail-service.md)
- [Run Users](/docs/tagoio/profiles/services/end-users-service.md)
- [Push Notifications](/docs/tagoio/profiles/services/notification-service.md)
- [File Storage](/docs/tagoio/profiles/services/file-storage-service.md)
For example, you can configure an Action to trigger when Analysis usage reaches a specified threshold. Once that threshold is met, a notification can be sent to the profile administrator.
## Setting the Trigger Conditions
To set up an alert, define the conditions that will trigger the Action. You can specify multiple conditions; the Action will be executed if any one of them is met.

1. **Select a Resource or Service:** Choose the specific resource or service you want to monitor as a trigger.
2. **Define Condition and Percentage:** Specify the condition that needs to be met for the action to be triggered, and determine the usage percentage that will activate the trigger.
3. **Add or Remove Conditions:** You can add or remove conditions as needed, with a maximum of 10 conditions. Remember, the action will be executed if at least one of the specified conditions is satisfied.
---
## Trigger by Variable
The trigger type, [Actions](/docs/tagoio/actions/), Trigger by Variable, allows you to execute an action when a variable meets specified conditions. If a device sends data and the variables meet those conditions, the action will be triggered.
There are 2 categories inside this trigger type:
## 1. Single device
Allows you to watch a specific device from your device list. Any data sent by that device will be tested against your conditions to determine whether the action should be triggered.

## 2. Multiple devices
Allows you to watch multiple devices. Any data sent by those devices will be tested against your conditions to determine whether the action should be triggered. For this type, you must supply the tag key and tag value of the devices you want to watch.

:::warning
Note that only the **Single device** category allows you to set **Trigger Unlock** conditions. To learn more, see [Trigger Unlock](/docs/tagoio/actions/trigger-unlock.md).
:::
## Trigger Conditions
After setting up the device, you should set a condition for your action to be executed. To do so, head over to the **Trigger** section.
You can set multiple conditions in an action; if at least one of them results in a match, the action will be executed.
:::info
When you set up multiple conditions, the action will execute only once per data
message, even if that message matches multiple conditions. For example, if you
have two conditions:
- Condition 1: Variable `temperature` is greater than `25`
- Condition 2: Variable `temperature` is greater than `20`
And you receive a message with `temperature` set to `30`, it matches both
conditions, but the action will trigger only once for that single message.
:::
1. **Select a variable**: Choose a variable to be tested. This will be one of the variables that will be compared against the data from the device.
2. **Condition**: The type of condition to test the variable. The available test conditions are:
1. **Less than**: true when the value of the variable is less than the defined value;
2. **Greater than**: true when the value of the variable is greater than the defined value;
3. **Equal to**: true when the value of the variable equals the defined value;
4. **Different from**: true when the value of the variable differs from the defined value;
5. **Any**: true whenever a new value of the variable is sent to the device;
6. **Between**: true when the new value falls between a fixed range.
3. **Value**: The value that complements the condition. In the example above, the value for the condition is `15`.
4. **Field type**: The field type used in the comparison. The system will enforce this field type when comparing the device's data with the defined value (`15`). For instance, you could set the value to `0x1` and set this field to `string` to compare the number as a string.
The example above means that the action will be triggered when **my_variable is less than 15**.
:::tip
Looking to create more complex logic statements? You can use the [Action](/docs/tagoio/actions/) to trigger an [Analysis](/docs/tagoio/analysis/), and then manually create custom logic inside of the code.
:::
### Preventing the action from running every time the conditions are met
To prevent your action from activating every time the conditions are met, you can lock it by defining **Trigger Unlock** conditions. Note that only the **Single device** type allows you to set Trigger Unlock conditions.
---
## Trigger Unlock
Trigger Unlock is a feature that allows users to define specific conditions under which an action, once locked, can be unlocked and allowed to execute again. This locking mechanism safeguards against repetitive or unwanted triggers, so actions occur only when truly necessary. Before using Trigger Unlock, familiarize yourself with [Trigger Conditions](/docs/tagoio/actions/trigger-by-variable.md).
This feature is available exclusively for the trigger type: [Trigger by Variable](/docs/tagoio/actions/trigger-by-variable.md).
## How It Works
By default, when a Trigger Condition is met, the corresponding action typically executes immediately. However, if Trigger Unlock conditions are also defined, the action first enters a "locked" state upon meeting the initial Trigger Condition. This lock prevents the action from being triggered again until the specified unlock conditions are met. This mechanism is particularly useful when an action should only occur once under certain circumstances and be allowed to trigger again after other conditions are satisfied.
Thus, each time a Trigger Condition is met, the trigger is locked if there is at least one Trigger Unlock condition defined. If Trigger Conditions are met but there are no Trigger Unlock conditions, the action will not get locked.
### Locked Switch
You can check if your action is locked by looking at the **Locked** switch, as shown in the screenshot below.
- If the Locked switch is gray, it means your action is unlocked;
- If it's red, it means your action is locked.
The lock button will only appear once you add at least one condition.

### Manual Locking
You can also set this Locked parameter manually; all you have to do is click it to toggle between Locked or not, and then hit **Save**.
### Unlock Condition Example
You can also run the action again when an unlock condition is met. For example, if you are triggering an [Analysis](/docs/tagoio/analysis/creating-analysis.md) to set a device in alert mode due to high-temperature readings, you can also switch to safe mode upon meeting an unlock condition.

### Duplicate Trigger Warning
Enabling a trigger when unlocked can lead to a duplicate triggered action.
### Why Does This Exist?
One example of an undesirable situation could occur when you want to receive only one SMS when the temperature crosses above 95 °C, but instead, you receive one SMS for each time a new value of temperature above 95 °C is sent (95 °C, 96 °C, 97 °C).
You may want to implement a hysteresis using a Trigger Unlock condition. If you define the condition to reset when the temperature is less than 90 °C, for example, it would prevent this issue.
Only one SMS would be sent, and the system would remain locked until the temperature goes below 90 °C, which seems much more reasonable in this example.
---
## Control Tower
The Control Tower is an add‑on designed to enhance your profile by providing access to features that increase your visibility and control over data input and output. It also offers advanced statistics on the usage of your [Hard Limits (Rate Limits)](/docs/tagoio/profiles/services/rate-limits-hard-limits.md). By using Control Tower, you can:
- View data input and output statistics for each device.
- Set data input and output limits for each individual device or for the whole profile.
- Visualize Hard Limits usage statistics to gain insights into your requests per minute.
These features let you obtain detailed information about your device's activities and the consumption of your Hard Limits. That understanding of your application's performance helps you prevent reaching limits that could interrupt your application's operation due to account limitations. Control Tower was developed to improve the reliability, scalability, and efficiency of your application.
Activate the Control Tower add‑on on your [Billing page](https://admin.tago.io/account/billing), or check out our [Pricing Page](https://tago.io/pricing).
## View your device statistics
Once you activate Control Tower, you can view data input and output statistics for each device under your profile. This helps you better understand how much data is being streamed to and from your devices.
You can monitor the statistics by going to your Devices module on your Admin and selecting the device you want to visualize:

:::info
Statistics are recorded only after you activate Control Tower on a profile. Adding the add‑on does not generate retroactive data for devices prior to purchase.
:::
### Set rate limits for your devices and profiles
Control Tower allows you to set custom request rate limits for your profile or individual devices. These limits apply to interactions across the platform and are based on your account's plan.
There are two types of limits:
- **Hard limits**: fixed limits that apply to all accounts, determined by your plan.
You can read more about them in the [Rate Limits (Hard limits)](/docs/tagoio/profiles/services/rate-limits-hard-limits.md) documentation.
- **Soft limits**: flexible limits that you can customize. They let you control the input and output of your profile or devices and prevent excessive consumption.
You can apply a soft rate limit to:
- **Your whole profile**: all devices in the profile follow this limit.
To set it, navigate to your Admin page → [Profiles & Teams](https://admin.tago.io/profile) → "More" tab.

- **A specific device**: overrides the profile limit for that device.
To set it, go to the [Devices](/docs/tagoio/devices/) module, select the desired device, and click the "More" tab.

:::info
The **hard limits** take precedence over **soft limits** due to restrictions on the API level. You can't set a soft limit higher than the ones in the [Rate Limits documentation](/docs/tagoio/profiles/services/rate-limits-hard-limits.md).
For example: if the hard limit of your plan is 5,000 , even if the soft limit is set to 10,000 requests in a profile or a device, the limit will still be maintained in 5,000 requests per minute.
:::
### Monitor your request per minute statistics
A significant feature provided by the Control Tower is the capability to access detailed statistics on Hard Limits through your Admin page. Upon activating the add-on, data regarding your requests will begin to be recorded and presented on the [Hard Limits > Requests page](https://admin.tago.io/limits/hard). This enables you to view the current, highest, lowest, and average usage of each hard limit. Furthermore, it allows for the display of advanced statistics, offering a historical view of your usage.

In the advanced statistics view, you have the option to filter your usage by date and time and to view only the highest, average, and lowest usage. This is beneficial for understanding how your application is performing and determining if any actions are necessary to mitigate the usage of your hard limits.
:::info
**Graph behavior:** The graph always displays a minimum value of 25 % for usage, even if the actual usage falls below this threshold. If usage is 0 %, it will still be represented as 25 %.
:::
Activate now the Control Tower add‑on on your [Billing page](https://admin.tago.io/account/billing), or check out our [Pricing Page](https://tago.io/pricing) for more information.
---
## Custom Domain Configuration
The Custom Domain & Whitelabel add-on lets you promote your organization's brand using a custom URL for your Run portal. Configuring your Custom Domain is simple, but there are a few requirements to make it work.
## Pre-requisites
Before configuring your Custom Domain in your Admin, ensure you have:
- A domain or a sub-domain for your organization; you can obtain one from any domain provider. Ensure you can edit the DNS (Domain Name System) records for your custom domain in the domain provider's settings.
- The Custom Domain & Whitelabel add-on is activated in your profile. Check the Pricing page for more information (see [Custom Domain & Whitelabel](/docs/tagoio/addons/custom-domain/) and [Pricing](https://tago.io/pricing)).
:::info
The Custom Domain & Whitelabel add‑on is available per profile. If you need to use a custom domain in another profile, you will need to acquire another add‑on.
:::
## Adding your Custom Domain
To configure the Custom Domain add‑on for your profile:
1. Access your Run module.
2. Click the **Domain** tab.
3. Paste your domain and sub‑domain in the respective fields.
You can also configure the e‑mail domain for messages sent from your application, choosing between the available options shown in the Domain tab of the Run module:
- **Use only the domain name:** The e‑mail address will contain only the domain. For example `noreply@mycompany.com`.
- **Use full address:** The e‑mail address will contain both domain and sub‑domain. For example `noreply@portal.mycompany.com`.
Once you are done, confirm your settings to start mapping your Custom Domain.
## DNS Configuration
After adding your custom domain, a new tab appears with the DNS records you need to add to your domain provider's admin console.
1. **Create CNAME records** for each of the listed entries:
- Record type: `CNAME`
- Key (name): copy from the table.
- Value (target): copy from the table.
In total, you will create one record for the endpoint and SSL certificate, and three records for e‑mail.
2. Once all records are added, click **Check records** to verify your configuration. The status icons next to each record indicate whether the record is correctly set up. Hover over the icon on the right side of each record for more details about its status.
3. Due to DNS propagation, it may take a few minutes (up to 30 minutes) before TagoIO detects the changes. If after 30 minutes the domain still does not work, double‑check that the records were created exactly as shown.
:::info
SSL certificate is mandatory; if you click **Check records** without setting up the SSL certificate, the domain will be mapped but disabled.
:::
---
## Custom Domain (Whitelabel)
Adding the Custom Domain & Whitelabel add-on unlocks additional branding and URL customization options for the Run module.
The Custom Domain & Whitelabel add‑on allows a higher level of brand engagement with your end‑users.
## What this add-on unlocks
1. Ability to choose a custom URL for the application
- Example: If your company domain is mycompany.com, you can use a Run URL such as portal.mycompany.com.
2. Custom e-mail domain
- Allow outgoing emails from the platform to use your organization's email domain. Emails sent to users will originate from your domain, not @tago.io.
3. Removal of references to the TagoIO brand
:::info
Custom Domain is not applied to API routes; traces of TagoIO can still be found when inspecting the page using Dev Tools.
:::
You can select this add‑on in your [Profile](https://admin.tago.io/profile/).
---
## Custom Mobile App
Add the Custom mobile app add‑on to manage the deployment of your own white‑label mobile application.
Your mobile app will be completely white‑label, so when your clients find and download it from the App Store and Google Play it will appear as a professional application branded as your own.

## Pricing
Check the pricing for the Custom mobile add‑on on your [Profile & Teams](https://admin.tago.io/profile/) page.
:::info
There is a one‑time fee to prepare the package and deploy the app to the stores on your behalf. [Contact us](https://tago.io/contact-us) for details.
:::
## Learn more
Learn about the other add‑ons on the [Add‑ons](/docs/tagoio/addons/) page.
---
## Add-ons Overview
On top of the fundamental IoT services provided to run your applications, additional add-on modules are available to customize your application using TagoRUN. Add-ons are applied separately to each of your Profiles.
## Enabling add-ons
In your account, go to [Profiles & Teams](https://admin.tago.io/profile/), select a Profile, and add one or more of the add-ons available for your account.
## Available add-ons
Currently, these add-ons are available:
1. [Custom Domain & Whitelabel](/docs/tagoio/addons/custom-domain/)
2. [Custom Mobile App](/docs/tagoio/addons/custom-mobile-app.md)
3. [Control Tower](/docs/tagoio/addons/control-tower.md)
:::info
- Add-ons are applied per Profile, so each Profile can have a different set of add-ons enabled.
:::
---
## Console for Debug
Use the console to monitor the variables and status of your script. You can see any errors or text generated by console.log (or context.log). This console is a simple and useful debugging tool.
## Console UI

- Console Area: displays errors and any output generated by your script.
- Clear Button: clears all output shown in the console area.
- Download Button: downloads the console output as a text file.
:::tip
[TagoAI](/docs/tagoio/tago-ai/) can also help debug your script: ask about errors or unexpected behavior and it will use the Analysis console as context.
:::
## How it works
1. Console Screen: any errors or responses generated by console.log (or context.log) will appear here.
2. Clear Console: clears everything currently shown in the console screen.
3. Download Console: downloads the console output as a text file.
## Example
Use console.log in your Analysis script to write to the console:
```javascript
console.log("Debug message:", myVariable);
```
You can learn more about Analysis [here](/docs/tagoio/analysis/).
---
## Creating Analysis
Creating your own analysis is easy. First, click on **Analysis** in the sidebar, then click the **+ New Analysis** button in the upper‑right of the Analysis main screen. Enter a name and configure the options described below to create your analysis.

## Name
This is the identifier for your analysis. You can use whitespace, numbers, and any other characters in this field. Choose a descriptive name that explains the purpose of the code.
## Runtime Version
Select the runtime environment for your analysis. An example shown in the dialog is:
- **Deno** (deno‑2025‑08‑01)
Currently supported runtimes include **Deno**, **Python**, and **Node.js** if you run your script on TagoIO.
## Run this script on
Choose where the analysis will run:
- **TagoIO**: Edit and run the script directly on TagoIO.
- **External**: Run the script on your own computer or server.
If you select the external option, we provide the following SDKs so you can run the analysis directly from your machine:
- [Python SDK](/docs/tagoio/analysis/sdk/python-sdk.md)
- [Javascript SDK](/docs/tagoio/analysis/sdk/nodejs-sdk.md) for Deno and NodeJS
:::warning
When changing between External mode and TagoIO mode, the Analysis Token will be reset and a new token will be generated.
:::
## Code example
You can optionally start your Analysis from a code example template. The Add Analysis dialog includes a dropdown to choose a code example. If you don't want a template, you can ignore this field.
:::tip
It is highly recommended for beginners to use an Analysis template, as it provides insight into the functions and scopes of the script.
You can only select one of these code examples if you are running your script on TagoIO.
:::
## Other fields
After creating the analysis you can open the [Script Editor](/docs/tagoio/analysis/script-editor.md) to add and manage the analysis code, adjust environment variables, and configure distribution or external execution as needed.
---
## Distributing Analysis
You can distribute your Analysis by generating a shareable link that lets others install their own copy through a customized install page. Each recipient gets a completely independent copy in their TagoIO account, which they can freely edit without impacting your original Analysis.
> Others will be able to edit the analysis that they received without impacting yours.
When other users access the link you sent, they will see a screen similar to the one shown below. The images and text on that install page are fully customizable.
To distribute an analysis, first click on the analysis menu of your admin panel, then select the desired analysis.
Now navigate to the distribute section of your analysis by clicking on the **Distribute** tab.

The **Distribute** function relies on creating a customized install page for anyone to install a copy of your analysis. To tailor this page you can modify the title, logo and main image in the corresponding fields. At the bottom of the page there is also a section where you can set values for the copy's environment variables; these default to the values defined in your own environment but can be changed without affecting your original settings.
After making your adjustments click the **Build template** button to save the information and build the page. The first time you do this, a new section will appear with the link that you can share. All you have to do is click the **Copy** button or manually copy the link and distribute it to anyone you would like.
It is always a good idea to access the link yourself before sharing to ensure the customized install page meets your standards.
---
## Environment Variables
Environment variables let you pass values into the execution context of your Analysis script. Use them to store tokens, account IDs, device IDs, or any other values that the script will use at runtime. The Analysis retrieves these values as environment parameters and exposes them to your code via the `context.environment` global object.

## How to use Environment Variables
Use environment variables to change how your code behaves. You can access them using the `context.environment` global variable in your Analysis code. Example (JavaScript):
```javascript
// Example: read an environment variable named MY_TOKEN
const myToken = context.environment.MY_TOKEN;
// Provide a default when necessary
const timeout = parseInt(context.environment.TIMEOUT || "30", 10);
```
In the environment variables UI (see image above):
1. New Environment variable: Add or remove an environment variable using the plus/minus buttons (marked as 1 in the image).
2. Variable Key: The variable name (marked as 2 in the image). This is the key you will use in `context.environment` (e.g., MY_TOKEN).
3. Variable Value: The value of the variable (marked as 3 in the image). Values can be integer or string; convert or parse values in your code as needed.
You can have up to **20 environment variables** per Analysis. If you exceed this limit, the UI will display a notification indicating that the maximum capacity has been reached.
If you need to store sensitive information such as passwords or API keys, consider using **Secrets**, which are encrypted environment variables designed for this purpose. Learn more about [Secrets](/docs/tagoio/security/secrets.md).
Note: After adding or editing environment variables in the Analysis editor, save or run the Analysis so the changes take effect.
---
## Analysis Overview(Analysis)
Analysis lets you run custom code inside TagoIO to process data in real time, call third‑party APIs, and work with your TagoIO resources.
Common things people do with Analyses:
- Convert units and run math on variables
- Write new values into other devices
- Read across devices to correlate events
- Send email, SMS, or webhooks, or push data back to a device or dashboard
- Automate user workflows to create devices, read files, and manage user tags
Analyses can be triggered by Actions (schedule or condition), by Dashboard UI elements, or by external services through API endpoints. They provide a simple way to plug your application logic into the rest of your solution.
## Environments
Analyses run in TagoIO as an asynchronous, serverless service. There's no strict queue order, and multiple executions can run at the same time. Usage counts against your profile's Analysis Run limits.
If you prefer to host code yourself, you can run External Analyses from your own infrastructure. TagoIO provides the [TagoIO CLI](https://github.com/tago-io/tagoio-cli) and [Javascript SDKs](/docs/tagoio/analysis/sdk/nodejs-sdk.md) for Deno/Node.js, and a [Python SDK](/docs/tagoio/analysis/sdk/python-sdk.md) to help with local development, packaging, and deployment.
## Runtimes
TagoIO supports three runtimes for Analysis. Each runtime has different approaches to dependency management and development workflows.
All runtimes support the same TagoIO SDK functionality for working with devices and other platform features. The main differences are in language features and how you manage external dependencies.
### Deno Runtime
Deno runs TypeScript and JavaScript with built-in TypeScript support: no compilation step needed. The standout feature is remote imports: you can import packages directly from URLs without any bundling or upload process.
```ts
import { Analysis, Device } from "npm:@tago-io/sdk";
import { DateTime } from "npm:luxon";
import { z } from "https://deno.land/x/zod/mod.ts";
```
This means you can write your entire Analysis in the TagoIO editor, add imports as you need them, and run immediately. The editor includes linting and formatting to help catch issues early.
Dependencies are fetched and cached on first run, so initial executions may take a bit longer.
### Node.js Runtime
Node.js runs JavaScript and requires you to bundle dependencies before uploading. Unlike Deno, you can't import npm packages directly in your Analysis code. You'll need to use the Analysis Builder CLI locally to create a bundle that includes all your dependencies.
```bash
# Local development workflow
npm install @tago-io/sdk axios
tagoio-builder pack
# Upload the generated .tago file
```
This approach gives you full control over your dependencies and build process, but requires local development tools. If you're already comfortable with Node.js workflows or have existing code to migrate, this runtime fits naturally.
### Python Runtime
Python supports [remote package installation](https://docs.astral.sh/uv/guides/scripts/#declaring-script-dependencies) at runtime. When your Analysis runs, it automatically installs any imported packages that aren't part of the standard library.
```python
# /// script
# dependencies = [
# "tagoio-sdk",
# #"pandas",
# #"requests<3",
# ]
# ///
from tagoio_sdk import Analysis, Device
import pandas as pd
import requests
from datetime import datetime, timedelta
```
Just write your imports and TagoIO handles the UV installation behind the scenes. This makes Python ideal for data processing tasks where you need libraries like pandas, numpy, or scipy without the hassle of packaging them yourself.
The runtime uses standard Python package resolution, so you can import from PyPI just like you would locally. First runs will be slower while packages install, but subsequent runs use the cached environment.
## Getting Started
Here's the short path to get an Analysis running:
1. **Create an Analysis and choose your runtime**
In your Admin, go to the [Analysis module](https://admin.tago.io/analysis), create an analysis and choose a runtime: Deno, Node.js, or Python based on your stack and dependency needs.
2. **Grant the needed permissions to your analysis.**
When you create an analysis, it does not have the necessary permissions to access data or run services. For that reason, you need to generate a policy that gives access only to operations and data within scopes your Analysis needs (e.g., users, devices, entities). Read more about [Creating a Policy](/docs/tagoio/tagorun/access-management/creating-a-policy.md).
3. **Code your script**
Use the [SDK](/docs/tagoio/analysis/sdk/) for your runtime to read/write data, call external APIs, and log output. Keep functions idempotent when possible.
4. **Add triggers (optional)**
1. [Actions](/docs/tagoio/actions/): schedule (cron) or condition (e.g., variable thresholds)
2. [Dashboards](/docs/tagoio/dashboards/): buttons, inputs, or other UI hooks
3. [External](/docs/tagoio/analysis/running-analysis-as-external-using-nodejs.md): expose an endpoint for webhooks
5. **Run and monitor**
Execute on demand to test, then let triggers handle it. Check logs, run history, and usage.
---
## Running Analysis as External using Deno
Analysis enables you to create custom applications on TagoIO. When creating an Analysis, you can choose to run it on TagoIO or externally on your own infrastructure.
Running an Analysis externally with Deno provides several advantages:
1. **Development Experience**: Full IDE support with TypeScript, debugging capabilities, and Deno's built-in development tools.
2. **Security**: Keep sensitive code on your infrastructure when compliance or security policies require it.
3. **Performance**: Direct control over the execution environment and resource allocation.
## 1. Install Deno
Deno is a secure runtime for JavaScript and TypeScript built on V8, Rust, and Tokio. It includes TypeScript support out of the box, a built‑in formatter and linter, and secure defaults. Learn more about [Deno](https://deno.land).
Visit the [Deno Installation Guide](https://deno.land/manual/getting_started/installation) for platform‑specific instructions.
## 2. Create your Analysis
### 2.1 Create a new file for your Analysis
For this example, call it `analysis.ts`:
```ts
import { Analysis, Device, Utils } from "npm:@tago-io/sdk";
import { DateTime } from "npm:luxon";
async function myAnalysis(context: any) {
// Your analysis logic here
console.log("Running external Deno analysis");
console.log("Context:", context);
const now = DateTime.now().toISO();
console.log("Current time:", now);
}
Analysis.use(myAnalysis, { token: "MY-ANALYSIS-TOKEN-HERE" });
```
## 3. Configure Permissions
Deno runs with secure defaults. Create a deno.json configuration file to specify permissions:
```ts
{
"tasks": {
"start": "deno run --allow-net --allow-env analysis.ts"
}
}
```
The `--allow-net` flag permits network requests to TagoIO APIs, and `--allow-env` allows reading environment variables.
## 4. Running your Analysis
Replace `MY-ANALYSIS-TOKEN-HERE` with your actual Analysis token. You can get this by:
1. Access your Analysis page at TagoIO
2. Select External for 'Run the scripts from'
3. Copy the generated token
For better security, use environment variables:
```ts
const token = Deno.env.get("ANALYSIS_TOKEN") || "MY-ANALYSIS-TOKEN-HERE";
Analysis.use(myAnalysis, { token });
```
Run your Analysis:
```ts
# Using the task defined in deno.json
deno task start
# Or directly
deno run --allow-net --allow-env analysis.ts
```
You should see output indicating the Analysis is connected and waiting for triggers.
## More Examples
Check our [Deno Analysis examples](https://github.com/tago-io/analysis-snippets/tree/main/snippets).
You now have everything needed to run external Analysis with Deno, using TypeScript support, remote imports, and modern development tools to create TagoIO applications.
---
## Running Analysis as External using Node.JS
Analysis is what allows you to build applications on TagoIO. When creating an analysis, you must choose whether it will run on TagoIO or externally on your own machine.
You may choose to run an analysis on an external machine for several reasons:
1. Development: While developing an analysis, it's best to use a full IDE and debugging mode, which are only available when running Node.js locally on your machine.
2. Security: Your company may have legal or compliance requirements that prevent uploading source code to TagoIO services.
3. Using third-party packages: The built-in analysis environment includes some packages (for example, axios and moment.js), but you may want to use additional npm packages. In combination with the Analysis-Builder, you can use any third-party package you like in your scripts for TagoIO.
Follow the steps below to get your analysis working in an external environment.
## 1. Install Node.js and NPM
Node.js is a widely used runtime for running JavaScript outside the browser. It is non-blocking and event-driven, making it suitable for data-intensive real-time applications. Learn more about [node.js](https://nodejs.org/).
Open the [Node.js Installation Guide](https://nodejs.org/en/download/) for instructions on how to install NPM and Node.js.
## 2. Create your Environment
To start writing your code, you must first set up the environment with Node.JS and NPM.
**2.1** Open your favorite command‑line tool like the Windows Command Prompt, PowerShell, Cygwin, Bash or the Git shell (which is installed along with Github for Windows). Then create or navigate to your new project folder.
**2.2** If the folder is empty, run `npm init` and fill in all prompted requests. If this is your first time, you can leave all defaults by just pressing enter.
**2.3** Now install the TagoIO SDK and the TagoIO‑Builder SDK:
```bash
npm install --save @tago-io/sdk
npm install -g @tago-io/builder
```
:::tip
To know how to use the SDK for coding an Analysis, use our SDK documentation.
:::
This will start the installation of the TagoIO SDK and the TagoIO‑Builder if you wish to upload your script later at TagoIO.
**2.4** Create a file called `analysis.js`. Open it with your favorite editor, like Visual Studio Code or even Notepad. You can write the code exactly as you would do on the TagoIO editor.
You can get the Hello World analysis example by clicking [here](https://raw.githubusercontent.com/tago-io/analysis-example-console/master/analysis.js).
## 3. Running your Analysis
After the code and the environment are set, you only need to make sure you put the right Analysis token into your analysis file.
To do that, every analysis has the following line below. Replace `MY-ANALYSIS-TOKEN-HERE` with your analysis token:
```js
module.exports = new Analysis(myAnalysis, { token: "**MY-ANALYSIS-TOKEN-HERE**" });
```
You can get the analysis token by accessing your analysis page at TagoIO and selecting **External** for "Runs on". Create a new analysis if you don't have any.

Now run the script from your command line:
```bash
node ./analysis.js
```
You should see a prompt similar to this:

Go back to TagoIO and click **Run** to run your script. You can also configure an [Action](/docs/tagoio/actions/running-analysis-via-action.md) to run your script automatically.

## 4. Use Analysis‑Builder to Upload the Script
If you only want to run the scripts on your machine, this step is not required. But if you want to build scripts to upload at TagoIO, with several folders, imports, and third-party packages, you must use the analysis-builder to compile the scripts.
To do that:
1. Ensure `@tago-io/builder` is installed globally:
```bash
npm install -g @tago-io/builder
```
2. In your terminal run:
```bash
analysis-builder my_script.js
```
3. Upload the generated `new_name.tago-io.js` file to your analysis at TagoIO.
## More Examples
You can check our analysis examples with the full environment already set up for you below:
- [Hello World Example](https://github.com/tago-io/analysis-example-console)
- [Average Min/Max Example](https://github.com/tago-io/analysis-example-avgMinMax)
- [Find Example](https://github.com/tago-io/analysis-example-find)
Now you have everything you need to know about running analysis on your machine and can start creating applications and Analyses using TagoIO.
---
## Script Editor
With the script editor, developers can create and edit their own scripts at TagoIO. These scripts are executed according to the configuration defined in the Analysis section.
To use the script editor, go to [Analysis](/docs/tagoio/analysis/), then create or edit an analysis. Set the name, runtime version, and choose to run it at TagoIO.
:::warning
The editor tab is only shown when you choose to run the script at TagoIO.
:::
By clicking on the Analysis, you can open the editor. Here are some of the options you can find:
- Upload scripts from your local machine
- Change the maximum runtime of the script
- Choose from one of our snippet examples
- Directly edit the script, environment variables, and more...

When you save the script, it will be deployed on TagoIO servers. You can then click **Run** to execute it immediately and view the results in the console below.
To learn more about our examples, you can visit our [Script Examples](/docs/tagoio/analysis/script-examples.md) article, or access the [Analysis](https://admin.tago.io/analysis), click on the "+ Add Analysis" button in the top right corner, and select a code example.
## TagoAI Assistant
The TagoAI Assistant helps you write, debug, and generate code for your Analysis scripts.
You can open it by clicking the star-shaped TagoAI button in the top-right corner of the script editor.
For an overview of the assistant, available modes, and privacy controls, see [TagoAI](/docs/tagoio/tago-ai/).

The assistant lets you:
- Ask questions about your current script.
- Get help writing or improving code.
- Debug issues by asking about errors or unexpected behavior using the Analysis console.
- Generate script snippets tailored to your needs.
---
## Script Examples
TagoIO provides a list of script examples to help you learn how to use the Analysis service. These examples show how to get and insert data into a device's data storage, send emails, execute calculations, and perform other useful tasks that can be added to your applications.

## Accessing the examples
To access the examples, [create an Analysis](/docs/tagoio/analysis/creating-analysis.md) and go to the "Analysis" tab. There you will find the option to **Load a snippet**.
After selecting an example, the code will be added to the body of the script screen.
---
## SDK
Use TagoIO's Software Development Kits to integrate TagoIO functionality into your applications.
## Available SDKs
### Node.js SDK
- [Node.js SDK](/docs/tagoio/analysis/sdk/nodejs-sdk.md) - JavaScript/Node.js integration
### Python SDK
- [Python SDK](/docs/tagoio/analysis/sdk/python-sdk.md) - Python integration
## Getting Started
Choose the SDK that matches your development environment:
- For JavaScript/Node.js applications, start with [Node.js SDK](/docs/tagoio/analysis/sdk/nodejs-sdk.md)
- For Python applications, start with [Python SDK](/docs/tagoio/analysis/sdk/python-sdk.md)
---
## Node.js SDK
You can access the Node.js SDK documentation [here](https://js.sdk.tago.io).
## Legacy (9.x.x) documentation
If you are looking for documentation for the old version (9.x.x), access it [here](https://tago-sdk-js-documentation.rtfd.io/).
> Note: The old SDK will be discontinued in 2030.
## Rate limits
When executing requests to TagoIO, there is a limit on the number of requests that can be made within a given time period. Read more about our [Rate Limits](/docs/tagoio/profiles/services/rate-limits-hard-limits.md).
---
## Python SDK
You can access the full SDK documentation at: https://py.sdk.tago.io/
## Installation
Install the Python SDK with pip:
```bash
pip install tagoio-sdk
```
## Quick Start
Basic example to access your account and list devices. Replace "your-token" with your account token:
```python
from tagoio_sdk import Account
account = Account({"token": "your-token"})
devices = account.devices.list()
print(devices)
```
## Remote Imports in Analysis
Python Analyses support remote imports. Import packages directly in your analysis script and they will be installed at runtime. Example imports:
```python
from tagoio_sdk import Analysis
from pandas import DataFrame
import requests
```
When executing requests to TagoIO, you will have a limit on the number of requests that can be made during a certain time period. Read more about our [Rate Limits](/docs/tagoio/profiles/services/rate-limits-hard-limits.md).
---
## API Overview
It is easy to connect devices, data sources, or third‑party apps to your account
using the TagoIO Application Programming Interface (API).
We provide APIs that give you full control to manage your accounts, data, devices, dashboards, and scripts. You can use the resources available in the Admin page to create, delete, or edit your accounts and dashboards, or you can perform all the same actions directly using the API.
Access the [API documentation](https://api.docs.tago.io/).
We follow RESTful principles. Before checking the API documentation, there are a
few details you should know.
:::tip
Looking for our [SDK](/docs/tagoio/analysis/sdk/)?
:::
## API response pattern
All responses from the TagoIO API follow a consistent pattern. You will
typically receive responses similar to the examples below:
```javascript
// For success:
{
"status": true,
"result": [...]
}
// For warning:
{
"status": true,
"result": {...},
"message": "Warning message"
}
// For error:
{
"status": false,
"result": {
"message": "Error message"
}
}
```
- **status**: boolean indicating general request success (true) or failure (false).
- **result**: contains the response data (array or object) or error details.
- **message**: optional text included for warnings or additional information on
failures.
For more details and endpoints, refer to the full
[API documentation](https://api.docs.tago.io/) and the SDK page above.
## Security
TagoIO takes the necessary steps to protect your data in the database and also
during the communication between our server and your devices.
All communication is performed through Hypertext Transfer Protocol Secure
(HTTPS) to avoid man‑in‑the‑middle and wiretapping attacks. Although HTTP can be
used, it is not recommended because it removes the security of authentication
and encryption provided by SSL/TLS protocols that are part of HTTPS. If you must
use HTTP without SSL, add `_ssl=false` in the header or URL query string.
## Tokens
There are four types of tokens: **Account‑Token**, **Device‑Token**,
**Analysis‑Token** and **Middleware‑Token**. You can generate all tokens from
the TagoIO admin or directly using the API. The type of token and its expiration
can also be defined. Add them in the header of your HTTP request:
| Header Key | Header Value |
| ---------------- | ----------------------------- |
| Account-Token | Only Account Token |
| Device-Token | Only Device Token |
| Analysis-Token | Only Analysis Token |
| Middleware-Token | Only Middleware Token |
| Authentication | Any Token (Account or Device) |
Check the [usage policy](/docs/tagoio/profiles/services/usage-policy.md) based
on the number of requests per a certain period.
## Regional endpoints
When making a request to the TagoIO API, you must also specify the appropriate
regional endpoint. The device data URL is `https://api..tago.io/data`
through port `443`. For other regions use:
```
https://api..tago.io/
```
Check out the available regions in the
[API documentation](/docs/api/sidebar/tagoio-api-intro).
---
## RESTful API
We provide RESTful API endpoints and documentation that's used by Analysis behind the scenes to perform requests to TagoIO. See [Restful API documentation](https://api.docs.tago.io/) and [Analysis](/docs/tagoio/analysis/) for more details.
## What you can do with the API
The RESTful API supports many operations, including (but not limited to):
- Creating, deleting, and editing **devices**
- Creating, deleting, and editing **notifications**
- Creating, deleting, and editing **users**
- Creating, deleting, and editing **actions**
## Token types
To use the RESTful API you must provide a token. There are three different types of tokens in TagoIO:
- `Device-Token`: Performs data requests. See [Device-Token documentation](/docs/tagoio/devices/device-token.md).
- `Account-token`: Performs entity requests, such as creating devices. See [Account-Token documentation](/docs/tagoio/profiles/account-token.md).
- `Network-token`: Performs data requests through a network connection; only useful if you are using your own network. See [Network integration documentation](/docs/tagoio/integrations/general/creating-a-network-integration.md).
## Rate limits
When executing requests to TagoIO, there is a limit on the number of requests that can be made during a certain time period. Read more about our [Rate Limits](/docs/tagoio/profiles/services/rate-limits-hard-limits.md).
---
## Access and Limits
AI Dashboards are experimental and intended for generation and use inside the TagoIO Admin. They do not support TagoRUN, public sharing, or distribution.
Use a Normal or Blueprint dashboard when you need a stable customer-facing dashboard or any of the unsupported delivery options listed below. Generation also depends on the profile AI settings, covered on this page.
## Experimental status
:::warning
Experimental. This feature may change or be removed.
:::
AI Dashboard is experimental. Expect changes to:
- Prompt behavior and generated layout quality
- Admin UI (prompt bar, starters, refresh controls)
- What is supported for share, distribute, and TagoRUN
Do not treat a generated layout as a permanent contract. Prefer regenerating when the product updates.
## AI availability
Generation uses the same AI path as [TagoAI](/docs/tagoio/tago-ai/): profile AI must be enabled and able to serve requests.
| Requirement | Where |
| ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| TagoAI / AI provider enabled | **Profile Settings → Services → AI Provider** |
| Credits, if generation runs on a paid model | [AI Credits](/docs/tagoio/tago-ai/ai-credits) balance and profile spend cap |
| Monthly allowance, if generation runs on the free model | Generation counts against the profile's monthly free model allowance ([TagoAI Service](/docs/tagoio/profiles/services/tago-ai-service)) |
| Own provider (optional) | [Using your own AI provider](/docs/tagoio/tago-ai/ai-provider); billed by that provider |
When AI is disabled on the profile, the create dialog **AI** tab is hidden. When AI is enabled but cannot serve requests (no credits, allowance used up, provider error):
- Generate and refine fail with the error naming the block, until AI can serve requests again.
- Existing layouts still load in the Admin; you just cannot produce a new version.
Disabling TagoAI on a profile does not delete dashboard resources. It only blocks new generation.
### Who can generate
Anyone who can edit the dashboard in Admin and whose profile AI path is live can use the prompt bar. Generation runs under that profile's AI configuration and scope. There is no separate "generate without AI" path.
## Unsupported
| Area | Behavior |
| ------------------ | ------------------------------------------------------------------------------------------------------------------- |
| **TagoRUN** | Not supported. Expect an empty or unsupported state for end users; do not publish AI Dashboards as RUN experiences. |
| **Share public** | Not supported. |
| **Distribute** | Not supported. |
| **Period presets** | Not supported. |
| **Widget grid** | Not present. Unlike Normal and Blueprint, you do not place widgets on a grid. |
Use [Normal](/docs/tagoio/dashboards/) or [Blueprint](/docs/tagoio/dashboards/blueprint-dashboard) when you need share, distribute, RUN portals, or a classic widget grid.
## Settings that still apply
These work like other dashboard resources:
- Name, description, [tags](/docs/tagoio/devices/tags-system), [icons](/docs/tagoio/dashboards/dashboard-icons)
- Profile visibility and team access in Admin
## Data and usage
- Loading dashboard data counts toward normal dashboard data usage ([Data Output for Dashboards](/docs/tagoio/dashboards/data-output-for-dashboards)).
- Generate / refine is recorded as AI usage and, on a paid model, spends AI credits ([AI usage statistics](/docs/tagoio/tago-ai/ai-usage)).
- On the free model, each generate or refine counts toward the profile's monthly free model allowance ([TagoAI Service](/docs/tagoio/profiles/services/tago-ai-service)).
- Interval refresh multiplies data fetches while the dashboard stays open; choose intervals deliberately.
## Practical guidance
- Prototype and internal Admin views first; keep customer portals on Normal or Blueprint when you need RUN, share, or distribute.
- Keep prompts and important layout notes outside the session-only chat if you need a record later ([Generating and Refining](/docs/tagoio/dashboards/ai-dashboard/generating.md#conversation-history)).
## Related
- [AI Dashboard overview](/docs/tagoio/dashboards/ai-dashboard/)
- [TagoAI](/docs/tagoio/tago-ai/)
- [TagoAI Service](/docs/tagoio/profiles/services/tago-ai-service)
---
## AI Dashboard
:::warning
Experimental. This feature may change or be removed.
:::
An AI Dashboard is a full-page dashboard built from a natural language prompt. You describe the charts and layout you want; TagoIO generates the view and loads data from your devices. Unlike [Normal](/docs/tagoio/dashboards/) and [Blueprint](/docs/tagoio/dashboards/blueprint-dashboard) dashboards, you do not place widgets on a grid. You work in the Admin prompt bar.
## Where to use it
| Context | Support |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| **Admin** | Yes: create, generate, refine, and view. |
| **TagoAI** | Required for generate / refine. If AI is off for the profile, the AI create tab is gated and new layouts cannot be produced. |
| **TagoRUN** | Not supported. |
| **Share public** | Not supported. |
| **Distribute** | Not supported. |
Use AI Dashboard for Admin prototyping and internal views while it is experimental. For customer portals, share links, templates, or a classic widget grid, use Normal or Blueprint instead.
## Why use it
- **Prompt instead of placing widgets.** Describe the dashboard in plain language; the model produces the layout. On Normal and Blueprint you build the same kind of view by adding widgets to a grid.
- **Refine by chat.** Iterate on layout, colors, charts, and copy without dragging widgets or rebuilding the page. See [Generating and Refining](/docs/tagoio/dashboards/ai-dashboard/generating.md).
- **Refresh live data.** Manual and interval refresh update the numbers without regenerating the layout. Generate again only when you want a new design. See [Data and Refresh](/docs/tagoio/dashboards/ai-dashboard/data-and-refresh.md).
- **No widget grid.** Presentation is one generated full-page layout, not tiles you arrange yourself.
## How it works
1. [Create](/docs/tagoio/dashboards/ai-dashboard/creating.md) an AI Dashboard from the create dialog (**AI** tab). Optionally copy an Example prompt for later.
2. Use the prompt bar to [generate or refine](/docs/tagoio/dashboards/ai-dashboard/generating.md) the layout. Conversation history stays in the browser session only.
3. After generation finishes, Admin loads data so charts and tables fill in.
4. Refresh data on demand or on an interval when you want updated numbers. Generate again only when you want a different layout.
## Compared to other dashboard types
| Type | How you build it | Presentation |
| ----------------------------------------------------------------- | ------------------------------------------------ | ------------------- |
| **Normal** | Drag-and-drop widgets on a grid | Widget grid |
| **[Blueprint](/docs/tagoio/dashboards/blueprint-dashboard)** | Same grid, slots resolve devices at runtime | Widget grid |
| **AI Dashboard** | Natural language generate / refine | Full-page generated |
| **[Custom Dashboard](/docs/tagoio/dashboards/custom-dashboard/)** | You upload the HTML; SQL bridge to saved queries | Full-page uploaded |
Normal and Blueprint are the standard path when you need share, distribute, RUN, or fine-grained widget control. AI Dashboard is for generating a full-page Admin layout from a prompt. Custom Dashboard is for a full-page Admin layout you author and upload yourself.
## Prerequisites
- [TagoAI](/docs/tagoio/tago-ai/) (or profile AI settings) must be available so generation can run. See [Access and Limits](/docs/tagoio/dashboards/ai-dashboard/access-and-limits.md).
- Device data you want the dashboard to chart, using names the model can match in your profile.
## Next steps
1. [Create an AI Dashboard](/docs/tagoio/dashboards/ai-dashboard/creating.md)
2. [Generate and refine with the prompt bar](/docs/tagoio/dashboards/ai-dashboard/generating.md)
3. [Understand data load and refresh](/docs/tagoio/dashboards/ai-dashboard/data-and-refresh.md)
4. [Check access, RUN, share, and experimental limits](/docs/tagoio/dashboards/ai-dashboard/access-and-limits.md)
---
## Creating an AI Dashboard
:::warning
Experimental. This feature may change or be removed.
:::
You create an AI Dashboard from the same create flow as other dashboard types. The AI tab is available only when generation is allowed for the profile.
## Create from the dialog
1. In Admin, open the dashboard list and click **+** (or the control that opens the create dialog).
2. Open the **AI** tab.
3. Enter a name.
4. Optionally copy an **Example prompt** from the list (temperature and humidity, fleet tracking, and similar). Copy only puts the text on the clipboard; it does not run generation at create time.
5. Save.
Create opens the new AI Dashboard with the prompt bar ready and no layout yet. Paste a copied example or write your own prompt there, then generate. See [Generating and Refining](/docs/tagoio/dashboards/ai-dashboard/generating.md).
### When the AI tab is gated
The AI tab is hidden when AI is off or unavailable for the profile:
- TagoAI (or the profile AI provider) is disabled under **Profile Settings → Services → AI Provider**.
- The feature is not enabled for the account or region.
- Monthly AI limits or provider errors block generation.
Fix AI availability first, then create or open the dashboard again. Details: [Access and Limits](/docs/tagoio/dashboards/ai-dashboard/access-and-limits.md) and [TagoAI](/docs/tagoio/tago-ai/).
## Settings after create
AI Dashboards support the usual resource metadata in settings:
| Setting | Notes |
| -------------- | ----------------------------------------------------------------------------- |
| **Name** | Display name in the sidebar and lists. |
| **Tags** | `{ key, value }` labels for grouping and Access Management. |
| **Visibility** | Same profile visibility rules as other dashboards. |
| **Icon** | [Dashboard icons](/docs/tagoio/dashboards/dashboard-icons) work the same way. |
### What is not available on AI Dashboards
| Feature | Status on AI Dashboard |
| -------------------------------------- | ---------------------------------------------------------------------- |
| Period presets / global time filter UI | Not supported. |
| Share public | Not supported. |
| Distribute (template export) | Not supported. |
| Widget grid edit mode | Not applicable; there is no widget grid. |
| TagoRUN | Not supported; RUN users do not get a working AI Dashboard experience. |
For share and distribute on other types, see [Sharing Dashboards](/docs/tagoio/dashboards/sharing-dashboards) and [Distributing Dashboards](/docs/tagoio/dashboards/distributing-dashboards).
## After creation
1. [Generate or refine](/docs/tagoio/dashboards/ai-dashboard/generating.md) the layout with the prompt bar.
2. Confirm [data loads and refresh](/docs/tagoio/dashboards/ai-dashboard/data-and-refresh.md) look right.
3. Keep [access, AI gating, and experimental limits](/docs/tagoio/dashboards/ai-dashboard/access-and-limits.md) in mind before relying on it in production.
---
## Data and Refresh
An AI Dashboard keeps **layout** (what generate produces) separate from **data** (live values from your devices). You control both from Admin: the prompt bar for layout, and refresh for numbers.
## After generate
When a generate or refine finishes:
1. Admin shows the new layout.
2. Data loads automatically so charts and tables fill in.
3. If charts stay empty, check devices and variable names, then try a manual refresh or refine the prompt. See [Empty charts](#empty-charts) below.
## Manual refresh
Use the dashboard refresh control when you want updated numbers without changing the layout.
- Reloads data for the current layout.
- **Does not** regenerate the layout or change the design.
Use this after devices report new points, or when charts look stale.
## Interval refresh
You can enable interval refresh so Admin reloads data on a timer while the dashboard stays open.
- Same idea as manual refresh: new numbers only, same layout.
- Interval applies while the view is active; leaving the dashboard stops the timer for that session.
- Pick an interval that matches how often your data actually updates. Aggressive intervals burn more data output for little gain when values change slowly.
Exact interval options follow the Admin UI for this dashboard type.
## Generate vs refresh
| Action | Layout | Data |
| ---------------------------- | ------------------------------- | ------------------------------------- |
| First open / navigate back | Shows the last generated layout | Loads data for that layout |
| Manual or interval refresh | Unchanged | Reloads data |
| Successful generate / refine | Replaces the layout | Loads data for the new layout |
| Failed generate | Previous layout stays | Unchanged until a successful generate |
Generating always replaces the presentation. If charts look wrong after a refine, try a refresh first, then adjust the prompt if the layout is still off.
## Empty charts
If the layout renders but charts or tables are empty:
- Confirm devices and variables exist under the names you used in the prompt.
- Check that the profile can read those resources.
- Refresh data once after devices report new points.
- Refine the prompt to name the correct variables, tags, or time window.
Data reads count toward normal dashboard data usage. See [Data Output for Dashboards](/docs/tagoio/dashboards/data-output-for-dashboards).
---
## Generating and Refining
:::warning
Experimental. This feature may change or be removed.
:::
Generation turns a natural language prompt into a new dashboard layout. Refinement uses the same prompt bar: you ask for changes, and the model produces an updated layout.
## Prompt bar
The prompt bar sits on the AI Dashboard view in Admin. Use it to:
- **Generate** a first version (or a full rewrite) of the layout.
- **Refine** layout, charts, labels, colors, or which data appears.
Write what you want in plain language. Good prompts name devices or variables, the kind of charts, and any layout constraints:
```text
Fleet overview for devices tagged type=sensor: last temperature and battery
as cards, a 24h line chart of temperature, and a table of devices with
battery below 20%. Dark theme, compact header.
```
Vague prompts still run, but the model has less to anchor on. Prefer concrete variables, time ranges, and chart types.
## What generation does
When you generate or refine from the prompt bar:
1. Your prompt (and recent session conversation context) goes to the configured AI path.
2. A new layout is stored on the dashboard.
3. Admin shows the new layout and loads data so charts fill in.
Only **generate** replaces the layout. Refreshing data alone leaves the design in place. See [Data and Refresh](/docs/tagoio/dashboards/ai-dashboard/data-and-refresh.md).
## Refining
After the first layout exists, keep using the prompt bar to iterate:
- "Make the temperature chart full width."
- "Add humidity next to temperature on the same line chart."
- "Use a lighter background and larger titles."
- "Drop the map; keep the table and KPIs only."
Each successful refine replaces the previous layout. Data reloads for the new design.
Review the result the way you would review a teammate's layout change. Generated output can be wrong for your data shape; correct with another prompt, or start over if the layout is far off.
## Conversation history
The generate/refine chat is **session-only**:
- History lives in the browser for the current Admin session while you work on that dashboard.
- It is **not** a full server-side chat store (unlike the main [TagoAI](/docs/tagoio/tago-ai/chat) assistant history).
- Closing the tab, signing out, or starting a fresh session drops that conversation context.
- The saved layout on the dashboard remains; only the chat thread is ephemeral.
If you need a durable record of prompts, copy them out yourself before the session ends.
## When generation fails or is blocked
Generation depends on [TagoAI / profile AI settings](/docs/tagoio/tago-ai/). Common blocks:
| Situation | What to do |
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| AI disabled on the profile | Enable under **Profile Settings → Services → AI Provider**. |
| Out of AI credits on a paid model | Buy credits, raise the profile spend cap, or change the dashboard model to the free one. The model used for AI Dashboards is part of the profile's AI settings, not a per-dashboard or per-prompt choice, so switching means changing the model selected on the profile and it applies to every AI Dashboard there. See [AI Credits](/docs/tagoio/tago-ai/ai-credits). |
| Monthly free model allowance reached | Wait for the next monthly cycle, or set the profile's dashboard model to a paid one, billed with credits or through your own provider. The free model always counts against the allowance, even on a profile with its own provider. See [TagoAI Service](/docs/tagoio/profiles/services/tago-ai-service). |
| Provider error or timeout | Retry; if it persists, check provider keys and status. |
| Feature unavailable | AI Dashboard may be experimental or region-gated; see [Access and Limits](/docs/tagoio/dashboards/ai-dashboard/access-and-limits.md). |
Failed generates leave the previous layout (if any) in place until a successful generate completes.
## Tips
- Start with one clear layout goal; refine in small steps rather than rewriting everything in one long prompt.
- Name variables and tags the way they exist on your devices so the generated layout can match real data.
- After a big structural change, use [manual refresh](/docs/tagoio/dashboards/ai-dashboard/data-and-refresh.md) to confirm numbers still line up.
- Treat AI Dashboard as experimental: prompts and output quality will evolve.
---
## Blueprint Dashboard
Blueprint is a type of [Dashboard](/docs/tagoio/dashboards/) that links [widgets](/docs/tagoio/widgets/) to devices at runtime. Instead of building one dashboard per device or user, you build one reusable layout and let each blueprint slot resolve to a real device when the dashboard loads.
The same dashboard then scales across many devices, sites, or customers. Blueprint does not control access on its own: which users can open a shared dashboard, and which devices and users they can reach through it, is governed by [Access Management](/docs/tagoio/tagorun/access-management/).

## Before you start
Before creating a Blueprint Dashboard, make sure you have:
- Devices already created in TagoIO.
- [Tags](/docs/tagoio/devices/tags-system.md) applied to the devices you want each slot to show.
- The same variable names across devices that share a slot.
- [Access Management](/docs/tagoio/tagorun/access-management/) policies set up so end users can access the dashboard and Entities, Devices, or Users it displays.
Variable names matter. If a widget reads the `pressure` variable, every device the slot can resolve to should send data under `pressure`. A device that uses `pump_pressure` instead leaves that widget blank when it is selected. This is one of the most common reasons a dashboard works for one device but not another.
## Concepts
### Blueprint slots
A blueprint slot is a placeholder data source in the dashboard. Instead of pointing a widget at one fixed device, you point it at a slot. The slot resolves to whichever device the user picks from a dropdown at the top of the dashboard, and changing that selection reloads every widget bound to the slot.
Each slot has an **identifier** (the name shown in the widget data-source picker) and a **tag filter** (the tag key and value that decide which devices appear in its dropdown). You can add up to 20 slots per dashboard.

### Blueprint Devices and Entities
[Blueprint Devices](/docs/tagoio/devices/blueprint-devices-entities.md) use devices as their data source. Blueprint Entities work the same way but use [Entities](/docs/tagoio/entities.md) instead. Not all widgets support entities: check the widget's data-source picker to see if it appears as an option.
### Data scope across slots
Each slot keeps its own selection. When the user changes the device in one slot, TagoIO reloads only the widgets and series bound to that slot. A widget bound to the `Pump` slot never reads data from the device selected in the `Tank` slot. If a widget supports more than one data source, each series follows the slot assigned to it.
By default, slots resolve independently: the device picked in one slot does not change the options in another. Filter conditions change that, as covered next.
### Filter conditions
Filter conditions link one slot's options to the device selected in another slot. In **Blueprint Settings**, open a slot's menu (the gear icon) and choose **Filter conditions** to open the configuration modal.
Each condition reads as **IF (association)** ... **MATCHES (tag)** ...: the slot only shows devices whose tag value matches the device selected in the association you point it at. Tag both sides with a shared key, and one dropdown drives the other instead of resolving on its own.
For example, say a dashboard has two slots, both filtered by the `device_type` tag: an `organization` slot (`device_type=organization`) and a `sensor` slot (`device_type=sensor`). You want the sensor list to depend on the organization, so that picking an organization shows only its sensors.

Give each organization device an `organization_id` tag, and tag every sensor with the `organization_id` of the organization it belongs to. Then open the `sensor` slot's gear menu, choose **Filter conditions**, and add a filter: **IF (association)** `organization` **MATCHES (tag)** `organization_id`.

Now selecting an organization filters the sensor dropdown to the sensors whose `organization_id` matches it. Sensors from other organizations stay hidden until their organization is selected.
## Set up a Blueprint Dashboard
This walkthrough builds one dashboard for a water treatment facility: pump data in one slot, tank data in another, both selectable at runtime.
### 1. Tag your devices
Slots filter devices by [tag key and value](/docs/tagoio/devices/tags-system.md), so decide on a tag scheme first. This example uses four devices:
| Device | Variables | Tags |
| ----------- | ----------------------- | ----------- |
| Main Pump | `pressure`, `flow_rate` | `type=pump` |
| Backup Pump | `pressure`, `flow_rate` | `type=pump` |
| Tank 1 | `level`, `temperature` | `type=tank` |
| Tank 2 | `level`, `temperature` | `type=tank` |
Apply the tags under [**Devices**](/docs/tagoio/devices/) **> (select device) > Tags**.
### 2. Create the blueprint slots
Open the dashboard's edit page and go to **Blueprint Settings**. For more on this screen, see [Blueprint Devices and Entities](/docs/tagoio/devices/blueprint-devices-entities.md). Add one slot for each device type:
| Identifier | Tag Key | Tag Value |
| ---------- | ------- | --------- |
| Pump | `type` | `pump` |
| Tank | `type` | `tank` |
The identifier is the name shown in the widget data-source picker, so pick something that makes the slot's role clear.
### 3. Bind widgets to slots
Open a [widget's](/docs/tagoio/widgets/) settings and go to its data source. The dropdown lists your blueprint identifiers (`Pump`, `Tank`). Assign each widget to the slot that holds its data:
| Widget | Slot | Variable |
| ---------------------- | ---- | ------------- |
| Gauge (Pump Pressure) | Pump | `pressure` |
| Line chart (Flow Rate) | Pump | `flow_rate` |
| Gauge (Tank Level) | Tank | `level` |
| Dial (Water Temp) | Tank | `temperature` |
Widgets bound to `Pump` show data from the selected pump, and widgets bound to `Tank` show data from the selected tank.
### 4. Test the dashboard
Open the dashboard and switch the Pump dropdown between Main Pump and Backup Pump, then switch the Tank dropdown between Tank 1 and Tank 2. Each switch should reload only the widgets bound to that slot.
## Troubleshooting
### The device selector is empty
Check that the devices carry the tag the slot filters on. The tag key and value must match the slot configuration exactly.
### A widget shows no data after selecting a device
Check that the selected device sends the variable the widget reads. Devices that share a slot should use the same variable names.
### A user sees devices they should not access
Slots decide which devices can appear based on tags; they do not grant access. Review the device and user tags, along with the [Access Management](/docs/tagoio/tagorun/access-management/) policies that match users to the devices they are allowed to see.
### The wrong widgets update when I change a slot
Open each widget's settings and confirm it is bound to the right blueprint identifier.
### A filter condition does not narrow the dependent dropdown
Check that both slots' devices carry the shared tag, and that the filter condition points at the right association and tag. If the matched tag value differs between the two devices, they will not link.
---
## Creating Dashboard Tabs
By using **Tabs**, you can divide [dashboards](/docs/tagoio/dashboards/) into multiple sections and add links to them. This is a great feature to create applications where the user has to deal with some setups and data in the same dashboard.

Notice that in the example above we have three tabs: the dashboard itself, a hidden tab, and a link tab.
## Creating & Editing a Tab
To create a **tab** on your application, go to the dashboard you want to create it in, click the edit button to enter **edit mode**, and then click on the settings icon next to the tabs.

Once the tabs section opens, click the **Add Tab** button.
To create a tab, you must set the name of the tab and select its type. The type can be one of the following:
- **Dashboard**: The tab will be able to contain widgets.
- **Link**: The tab will be a link to another dashboard or another web page. You **cannot** put widgets inside of this tab.
- **Hidden tab**: The tab will be able to contain widgets, but it will be hidden from your users. You will only be able to access this tab in **edit mode**. A good use of this option is to use it with the [Widget Header](/docs/tagoio/widgets/general/widget-header.md).

:::info
You can add up to **10** tabs in your dashboard.
:::
## Visibility Conditions
You can configure the tabs on your dashboard to restrict access for your end users on [TagoRUN](/docs/tagoio/tagorun/) according to the **tags** you defined in the [User Management](/docs/tagoio/tagorun/access-management/) module. This functionality is very useful since you can have fine-grained control over which tabs are displayed for different types of users without having to duplicate the dashboards for different user roles.
> In order to configure the visibility conditions, you will need to access your dashboard settings, then Tabs and click on the eye icon.
For instance, you can have a dashboard where one of the tabs is only displayed for users that match the tag with key **user_access** and value **admin**.

---
## Authoring HTML
:::warning
Experimental. This feature may change or be removed.
:::
Your shell is a normal HTML document loaded in an Admin iframe. The host talks to it with `window.postMessage`.
There is no custom-widget library and no requirement to host the page yourself.
To list and run saved TagoSQL queries, use the request/response bridge in [SQL Bridge](/docs/tagoio/dashboards/custom-dashboard/data-sources.md). This
page covers ready, theme, and style only.
## Contract
### 1. Shell signals ready
When your page has registered message listeners and is ready:
```js
parent.postMessage({ type: "dashboard:ready" }, "*");
```
Post ready **after** you attach the `message` listener so the first host reply is not dropped.
### 2. Host sends theme and optional style
The host listens for `dashboard:ready` from your iframe, then posts:
| Message type | Payload | Purpose |
| ----------------- | ----------- | ------------------------------------------------ |
| `dashboard:theme` | `{ theme }` | `"dark"` or `"light"` to match Admin. |
| `dashboard:style` | `{ style }` | Optional host-driven style object. May be empty. |
Theme may be sent again when the Admin color scheme changes, without a full shell reload.
### 3. Data over request/response
The shell **initiates** data loads. Post `dashboard:request` for `sql.list` and `sql.run`; the host answers
with `dashboard:response` (same `id`). Full shapes, errors, and examples: [SQL Bridge](/docs/tagoio/dashboards/custom-dashboard/data-sources.md).
## Minimal example
```html
Custom Dashboard
Custom Dashboard
theme: ...
```
This matches the snippet shown on the empty state in Admin. For fetching data, add the request helper from
[SQL Bridge](/docs/tagoio/dashboards/custom-dashboard/data-sources.md).
## Origin checks
There are two directions. Do not mix them up:
| Who listens | What `event.origin` is | What to allowlist |
| ---------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------- |
| **Your shell** (`message` in the iframe) | The **Admin** parent window that posts theme, style, and responses | Admin origins for your region (for example `https://admin.tago.io`), not the API host |
| **Admin host** (`message` on the parent) | Your iframe document origin | The API host that serves `GET /dashboard/{id}/html` |
Admin posts into the iframe with the API origin as the postMessage _target_ so the browser only delivers to
that document. The _sender_ origin on those messages is still Admin. If your shell allowlists only the API
host, every legitimate `dashboard:theme`, `dashboard:style`, and `dashboard:response` message is dropped.
Ignore messages from origins you do not expect.
## What not to do
- Do not expect the custom-widget SDK handshake or realtime variable streams. That is a different product
surface; see [Custom Dashboard vs Custom Widget](/docs/tagoio/dashboards/custom-dashboard/custom-dashboard-vs-custom-widget.md).
- Do not embed profile tokens in the HTML file. Data comes through the postMessage request/response bridge
you initiate; Admin uses the signed-in session on the host side.
- Do not rely on an in-app editor: download, edit, upload ([HTML shell](/docs/tagoio/dashboards/custom-dashboard/html-shell.md)).
## Sandbox
Admin loads the shell in an iframe with a restricted sandbox (`allow-scripts` and `allow-same-origin` in the
current host). Design for that environment: no parent DOM access beyond postMessage, and careful use of
storage and navigation APIs.
---
## Creating a Custom Dashboard
:::warning
Experimental. This feature may change or be removed.
:::
Custom Dashboards are created in TagoIO Admin in the same place as other dashboards, with a dedicated type tab.
## Steps
1. In Admin, click **+** in the left sidebar (or open the create dashboard flow from your usual entry point).
2. Open the **Custom HTML** tab.
3. Enter a name and create the dashboard.
This creates an empty Custom Dashboard. You do not pick a widget template, and there is no generate step.
The type on the resource is `"custom"`.
## What happens next
After create, open the dashboard:
1. **Upload HTML** from the empty state (or later from the gear menu: **Upload new HTML**). See
[HTML shell](/docs/tagoio/dashboards/custom-dashboard/html-shell.md).
2. Post `dashboard:ready` and handle theme (and optional style) in your page. See
[Authoring HTML](/docs/tagoio/dashboards/custom-dashboard/authoring-html.md).
3. Load data through the postMessage [SQL bridge](/docs/tagoio/dashboards/custom-dashboard/data-sources.md): list and run saved
[TagoSQL](/docs/tagoio/tagosql/) queries from the shell.
The empty state also expands a short postMessage snippet so authors know the contract before the first
upload.
## Admin only
Custom Dashboards run in **Admin**. They are not available in [TagoRUN](/docs/tagoio/tagorun/). Public share
and distribute are not supported for this type. If you need end-user portals or widget-based sharing,
use a Normal or [Blueprint](/docs/tagoio/dashboards/blueprint-dashboard) dashboard with
[Custom Widgets](/docs/tagoio/widgets/custom-widget/) instead.
## Compared to other create tabs
| Tab | Result |
| --------------- | --------------------------------------------------------------------------------- |
| **Normal** | Widget dashboard bound to fixed devices and variables. |
| **Blueprint** | Widget dashboard with tag-based device slots. |
| **Custom HTML** | Full-page HTML shell you upload; data via postMessage SQL bridge (saved queries). |
For the product overview, see [Custom Dashboard](/docs/tagoio/dashboards/custom-dashboard/).
---
## Custom Dashboard vs Custom Widget
:::warning
Custom Dashboard is experimental. This feature may change or be removed.
:::
Both let you ship your own HTML, but they solve different jobs. Pick the wrong one and you will fight the
platform (or hit limits that look like bugs).
## Side by side
| | **Custom Dashboard** | **Custom Widget** |
| --------------- | ---------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| **Scope** | The whole dashboard is your HTML page. | One tile on a Normal or Blueprint dashboard. |
| **Hosting** | Upload one HTML file to TagoIO. TagoIO hosts the uploaded file (versioned). | You host the URL yourself (for example [Files](/docs/tagoio/files/)). |
| **Data** | postMessage SQL bridge: list and run saved TagoSQL queries. | Device variables, resources, realtime, send data, Analysis, full widget data model. |
| **Protocol** | `dashboard:ready`, `dashboard:theme`, optional `dashboard:style`; data via `dashboard:request` / `dashboard:response`. | Full [custom-widget](https://github.com/tago-io/custom-widget) library handshake. |
| **RUN / share** | Admin only. No TagoRUN, public share, or distribute. | Supported with widget dashboards (subject to your Run and share setup). |
| **Editor** | No in-app HTML editor. Download, edit offline, re-upload. | Edit and host outside Admin; point the widget at the URL. |
| **Create UI** | Create dialog tab **Custom HTML** (type `"custom"`). | Add a Custom Widget on a Normal or Blueprint dashboard. |
## Choose a Custom Dashboard when
- You need a **full-page** app or report, not a cell in a widget grid.
- **Saved TagoSQL queries** over the postMessage bridge are enough for the page.
- Viewers are **Admin** users.
- You want TagoIO to **host and version** the HTML file.
Start at [Custom Dashboard](/docs/tagoio/dashboards/custom-dashboard/), then [Authoring HTML](/docs/tagoio/dashboards/custom-dashboard/authoring-html.md)
and [SQL Bridge](/docs/tagoio/dashboards/custom-dashboard/data-sources.md).
## Choose a Custom Widget when
- The custom UI is **one component** among other TagoIO widgets.
- You need **device variables, realtime, send data, or Analysis** hooks.
- The dashboard must work in **TagoRUN**, share, or distribute flows.
- You already host static assets (or will use Files) and can point a widget at a URL.
See [Custom Widget Overview](/docs/tagoio/widgets/custom-widget/).
## Common mix-ups
| Assumption | Reality |
| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| "Custom HTML" in create means a Custom Widget. | That tab creates a **Custom Dashboard** (type `"custom"`). Widgets are added inside Normal or Blueprint dashboards. |
| Custom Dashboard can use the custom-widget library the same way. | Different protocol. Custom Dashboard uses `dashboard:*` messages only. |
| Custom Dashboard binds devices like a Normal dashboard. | Data is **saved TagoSQL queries** via the postMessage SQL bridge, not device-variable widgets. |
| Custom Dashboard is available to Run end users. | **Admin only**. |
## Related
- [Creating a Custom Dashboard](/docs/tagoio/dashboards/custom-dashboard/creating.md)
- [SQL Bridge](/docs/tagoio/dashboards/custom-dashboard/data-sources.md)
- [Custom Widget Overview](/docs/tagoio/widgets/custom-widget/)
- [Custom Widget parameters](/docs/tagoio/widgets/custom-widget/custom-widget-parameters)
---
## Custom Dashboard
:::warning
Experimental. This feature may change or be removed.
:::
A **Custom Dashboard** is a dashboard whose entire canvas is an HTML page you write. You upload a single HTML
file (the **shell**). Admin hosts it. Your page talks to Admin only through `window.postMessage`: ready,
theme, style, and a request/response [SQL bridge](/docs/tagoio/dashboards/custom-dashboard/data-sources.md) to list and run saved TagoSQL queries.
In the create dialog the tab is labeled **Custom HTML**. The dashboard type string is `"custom"`.
Custom Dashboard is not a Custom Widget. A Custom Widget is one tile on a Normal or Blueprint dashboard.
A Custom Dashboard replaces the whole dashboard with your page. See
[Custom Dashboard vs Custom Widget](/docs/tagoio/dashboards/custom-dashboard/custom-dashboard-vs-custom-widget.md).
## Why use it
- **Own the full surface.** Layout, charts, and interaction are your HTML, CSS, and JavaScript, not a grid of
TagoIO widgets.
- **Saved queries over postMessage.** List and run [TagoSQL](/docs/tagoio/tagosql/) queries saved on the
profile. No free-form SQL from the embed; Admin fulfills each call with the signed-in session.
- **Simple host protocol.** Signal ready, receive theme, request data. No custom-widget library handshake.
- **Versioned shell.** TagoIO hosts the uploaded file (versioned). Dashboard
[version restore](/docs/tagoio/dashboards/restoring-your-dashboard-to-a-previous-version) can bring back a
previous shell.
## Where to use it
- **Admin only.** Custom Dashboards run in TagoIO Admin. They are not available in
[TagoRUN](/docs/tagoio/tagorun/). Public share and distribute are not supported for this type.
- **Full-page apps and reports.** Use a Custom Dashboard when the whole canvas should be your HTML, not a
single tile on a widget grid. For a component inside a Normal or Blueprint layout, use a
[Custom Widget](/docs/tagoio/widgets/custom-widget/) instead.
- **Query-driven pages.** Use the [SQL bridge](/docs/tagoio/dashboards/custom-dashboard/data-sources.md) when the page needs profile data through saved
queries. Skip requests if the shell is self-contained.
## What you get
| Area | Behavior |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| Shell | One uploaded HTML file. Upload and download from the gear menu after create. |
| Data | postMessage SQL bridge: `sql.list` and `sql.run` on saved TagoSQL queries. |
| Host messages | `dashboard:ready` from the shell; host sends `dashboard:theme` and optional `dashboard:style`; data via `dashboard:request` / `dashboard:response`. |
| Audience | Admin only. Not available in TagoRUN. No public share or distribute. |
| Editor | No in-app HTML editor. Edit offline, re-upload. |
## How it works
1. [Create](/docs/tagoio/dashboards/custom-dashboard/creating.md) a Custom Dashboard from the create dialog (**Custom HTML** tab).
2. On first open, [upload](/docs/tagoio/dashboards/custom-dashboard/html-shell.md) your HTML shell. Until then you see an empty state with **Upload HTML**
and a short author note.
3. [Author](/docs/tagoio/dashboards/custom-dashboard/authoring-html.md) the page to post `dashboard:ready` and handle `dashboard:theme` (and optional
style).
4. Load data with the [SQL bridge](/docs/tagoio/dashboards/custom-dashboard/data-sources.md): list saved queries, run them, render the results in your
page.
## Related
- [Creating a Custom Dashboard](/docs/tagoio/dashboards/custom-dashboard/creating.md)
- [HTML shell](/docs/tagoio/dashboards/custom-dashboard/html-shell.md)
- [SQL Bridge](/docs/tagoio/dashboards/custom-dashboard/data-sources.md)
- [Authoring HTML](/docs/tagoio/dashboards/custom-dashboard/authoring-html.md)
- [Custom Dashboard vs Custom Widget](/docs/tagoio/dashboards/custom-dashboard/custom-dashboard-vs-custom-widget.md)
- [Dashboard overview](/docs/tagoio/dashboards/)
- [TagoSQL](/docs/tagoio/tagosql/)
---
## SQL Bridge
:::warning
Experimental. This feature may change or be removed.
:::
Your Custom Dashboard HTML talks to TagoIO only through `window.postMessage` with its parent (Admin). Use
that channel to **list** and **run** [saved TagoSQL queries](/docs/tagoio/tagosql/queries) on the profile.
There is no free-form SQL from the embed, and the host does not inject query results until your page
requests them.
The shell supplies `params` values itself. The host does not inject dashboard or user identity (no
host-context binding); use TagoSQL session functions for identity inside SQL where supported.
Admin fulfills each request with the signed-in user's session against existing APIs. Custom Dashboard is
Admin only: not available in [TagoRUN](/docs/tagoio/tagorun/), and public share and distribute are not
supported.
Host messages for ready, theme, and style stay on the shell side. See [Authoring HTML](/docs/tagoio/dashboards/custom-dashboard/authoring-html.md) for
`dashboard:ready`, `dashboard:theme`, and `dashboard:style`. This page covers the SQL request/response bridge
only.
## Envelope
### Embed to host (request)
```json
{
"type": "dashboard:request",
"id": "unique-string",
"op": "sql.list",
"payload": {}
}
```
| Field | Required | Meaning |
| --------- | -------- | ---------------------------------------------------------------------- |
| `type` | Yes | Always `"dashboard:request"`. |
| `id` | Yes | Any unique string your page picks. The host echoes it on the response. |
| `op` | Yes | Operation name (see below). |
| `payload` | Depends | Op-specific body. Use `{}` or omit when the op takes no fields. |
A malformed request **without** `id` is silently dropped. When `id` is present but the request is otherwise
malformed, the host responds with `bad_request`.
### Host to embed (response)
```json
{
"type": "dashboard:response",
"id": "unique-string",
"ok": true,
"result": {}
}
```
On failure:
```json
{
"type": "dashboard:response",
"id": "unique-string",
"ok": false,
"error": { "code": "not_found", "message": "..." }
}
```
| Field | Meaning |
| -------- | --------------------------------------------------------------------------- |
| `type` | Always `"dashboard:response"`. |
| `id` | Same string as the request. |
| `ok` | `true` with `result`, or `false` with `error`. |
| `result` | Present when `ok` is true. Shape depends on `op`. |
| `error` | Present when `ok` is false. Includes `code` and a human-readable `message`. |
Match responses to in-flight calls by `id`. Ignore responses whose `id` you did not send.
## Operations
| Op | Payload | Result on success |
| ---------- | ----------------------- | ------------------------------------------------------------------------------------------------------------- |
| `sql.list` | None / `{}` | `{ queries: [{ id, name }] }` for the profile's saved queries. |
| `sql.run` | `{ query_id, params? }` | `{ columns: string[], rows: object[], meta: { row_count, execution_ms, served_from_cache } }` (bridge shape). |
Only **saved** queries are available. The embed cannot send a SQL string to run.
### `sql.list`
Lists saved TagoSQL queries on the profile.
```js
const { queries } = await request("sql.list");
// queries: [{ id: "...", name: "..." }, ...]
```
### `sql.run`
Runs one saved query by id. Optional `params` override the query's saved default parameters for this run.
```js
const result = await request("sql.run", {
query_id: "...",
params: [{ key: "$1", value: "30" }],
});
// result: {
// columns: string[],
// rows: object[], // each row is an object keyed by column name
// meta: { row_count, execution_ms, served_from_cache }
// }
```
| Field | Required | Meaning |
| ---------- | -------- | ---------------------------------------------------------------------- |
| `query_id` | Yes | Id of a saved query from `sql.list` (or known from your profile). |
| `params` | No | Array of `{ key, value }` pairs. Keys match placeholders such as `$1`. |
When you omit `params`, the query runs with its stored defaults. See
[TagoSQL parameters](/docs/tagoio/tagosql/parameters) and
[Executing queries](/docs/tagoio/tagosql/executing-queries) for parameter shapes.
The bridge forwards only `query_id` and `params`. Other execute body fields such as `after_device` and
`test` are not available.
On success, the bridge returns a normalized result:
| Field | Meaning |
| --------- | -------------------------------------------------------------- |
| `columns` | Column names as strings, in order. |
| `rows` | Array of row objects; each row is keyed by column name. |
| `meta` | `{ row_count, execution_ms, served_from_cache }` for this run. |
This shape differs from calling `POST /sql/{id}/execute` directly. Raw execute returns typed
`{ name, type }` columns and flat metadata; the bridge normalizes columns to name strings and nests meta.
## Errors
| Code | Meaning |
| ------------- | ------------------------------------------------------------------------------------------------------- |
| `not_found` | Query id unknown or not on this profile. |
| `forbidden` | The API denied permission to run the query (HTTP 403 on the host's execute call). |
| `bad_params` | Other client-side rejections of the run request (for example bad or missing parameter values). |
| `api_error` | The host could not complete the underlying API call for this session. |
| `unknown_op` | `op` is not a supported operation. |
| `bad_request` | Malformed bridge payload (for example missing `query_id` or wrong types); rejected before any API call. |
Treat `error.message` as human-readable detail; branch on `error.code` in code.
## Example
Core request/response helper and a first-query run:
```html
```
Full page that also signals ready, applies theme, and renders rows into a table. The request/pending pattern
matches the script above:
```html
Custom Dashboard
SQL Bridge example
Loading...
```
Register the response listener before you call `request`. Prefer allowlisting the Admin origin on inbound
messages; see [Authoring HTML](/docs/tagoio/dashboards/custom-dashboard/authoring-html.md#origin-checks).
## Related
- [Authoring HTML](/docs/tagoio/dashboards/custom-dashboard/authoring-html.md) (ready, theme, style)
- [Custom Dashboard](/docs/tagoio/dashboards/custom-dashboard/)
- [TagoSQL queries](/docs/tagoio/tagosql/queries)
- [Executing queries](/docs/tagoio/tagosql/executing-queries)
- [HTML shell](/docs/tagoio/dashboards/custom-dashboard/html-shell.md)
---
## HTML Shell
:::warning
Experimental. This feature may change or be removed.
:::
The shell is the single HTML file that fills the Custom Dashboard canvas. TagoIO hosts it for you
(versioned storage). You do not host a public URL for the dashboard page itself.
There is no in-app HTML editor. Edit the file offline, then upload again.
## First upload
Until a shell exists, the dashboard shows an empty state:
- **Upload HTML** to choose a local `.html` file.
- A collapsible note with the postMessage snippet so you can wire `dashboard:ready` correctly.
After a successful upload, Admin loads the shell in an iframe and waits for your page to signal ready.
## Gear menu (shell actions)
When the dashboard is a Custom Dashboard, the gear menu includes shell actions that Normal and Blueprint
dashboards do not have:
| Action | What it does |
| ------------------------- | --------------------------------------------------------------- |
| **Upload new HTML** | Replace the current shell with another local file. |
| **Download current HTML** | Save the hosted shell as a local `.html` file for offline edit. |
Use download, edit locally, then upload new HTML as the normal edit loop.
Data is not configured in the gear menu. Your shell lists and runs saved queries over the postMessage
[SQL bridge](/docs/tagoio/dashboards/custom-dashboard/data-sources.md).
## Version restore
Dashboard [version restore](/docs/tagoio/dashboards/restoring-your-dashboard-to-a-previous-version) works for
the shell. Restoring a version can return a previous HTML upload (or clear the shell if that version had
none). After restore, Admin reloads the canvas to match what is stored.
Restore applies to the shell and other dashboard record fields through the same version tooling as other
dashboard types.
## API surface (shell)
Most authors stay in Admin. If you automate shell upload:
| Method | Path | Notes |
| ------ | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `PUT` | `/dashboard/{id}/html` | Body is raw HTML (`Content-Type: text/html`). Profile token required. |
| `GET` | `/dashboard/{id}/html` | Returns the hosted shell. Auth via short-lived `html_access` (`exp` + `sig`) from dashboard info, or a token query parameter. |
Full request details live in the [TagoIO API reference](https://api.docs.tago.io/) under dashboards.
## Tips
- Ship a **single file**: inline CSS and JS, or load third-party assets from absolute HTTPS URLs your
page is allowed to use.
- Post `dashboard:ready` only after listeners are registered so you do not miss the first host messages. See
[Authoring HTML](/docs/tagoio/dashboards/custom-dashboard/authoring-html.md).
- Prefer download + re-upload over hand-editing production shells from memory.
---
## Dashboard Global Time Filter and Period Presets
## Overview
The Dashboard Period Preset allows users to set global time-based filters that apply across all widgets in a dashboard. This enables effortless comparison of data across different periods, providing valuable insights and supporting better decision-making.
With the Dashboard Period Preset, users can create filters in the dashboard navigation to select various time periods for all widgets. For example, comparing data from this month to the previous month is achievable in just a few clicks. The feature is highly flexible and provides quick presets for relative dates such as "this week," "this quarter," "today," and more.
When enabled, a date picker is available to choose specific date ranges. You can also define a maximum period range that limits how large a range users can select in the date picker.

## How to apply global time-filters to your dashboards
You can enable the date picker and define preset periods by accessing your dashboard settings.
- Click the edit menu (the gear icon on the top right corner of the dashboard).
- Select **Period Preset**.
- Enable the **Enable Period Preset** field. Once active, you will be able to define a maximum period range to be filtered and set preset periods that can be quickly selected in the date picker.
### Applying a maximum period range to your date picker
You can define a maximum period for date selection, so that users cannot filter data beyond the specified period range. You still have the flexibility to set preset periods that exceed this maximum range, allowing for broader default selections if necessary.
### Setting period presets
You can set period presets to allow users to easily change the filter range by selecting predefined periods. These periods are defined by you and can be accessed when users click on the calendar icon. You can choose from several periods, create up to five presets, and designate one of them as the default.
### Disabling the Dashboard Period Preset for a single widget
If you want to disable the Dashboard Period Preset for a specific widget, enable the **Override Dashboard Presets** option for widgets that have their own preset period (e.g., Line Chart, Area Chart, Map).
---
## Dashboard Icons
You can customize the appearance of your dashboard by adding icons. Icons are displayed on the left side of each dashboard item. Colors are fully customizable, and there are more than 100 free icons available.

## Adding an icon to a dashboard
To add or change a dashboard icon:
- Open the dashboard you want to edit.
- Click the edit menu (the gear icon on the top right corner of the dashboard).
- Select **Customize Theme**.
- In the _Theme Customization_ tab, click on the **Dashboard icon** field.
- Choose an icon from the list.
- To set its color, click the square button next to the icon; a color palette will appear for you to pick a color.
## Notes
- Icons appear on the left side of the dashboard item.
- Icon colors are completely customizable.
- Over 100 free icons are available for use.
---
## Data Output for Dashboards
In TagoIO, each time someone accesses data from the Dashboards using TagoIO Admin or TagoRUN, a data output is counted for each variable displayed. For example, when an end user loads a dashboard that displays 3,000 registers, that same number will be counted against your dashboard's output limit. This counter is separate from the [Data Output Service](/docs/tagoio/profiles/services/data-output-service.md).
The data output limit for dashboards is predefined by your plan, applied to each profile, and resets every month. If attempts are made to visualize data from your dashboards after this limit is reached, an error will occur. The table below lists the output limits for each plan.
## Dashboard output limits
| Plan | Data Output for Dashboards (registers) |
| ------- | -------------------------------------- |
| Free | 3,000,000 / month |
| Starter | 15,000,000 / month |
| Scale | 50,000,000 / month |
:::warning
Data consumed by dashboards will not be counted against the [Data Output Service](/docs/tagoio/profiles/services/data-output-service.md). You are not billed by data output for Dashboards.
:::
## What happens when I reach the limit
When your account approaches the predefined limit, you will receive a notification suggesting an upgrade to your plan. This notification will be sent to your Admin panel and to your registered email address. Note that you cannot increase the dashboard data output limit yourself (this is different from the limit control available for the general Data Output Service).
### Monitoring your limit
You can monitor the amount of your dashboard's data output that has been consumed by accessing the profile menu in the top left corner of your Admin panel and selecting the '[Hard Limits](https://admin.tago.io/limits/hard)' option: search for the row "**Dashboard Output**".
---
## Distributing Dashboards
You can create and share a dashboard template, either without your personal data or using mockup data, and send a link so anyone can install it into their account through a customized installation page. Once the template is installed, users are free to modify the dashboards to suit their needs. The template can be added to your own profile or to other TagoIO accounts.
When people click the link you provide, they will be taken to a screen similar to the one shown below. Both the images and the text on the installation page are fully customizable.

### Creating & Editing a Template
To distribute a dashboard, first click on the options menu of your dashboard, and then on the **Distribute** option.

This form will be displayed:

The **Distribute** function relies on creating a customized install page for anyone to install a copy of your dashboard. In the image above, you can set a custom title for the install page in the field **Title of the page**. You can also replace the default images with custom ones using the fields **Logo of the page** and **Main image of the page**.

You can add descriptions to your **Devices**, or **Blueprint Devices**, and **Analyses** used on the dashboard as well. Descriptions are optional, but they help whoever is installing the template to find their own devices to associate with the ones used on the dashboard.
For example, specifying that one of the devices is a temperature sensor in the description allows the user installing the template to select a matching device on their own profile.

Once you have made the necessary adjustments, click the **Create Template** button (or **Update Template** if the template has already been created) to save your information and build the page. When you build your template for the first time, a new section will appear on the page containing the link to be shared with other users.
Alternatively, you can download a file in JSON format, either the **Dashboard Structure** or **Manifest**. These files allow you to make your dashboard template publicly available to all TagoIO users so that they can install your template when creating new dashboards within their Admin. Read more at [Public Dashboards - TagoIO GitHub](https://github.com/tago-io/public-templates).
To share your template using a link, simply click the **Copy button** or manually copy the link, then distribute it to anyone you would like.
### Installing a Template
When using a template for a **Blueprint dashboard**, the **Blueprint Devices** will be configured exactly like in the dashboard, but with helpful descriptions in case the user needs to make any changes. This also lets the user know which tag keys and values the devices must have configured.
If the dashboard has no Analysis in its widgets, this is the only step and clicking **Create Dashboard** will install the dashboard.

If the dashboard has Analyses, instead of **Create Dashboard** the button will be **Continue** and it will guide the user to another page to select their own Analyses to be used in place of the Analyses in the original dashboard.
**Note that Analyses also have the Distribute feature**, and they can be installed previously to installing a dashboard requiring Analyses. Read more about [Distributing analysis](/docs/tagoio/analysis/distributing-analysis.md).

Selecting the required Analyses will enable the **Create Dashboard** button, and the template will be installed using the configurations made on this setup wizard.
For Normal dashboards, the device association page looks like the one above and also functions similarly: all the required devices must be associated before the **Create Dashboard** button is enabled and allows the user to finish installing the template.
### Sharing a Template with Mockup Data
You can also share your template along with mockup data from your device so that the widget displays data even before the template is fully configured during installation. This method allows the installer to proceed without immediately associating any device, although the analysis will still require an association. The mock data is limited to 200 KB.
---
## Downlinks using Dashboards
Learn how to perform downlinks for Class C or Class A devices using [Analysis](/docs/tagoio/analysis/). This article also shows how to send downlink data from [Dashboards](/docs/tagoio/dashboards/). This approach can be used in the following cases:
## Use cases
- For devices with class C: immediately send a downlink message to the device.
- For devices with class A: schedule downlink messages for the next available time slot.
## Required Analysis template
To use this method you must install the Analysis template available at the following [link](http://admin.tago.io/template/5f513aabd4555600277f858c).
## After installing the template
After installing the template, set up the environment variables. Go to the "Environment Variables" tab of your [Analysis](/docs/tagoio/analysis):
> Configure the environment variables according to the template instructions so the Analysis can correctly address and send downlink payloads to your device.
**Environment Variables**
- **Account_token:** Any account token from your account with full permission. Go to your [profile configurations](https://admin.tago.io/profile), tokens section, and generate a new one.
- **default_PORT:** The default port to be used if not sent by the dashboard.
With all variables set, press **Save** to save the analysis.
## Setting up the Dashboard
After completing the previous steps, you may want to send these downlinks using a dashboard. This method can be useful to easily select the device to send the payload or to share the dashboard with others and allow them to send the downlink messages.
1. Select the Analysis you've recently imported, **"Sending Downlink using dashboard"**.
2. Press **"Confirm associations"** to apply the changes and you're all set to start using the form and send your downlinks.
---
## Grouping Dashboards
You can group related dashboards using dashboard tags. Use the dashboard settings to create tags (key/value pairs) and then select those tag keys to define grouping levels. Dashboards will be grouped in the sidebar using the chosen tag keys and their values.
## Steps to group dashboards
1. Open the dashboard you want to group and click the gear icon to edit it (Dashboard > Gear icon > Tags).
2. Tags are key/value pairs that appear as custom columns in the dashboard list.
- Example keys and values:
- Key: country, Value: USA
- Key: state, Value: California
- Key: city, Value: Los Angeles
3. In the "Group dashboards using Tags" section, select the Tag Key to be used for each grouping level (Level 1, Level 2, Level 3). You can include the tag in Run users by toggling the corresponding switch.

## Notes and behavior
- To create sub-levels, repeat the selection procedure for each level. It is possible to create up to three grouping levels.
- After configuring grouping, dashboards will be grouped in the sidebar according to the selected tag keys and their values.
- If you delete a tag that is used for grouping, any sub-levels that depend on that tag will be removed as well.
- When you share a grouped dashboard, the grouping will be visible on the shared account's list of dashboards.
---
## Dashboard Overview
A dashboard is where you place your widgets to visualize and interact with data, all in real-time. Dashboards can also be shared with end-users that will access your application using [TagoRUN](/docs/tagoio/tagorun/).
All your dashboards are listed on the sidebar of the screen. On the top right you will find the **Edit Dashboard** menu. From there you will have access to other dashboard functionalities such as **rename**, **share, distribute, duplicate,** and **delete**. Share and distribute apply to Normal and Blueprint dashboards.
Dashboard types include:
- **Normal**: create a dashboard for pre-selected devices and variables on a widget grid.
- [**Blueprint**](/docs/tagoio/dashboards/blueprint-dashboard): associate **tags** so one layout scales across many devices and users.
- [**AI Dashboard**](/docs/tagoio/dashboards/ai-dashboard/): generate a full-page layout from a natural language prompt; refine by chat (**experimental**).
- [**Custom Dashboard**](/docs/tagoio/dashboards/custom-dashboard/) (**Custom HTML** in the create dialog): upload your own full-page HTML shell; load data over a postMessage SQL bridge to saved TagoSQL queries (Admin only, **experimental**).
:::info
There is a limit of **100 widgets** per Normal or Blueprint dashboard. Types that present a full-page layout instead of the widget grid are not subject to that limit.
:::
## Building dashboards
To create a new dashboard, start by clicking the **+** button on the left side of Admin page. A new window will appear with tabs and options for the dashboard types available to your profile. You can also start from a pre-designed [template](/docs/tagoio/dashboards/distributing-dashboards.md) where templates apply.
Enter a name for your new dashboard and click **Save**. For Normal and Blueprint dashboards, you can then begin adding [Widgets](https://help.tago.io/portal/en/kb/tagoio/9-widgets). For AI Dashboard, use the prompt bar to generate the layout. For Custom Dashboard, upload your HTML shell.

## Dashboard Icons
You can add [icons](/docs/tagoio/dashboards/dashboard-icons.md) to your dashboard, making it look just the way that you want it.

## Dashboard Background
You can also edit the background color of the dashboards, or add images and animations. Click on the **cog** icon to edit the configurations inside the 'General information' tab.

Although video formats are not accepted, you can upload animations using the **.gif** format as shown below. Images can be uploaded into your [Files](/docs/tagoio/files).
> **Tip:** You have full control of the opacity level for the background and the widgets.
## Reordering widgets for mobile app on TagoRUN
When accessing dashboards through the [TagoRUN app](/docs/tagoio/tagorun/getting-started/tagorun-mobile-app.md), widgets appear in the sequence of their creation by default. To modify this arrangement, simply switch your dashboard to **Edit mode** and click on the small smartphone icon located at right top corner.
To reorder the widgets for the mobile app, just drag them to your preferred position. The adjustments will take effect immediately, although you might need to refresh the dashboard on the mobile app to see the changes.
---
## Restoring your Dashboard to a Previous Version
In TagoIO Admin, each time you make changes to your dashboards and widgets, the system automatically generates a backup version of each action taken. This ensures you can revert to a previous version of your dashboard if you encounter issues or want to undo recent modifications.
## Accessing the restore option
To access the restore feature, open your dashboard in edit mode and click the "Restore dashboard to a previous version" button.

## Choosing and restoring a version
A chronological list of your dashboard versions will appear, with each entry timestamped so you can identify when each backup was created. Select the desired version from the list and proceed to restore it.

### Version limits per plan
The number of versions you can have is predefined by your plan:
| Plan | Versions per dashboard |
| ------- | ---------------------- |
| Free | 5 |
| Starter | 50 |
| Scale | 150 |
Every time you restore a version, the system creates a new version post-restoration, so the restored state is added to your dashboard's retained version history.
## Advanced Settings
In the **Advanced settings** section, you have fine‑grained control over what is restored. For instance, if you want to retrieve only the tags from a previous version, simply select the tags; all other elements remain unchanged. By default, the system restores the entire dashboard.
---
## Sharing Dashboards
A great feature of TagoIO is its native sharing capability for dashboards, useful when a dashboard becomes a feature you want to share with others.
:::tip
If you want to share only the template of your dashboards, use the [Distribute](/docs/tagoio/dashboards/distributing-dashboards.md) feature.
To share applications with your end-users using your brand and a custom URL, use [TagoRUN](/docs/tagoio/tagorun/).
:::
## Share Public
To share a dashboard, open the menu for the dashboard you want to share and click on "Share Public". Example:

The Share Public section can also be accessed from the dashboard settings page when viewing the dashboard in your Admin.
### Requirements for Public Sharing
To generate a public link, you must have **TagoRUN** enabled and an **Anonymous User** created. The Anonymous User should have sufficient permissions to access the resources that will appear on the public dashboard. You can grant these permissions by creating a policy in **Access Management**.
Once set up, the Share Public page allows you to copy a link for both normal dashboards and [Blueprint Dashboards](/docs/tagoio/dashboards/blueprint-dashboard.md). Anyone who receives this URL can view the dashboard without needing a TagoIO account or credentials.
### Data Output Considerations
Data accessed from widgets on public dashboards, including data downloaded in CSV format, counts toward your **Data Output for Dashboards** quota.
## Dashboard Templates
You can also share only the template of a dashboard using the [Distribute](/docs/tagoio/dashboards/distributing-dashboards.md) feature. In this case, no data is shared; only the layout and widget configuration are exported.
---
## Adding Devices with Connectors
[Connectors](/docs/tagoio/devices/payload-parser/connector/connector-overview.md)
allow you to create a [device](/docs/tagoio/devices/) with built‑in behaviors
that allow it to communicate with a network easily.
When creating devices at TagoIO, there are different ways to allow them to send
and receive data through connectors:
1. It can be done directly using MQTT or HTTPS methods with our APIs, where one
[Device token](/docs/tagoio/devices/device-token.md) per device is required
to grant access.
2. When using external services from providers (e.g., LoRaWAN, Sigfox, or
cellular), it is necessary to generate an
[Authorization](/docs/tagoio/integrations/general/authorization.md) per group
or application. The Authorization is used to direct data to the correct profile.
That is why you need to select the right connector when creating your device at
TagoIO: to take advantage of the integrations and parsers already implemented with these
services.
## Connector functions
Connectors may provide one or more of the following functions:
- Interface with the
[middleware](/docs/tagoio/integrations/general/middleware.md) that connects
with the provider when necessary.
- Present logo, name, description, and tags for the device.
- Add [payload parsers](/docs/tagocore/device/payload-parser.md) in the device
(optional).
---
## Blueprint Devices & Entities
Blueprint Devices and [Entities](/docs/tagoio/entities.md) serve as dynamic data sources for [Blueprint Dashboards](/docs/tagoio/dashboards/blueprint-dashboard.md). These dashboards let you change data sources dynamically while keeping the same widgets and configurations. By creating blueprint devices or entities, you define which data sources can be dynamically altered. The selected data sources appear in a dropdown menu at the top of a Blueprint Dashboard.
:::note
Blueprint Entities can only be used by widgets that support them. If a widget does not display the entity in its data-source selection, that widget does not support entities yet.
:::
## How it works
Blueprint devices or entities provide the dynamic data sources for Blueprint Dashboards. The system works by associating devices or entities with specific tag keys and values. Devices or entities that match those tags become available for selection in the dashboard dropdown menu.
This feature is particularly beneficial when you want a single dashboard to be used by different end-users without exposing each other's data. It allows you to focus on one centralized dashboard, eliminating the need to maintain multiple dashboards for each type of end-user.

To create blueprint devices or entities, it is essential to first assign tags to them. For more information, please refer to the [Tags System documentation](/docs/tagoio/devices/tags-system.md).
### Creating a blueprint
You can create blueprints during the creation of a blueprint dashboard or by accessing the Blueprint Settings on the edit page of your blueprint dashboard.


**Key concepts for each field:**
1. **Type:** Choose between Blueprint Device or Entity.
2. **Identifier:** This will be the name of your blueprint, appearing in the data sources selection menu when creating widgets.
3. **Tag Key:** This is the tag key used to filter the devices.
4. **Tag Value:** This is the tag value used to filter the devices.
You can add up to 20 blueprints in a single dashboard.
### Visualization Settings
You can easily modify the appearance of your Blueprint and the behavior of the blueprint selector.

For instructions on creating and configuring blueprints or adjusting visualization options, see [Creating a blueprint](/docs/tagoio/dashboards/blueprint-dashboard.md) and [Visualization Settings](/docs/tagoio/widgets/).
---
## Configuration Parameters for Devices
Configuration Parameters are key-value pairs that enable you to customize the behavior of your [Devices](/docs/tagoio/devices/) in different scenarios. For example, you can use them to specify how to decode data or send downlink messages, filter your devices on [Widgets](/docs/tagoio/widgets/), or interact with [API & Analysis](/docs/tagoio/analysis/) scripts.
Configuration Parameters are stored on the configuration page of your device. You can access them by clicking on the Devices module in the left menu and selecting a device. You will see a tab called "Parameters", where you can add, edit, or delete parameters. You can set parameters and retrieve them later by making a request from the device.

## How it works
Each Configuration Parameter has three configurable fields:
1. **Read & Unread**: A boolean value (true or false) that indicates whether the parameter has been sent to the device.
2. **Key**: Name of the parameter that you can use to reference it in your code or widgets.
3. **Value**: The value you want to assign to your device for that parameter.
:::note
**Maximum limit:** The maximum number of parameters per Device is **60**. If you attempt to add additional items beyond this limit, a notification will appear indicating that the maximum capacity has been reached.
:::
## Managing Configuration Parameters
You can manage your device's configuration parameters through several methods:
- **Manually**: Add, edit, or delete parameters directly from the device page on TagoIO using its user interface.
- **Analysis SDK**: Use JavaScript code in an analysis script to create, update, or remove parameters with functions such as `paramList`, `paramSet`, and `paramRemove`.
- **API endpoint**: Send HTTP requests (GET, POST, PUT, DELETE) with JSON payloads to TagoIO's API for programmatic parameter management.
- **Widgets**: Some widgets allow you to edit device configuration parameters natively, such as the [Device List Widget](/docs/tagoio/widgets/tables/device-list-widget.md).
## Common Use Cases
- **Payload Parser**: Dynamically change parsing logic based on a parameter value (e.g., `mode` set to `normal` or `debug`).
- **Widget Display**: Show additional device details like firmware version or battery level, and filter or sort devices by parameters.
- **API & Analysis**: Send commands or queries to devices using their Device-Token; for example, a `downlink` parameter can carry payloads that are sent as instructions.
---
---
## Chunk Management
The Chunk Management feature lets you view and manage the data of Immutable devices to create backups or delete information. [Hybrid devices](/docs/tagoio/devices.md#hybrid-immutable--mutable) also have an immutable side, so their immutable data is managed through chunks in the same way.
## Accessing Chunk Management
Access the Device module and select a Device Data Optimized (Immutable Device) from your list. In the device page, you will find the Chunks section, which displays a graph of your data chunks according to the period you chose when creating the device. See [Immutable devices](/docs/tagoio/devices/) and [Device](/docs/tagoio/devices/) for details on device types and the Device module.

## Export chunks to TagoIO Files
By selecting one or multiple chunks, you can export them to your TagoIO Files in CSV format to create a backup of your data. During this process, the chunks are added to a queue and will become available at the file address once processed. This operation can take several minutes depending on the amount of data being exported. See [TagoIO Files](/docs/tagoio/files) for more on file storage and access.
### File address template
During export you can customize the file address so each chunk file has its own path inside your TagoIO Files. In the Advanced Settings you can use variables to change the path that will be created. The available variables are:
- `$DEVICE$`: the device ID
- `$CHUNK$`: the chunk ID (useful if you need a unique identifier)
- `$CHUNK_START$`: the chunk initial date (UTC)
- `$CHUNK_END$`: the chunk end date (UTC)
- `$FROM$`: the chunk start date (ISO 8601 format)
- `$TO$`: the chunk end date (ISO 8601 format)
- `$ORGID$`: the organization ID
- `$ENVIRONMENT$`: the environment name
Use these variables in the file address template to automatically generate meaningful file paths for each exported chunk. For example, a template such as:
```
device/$DEVICE$/$FROM$_$TO$
```
would result in a file address like:
```
/device/6297aa7691e70a00654f9816/2022-04-01_2022-06-30.csv
```
Date variables such as `$FROM$` and `$TO$` will be in the ISO 8601 format (YYYY‑MM‑DD).
#### Include Header option
When exporting a chunk, you can activate the **Include Header** option to add column headers to the CSV file. Headers describe each variable's data in the first row. If you prefer a raw list of rows only, disable this option in Advanced Settings.
### How exporting is billed
Exporting data consumes the [Data Output service](/docs/tagoio/profiles/services/data-output-service.md). The number of transactions billed is rounded to the next multiple of 10 000. For example:
1. Exporting 700 registers will consume 10 000 data output transactions.
2. Exporting 12 000 registers will consume 20 000 data output transactions.
3. Exporting 32 000 registers will consume 40 000 data output transactions.
4. Exporting 103 000 registers will consume 110 000 data output transactions.
The billing scheme above does not apply when fetching data using the API; for example, a GET request consumes one transaction per fetched register.
## Delete chunks
You can delete chunks to optimize your data storage. This is an irreversible process, and once done there is no going back, so make sure you are certain before removing anything. Hover over the graph to check the chunk range information before deleting. All interactions with this feature are logged in the [Audit Log](/docs/tagoio/profiles/audit-log.md).
## Automation
You can also access the chunk management functions programmatically using the SDK (`Devices.getChunk`), Analysis, and Actions to automate processes for backing up or deleting your data.
---
## Data Export
At TagoIO, you can export device data in several ways: directly from the Admin
panel on a device's page, via the [API](/docs/tagodeploy/project/api.md), or from
[Widgets](/docs/tagoio/widgets/) in either the Admin panel or the
[TagoRUN portal](/docs/tagoio/tagorun/getting-started/tagorun-mobile-app.md).
Exporting data consumes your
[Data Output](/docs/tagoio/profiles/services/data-output-service.md) service
quota.
Here's how to use each export source.
## From the device page
Access this feature in the Admin panel under the
[Device module](/docs/tagoio/devices/). Select a device and open the "Data" tab.
Before exporting, use the filtering options to refine the data you need and
avoid unnecessary consumption of your Data Output quota. Learn more in
[Filtering Variables](/docs/tagoio/devices/payload-parser/filtering-out-variables-with-parser-code.md).
To export, click the "Export" button in the top-right corner. The export dialog
provides the following options:
- **Columns**: choose which columns to include in the export:
- ID
- Variable
- Value
- Group
- Location
- Metadata
- Time
- **File format**: CSV is the only available format.
- **Amount of records**: enter the number of records to export, or select the
"All data" option to export all available records (the example shows "All data
(~157K)").
- A reminder that "Export data will consume your Data Output service."
- **Export button**: located at the bottom-right of the dialog to start the
export.
:::info
For immutable devices that group data into chunks, you can also export data in
chunks. Learn more about exporting data chunks here:
[Chunk Management](/docs/tagoio/devices/data-management/chunk-management.md).
:::
Notes:
- Use filtering options on the device "Data" tab to limit the exported data and
reduce Data Output quota usage.
- For instructions on exporting via the API, refer to the
[API documentation](/docs/tagoio/api/api_overview.md).
## From widgets
Supported widgets can export data from dashboards in both the Admin panel and
the [TagoRUN portal](/docs/tagoio/tagorun/getting-started/run-module.md).
To export from a supported widget:
1. Open the dashboard containing the widget.
2. Select the three-dot menu in the widget header.
3. Select **Export**.
:::info
Downloading CSV files from widgets uses the dashboard's Data Output allocation,
not the general Data Output allocation for the profile. See
[Data Output for Dashboards](/docs/tagoio/dashboards/data-output-for-dashboards.md).
:::

Widgets with a built-in export option:
- [Dynamic Table](/docs/tagoio/widgets/tables/dynamic-table-widget.md)
- [Entity Table](/docs/tagoio/widgets/tables/entity-table-widget.md)
- [Device List](/docs/tagoio/widgets/tables/device-list-widget.md)
- [User List](/docs/tagoio/widgets/tables/user-list-widget.md)
- [Map](/docs/tagoio/widgets/map-and-location/map-widget.md)
- [Line Chart](/docs/tagoio/widgets/charts/line-chart-widget.md)
- [Area Chart](/docs/tagoio/widgets/charts/area-chart-widget.md)
- [Vertical Column](/docs/tagoio/widgets/charts/vertical-column-widget.md)
- [Horizontal Bar](/docs/tagoio/widgets/charts/horizontal-bar-widget.md)
- [Pie](/docs/tagoio/widgets/charts/pie-widget.md)
Widgets not listed above do not provide a built-in export option.
Widget exports generate a CSV file. The Dynamic Table and Device List widgets
prompt you to select a CSV delimiter before downloading the file.
---
## Data Retention Feature
## Overview
The Data Retention feature automatically removes old data from Immutable devices
after the period you define. Its purpose is to help customers avoid unnecessary
costs by deleting data that does not need to be kept long-term.
[Hybrid devices](/docs/tagoio/devices.md#hybrid-immutable--mutable) also have an
immutable side subject to this retention policy; their mutable-side variables
are never dropped by retention.
:::info
The Data Retention feature is available only for
[Optimized Device Data](/docs/tagoio/devices/).
:::
## Selecting Data Retention
To configure Data Retention, you must select two fields:
- **Period**: the chunk period for retaining data.
- **Retention**: how many of those periods to keep the data.
These two fields are combined to create the device's data retention policy.

UI helper text shown in the configuration:
- This selection limits the storage for this device to 1 million data registers
per month, you cannot change the chunk period. But you can always edit the
retention from 0 to 36 months.
- The retention starts considering the current period: if you select 0 months
it will delete all data when a new month starts.
## Period
The **Period** field lets you choose the time chunk used to retain data (for
example, Daily or Monthly). In combination with the Retention field, it defines
how long data should be kept on your device.
Examples and important considerations:
- Setting Period to _Monthly_ allows storing up to 1 million data registers per
month.
- Setting Period to _Daily_ allows storing up to 1 million data registers per
day.
- You cannot change the Chunk Period field later on, so choose it carefully.
- Chunk Retention can be edited between the range of the selected period.
:::info
Setting your devices Chunk Retention to 0 will cause the platform to delete all device data when the next period starts.
:::
### Retention ranges by period
| Period | Retention range |
| --------- | --------------- |
| Daily | 0 to 31 |
| Weekly | 0 to 26 |
| Monthly | 0 to 36 |
| Quarterly | 0 to 36 |
:::tip
Each period chunk is limited to **1 million registers**. The retention feature keeps data for the selected retention _plus_ the data in
the current period.
Keep that in mind when setting your Chunk Retention, if your device sends more than 1 million data registers per period, you will lose data.
:::
### How it works
The removal schedule depends on the chosen Period:
- **Daily**: starts removing old data every day at 00:00 UTC.
- **Weekly**: starts removing old data every Monday at 00:00 UTC.
- **Monthly**: starts removing old data on the first day of each month at
00:00 UTC.
- **Quarterly**: starts removing old data every three months at 00:00 UTC,
starting from January 1st.
Data is removed by a task that runs at these times. Therefore, data in your
device can still be presented for several hours after the period crosses 0:00 h
in your local time.
## SDK
You can also access the data retention parameters using our
[SDK](https://js.sdk.tago.io/classes/_internal_.Devices.html#create).
---
## Device Data Management
The device page provides tools for visualizing, filtering, editing, importing, and exporting variables for managing the data received from your device. You can access these tools by navigating to the Device module, selecting a device, and clicking on the "Data" tab. Refer to the Device module documentation for navigation details.
## Amount of data records
The amount of data stored in each device is displayed at the right side of the page. When the number of data records exceeds 1,000 registers, TagoIO will start to display a truncated value. This represents an estimated number of data records stored in the device.
When the value is greater than 1,000 registers it may be shown with a letter suffix: **K** for thousand, **M** for million, **B** for billion, and **T** for trillion.


## Last Input
The last input information refers to the last time the device received communication; it does not necessarily mean that the data sent was actually stored as data. This information is useful to verify the last time your sensor received an attempt of data input.
If you want to verify if your data was received and added to your device, the best approach is to check the variable in the **Data** tab, or use the [Live Inspector](/docs/tagoio/devices/live-inspector.md) feature.

## Managing variable data
The **Data** tab on your device page lists all stored variables, including their names, values, group, metadata, and time. Here, you can inspect each variable individually and import and export them. For [Mutable devices](/docs/tagoio/devices.md), you can even edit or delete them individually. For [Hybrid devices](/docs/tagoio/devices.md#hybrid-immutable--mutable), individual edit and delete work for variables routed to the mutable side, while data on the immutable side is chunk-only.
## Filtering
Filter options within the **Data** tab enable you to refine your variable search based on specific criteria.
The table visualization can be customized by adjusting column visibility or changing the time format; this option is accessible through the display icon immediately above the table, on the left side.

:::tip
You can also increase the number of rows that are displayed per page by clicking the display icon and selecting the desired number of rows.
:::
## Editing and deleting variables individually
For [mutable devices](/docs/tagoio/devices/), individual variables can be edited or deleted directly from this tab. Simply hover over the desired information and click the pencil icon to make edits.
To delete variables, first select the ones you want to remove. Once selected, click **Delete selected** to proceed with the deletion. This action is irreversible.

For [immutable devices](/docs/tagoio/devices/), it is not possible to edit data individually; data can only be deleted through the data chunks. Read more about it here: [Chunk Management](/docs/tagoio/devices/data-management/chunk-management.md).
For [hybrid devices](/docs/tagoio/devices.md#hybrid-immutable--mutable), variables routed to the mutable side can be edited or deleted individually, while variables on the immutable side are chunk-only, just like an immutable device.
## Importing
To import data in bulk into your device, navigate to the **Data** tab and click the **More** button in the top right corner. Then, select **Restore from Files**. Before importing, ensure that your data is uploaded to your [Files](/docs/tagoio/files/). If you find that you don't have sufficient storage space, you can easily increase your limit by visiting the [Billing page](https://admin.tago.io/account/billing) in your Admin panel.
The supported file format is CSV.

The device payload parser does not execute during data import. Therefore, ensure that the data is properly formatted before importing.
### CSV format requirements
To ensure a successful import, your CSV file must adhere to the following requirements:
1. Include headers. The **variable** field is mandatory, while others are optional.
2. Values should be separated by a comma. Semicolons are not supported.
3. The file size should not exceed 200 MB.
4. The file should contain a maximum of 1 million rows.
5. Data integrity and structure should align with TagoIO's data format. For more information, refer to the [Sending Data](/docs/tagoio/devices/sending-data.md) documentation.
The following headers are available for import. Any additional headers beyond those listed will be ignored:
- variable (mandatory)
- value
- group
- unit
- location
- metadata
- time
### Service limits for importing
When importing data, the [Data Input](/docs/tagoio/profiles/services/data-input-service.md) limit is not affected. However, since you are adding new data to your devices, the amount of new data will count against your [Data Storage](/docs/tagoio/profiles/services/data-records.md) service. You must have at least 10 000 Data Storage registers available to perform the import, even if you are importing less data than that.
Before uploading the data to your device, you must first upload it to your [Files](/docs/tagoio/files). Ensure you have enough space to upload your CSV file.
If needed, you can increase the data storage or file limits by accessing the [Billing page](https://admin.tago.io/account/billing) in your Admin.
### Differences between importing data to Mutable vs. Immutable Devices
TagoIO supports three types of devices: [Mutable](/docs/tagoio/devices.md), [Immutable](/docs/tagoio/devices.md), and [Hybrid](/docs/tagoio/devices.md#hybrid-immutable--mutable). Each has specific considerations when importing data.
1. **Immutable Devices**: If your CSV includes data for the **time** field, the time must fall within your [chunk period and retention configuration](/docs/tagoio/devices/data-management/data-retention-feature.md). If you attempt to import data outside the retention period, you will encounter an error. To resolve this, ensure the date and time information fits within your retention period.
2. **Mutable Devices**: These devices can store up to 50 000 records. Your import will fail if this limit is exceeded. You can check the number of data records in your device in the Device module.
3. **Hybrid Devices**: Each imported row is routed by the device's `mutable_variable_regex`, exactly like live data. Rows that match the regex count toward the 50 000-record mutable limit; rows that do not match must have a **time** within the chunk period and retention configuration, like an immutable device.
## Exporting
The **Export** button, located in the top right corner of the **Data** tab, allows for variable data export in various formats. Further details on exporting data for specific device types can be found under: [Data Export](/docs/tagoio/devices/data-management/data-export.md).
## Backing up your data
To back up your device data, click the **More** button located in the top right corner, then select **Backup to Files** under the **Data** tab. This will export all your device data to your [Files](/docs/tagoio/files), creating a secure copy of your sensor data. This feature is particularly useful for safeguarding your data or capturing snapshots at specific points in time.
The tool automatically backs up all your device data. If you need to apply filters before backing up, use the Data Export function to refine your data, and then save it locally or in your Files. For [Immutable devices](/docs/tagoio/devices/), clicking the **Backup to Files** button will redirect you to the **Chunk Management** tab, where you can select specific chunks of data to back up to your Files.

### File Address Template
During the backup process, you can customize the file address before exporting. As a result, each backup file will have its own unique file address. In the Advanced Settings, you can use key variables to modify the path name that will be created within your Files. The variables available for customizing your file address are listed below:
- $DEVICE$: the device ID
- $CHUNK$: the chunk ID
- $FROM$: the chunk start date
- $TO$: the chunk end date
For example, the following file address template:
```
device/$DEVICE$/$FROM$_$TO$
```
would result in a file address such as:
```
/device/6297aa7691e70a00654f9816/2022-04-01_2022-06-30.csv
```
Date variables such as $FROM$ and $TO$ will be in the ISO 8601 format (YYYY-MM-DD).
When exporting a chunk to a location where a file already exists, all the data will be replaced with the new one.
## Emptying your Device Data
While it's possible to remove variables one by one, there's a more efficient method to wipe all data simultaneously.
Navigate to the [Device](https://admin.tago.io/devices) module and choose the device you wish to clear. Then, click the **More** button located in the top right corner, then select **Empty Device Data** under the **Data** tab.
---
## Deleting Data
:::info
When making a request to the TagoIO API, you must also specify the appropriate regional endpoint. Check out the available regions: [API documentation](https://api.docs.tago.io/)
:::
## Endpoint
```
DELETE https://api..tago.io/data
```
## Headers
- Authorization: Your device token
Example header format:
```http
Authorization:
```
## Optional Query Strings
Use the same query strings you use to get data to select which data will be deleted. The available optional query string parameters are:
| KEY | TYPE | DESCRIPTION |
| ---------- | ------ | ------------------------------------- |
| query | string | Pre-defined by TagoIO |
| qty | string | Maximum number of data to be returned |
| start_date | string | Start date |
| end_date | string | End date |
| variable | string | Filter by variable |
To delete data, TagoIO uses the same query string parameters used to get data; for more details about each query, see [here](/docs/tagoio/devices/getting-data.md).
## Examples
- Delete the last 99 items from the variable **temperature**
`https://api..tago.io/data?variable=temperature&qty=99`
- Delete the last item from the variable **temperature**
`https://api..tago.io/data?variable=temperature&query=last_item`
- Delete the last value from the variable **temperature** (requires the `_value_` attribute on the variable object)
`https://api..tago.io/data?variable=temperature&query=last_value`
- Delete the last location from the variable **temperature** (requires `_location_` attribute on the variable object)
`https://api..tago.io/data?variable=temperature&query=last_location`
- Delete data by period
`https://api..tago.io/data?variable=temperature&start_date=2014-12-25&end_date=2014-12-26`
- Delete the last value from an array of variables
`https://api..tago.io/data?variable[]=temperature&variable[]=pressure&query=last_value`
:::tip
Make sure your request is `DELETE` and not `POST` when trying to delete items. Posting data is relatively instantaneous, while deleting data can take between 10 and 30 seconds to complete.
:::
## Rate Limits
When executing requests to TagoIO, you will have a limit on the number of requests that can be made during a certain time period. Read more about our [Rate Limits](/docs/tagoio/profiles/services/rate-limits-hard-limits.md).
---
## Device Emulator Functions
Functions are helper variables you can add to your payload when using the [Device Emulator](/docs/tagoio/devices/simulator-data-stream.md). Each function serves a specific purpose. To insert a function into your payload, click the `Functions` button and select the desired function.
You can use functions in both JSON and Raw (CSV) payload types.
## Examples
JSON example using a function:
```json
[
{
"variable": "temperature",
"value": $random$,
"unit": "F"
}
]
```
CSV example using a function:
```csv
variable,value,time,group
temperature,$random$,2019-09-19,1568913302243
```
## List of functions
- **$random$**: This function will be replaced by a random number that ranges from 0 to 100 whenever you send your payload.
- **$randomBoolean$**: This function will be replaced by a random boolean (true or false) whenever you send your payload.
- **$busRoute$**: This function will be replaced by a sequential bus route through Chicago. The location will change to a new one every time you send your payload. You should only use this function in the `location` field of a JSON payload.
- **$sequential$**: This function will be replaced by a number that is increased by 1 every time you send your payload.
- **$sequentialHex$**: This function will be replaced by a hexadecimal value that is increased by 1 every time you send your payload. If you are using this function in a JSON payload, wrap it around quotes (`"$sequentialHex$"`).
- **$unixtime$**: This function will be replaced by the current date and time as a Unix epoch timestamp.
- **$timestamp$**: This function will be replaced by the current date and time as 'YYYY-MM-DDTHH:mm:ss.sssZ' (ISO-8601) format.
- **$date$**: This function will be replaced by the current date in the format `YYYY-MM-DD`. If you are using this function in a JSON payload, wrap it around quotes (`"$date$"`).
:::tip
If you are unsure of how to use functions, look for samples in the editor.

:::
---
## Device Emulator Overview
The device emulator is a tool for developers to send data as if it were coming
from a real device. The emulator is intuitive and requires just a few clicks to
validate and send your data package.
To access the device emulator, go to the [Device](/docs/tagoio/devices/)
section, select the device, and then click on the Emulator tab.

## Supported payload types
TagoIO accepts two types of payloads: JSON and Raw.
- **JSON**: The payload is a JSON object that follows TagoIO's data schema by
using the following fields: `variable`, `value`, `unit`, `time`, `group`,
`location`, and `metadata`.
- **Raw**: Raw binary data. If you need to parse text data such as CSV or XML,
add a [Payload Parser](/docs/tagocore/device/payload-parser.md).
## Notes
- Use the Emulator tab to validate and send payloads using the device's
credentials.
- The emulator UI includes a payload editor, an option to load samples, function
helpers, and a **Send payload** button to submit the data.
## Sending Payload Data
To send a JSON payload from the emulator, you will need to:
1. Build a data payload that should contain at least the `variable` field (pick
one example from the list);
2. Edit the fields of the payload;
3. Click on **Send button** to send the payload.

### Functions
Functions are helper variables that you can add to your payload. Each function
has a purpose. To add a function to your payload, click on **Add function**
button and select a function.

To learn more about using functions in the device emulator, read the article
[Device Emulator Functions](/docs/tagoio/devices/device-emulator/device-emulator-functions.md).
### Timer
The timer helps to automatically send your payload at a fixed time interval.
This is extremely useful if you wish to see the data coming in while you look at
a dashboard, for example.

:::tip
If you close the **Emulator** tab, the system will stop sending payloads. You may want to use the [Simulator](/docs/tagoio/devices/simulator-data-stream.md) or create a script to keep sending all the time.
:::
:::warning
Before sending any kind of payload, always make sure that the device is **Active**.
:::
Learn more in our [community](https://community.tago.io/).
---
## Device Token
The secret key used between TagoIO and your device is called a Device Token. Any access from a device is granted only with a valid token. This token should be kept secret and shared only with people you trust.
:::note
Each time a device is created, the system automatically creates a device token.
:::
Unlike other [token types](/docs/tagoio/profiles/account-token.md#token-format-and-prefixes), device tokens are returned without a type prefix. They are provisioned into hardware and firmware where the token length is often fixed, so the raw form is preserved to avoid breaking those clients.
## Finding the Device Token
- Open the [Devices](https://admin.tago.io/devices) module in the TagoIO Console.
- Select the device you want to get the token for.
- Go to the "General" tab.
- In the "Token & Serial Number" window, click on the 3 dot menu.
- Select "Copy Token" to copy the token to the clipboard.

## Security recommendations
- Treat the device token like a password: do not expose it in public repositories, logs, or client-side code.
- Only share the token with systems or people that must interact with the device.
---
## Getting Data
:::info
When making a request to the TagoIO API, you must also specify the appropriate regional endpoint. Check out the available regions here: API documentation (link included in the original article).
:::
## Headers
Include your device token in the Authorization header.
```http
Authorization: Your-Device-Token
```
## Endpoint
Use the regional data endpoint:
```http
GET https://api..tago.io/data
```
## Query Parameters
The following querystring parameters are supported:
| Key | Type | Description |
| ---------- | --------------- | ------------------------------------------------------ |
| variable | string or array | Get variables |
| query | string | Pre-defined by TagoIO |
| qty | string | Maximum number of data points to be returned |
| start_date | string | Start date (e.g., "1 day" or ISO string) |
| end_date | string | End date (e.g., "1 day" or ISO string) |
| detail | bool | Include additional JSON fields in the returned results |
## Response Example
Below is a sample response when no query parameters are supplied:
```json
{
"status": true,
"result": [
{
"id": "547e42847dbf3af122c02582",
"location": {
"coordinates": [41.878876, -87.635915],
"type": "Point"
},
"time": "2014-12-02T22:51:48.005Z",
"variable": "location"
},
{
"id": "547e353d7dbf3af122c0257d",
"time": "2014-12-02T21:55:09.301Z",
"unit": "%",
"value": 32,
"variable": "fuel_level"
},
{
"id": "547e41f97dbf3af122c02580",
"time": "2014-12-02T22:49:29.777Z",
"unit": "psi",
"value": 25,
"variable": "oil_pressure"
}
]
}
```
## Variables
The `variable` parameter specifies which variable(s) to return.
Example for a single variable:
```
https://api..tago.io/data?variable=temperature
```
To request multiple variables, use an array syntax:
```
https://api..tago.io/data?variable[]=temperature&variable[]=pressure
```
## Query
The `query` parameter triggers predefined functions. Only one query can be used per request.
| Query | Description |
| ------------- | ---------------------------------------------------------------------- |
| last_item | Returns the most recent data point based on `_time_`. |
| last_value | Returns the most recent data point that contains a `_value_` field. |
| last_location | Returns the most recent data point that contains a `_location_` field. |
Example:
```
https://api..tago.io/data?variable=temperature&query=last_item
```
## Quantity
The `qty` parameter limits how many results are returned (default 15).
Example to retrieve 99 records:
```
https://api..tago.io/data?variable=temperature&qty=99
```
## Start and End Dates
Use `start_date` and `end_date` to filter data between two timestamps. They accept various formats, including relative time expressions.
Example:
```
https://api..tago.io/data?variable=temperature&start_date=2014-12-25&end_date=2014-12-26
```
If the interval contains more than 15 items, add `qty` to increase the limit.
### Date Formats
| Format | Example |
| -------------- | ------------------------ |
| ISO string | 2014-12-25T23:33:22.000Z |
| Human readable | 2014-12-25 23:33:22 |
| Relative | 1 day, 1 month, 1 year |
Relative dates are calculated from the current time.
## Rate Limits
When executing requests to TagoIO, you will have a limit on the number of requests that can be made during a certain time period. Read more about our [Rate Limits](/docs/tagoio/profiles/services/rate-limits-hard-limits.md).
---
## Grouping Variables
It is expected that most [devices](/docs/tagoio/devices/) will send more than one variable at a given time. When visualizing the data, users often want those variables synchronized for each sample period.
For example, a device may send GPS location, speed, and voltage variables at a certain frequency. If those variables are acquired and sent with exactly the same timestamp, the user can visualize all three together at each time sample. However, if there is any delay between them (timestamps of the variables do not match exactly), the system cannot group them correctly "by time".
To solve this, include a `group` field in the JSON for each sample. A batch of acquired variables can be synced by adding the same `group` value to their samples. For example:
- The first acquisition could have `group = 0001` for GPS location, speed, and voltage.
- The second acquisition could have `group = 0002`, and so on.
When visualizing these batches of data combined in a Table or Map, select the option "Group by group" to view variables grouped by their `group` value rather than by timestamp.
To learn how to add the `group` field in the JSON, see the [API documentation](https://api.docs.tago.io/).
---
## Devices
Devices are the link between your external things and the data in your account. To
allow anything to send or receive data from TagoIO, you need to create a device.
The communication between external devices and TagoIO is done via HTTP or MQTT
using [JSON format](/docs/tagoio/devices/sending-data.md). To enable this communication, a device must be created within
the platform; this involves specifying the device type and configuring it to
send and receive data correctly.
Devices are managed through your [Admin](https://admin.tago.io/) interface,
where users can access detailed information about each device, including its ID
and other settings.
:::info
Our [TagoIO API documentation](/docs/tagodeploy/project/api.md) offers detailed instructions on how to interface with devices, so developers can effectively integrate their hardware with the platform.
:::
## Adding devices
Devices are connected to TagoIO using
[Connectors](/docs/tagoio/devices/payload-parser/connector/connector-overview.md),
which act as a bridge between TagoIO and external networks to transmit and
receive data. To follow a step‑by‑step tutorial on how to add a device, see the
[Getting Started](/docs/tagoio/getting-started.md) article.
## Device type and data storage
Once you create a device, it will store all the data sent by your sensors. During the creation process, you will be prompted to select the type of data storage to be used. The right choice depends on one question: **will you ever need to edit or delete individual data points?** If not, choose Immutable. If yes, choose Mutable. If you need both in the same device, choose Hybrid.
There are three types of data storage you can choose from:
### Device Optimized Data (Immutable database)
Best for sensor telemetry: readings that represent a moment in time and never change.
- Stores up to **36 million** data points per device.
- Native [data retention](/docs/tagoio/devices/data-management/data-retention-feature.md).
- Highly optimized for short and long retention periods; query responses are faster, which means less latency for the devices and a cost reduction when running an [Analysis](/docs/tagoio/analysis/creating-analysis.md).
- Because the data is immutable, no one can change or delete individual sets of data, which is ideal for compliance.
### Managed Data Optimized (Mutable database)
Best for data that changes over time, such as device settings, form entries, or values a script updates in place.
- Allows you to edit or delete data.
- **No** native data retention; you can use Analysis to delete data on a scheduled basis.
- Limited to **50 k** data registers.
- Optimized for the storage and manipulation of configurable variables coming from sensors, web services, and forms.
### Hybrid (Immutable + Mutable)
Best when the same device produces raw telemetry _and_ values that need to be updated, such as an Analysis that regenerates an hourly average or an editable status flag. Before Hybrid, this required two separate devices: one immutable for the telemetry and one mutable for the editable values, which meant managing two tokens, splitting dashboards and Analysis code across both, and paying attention to two sets of limits. Hybrid removes that split.
A hybrid device stores both kinds of data in a **single** device:
- An **immutable side** for high-frequency telemetry: time-partitioned, append-only, and subject to [data retention](/docs/tagoio/devices/data-management/data-retention-feature.md) (`chunk_period` + `chunk_retention`), with the same behavior as an immutable device.
- A **mutable side** for a small set of variables that support update and delete and are never dropped by retention, with the same behavior as a mutable device.
A required per-device regular expression, `mutable_variable_regex`, routes each incoming datum at insert time: if the data's `variable` matches the regex, it is stored on the mutable side; otherwise it goes to the immutable side. Reads return both sides merged.
A practical pattern is to prefix the variables you'll need to edit. For example, set the regex to `^calc_` and name your Analysis outputs `calc_hourly_average`: raw sensor data stays on the immutable side, while computed results land on the mutable side, ready to be regenerated each hour.
The regex is an **unanchored substring match**: it matches anywhere in the variable name unless you anchor it with `^` or `$`.
| Regex | Effect |
| ---------- | ---------------------------------------------------------------- |
| `^calc_` | variables starting with `calc_` → mutable side; others immutable |
| `^status$` | only the exact variable `status` → mutable |
| `temp` | any variable containing `temp` → mutable |
The following regex constructs are **rejected** at create and edit time:
- Lookahead / lookbehind: `(?=`, `(?!`, `(?<=`, `(?.tago.io/device`
### Request
```json
{
"name": "My first device",
"description": "Creating my first device",
"active": true,
"visible": true,
"tags": [
{
"key": "client",
"value": "John"
}
],
"configuration_params": [
{
"sent": false,
"key": "check_rate",
"value": 600
},
{
"sent": false,
"key": "measure_time",
"value": 0
}
]
}
```
## Info
Retrieve information for a device using its ID.
GET - `https://api..tago.io/device/:id`
### Response
```json
{
"status": true,
"result": {
"created_at": "2016-11-03T23:24:19.787Z",
"updated_at": "2016-11-03T23:24:19.787Z",
"last_access": "2016-11-03T23:24:19.787Z",
"visible": true,
"active": true,
"tags": [{ "key": "client", "value": "John" }],
"name": "My Device",
"id": "581bc7233148f62587e2d507",
"configuration_params": [
{ "sent": false, "key": "check_rate", "value": "600" },
{ "sent": false, "key": "measure_time", "value": "" }
],
"bucket": {
"name": "My Bucket",
"id": "577bdd94567190920cfe9cfd"
}
}
}
```
## List
Retrieve a list of all devices in the account.
GET - `https://api..tago.io/device`
## Delete
Delete the device by its ID.
DELETE - `https://api..tago.io/device/:id`
### Response
```json
{
"status": true,
"result": "Successfully Removed"
}
```
## Generate Token
Generate a new token for the device and return it.
POST - `https://api..tago.io/device/token`
### Response
```json
{
"status": true,
"result": {
"token": "8be70d95-0dbc-4e16-89b6-b23b077d05e5"
}
}
```
## Delete Token
Delete device token by its token.
DELETE - `https://api..tago.io/device/token/:token`
### Response
```json
{
"status": true,
"result": "Successfully Removed"
}
```
:::info
## Rate Limits
When executing requests to TagoIO, you will have a limit on the number of requests that can be made during a certain time period. Read more about our [Rate Limits](/docs/tagoio/profiles/services/rate-limits-hard-limits.md).
---
## Building your Own Parser
In this tutorial, you will learn how to convert (parse) a raw payload sent by a
device into measurable variables.

You can create parsers for devices that are not listed in the Devices registry
(for example, when using a Custom device connector). You can also perform
additional calculations on the data after the system runs the parser provided by
the selected connector.
If you want to learn more about how data packages are sent to TagoIO, see
[Integration](/docs/tagoio/integrations.md).
## Tutorials
We provide three tutorials to help you understand how the Payload Parser works:
- [In-Depth Guide for Payload Parser](https://community.tago.io/t/in-depth-guide-to-payload-parser/898)
- [LoRaWAN/Sigfox Payload Parser Tutorial](https://community.tago.io/t/how-to-build-a-lorawan-sigfox-payload-parser/843)
- [MQTT Payload Parser Tutorial](https://community.tago.io/t/how-to-build-an-mqtt-payload-parser/1032)
Learn more about the overall concept in the
[Payload Parser Overview](/docs/tagocore/device/payload-parser.md).
---
## Connector Overview
Connectors let you create devices with built‑in behaviors that enable them to
communicate with a Network easily. Once a device is created using a connector,
the device will automatically run the connector's payload parser to decode
incoming data.

Device data flow
They also help you automate and scale your devices: after creating a device with
a connector, the connector's payload parser will be executed to decode incoming
data.
## Creating a connector
To create a new connector, go to the
[Integrations](https://admin.tago.io/integrations/connector) page and
press the '+ Add connector' on the right side of the page.
You must specify the connector's name and the network(s) it belongs to. You can
add as many networks as you like, but at least one network must be selected.
### Connector parameters
After creating a connector you can define several optional parameters that will
be available in the payload parser:
| Parameter | Description |
| --------------- | ---------------------------------------------------------------------------- |
| **Name** | The connector's name. |
| **Description** | A quick description of what the connector does. |
| **Logo URL** | An image URL for the sensor or company logo (used when a device is created). |
By default, a newly created connector is visible only to you. You can share it
with other [Profiles](/docs/tagoio/profiles) from different accounts. Read more
about
[Sharing Connectors & Networks](/docs/tagoio/devices/payload-parser/connector/sharing-connectors-networks.md).
If you want to make your connector public, submit a Pull Request on our GitHub
repository: https://github.com/tago-io/decoders. For details, see the article on
[Publishing, updating and accessing decoders](/docs/tagoio/devices/payload-parser/connector/publishing-updating-and-accessing-decoders).
### Documentation
You can provide additional information that will be shown to users when they
create a device with this connector:
- **Description**: A full description of the device. Include links to
datasheets or other resources.
- **Completion Text**: Information displayed immediately after the device is
created (optional).
- **Device Annotation**: Text always available on the user's devices page.
It's recommended to add links to dashboard templates here so users can quickly set up visualizations for their new devices.
### Device parameters
When a user creates a device with your connector, you can expose custom
parameters that will be accessible in the payload parser. Supported types
include:
- **Number**
- **Text**
- **Dropdown**: Users choose from predefined options.
- **Switch**: Boolean on/off value.
These values are available as global variables in the payload parser context
(see the
[Payload parser context overview](/docs/tagoio/devices/payload-parser/context-global-variables.md)).
### Payload parser
The connector's payload parser follows the same rules and syntax as a regular device payload parser. All parsing logic, variable names, and error handling are identical to those described in the article on [Payload Parser](/docs/tagocore/device/payload-parser.md).
---
## Publishing, Updating and Accessing Decoders
TagoIO provides a list of [pre-integrated IoT devices](https://admin.tago.io/connectors) for easy connection. However, due to the wide variety of manufacturers and ongoing sensor development, you might not find a connector for your specific device.
In such cases, you can [create your own connector](/docs/tagoio/devices/payload-parser/building-your-own-parser.md) and optionally make it available to all TagoIO users. Users and manufacturers can submit decoders that convert raw payloads to the TagoIO format. This collaborative approach supports the creation of new connectors, bug fixes, and updates to existing ones, improving IoT integration overall.
## Making your connector public
If you wish to make your connector accessible to all TagoIO users, request to make your connector public by submitting a Pull Request to our GitHub repository:
https://github.com/tago-io/decoders
## Video walkthrough
:::tip
When you publish your connector, anyone on the internet will be able to see the decoder code because our GitHub is open‑source.
If you want to keep your code private, share your connector directly with others instead of publishing it. Read more about [Sharing Connectors & Networks](/docs/tagoio/devices/payload-parser/connector/sharing-connectors-networks.md).
:::
## Repository Overview
The repository offers detailed resources for:
1. **Adding, fixing, and updating connectors**: by submitting pull requests in the GitHub repository.
2. **Validation and testing**: commands and tools to ensure decoders are properly formatted and functional.
3. **Access to the payload parser (decoder)**: since the repository is open-source, you will find the parser code of every connector available in our list.
## Getting Started
To publish connectors or manage existing ones, refer to the repository [README file](https://github.com/tago-io/decoders/blob/main/README.md). This file includes detailed instructions on:
- Folder structure and manifest file formats
- Steps for creating and managing decoders
- Validation and testing procedures
- Submission process for pull requests
## Accessing the Repository
For detailed information and resources on working with decoders, visit the GitHub repository:
- [TagoIO Decoders Repository](https://github.com/tago-io/decoders)
The repository contains all necessary guidelines, examples, and schema files to support work with decoders.
---
## Sharing Connectors & Networks
TagoIO provides many pre-integrated devices and networks so you can quickly connect devices. When a new device or network is not yet integrated, you can create a custom Connector or network integration. Those custom solutions can be shared with members of your team or with any external Profile for their use.
## Sharing a custom Connector or Network
To share a custom Connector or Network with another Profile:
1. Go to the [Integrations page](https://admin.tago.io/integrations/connector).
2. Select the connector or network you want to share.
3. Open the Share tab.
4. Click on the "Invite to Connector" or "Invite to Network" button.
5. Enter the Profile ID of the person you want to share it with.
6. Click the Share button.
Note: Profiles with access to the shared connector cannot modify it; they can only use it to create devices.

### Using a shared Connector
Once shared, the recipient profiles can select your connector when adding new devices through the [Connector Selection](https://admin.tago.io/integrations/connector) page. They can view all connectors that have been shared with them by navigating to the **My Shared Connectors** list.
### Using a shared Network
Custom networks are only applicable for Connectors created by you. To make your custom network available for your connector, head to the configuration page of your network within the [Integrations](https://admin.tago.io/integrations/connector) page and select the connector. Then:
- Navigate to the **General** tab.
- Select your custom network from the "**Select the networks for this connector**" option.
Ensure that the Profiles you are sharing the Connector with have access to at least one of the Networks used by the Connector. If the Connector uses a non-public Network, you must share both the Connector and the Network with the Profile.
Once you enable your custom network for a specific connector, the user will be able to choose your network before adding the device.
## Making a connector or network public
If you want your connector or network to be accessible to all TagoIO users, you can request to make it public. Read more about [Publishing connectors](/docs/tagoio/devices/payload-parser/connector/publishing-updating-and-accessing-decoders).
---
## Context & Global Variables
If you are going to create your own parser, you need to understand how context works.
When you start writing your own Payload Parser, you can use certain global variables in your code. Think of these global variables as variables you can access anywhere without explicitly creating them, similar to built-in keywords like module or process in Node.js.
These global variables can vary from simple objects to complex functions or arrays. To see a list of the global variables available, check the Payload Parser tab in the [Device](/docs/tagoio/devices/) details.
## Payload parser context (common globals)
| Payload parser context | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| payload\* | Content of the message that was sent. It usually is an Array of data or a string. At the end of the parser, the payload content is added to your device. |
| device | Device information containing configuration parameters, tags, device ID, etc. |
| raw_payload | Payload sent in the original request, without changes by any parser. Useful when you want to access the data before it has been changed by a Network parser. |
| timeUtils | [timeUtils library](/docs/tagoio/devices/payload-parser/payload-parser-timeutils-library.md) |
| dayjs\*\* | [Day.js library](https://day.js.org/) |
| console.log\*\* | Show an info message in the [Live Inspector](/docs/tagoio/devices/live-inspector.md). |
| console.debug\*\* | Show a debug message in the [Live Inspector](/docs/tagoio/devices/live-inspector.md). |
| console.error\*\* | Show an error message in the [Live Inspector](/docs/tagoio/devices/live-inspector.md). |
> - This variable must be an [Array of TagoIO data](/docs/tagoio/devices/sending-data.md) when the code ends.
> - These are functions available to be used in the Payload Parser.
:::info
The list above shows the most commonly used globals visible in this article. Additional global variables (which can range from simple objects to complex functions or arrays) are available: see the Payload Parser tab in the [Device](/docs/tagoio/devices/) details for the complete list.
:::
---
## Filtering Out Variables with Parser Code
When your device sends sensor data to TagoIO, you can add parser code to the device (in the "Payload Parser" tab) to extract and create variables in the device's data storage using TagoIO format. If you want to save space in register storage, you can ignore variables you don't want added by editing the parser.
:::info
By default, some fields and metadata sent by providers like LoRaWAN and Sigfox are already ignored to avoid unnecessary storage.
:::
## How it works
- The parser runs on incoming payloads and decides which variables to create in the device's data storage.
- By changing the parser logic, you can skip creating variables for unneeded data fields, avoiding extra storage usage.
## Default ignored fields
Some common provider metadata and fields that typically do not need to be stored are automatically ignored. This helps avoid storing duplicate or unnecessary information sent by networks such as LoRaWAN and Sigfox.
## Example
For example, if your device sends data from TTN (The Things Network), most variables, including the raw payload, will normally be created in the device's data storage. You can modify the parser to prevent storing specific fields or metadata you don't need.
A variable called **location** is automatically created by combining the fields `latitude` and `longitude`. Use this combined field when plotting maps.

## Editing the parser
- Go to [Devices](https://admin.tago.io/devices) at your Admin panel, select your device, open the device settings and go to the "Payload Parser" tab.
- Update the parser code to filter out unwanted variables before sending the parsed output to the device's storage.
- To ignore specific variables, add their names to the `_ignore_vars_` list in your parser configuration. For example:
```json
{
"ignore_vars": ["temperature", "humidity"]
}
```
This will prevent those variables from being created in the device's data storage.
- Test changes by sending sample payloads and verifying which variables are created in the device's storage ([Device's data storage](/docs/tagoio/devices/)).
We offer this flexibility to allow you to optimize your storage by avoiding the creation of unnecessary variables.
---
## Payload Parser(Payload-parser)
The Payload Parser handles the raw payload sent by devices to extract measured
variables. For example, it can transform a HEX payload sent by a device into
temperature and battery levels.
You can also use the parser to process payloads in real time and convert values
to the desired unit. For example, if your data contains a temperature in °F, you
can use the parser to convert it to °C before inserting the data into the
[Device](/docs/tagoio/devices/).
You can learn about the
[differences between Payload Parser and Analysis](/docs/tagocore/device/payload-parser.md).
## Parser execution
TagoIO can run up to two parsers for a device:
- The Connector parser (added automatically during device creation)
- Your own custom parser (optional)
:::info
The parsers are executed in sequence. First the one from the
[Connector](/docs/tagoio/integrations.md), then your custom
parser.
:::
## Connector type
The connector type is defined based on your selection from the list of devices
during the Add Device step. The connector contains the script necessary to work
with that device.
## Parser settings

> The script contained inside the parser from the Connector is not visible.
### How is the Payload Parser triggered?

The payload parser is automatically triggered every time the device sends data
to TagoIO (no need to set up anything). Your payload parser is the first step
that gets triggered before saving data in the [Device's](/docs/tagoio/devices/)
data storage, so if your code has an error it will be returned directly to the
device HTTP post response.
There are some concepts about
[global variables and context](/docs/tagoio/devices/payload-parser/context-global-variables.md)
that you may want to learn.
### A parse example
For this example, connect a toaster to TagoIO.
A toaster could send data to TagoIO when your bread is toasted in the following
format:
```javascript
{
"variable": "toastValues",
"value": "01 350 00"
}
```
The value above could represent XX YYY ZZ. Where:
| Values | Representation |
| ------ | --------------------------------- |
| XX | 00 - Interrupted, 01 - Successful |
| YYY | Temperature in Fahrenheit |
| ZZ | Error code or 00 - No error |
That value is good enough for machines or engineers, but for humans, it doesn't
mean anything and also it's not optimized for the TagoIO dashboard. Therefore, we
need to parse this payload.
Code the Payload Parser to transform that payload into real variables.
First, click on your Device and go to the Payload Parser tab.

On the Payload Parser tab you will see the code editor. Then, you can write the
following code:
```javascript
const rawValues = payload.find((item) => item.variable === "toastValues");
if (rawValues) {
const valuesString = rawValues.value;
const splitValues = valuesString.split(" ");
const successCode = splitValues[0];
const tempF = splitValues[1];
const errorCode = splitValues[2];
payload.push({ variable: "successCode", value: successCode });
payload.push({ variable: "temperature", value: tempF, unit: "F" });
if (errorCode !== "00") {
payload.push({ variable: "error", value: errorCode });
}
}
```
Save, and you are ready. Now, every time the toaster sends those variables, the
parser will transform it into real variables, and you can use them to build
Dashboard, Analysis, or Action.
---
## Metadata
Metadata is one of the most important concepts when creating [dashboards](/docs/tagoio/dashboards/) and [widgets](/docs/tagoio/widgets/). Metadata is a piece of information that you can store in the variable payload.
With metadata, it is possible to dynamically change the behavior of the widget and how it displays the variable data. For example, it can change a [map](/docs/tagoio/widgets/map-and-location/map-widget/) pin color or place a [heat map](/docs/tagoio/widgets/map-and-location/heat-map-widget.md) bubble in another position, and more.
## JSON example
```json
{
"variable": "current",
"value": 5.3,
"unit": "A",
"metadata": {
"color": "red",
"my_application_id": "378392922"
}
}
```
## Using custom metadata fields
You can define your own metadata fields and use them in your [Analysis](/docs/tagoio/analysis/) or Application. Some metadata fields trigger specific widget behaviors or change how the widget displays the data.
:::info
Metadata has priority over customizations set in the widget edit screen. Some widgets provide an option to change this behavior.
:::
For example, this article uses the `color` metadata field in the following JSON payload:
```json
{
"variable": "temperature",
"value": 71,
"metadata": {
"color": "red"
}
}
```

## Available Metadata Fields
| Field | Description | Widget |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| background_color | Changes the widget background color. It accepts CSS color keywords or HSL, RGB, and HEX color codes. | Angular, Card, Dial, Grain Bin, Image, Solid, Vu Meter |
| button_color | Sets a color for the button. It accepts CSS color keywords or HSL, RGB, and HEX color codes. | Push Button, Step Button |
| chart_color | Changes the chart color. It accepts CSS color keywords or HSL, RGB, and HEX color codes. | Card |
| color | Sets a color for this variable data. It accepts CSS color keywords or HSL, RGB, and HEX color codes. | Angular, Area Chart, Card, Cylinder, Dial, Dynamic Table, Grain Bin, Horizontal Bar, Icon, Image Marker, Keypad, Line Chart, Map, Multiple Charts, Pie, Semi Donut, Semi Pie, Solid, Static Table, Step Button, Vertical Column, Vu Meter |
| contributors | Lists the top contributing variables for a scored cell, as an array of `{ variable, weight }` with weight from 0 to 1. | Calendar Heatmap |
| dropdown_option_hidden | Dynamically hides the option from dropdown fields. Accept **true** or **false** | Dynamic Table, Static Table, Device List, User List, Input Form |
| embed | Sets an embedded URL for the variable display | Compose, Image Marker |
| end_date | Specifies the end of a date range. It accepts the date and time in the ISO 8601 format. | Area Chart, Horizontal Bar, Line Chart, Multiple Charts, Vertical Column |
| expected_max | Upper bound of the expected range for a scored cell. It only accepts numerical values. | Calendar Heatmap |
| expected_min | Lower bound of the expected range for a scored cell. It only accepts numerical values. | Calendar Heatmap |
| icon | Specifies an icon for the variable data. It accepts the TagoIO icon's name, and an SVG file URL. | Compose, Icon, Image Marker, Map |
| img_pin | Sets an image for the infobox. It only accepts image file URLs. | Image Marker, Map |
| label | Displays a label instead of the variable value | Compose, Dynamic Table, Image Marker, Map, Static Table |
| layer | Sets the specific layer id that this data is related to | Image Marker |
| limit_superior | Specifies the end of a scale range. It only accepts numerical values. | Angular, Dial, Cylinder, Grain Bin, Heat Map, Solid, Vu Meter, Line Chart |
| limit_inferior | Specifies the start of a scale range. It only accepts numerical values. | Angular, Dial, Cylinder, Grain Bin, Heat Map, Solid, Vu Meter, Line Chart |
| lower | Lower confidence bound for a forecast point. It only accepts numerical values (absolute, not an offset). | Line Chart |
| observed | Observed value for a scored cell, shown in the hover card. It only accepts numerical values. | Calendar Heatmap |
| point_color | Sets a color for the chart point (can be replaced by the color field). It accepts CSS color keywords or HSL, RGB, and HEX color codes. | Area Chart, Horizontal Bar, Line Chart, Multiple Charts, Vertical Column |
| radius | Sets a point radius for charts. It only accepts numerical values, and the default value is 4. | Area Chart, Heat Map, Horizontal Bar, Line Chart, Multiple Charts, Vertical Column |
| recolor | Repaints the series inside an anomaly time or value band with the band color. Accepts **true** or **false**. | Line Chart |
| severity | Overrides the severity tier for a scored cell. Accepts normal, low, elevated, warning, high, or critical. | Calendar Heatmap |
| start_date | Specifies the start of a date range. It accepts the date and time in the ISO 8601 format. | Area Chart, Horizontal Bar, Line Chart, Multiple Charts, Vertical Column |
| text_color | Sets a color for the widget text. It accepts CSS color keywords or HSL, RGB, and HEX color codes. | Angular, Cylinder, Dial, Grain Bin, Push Button, Solid, Vu Meter |
| upper | Upper confidence bound for a forecast point. It only accepts numerical values (absolute, not an offset). | Line Chart |
| url | Shows an external URL | Compose, Dynamic Table, Image Marker, Map, Static Table |
| url_label | Adds a custom label instead of showing the external URL link | Compose, Image Marker |
| x | Sets the x coordinate. It only accepts numerical values from 0 to 1. | Compose, Image Marker, Heat Map |
| y | Sets the y coordinate. It only accepts numerical values from 0 to 1. | Compose, Image Marker, Heat Map |
## Form Field Metadata
Some widgets such as **Input Form**, **Dynamic Table**, and others allow users to edit variable data through form fields. The following metadata can be used for custom behavior in these form fields:
| Field | Description | Form field type |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
| label | Displays a label instead of the variable value | Dropdown, Dropdown Multiple, Filtered Variables |
| end_date | Specifies the end of a date range. It accepts the date and time in the ISO 8601 format. | Calendar |
| start_date | Specifies the start of a date range. It accepts the date and time in the ISO 8601 format. | Calendar |
| type | Changes the box validation color. Accepts: "success", "danger", "error", "danger-light", "error-light", "info", "warning" and "dark". | Validation |
---
---
## Parser vs. Analysis Comparison
The [Payload Parser](/docs/tagocore/device/payload-parser.md) was created to handle raw payloads sent by devices to extract measured variables or execute simple operations. The [Analysis](/docs/tagoio/analysis/) feature is more capable: it includes access to [Devices](/docs/tagoio/devices/) and external services, but there is a cost to run analyses. Payload Parser uses a simple JavaScript engine developed by TagoIO to be easy, lightweight, and free.
## Comparison
| Features | Payload Parser | Analysis |
| --------------------------- | ---------------------------: | ---------------------------------------------: |
| Code size limit | 64kb | 5MB |
| Language | Simple JavaScript syntax | Full JavaScript and others |
| Async | No | Yes |
| Sync | Yes | Yes |
| Allows custom functions | Yes | Yes |
| Allows external integration | No | Yes |
| Allows requires/imports | No (only built-in functions) | Yes |
| Console log or context | Yes (Device Inspector) | Yes |
| Timeout | 1,000 ms | Custom |
| Try/Catch | No, an error stops parsing | Yes |
| Predefined variables | msg | device, context, scope, result |
| Needs Action to trigger it | No | Yes |
| Cost of the service | Free | [Check out /pricing](https://tago.io/pricing/) |
## Notes
- Payload Parser is intended for lightweight parsing and basic operations
directly on incoming device payloads.
- Analysis offers broader capabilities (access to devices, external services,
larger code size, and more advanced error handling), suitable for more complex
workflows.
- Use the [Payload Parser](/docs/tagocore/device/payload-parser.md) for simple,
free parsing needs and [Analysis](/docs/tagoio/analysis/) when you need
external integration, advanced JavaScript features, or persistence across
executions.
---
## Payload Parser timeUtils Library
The timeUtils library is available as a global variable in the Payload Parser and is designed to simplify timezone conversion and manipulation. It provides functions to convert, format, and manage time information easily.
## Available Functions
### compareTimezones(dateInputA, timezoneA, dateInputB, timezoneB)
Compares two dates in their respective timezones and returns:
- `1` if the first date is greater,
- `-1` if the second date is greater,
- `0` if they are equal.
Example:
```javascript
const dateA = "2023-01-01T12:00:00.000Z";
const dateB = "2023-01-01T10:00:00.000-03:00";
const result = timeUtils.compareTimezones(dateA, "UTC", dateB, "America/Sao_Paulo"); // result will be -1
```
### convertTimezone(dateInput, fromTimezone, toTimezone)
Converts a date (string or Date object) from one timezone to another and returns the result as an ISO string.
Example:
```javascript
const convertedTime = timeUtils.convertTimezone("2023-01-01T12:00:00.000Z", "UTC", "America/New_York");
```
### formatInTimezone(dateInput, timezone, format)
Formats a date in a specific timezone according to the specified format string. The formatting options are based on the chrono `strftime` specification. You can find all available format specifiers in the [chrono documentation](https://docs.rs/releases/search?query=chrono).
Example:
```javascript
const formattedTime = timeUtils.formatInTimezone(new Date().toISOString(), "Europe/Paris", "%Y-%m-%d %H:%M:%S %z");
```
### getTimezoneAbbreviation(dateInput, timezone)
Returns the timezone abbreviation for a given date and timezone (e.g., `EST`, `PST`).
Example:
```javascript
const abbreviation = timeUtils.getTimezoneAbbreviation(new Date().toISOString(), "America/New_York"); // e.g., EST or EDT
```
### getTimezoneInfo(timezone)
Retrieves detailed information about a specific timezone.
Example:
```javascript
const tzInfo = timeUtils.getTimezoneInfo("America/New_York");
```
### getTimezoneOffset(dateInput, timezone)
Calculates the offset in minutes from UTC for a given date and timezone.
Example:
```javascript
const offset = timeUtils.getTimezoneOffset(new Date().toISOString(), "America/Sao_Paulo");
```
### isValidTimezone(timezone)
Checks if a given timezone string is valid.
Example:
```javascript
const isValid = timeUtils.isValidTimezone("America/Los_Angeles"); // true
const isInvalid = timeUtils.isValidTimezone("Invalid/Timezone"); // false
```
### listTimezones()
Returns an array of all supported timezone names.
Example:
```javascript
const timezones = timeUtils.listTimezones();
```
### nowInTimezone(timezone)
Returns the current time in the specified timezone as an ISO string.
Example:
```javascript
const nowInTokyo = timeUtils.nowInTimezone("Asia/Tokyo");
```
---
## Payload Parser Troubleshooting
When using the Payload Parser on TagoIO, you may encounter common problems that prevent the parser from decoding your device data correctly. When the parser stops, the data will not be saved to TagoIO. These problems are usually related to mistakes in how the payload parser code is written, but they can be easily fixed.
For updates on system status, visit the health [status page](https://status.tago.io/).
## Common Issues
### 1. Function Parse Error: Payload Parser Terminated Unexpectedly
This happens when the Payload Parser encounters an error in code that is not formatted correctly and stops working. For example, using a plain string as an error, like:
- throw "my error message"
can cause the parser to stop unexpectedly.
**Fix:**
- Use the proper JavaScript `Error` class when throwing errors. The Payload Parser uses JavaScript, so create errors like this:
```javascript
throw new Error("my correct error message");
```
This method provides a clear and detailed error message, making it easier to identify and fix the problem in your code.
### 2. "your_variable" is Not Defined
This error appears when the Payload Parser tries to use a variable that hasn't been declared. For example, writing:
- temp = payload.value
without first defining what `temp` is will produce a "not defined" error.
**Fix:**
- Always declare your variables before using them with `const`, `let`, or `var`. This prevents the "not defined" error and avoids creating accidental global variables.
Examples of variable declaration:
```javascript
// Example 1
const temp = payload.value;
```
```javascript
// Example 2
let temp = payload.value;
```
```javascript
// Example 3
var temp = payload.value;
```
---
## Sending Data to Device
TagoIO supports two-way communication with devices. Below are the main options
for sending data from your application to a device.
## Methods to send data
- [Publish to MQTT](/docs/tagoio/integrations/networks/mqtt/mqtt-publishing-and-subscribing.md)
- [Send data over HTTP](/docs/api/send-data)
- [Run an Analysis script](/docs/tagoio/analysis/creating-analysis.md) that
calls an external API
- [Integrate through Network Services](/docs/tagoio/integrations.md)
- Work with Support to build middleware that speaks your device's native
protocol (support@tago.io).
## Reading data from TagoIO
Devices can also retrieve data from TagoIO using different endpoints depending
on the type of data needed:
- [Configuration parameters](/docs/api/get-configuration-parameters) to read
device settings (e.g., thresholds, behavior)
- [Data endpoint](/docs/api/get-data) to read device data (e.g., sensor values,
locations, history)
**Best Practice**: Use configuration parameters for device settings; use the
data endpoint for measurements.
---
## Sending Data
:::info
When making a request to the TagoIO API, you must also specify the appropriate regional endpoint. Check out the available regions here: [API documentation](https://api.docs.tago.io/)
:::
:::tip[Looking for a lighter protocol?]
[TagoTiP](/docs/tagotip/) lets you send the same data in ~130 bytes instead of ~487 - no JSON, no HTTP headers. Ideal for constrained devices. See [TagoTiP over HTTP](/docs/tagotip/transports/http) for the HTTP transport.
:::
A device can send data to TagoIO by using the POST method.
```
POST https://api..tago.io/data
```
## Request fields
| Key | Type | Required |
| -------- | ----------------------------------- | --------- |
| variable | string (max 100 characters) | yes |
| unit | string (max 25 characters) | no |
| value | string / number / boolean (max 6kB) | no |
| time | string | no |
| group | string (max 100 characters) | no / auto |
| location | object \| geoJSON | no |
| metadata | object (max 10kB) | no |
You must include a [Device-Token](/docs/tagoio/devices/device-token.md) to authorize the operation. Learn more about the fields necessary to send data to TagoIO, including the Header and other formats: [fields necessary](/docs/tagoio/api/restful-api.md).
## Location field
The `location` field accepts two formats:
**GeoJSON Point:**
```json
{ "type": "Point", "coordinates": [-85.628292, 42.2974279] }
```
**LatLng literal:**
```json
{ "lat": 42.2974279, "lng": -85.628292 }
```
Both are valid at ingestion time. TagoIO normalizes all location data to GeoJSON before storing it, so what you read back will always be `{ "type": "Point", "coordinates": [...] }`.
:::caution
GeoJSON uses **[longitude, latitude]** order, not the common lat/lng convention. Double-check your coordinate order when using the GeoJSON format.
:::
## Notes and restrictions
- Variables are always converted to lowercase.
- Variable names may not include special characters such as `*?!<>.-=$` or spaces.
- You can send multiple registers in a single POST by sending an array of objects. The maximum payload size is 200 kB (approximately 8,000 registers).
- [Metadata](/docs/tagoio/devices/payload-parser/metadata.md) can be attached to variables to influence widget behavior and store additional information. For example:
```json
{
"variable": "temperature",
"unit": "F",
"value": 55,
"metadata": { "color": "red", "icon": "car" },
"location": { "lat": 42.2974279, "lng": -85.628292 }
}
```
When displayed in a table, the row will be red; when displayed on a map widget, the pin will be red with a car icon.
- Rate limits apply to API requests. Read more about them here: [Rate Limits](/docs/tagoio/profiles/services/rate-limits-hard-limits.md).
- For testing, try our Device Emulator to post data and learn the JSON structure: [Device Emulator](/docs/tagoio/devices/device-emulator.md).
## Example payload:
```json
{
"variable": "temperature",
"unit": "F",
"value": 55,
"time": "2015-11-03 13:44:33",
"location": { "lat": 42.2974279, "lng": -85.628292 }
}
```
---
## Device Simulators
If you don't have real sensor hardware to send data through the Internet, you can still try all of TagoIO's features by selecting device simulators that feed data in real time. It's like adding a virtual device.

TagoIO's server provides a continuous stream of data, whether that data is simulated (e.g., bus, truck, freezer) or real (e.g., weather conditions in Raleigh, NC), through the Simulator network. Data is sent to the device's storage in TagoIO.
## Adding a Simulator Device
To use this feature, go to **[Devices](https://admin.tago.io/devices)**, then click on "+ Add Device", select the Network: **Simulator**, and from the list of connectors pick the one that simulates a type of device you want. For example, choose the "Bus Simulator" which will send location, fuel level, speed and other variables to your device data storage every 3 minutes.
After adding the device, a pop‑up will ask if you want to add a [dashboard](/docs/tagoio/dashboards/) from our [Dashboard Templates](/docs/tagoio/dashboards/distributing-dashboards.md). Use this option to get a dashboard to start and edit from there.
:::tip
TagoIO provides a list of Dashboard Templates for [you to choose from](https://tago.io/dashboard-templates). Some of them are made specifically for simulators, so you can start with a dashboard that is already configured to your device!
:::
## Data Visibility and Live Inspector
Data can take a few minutes to appear in the device. For example, weather data may take up to 15 minutes to present for the first time.
Use our [Live Inspector](/docs/tagoio/devices/live-inspector.md) to monitor raw data arriving on your account in real‑time.
---
## Tags System
Tags are a flexible and effective way to organize and label your assets within TagoIO. Tags let you manage access and identify key components such as [Devices](/docs/tagoio/devices/), [Actions](/docs/tagoio/actions/), [Analysis](/docs/tagoio/analysis/), [Dashboards](/docs/tagoio/dashboards/), and [Users & Access rules](/docs/tagoio/tagorun/access-management/).
Operating like key-value pairs, tags enable you to assign relevant metadata to your assets, such as:
- names
- descriptions
- categories
- locations
- customers
- products
- and more
## 1. How to use tags
Tags can be assigned to components throughout TagoIO. They are typically available through a designated **"Tag"** tab when configuring assets that support label assignment.
### Tag parameters
| Parameter | Description |
| --------- | ----------------------------------------------------------------------------- |
| **Key** | Name of the parameter used as reference; you may also select an existing key. |
| **Value** | The value you want to assign to your asset. |
### Maximum number of tags per asset
The maximum number of tags that can be attached to a single asset is **30**. If you attempt to add more than this limit, a notification will appear indicating that the maximum capacity has been reached.
### Delete protection
You can protect critical resources from accidental deletion by adding a special tag:
1. **Key:** `_delete_protection_`
2. **Value:** A custom error message to display when deletion is attempted
When this tag is present on a resource, any attempt to delete it will be blocked and result in an error or warning that you can customize.
### Managing tags
Tags can be managed from several sources:
1. **TagoIO Interface**: Manually add or edit tags by clicking the asset and then selecting the Tags tab.
2. **Analysis SDK**: Programmatically add or edit tags from an analysis script using methods such as `Resources.devices.edit()` or `Resources.actions.edit()`.
3. **TagoIO API**: Use REST API endpoints for each asset type to add or edit tags by sending HTTP requests with JSON payloads containing the tag information.
### Common use cases
- **Access Management:** Assign a tag like `_role: Manager_` to a user group and grant them access to all devices tagged with `_customer: ACME Inc._`.
- **Blueprint Dashboards:** Create a dashboard that allows users to switch between devices by selecting any device with a specific tag, e.g., `_device_type: Temperature Sensor_`.
(Example UI: the screenshot shows a "Tags" tab with a table-like interface where you can add a tag key and enter its corresponding value. There are controls to add or remove tag rows and a Save button.)
---
## Entities
The Entity feature is an excellent tool for storing data in a tabular format. Unlike the [Devices](/docs/tagoio/devices/) feature, which is optimized for storing sensor data with high-frequency write operations, the Entity feature provides a versatile solution for managing a wider variety of data types with less frequent writes.
Within an Entity, you can create custom fields similar to columns in a traditional database and define specific data types. You can also improve search and retrieval performance by implementing single or multiple indexes. Entities are well suited to store diverse data such as site information, organizational metadata, contact lists, equipment details, and product information.
Once your information is stored, you can access the data through [Analysis](/docs/tagoio/analysis/), the [TagoIO API](https://api.docs.tago.io/), and soon through [Widgets](/docs/tagoio/widgets/).

## Creating an Entity
To create a new Entity, open the [Entity module](https://admin.tago.io/entities) on your Admin page and click on "+ New Entity" (or the equivalent action). You will be prompted to define a name and add fields. Note that once an entity is created, its name cannot be changed; however, you can still add or remove fields as needed.
### Mandatory default fields
| Field | Description |
| ---------- | --------------------------------------------------------------------------------------------------- |
| id | A Universally Unique Identifier (UUID) for the data entry, automatically generated if not provided. |
| created_at | Timestamp of when the data entry was created. |
| updated_at | Timestamp of the last time the data was edited. |
These fields are always present and cannot be removed or modified.
---
## Managing Entities
Once your Entity is created, you can configure its fields and indexes, preprocess incoming data with the Schema Parser, and manage your data entries directly from the Admin or via the API.
## Managing fields
You can easily manage your fields by accessing the **Fields** tab within your Entity. Here, you have the option to add, edit, and remove fields.
### Data types
When adding new fields, you have several data types to choose from. Once a field is created, its data type cannot be changed.
| Data Type | Description |
| --------- | --------------------------------------------------------- |
| String | A variable‑length string. Maximum size of 255 characters. |
| Integer | An integer number (e.g., 10). |
| Float | A floating‑point number (e.g., 10.5). |
| Text | A long text string. Maximum size of 30,000 characters. |
| Timestamp | A date/time in ISO‑8601 format. |
| JSON Data | A JSON object, maximum size of 10 KB. |
## Indexing fields to improve searching and sorting
You can index your data to optimize search operations and determine the order of results. Indexes improve database performance and are required for API and Analysis requests.
By default, every entity will have an index on the field **id**.
### Adding new indexes
To create a new index, go to the **Fields** tab and click the "**+ Add Index**" button. From this menu, you can select the fields you want to include in the index and assign it a name. Note that indexes cannot be edited once created, but they can be deleted and recreated if necessary.
Since you can add multiple fields to your index, the order of selection matters because sorting is only possible by the last field chosen. Some dashboard functionalities will automatically select the most appropriate index based on the filters applied, unless a specific index is explicitly designated.
### Using Indexes
When filtering data, you must provide all filters matching the index from left to right:
1. You don't need to use all fields in the index, but you must use them in order, starting from the leftmost field.
2. For example, if you have an index with fields (A, B, C):
- You can filter using only field A.
- You can filter using fields A and B.
- You can filter using fields A, B, and C.
- You cannot filter using only B, or B and C, without including A.
## Preprocessing data before insertion
Before storing data in your entity, you can preprocess it using the **Schema Parser**. This tool is especially beneficial for validating, normalizing, or transforming data before storage. The Schema Parser operates as a JavaScript code that executes during data insertion.
To activate it, go to the Schema Parser tab and toggle the "**Enable**" button. Once activated, you can begin writing your code.
### Global Variables
The following global variables are available in the Schema Parser:
| Variable | Description |
| -------- | ---------------------------------------------------------------------------- |
| payload | Content of the message being inserted. Usually an Array of data or a string. |
| dayjs | Day.js library for date/time operations. |
## Managing the data in your Entity
Once the data is added to your Entity, you can easily manage it by viewing, filtering, inserting, editing, and deleting data entries. You can also manage your data through Analysis SDK or the [TagoIO API](https://api.docs.tago.io/).
You can completely empty the data of your Entity by going to the **More** tab and clicking the "**Empty Entity Data**" button.
### Guidelines for managing your data
1. Regularly review and clean up outdated or invalid data.
2. Use Analysis scripts for bulk operations and complex data management tasks.
3. Validate required fields and expected data types in your Schema Parser to protect data integrity.
The number of data entries available per entity is determined by your subscription plan. For more detailed information, please refer to the **Limits and Restrictions** section.
## Limits and Restrictions
The Entity has certain limits based on your TagoIO account plan. These limits apply to the number of entities, fields, indexes, and data entries you can create.
| Feature | Free | Starter | Scale |
| ------------------ | ----- | ------- | ------- |
| Fields per Entity | 10 | 30 | 30 |
| Indexes per Entity | 2 | 10 | 10 |
| Fields per Index | 5 | 5 | 5 |
| Data Entries | 1,000 | 100,000 | 100,000 |
Entity operations are subject to the same rate limits (Requests Per Minute) as other mutable operations in your plan. For detailed information on these limits, please refer to the Rate Limits documentation.
---
## Files
You and your IoT devices can send and receive files such as images, CSV, TXT, and many other formats. All of these files can be stored and accessed in TagoIO.
## Overview
File storage is commonly used to send and receive files from devices, store data that is generated by your devices, store images to be displayed in widgets, attach files to emails, or support any other needs you might have for storing files and controlling private or public access to them.

To use the files via **Admin**, go to _Files_ in the left menu. From there you can see and manage all the files and folders that are stored on your account, and upload new files.
> Developers can use our [SDK](/docs/tagoio/analysis/sdk/) to store and manage files using their applications, upload files from devices, and more.
### Features
- Drag‑and‑drop files onto the page to upload them to the current folder.
- Select multiple files by holding **Shift** or **Ctrl** while clicking on the desired files.
- Right‑click a file to open a context menu with actions such as rename, delete, move, and download.

- Selecting a row shows detailed information about the file or folder in the right‑side panel.
---
## Uploading Files
To upload a file using the admin platform, go to [Files](https://admin.tago.io/files) and click the "Upload File" button in the top-right corner of the page.
By default, all files are uploaded with Public access. This lets you copy a file's link and easily share it with someone.
:::tip
Remember to always set your files to Public if you plan on using them inside widgets.
:::
## Drag and Drop
If you do not wish to manually select the files, you can drag-and-drop them into a specific folder or into the main panel.

## Folders
Folders can be created before you upload your files. Simply press the button on the top right corner, or right‑click in an empty space and then select **New Folder**.
After you select the desired files, they will be uploaded to the current folder being viewed. If you wish to upload the files to another folder, simply access that folder and upload the files there.
If you uploaded the files in the wrong folder, simply select all of them and drag‑and‑drop them to the correct folder, or right‑click them and select the option **Move**.
---
## Getting Started
Welcome to TagoIO! Get started by adding a device to your account.
### Step 1. Add a device
Start by adding a [device](https://admin.tago.io/devices) to your account. This
will provide a link between the data in your account and the external world.
In this example, you will send a variable called _temperature_ from your device
to your account.
1. Click the **Device** button on the left sidebar.
2. Click the **Add Device** button.
3. Look for the type **HTTPS** and select it.
4. In the field _"Device Name"_, add any name for your device.
5. Click the **Create Device** button.

All devices should use a valid
[device-token](/docs/tagoio/devices/device-token.md) when accessing TagoIO. One
device‑token is generated when a device is created and it is available for you
to copy if you want to insert it in a real device.

### Step 2. Build a dashboard
You can build great [dashboards](/docs/tagoio/dashboards/) to visualize data,
interact with your devices and share with others. You can build it with the
[widgets](/docs/tagoio/widgets/) that best fit your needs.
1. Click **Dashboards** on the left side bar.
2. Click **+**, then select the type of dashboard, give a name for your
dashboard, and click **Save**.
3. Add one widget to show the variable _temperature_. Click **+** (the top right
button), and pick the widget **Card**.
4. Start the configuration of this widget by adding the device and variable to
be displayed. Click on the search area then type the variable name that will
be sent by the device as temperature, and click **Save**. Your widget will
be ready!

### Step 3. Send data
Now that you have completed the setup of your account, you are ready to send
data from your electronic devices using our
[APIs](/docs/tagodeploy/project/api.md). You can use one of our
[SDKs](/docs/tagoio/analysis/sdk/) designed for your platform.
You can simulate your device using any tool that transfers data to and from a
server with HTTP. Below, you will learn how to quickly send data depending on
the OS that you are using.
#### Mac or Linux
Use curl to post the data. Enter the following command replacing the
**<Device-Token>** by the token created earlier for your device.
```bash
curl -L 'https://api.us-e1.tago.io/data' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Device-Token: ' \
-d '[
{
"variable": "temperature",
"value": 27,
"unit": "F"
}
]'
```
#### Windows
To send data, there are some tools that support HTTP communication like
[Postman](/docs/api/sidebar/tagoio-api-intro) or Insomnia. Considering the same
input as used above, you can make a POST by entering the URL, Device-Token, and
configuration in Postman like this:

Then, input the variable information in the JSON body. Make sure that you select
the same configuration as shown in the picture below, and click send!

### Step 4. Create complete solutions
This is just the beginning! See how TagoIO helps you deploy solutions in
production.
Create an [Analysis](/docs/tagoio/analysis/) in real-time using our script capabilities in JavaScript. Program [actions](/docs/tagoio/actions/) to be taken based on your rules.
Learn more about our
[APIs](/docs/tagodeploy/project/api.md), and check out the
tutorials and [SDKs](/docs/tagoio/analysis/sdk/) prepared for different boards
and applications.
---
## Authorization
Authorization is used when you need to grant external services access to your
devices.
With a valid authorization, those services can also create new devices in your
account. These new devices will appear in the list as "Unassigned", making it
easier to scale your applications.
:::info
An Authorization token is different than a
[device-token](/docs/tagoio/devices/device-token.md).
> When to use each:
>
> - Use authorization only when integrating with external services, such as
> LoRaWAN or Sigfox.
> - Use device-token when connecting directly with HTTPS or MQTT.
:::
## Generating an Authorization
To generate an authorization and give an external service access to your
devices, follow these steps:
1. Go to the Device section of your TagoIO account and click the
"[Authorization](https://admin.tago.io/devices/authorization)" button.

2. In the next screen, enter a name you can identify later, fill any additional
parameters requested by the integration, and press the Generate button.

Only certain integrations require an **Additional Parameter**. Check out the
[list of integrations](/docs/tagoio/integrations.md) to learn the parameter
needed for each provider.
Once the authorization is created, you can copy and paste it into the external
service portal.
## Copying authorization credentials
Each authorization entry in the list has a **Copy** button with a dropdown that
exposes the following values:
| Field | Description |
| -------------- | ---------------------------------------------------------------------------- |
| **Token** | The full authorization token. Most integrations use this value directly. |
| **Token Hash** | A hashed representation of the token, used by TagoTiP for verification. |
| **User** | The user identifier derived from the token. Used in MQTT-based integrations. |
| **Password** | The password field for MQTT-based integrations. |
How to use the authorization depends on the service that you are integrating.
Here you can see a list of service integrations.
---
## Creating a Network Integration
To create a new integration, go to the admin panel and click on the [Integrations](https://admin.tago.io/integrations/connector) button in the sidebar menu.
From there you can click on the "My Networks" on the menu on the right side of the page and finally click on the "Add network" button.

All you'll need to add is the network's name and click on the "Create my Network" button. Now you can configure the network's parameters by clicking on the "My networks" list.
## Parameters
- **Name:** Network name.
- **Description:** Network description.
- **Documentation Link:** External link to documentation on how to use the
network. Preferably link to TagoIO's community page.
- **Icon:** Icon for the network, shown in the sidebar.
- **Banner:** Banner image for the network, shown when the network is selected.
> After you create a network, it will only be visible to you by default.
> However, you have the option to share it
## Tips
- Only the network name is required to create the integration; all other fields
are optional.
- Use the Documentation Link to point users to implementation details or
community resources.
## Sharing Networks
You can share a private network with other **[Profiles](/docs/tagoio/profiles)**
from different accounts. Read more about
[Sharing Connectors & Networks](/docs/tagoio/devices/payload-parser/connector/sharing-connectors-networks.md).
:::tip
If you wish to make your connector or network accessible to all TagoIO users,
you can request to make it public by submitting a Pull Request to our GitHub
[repository](https://github.com/tago-io/decoders).
:::
## Serial Number
You can configure whether the serial number is required for devices in this
network. If not required, the user will not be prompted to enter one. Usually
you want this enabled.
The serial number allows a device to use its **network token** and insert data
into the device.
- **Label:** Serial number label (e.g., Device EUI, Serial, IMEI).
- **Format:** Unique format of the serial number. It restricts the length and
allowed values.
- Allowed keywords:
- `F`: Hexadecimal value
- `A`: Alphabetic value
- `0`: Numeric value
- `-`: Hyphen for user visualization (not stored)
- Example: For a sixteen‑character hexadecimal serial number (Device EUI), use
`FF-FF-FF-FF-FF-FF-FF-FF`.
- **Image:** Provide an example image to help users locate the serial number (if
available).
- **Force Case:** Force the serial number to be lowercase or uppercase.
## Device Parameters
Device parameters let you set values that can later be accessed in the payload
parser. They can be of type Number, Text, Dropdown, or Switch.
Example: A "Payload Type" dropdown parameter appears in the user view as shown below.

For details on accessing these parameters in the payload parser, see the
[Payload parser context overview](/docs/tagoio/devices/payload-parser/context-global-variables.md).
## Tokens and Getting Devices
You can generate tokens for your network. The token is required to access
devices created in your network and is only useful if a serial number is
configured.
**HTTP GET:**
```
https://api..tago.io/integration/network/resolve/{serial_number}/{authorization}
```
Check available regions at the [TagoIO API](/docs/api/sidebar/tagoio-api-intro).
**Headers**
- `Authorization`: Network‑token
**Parameters**
- `Serial number`: The serial number used in device creation.
- `Authorization (Optional)`: User account authorization token, recommended for
security.
**Response (HTTP 200)**
```json
{
"status": true,
"result": "01369696-xxxx-xxxx-xxxx-feb5ddca1751"
}
```
The result is the device token, which you can use to send data with the
[HTTP data post](/docs/tagoio/devices/sending-data-to-device.md).
> **If you will be sending raw payload data**, follow these instructions to keep
> it compatible with all TagoIO connectors:
>
> - Send the variable as `payload`.
> - Always send the value in hexadecimal.
>
> Example:
>
> ```json
> {
> "variable": "payload",
> "value": "01000A0"
> }
> ```
## Payload Parser
The payload parser for a network behaves the same as for devices. All rules
apply.
If you are sending data in the correct format, no special handling is needed.
However, if you send a text file or unsupported JSON, it's best to process this
in the parser before forwarding to the device connector. Some integrations do
not send payloads in hexadecimal; you can convert them to hex here for
compatibility with other connectors.
## Creating a Connector
After creating your network, you need to create a **connector** that represents
the device itself. The network is how the device connects to TagoIO, while the
connector defines the device's behavior.
Create a connector via the
[Connector](https://admin.tago.io/integrations/connector) page. Once the
connector exists, you can start creating devices with the connector and the
network you just made.
---
## Middleware
Through a TagoIO middleware, companies can connect devices that use their own protocols. The middleware acts as a protocol converter from any transport type (for example, TCP/IP, UDP, etc.) to the standard TagoIO APIs.
For more detailed steps on implementing middleware, see [Creating a Networks Integration](/docs/tagoio/integrations/general/creating-a-network-integration.md).
Follow this step-by-step guide: [How to create your own network to integrate with TagoIO](https://community.tago.io/t/how-to-create-your-own-network-to-integrate-with-tagoio/407).
---
## Serial Number Format
You can specify a custom mask/format for your Network's Serial Number.
To create your format, use the characters defined below.
## Definitions
| Symbol | Meaning |
| ------ | ------------------------------------------------------------------------ |
| `F` | Allows characters that range from `a-f` and digits that range from `0-9` |
| `P` | Allows any alphanumeric character (`a-z`, `0-9`) |
| `0` | Allows only digits (`0-9`) |
| `A` | Allows only characters (`a-z`) |
## Examples
- To create a serial number limited to digits only, use the following format: `0000`.
This format accepts exactly 4 digits in the serial number.
```text
0000
```
- To use 16 characters in your serial number, allowing characters that range from `a-f` and digits that range from `0-9`, use the following format:
```text
FF-FF-FF-FF-FF-FF-FF-FF
```
This would allow combinations like:
```text
08-86-AB-56-82-6C-F2-EE
```
---
## Network Integration
Your devices can always connect directly to the TagoIO API using HTTPS or MQTT.
You can also connect devices to databases and web services, or translate data
from other protocols, by using one of the following methods:
1. Create a script that initiates access to external services using
[Analysis](/docs/tagoio/analysis/). For example: get a weather forecast or
read/write data in a Google Sheet.
2. Use our [Network Integration](/docs/tagoio/integrations.md) prepared to
connect and translate data from devices connected to providers such as
LoRaWAN, Sigfox, or satellite, or that use different protocols such as TCP/IP
or UDP.
3. Create your own [Network Integration](/docs/tagoio/integrations.md) that can
push data to TagoIO into the correct device based on its serial number, using
your own protocol.
## Device data flow

## Network Endpoints by Region
TagoIO provides network integrations fully hosted in different regions. Based on
your account's region, ensure you use the correct endpoint:
| Region | Network Endpoint |
| -------- | ------------------------ |
| Europe\* | middleware.eu-w1.tago.io |
| USA\* | middleware.us-e1.tago.io |
**For example, use everynet.middleware.us-e1.tago.io for Everynet network in the
USA.**
**Network usage is free for all users.** When receiving data from a network,
costs are only incurred for
[Data Input](/docs/tagoio/profiles/services/data-input-service.md) operations.
Network operations themselves do not generate any charges.
## Available Network Integrations
For the second method above, here is a list of some Network Integrations ready
to be used:
- [Sigfox](/docs/tagoio/integrations/networks/sigfox.md)
- [MQTT](/docs/tagoio/integrations/networks/mqtt.md)
- [Actility LoRaWAN](https://community.tago.io/t/how-to-integrate-tagoio-with-actility-ns/611)
- [Everynet LoRaWAN](/docs/tagoio/integrations/networks/everynet-lorawan.md)
- Kore Brazil LoRaWaN (select
[Everynet](/docs/tagoio/integrations/networks/everynet-lorawan.md))
- [Kerlink LoRaWAN](https://community.tago.io/t/how-to-integrate-tagoio-with-kerlink-lorawan/668)
- [Loriot LoRaWAN](/docs/tagoio/integrations/networks/loriot-lorawan.md)
- [machineQ LoRAWAN](/docs/tagoio/integrations/networks/machineq-lorawan.md)
- [Orbiwise LoRaWAN](/docs/tagoio/integrations/networks/orbiwise-lorawan.md)
- [Senet LoRaWAN](/docs/tagoio/integrations/networks/senet-network.md)
- [Swisscom LoRaWAN](https://community.tago.io/t/how-to-integrate-tagoio-with-swisscom-lorawan-ns/1016)
- [Tektelic LoRaWAN](https://community.tago.io/t/how-to-integrate-tektelic-network-server-with-tagoio/847)
- [TTI/TTN V3 LoRaWAN](https://community.tago.io/t/how-to-integrate-tagoio-with-ttn-v3/1026)
- [TTN LoRaWAN](/docs/tagoio/integrations/networks/the-things-network-lorawan.md)
- [Chirpstack LoRaWAN](https://community.tago.io/t/how-to-integrate-tagoio-with-chirpstack-lorawan/1017)
- [Helium LoRaWAN](https://community.tago.io/t/how-to-integrate-tagoio-with-helium/992)
- [Blecon BLE](https://community.tago.io/t/how-to-integrate-with-blecon-ble/1769)
- [Myriota](https://community.tago.io/t/how-to-integrate-tagoio-with-myriota-satellite-connectivity/451)
- [AWS IoT Core](https://community.tago.io/t/how-to-integrate-tagoio-with-aws-iot-core/669)
- [Kinéis](https://community.tago.io/t/how-to-integrate-tagoio-with-kineis/1491)
- [Generic HTTPS Endpoint](https://community.tago.io/t/how-to-integrate-tagoio-with-a-generic-https-endpoint/486)
- [KPN Things](https://community.tago.io/t/how-to-integrate-with-kpn-things-network/1571)
- [WiTTRA](https://community.tago.io/t/how-to-integrate-with-wittra/1593)
- [Particle](https://community.tago.io/t/how-to-integrate-with-particle-network/1913)
---
## Downlink for LoRaWAN™
TagoIO will send downlink messages to your devices through Configuration Parameters. To enable this, create a key parameter named `downlink` and uncheck the switch labeled "Unread". Your LoRaWAN™ provider may allow additional downlink parameters.
:::warning
Important: Most network servers require downlinks to be authenticated; otherwise they are denied access. If you're having problems sending downlinks, make sure you've correctly generated your [authorization codes](/docs/tagoio/integrations/general/authorization.md).
:::
## How it works
To send downlink messages from TagoIO to your LoRaWAN™ provider:
1. Go to your list of [devices](https://admin.tago.io/devices) and select the desired device.
2. Open the "Configuration Parameters" tab and edit or create a key parameter named `downlink`.
3. Insert the downlink payload into the value of the parameter (the field on the right side).
4. Make sure the `downlink` parameter's switch is unchecked (set to "Unread") so the platform will send it to the network server.
The downlink payload should be placed into the parameter's value field. Below is an example of the configuration screen showing the `downlink` parameter and where to enter the value.
:::info
- Downlink messages can only be sent when the device sends an uplink message.
- For Class C devices, you cannot rely on this automatic trigger; instead use the [Downlinks using Dashboards](/docs/tagoio/dashboards/downlinks-using-dashboards.md) article to send payloads manually.
- The downlink payload may be expressed in **base64** or **HEX** format; TagoIO will automatically convert it before sending.
- After a successful transmission, the `downlink` parameter is marked as **Read**.
:::
---
## Everynet LoRaWAN
This tutorial provides a step-by-step guide to integrate Everynet LoRaWAN with
the TagoIO Platform. By following these instructions, you will enable uplink and
downlink operations, allowing data exchange between your LoRaWAN devices and the
TagoIO application.
## Connecting the platforms

## Step 1: Create Authorization in TagoIO
1. **Access Authorizations**
- Go to Devices.
- Click on
[Authorizations](/docs/tagoio/integrations/general/authorization.md) at the
top of the page.
2. **Create a New Authorization**
- Click the _Create Authorization_ button.
- Provide a meaningful name for the authorization (for example:
`Everynet-Integration`).
- Select the token format: `Standard`.
- Click _Save_. The authorization will be created and the token will appear.
Copy the token and keep it safe because it will not be shown again.
> **Note:** You only need one authorization for all your integrations. Add more
> only if you want to separate them based on your specific needs.
## Step 2: Set up a Filter at Everynet Console
1. Go to your Everynet console and create a new filter under the menu **Filter >
Create a new filter**.
2. Fill in the required fields according to your application.
3. After pressing _Create_, copy the **Filter ID** for the next step.
> TagoIO only recognizes the following message types: **Uplink, Downlink,
> Downlink_request, Error, and Location**.\
> Make sure you have selected at least **Uplink**.
## Step 3: Create a Connection in Everynet Console
1. In the Connections section of your Everynet console, create a new connection.
2. Select **HTTP v2** as the connection type.
3. Fill out the fields:
- **Filter**: Enter the Filter ID you copied in Step 2.
- **Application URL**: Use the endpoint format:\
`https://everynet.middleware.REGION.tago.io/{type}`\
Replace `REGION` with your deployment region (e.g., `us-e1`, `eu-w1`). For
a list of available regions, visit
[TagoIO Network Integration](/docs/tagoio/integrations.md).
- **Description**: Enter a unique identifier (e.g., `tagoio-integration`).
- **Authorization Header**: Paste the Authorization token you copied in
Step 1.
4. Click _Save_ and copy the generated **Connector ID** for use in the next
step.
> The endpoint `everynet-oauth.middleware.tago.io` points to the USA region. If
> you are in the USA, update your webhook to
> `everynet.middleware.us-e1.tago.io/{type}`.
## Step 4: Update Authorization at TagoIO
1. Edit the authorization you created in Step 1 by clicking the pencil icon.
2. In the optional parameters field, enter:
```
;
```
Example: `us;603fff4ac972509cc4d5f1ad`
3. Save the changes.
## Finalizing Setup
1. **Power On Your Devices**: Turn on your LoRaWAN device and gateway to
initiate data transmission.
2. **Verify Data Reception**
- In TagoIO, navigate to the Device page and open the _Data_ tab to confirm
that data is being received.
- Alternatively, use the
[Live Inspector](/docs/tagoio/devices/live-inspector.md) within your device
settings to monitor incoming data in real time.
Once data successfully flows into TagoIO, you can start creating dashboards,
analyses, and actions to visualize and manage your device data effectively.
---
## Loriot LoRaWAN
This tutorial provides a step-by-step guide to integrate Loriot LoRaWAN™ with the TagoIO Platform. By following these instructions, you will enable uplink and downlink operations, allowing data exchange between your LoRaWAN devices and the TagoIO application.

To complete the integration, follow the two steps below.
---
## Step 1: Generate Access Token at Loriot
At the Loriot Console, open your Application and go to the **Access Token** section.
Click on **Generate another authentication token**, a token will be generated. Copy the Authentication Token to use in the next step.
### Step 2: Create Authorization in TagoIO
1. **Access Authorizations**
- Go to **Devices**.
- Click on **[Authorizations](https://admin.tago.io/devices/authorization)** at the top of the page.
2. **Create a New Authorization**
- Click the **Create Authorization** button.
- Provide a meaningful name for the authorization (e.g., `LORIOT-Integration`).
- Paste the **Access Token** you copied in Step 1 into the **Additional Parameter** field.
- Click **Save**.
3. **Copy the Authorization Token**
- After creation, copy the authorization token. You will need this in later steps.
### Step 3: Set Up Integration in Loriot
Go to your Loriot console and create a new output. Create a new integration under the menu **Output > + Add New Output > HTTP Push**.
Select **HTTP Push** and fill in the following fields:
| Field | Description |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Target URL for POSTs** | Use the endpoint format `https://loriot.middleware.REGION.tago.io/uplink`. Replace `REGION` with your deployment region (e.g., `us-e1`, `eu-w1`). |
| **Custom "Authorization" header** | Set its value to the authorization token you copied in Step 2. |
To finalize, click on **Save Output**. Now that your integration with Loriot is ready, you can start adding the devices at TagoIO.
### Step 4: Add Device in TagoIO
1. **Ensure Device EUI Consistency**
- When creating the device in TagoIO, use the exact same Device EUI that is configured in Loriot.
- The Device EUI must match exactly to establish a successful connection between Loriot and TagoIO.
2. **Select the Appropriate Network and Device Type**
- Choose the **LoRaWAN Loriot** network during the device setup process.
- Select the correct device type from the available options. If your device is not listed, you can add support through connectors.
3. **Add Device Support via Connectors (If Necessary)**
- If your device type is not available in the default list, enhance its compatibility by using connectors.
### Finalizing Setup
1. **Power On Your Devices**
- Turn on your LoRaWAN device and gateway to initiate data transmission.
2. **Verify Data Reception**
- In TagoIO, navigate to the Device and into the Data tab to confirm that data is being received.
- Alternatively, use the Live Inspector within your device settings to monitor incoming data in real-time.
With data successfully flowing into TagoIO, you can create and edit dashboards, add notifications, and create scripts to visualize and manage your device data effectively.
---
## machineQ LoRaWAN™
This tutorial provides a step-by-step guide to integrate machineQ LoRaWAN™ with
the TagoIO Platform. By following these instructions, you will enable both
uplink and downlink operations, allowing data exchange between your LoRaWAN
devices and the TagoIO application.

Follow these steps to integrate your devices. You may skip some steps depending on your situation. For example, if you have already generated an authorization and added it to machineQ, you can skip to Step 4.
## On this page
- Step 1: Generate an Application at machineQ
- Step 2: Create Authorization in TagoIO
- Step 3: Add Integration to machineQ
- Step 4: Add Device in TagoIO
- Finalizing Setup
## Step 1: Generate an Application at machineQ
First, make sure you create an Application in machineQ Central so that the
integration with TagoIO can be authorized.
Go to your machineQ Central, and follow
[this machineQ tutorial](https://www.machineq.com/documentation) to generate a
Client ID and a Client Secret that you will need to input in TagoIO later.
### Access Authorizations
1. Go to **Devices**.
2. Click on **[Authorizations](https://admin.tago.io/devices/authorization)** at
the top of the page.
### Create a New Authorization
1. Click the **Create Authorization** button.
2. Provide a meaningful name for the authorization (e.g.,
_MachineQ-Integration_).
3. Enter your Client ID and Client Secret from Step 2 into the **Additional
Parameters** section:
- Format: `CLIENT_ID;CLIENT_SECRET`
- Example: `64025-24054-230495-64034;ABT_QWzsSQAAH__HAwr...`
4. Click **Save**.
### Copy the Authorization Token
After creation, copy the authorization token. You will need this in later steps.
> _You must enter your Client ID and Client Secret separated by a semicolon in
> the Additional parameter field._\
> _Only one Authorization per application is necessary. Don't do it for each
> device._
## Step 2: Create Authorization in TagoIO
_(Content for creating an authorization token in TagoIO should be added here if
needed.)_
## Step 3: Add Integration to machineQ
Access the machineQ Central, enter into the **_Integration_** section, click on
**_Add Output Profile_**, and select **Restful API**. The output format can be
left as "extended".
Fill in the following fields:
| Field | Value |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Name** | Enter a unique identifier (e.g., _tagoio-integration_). |
| **URL** | Use the endpoint format `https://machineq.middleware.REGION.tago.io/uplink`. Replace **REGION** with your deployment region (e.g., us-e1, eu-w1). |
| **Token Type** | Enter the word **Authorization**. |
| **Token Value** | Set its value to the TagoIO Authorization token you copied in Step 2. |
## Step 4: Add Device in TagoIO
To integrate your device with the TagoIO Platform, follow these guidelines:
1. **Ensure Device EUI Consistency**
- When creating the device in TagoIO, use the _exact same Device EUI_ that is
configured in MachineQ.
- The Device EUI must match exactly to establish a successful connection
between MachineQ and TagoIO.
2. **Select the Appropriate Network and Device Type**
- Choose the MachineQ Network during the device setup process.
- Select the correct device type from the available options. If your device
is not listed, you can add support through connectors.
3. **Add Device Support via Connectors (If Necessary)**
- If your device type is not available in the default list, enhance its
compatibility by using connectors.
- For more information on available connectors and how to use them, refer to
the Connector Overview.
For detailed instructions on adding devices, visit the
[Adding Devices](/docs/tagoio/devices/) page.
## Finalizing Setup
1. **Power On Your Devices**
- Turn on your LoRaWAN device and gateway to initiate data transmission.
2. **Verify Data Reception**
- In TagoIO, navigate to the Device and into the **Data** tab to confirm that
data is being received.
- Alternatively, use the
[Live Inspector](/docs/tagoio/devices/live-inspector.md) within your device
settings to monitor incoming data in real-time.
With data successfully flowing into TagoIO, you can start creating dashboards,
adding notifications, and creating scripts to process your data.
Learn how to perform downlink for LoRaWAN:
[downlink for LoRaWAN](/docs/tagoio/integrations/networks/downlink-for-lorawan.md).
---
## Process data, Publish it and Subscribe to a topic
:::danger[End of Life: August 2, 2027]
**Legacy MQTT is deprecated and will reach end of life on August 2, 2027.** On
that date the TagoIO MQTT Broker (`mqtt.tago.io`) will be shut down and devices
will no longer be able to connect. The broker was available exclusively for
Starter and Scale accounts in the US database region that upgraded before
April 15, 2026; accounts created or upgraded after that date do not have access
to this feature.
Please migrate before the end-of-life date to one of the following options:
- [TagoTiP MQTT](/docs/tagotip/transports/mqtt): supports all account types
and regions (US and EU).
- [TagoDeploy MQTT](/docs/tagodeploy/project/mqtt): a single-tenant TagoIO
instance with full support for the MQTT protocol.
Existing devices and actions will continue to function until August 2, 2027,
but no new features or bug fixes will be provided.
:::
In this tutorial, you will learn how to process data, publish to a topic, and subscribe to it. The tutorial uses the **MQTTX** client throughout.
## Connecting to TagoIO MQTT Broker
To publish a topic via TagoIO's MQTT, you must first add a device.
- Navigate to the **[Devices](https://admin.tago.io/devices)** module in your Admin panel and create a new device. You may choose any connector you prefer; it will not make a difference.
- After creating the device, open the device's **General Information** tab and locate the tokens section.
- Copy your device token from the tokens section; this token is required to authenticate when publishing or subscribing via the TagoIO MQTT broker.
Open **MQTTX** and create a new connection
| Field | Value |
| ------------- | ----------------------------------- |
| **Name** | Any name you like |
| **Host** | `mqtt.tago.io` |
| **Port** | `1883` |
| **Username** | `"Token"` |
| **Password** | Your device token |
| **Client ID** | Can be set to any unique identifier |
:::warning
The Client Identifier (ClientID) is a required field to connect to the TagoIO MQTT Broker.
:::
## Subscribing to a topic
1. In MQTTX, go to the **Subscribe** tab.
2. Type `sensor/output` in the Topic field and click **Subscribe**.
3. You should now see any messages that are published on this topic appear in the client.
## Processing the data
In this step we create an analysis that converts temperature from Fahrenheit to Celsius and publishes it on the topic `sensor/output`. The analysis is triggered whenever a message is received on the topic `sensor/input`.
> **Note**: Replace "DEVICE_ID" with your actual device ID. You can obtain this ID by accessing your device's page and copying it from the URL, for example: https://admin.tago.io/devices/652425f4516e99000a522dce.
```js
const { Analysis, Services } = require("@tago-io/sdk");
const DEVICE_ID = "YOUR_DEVICE_ID"; // Replace with your actual device ID
async function mqttPushExample(context, scope) {
console.log(context, scope);
if (!scope.length) {
return context.log("Missing values");
}
const myData = scope.find((x) => x.variable === "payload") || scope[0];
if (!myData) {
return context.log("Couldn't find any variable in the scope.");
}
// Create your data object to push to MQTT
const myDataObject = {
variable: "temperature_celsius",
value: (myData.value - 32) * (5 / 9),
unit: "C",
};
// Options for publishing
const options = {
retain: false,
qos: 0,
};
// Publishing to MQTT
const mqtt = new Services().mqtt;
await mqtt.publish({
device: DEVICE_ID,
message: JSON.stringify(myDataObject),
topic: "sensor/output",
options,
});
}
module.exports = new Analysis(mqttPushExample);
```
**Permissions**
This analysis requires permission to send MQTT data to the device. Enable this access in the **[Access Management](https://admin.tago.io/am)** module by creating a new **Policy**.

**Creating an Action**
1. Go to the **[Actions](https://admin.tago.io/actions)** module and create a new action.
2. Set:
- **Name**: Any name
- **Type of Trigger**: MQTT Topic
- **Trigger**: `sensor/input`
- **Device**: The device you created earlier
3. For the action type, choose **Run Analysis** and select the analysis you just created.
4. Click **Create my Action**.
Now, whenever a message is published on the topic `sensor/input`, the analysis will run, convert the temperature to Celsius, and publish it to `sensor/output`. You can test this by publishing the value `32` on `sensor/input` using MQTTX; the converted value should appear in the client.
:::tip
If you need help with your MQTT connection, request help in our [Community](https://community.tago.io/)!
:::
---
## MQTT - Publishing and Subscribing
:::danger[End of Life: August 2, 2027]
**Legacy MQTT is deprecated and will reach end of life on August 2, 2027.** On
that date the TagoIO MQTT Broker (`mqtt.tago.io`) will be shut down and devices
will no longer be able to connect. The broker was available exclusively for
Starter and Scale accounts in the US database region that upgraded before
April 15, 2026; accounts created or upgraded after that date do not have access
to this feature.
Please migrate before the end-of-life date to one of the following options:
- [TagoTiP MQTT](/docs/tagotip/transports/mqtt): supports all account types
and regions (US and EU).
- [TagoDeploy MQTT](/docs/tagodeploy/project/mqtt): a single-tenant TagoIO
instance with full support for the MQTT protocol.
Existing devices and actions will continue to function until August 2, 2027,
but no new features or bug fixes will be provided.
:::
You can publish to your MQTT topics by coding a script that runs from an
[Analysis](/docs/tagoio/analysis/). When the Analysis runs, your script can
publish a topic that will be received by any device subscribed to that specific
topic. An Analysis can be started in different ways: by timer, by an
[Action](/docs/tagoio/actions/), or by another Analysis. The diagram below
illustrates the data flow from an Analysis to the MQTT network.

The diagram shows:
- An Analysis publishes to the broker using, for example:
```javascript
// Example: publish from an Analysis
services.mqtt.publish("mytopic", "my message");
```
- Any device that subscribes to the topic "mytopic" will receive the published
message.
- Typical subscribe text: Subscribe to the topic: "mytopic"
This section describes how to publish and subscribe to topics on the TagoIO MQTT
Broker. It assumes you already know how to connect to MQTT; if you do not, see
the [MQTT](/docs/tagoio/integrations/networks/mqtt.md) article for connection
instructions.
### Publishing to TagoIO
You can create an [Action](/docs/tagoio/actions/trigger-by-mqtt-topic.md) and
attach it to any specific topic or wildcard topics, then trigger actions from
there.
The payload can be sent directly to your [device](/docs/tagoio/devices/),
trigger an Analysis, or be delivered via SMS or E‑mail.
If you send the payload in JSON format, the TagoIO backend automatically adds a
`metadata` field with a child field called **topic**.\
For raw payloads, transform your data into the TagoIO JSON format by wrapping it
in a variable named **payload** and placing the raw data under **value**; this
will also add the same `metadata.topic` field.
### QoS and Retained Message Support
The Quality of Service (_QoS_) level is an agreement between the sender of a
message and the receiver. TagoIO officially supports QoS 0 and 1.
TagoIO does not support the native retain feature found in standard MQTT
protocol implementations; however, we offer a workaround to achieve similar
functionality. Read more here:
[MQTT Retain on TagoIO Broker](/docs/tagoio/integrations/networks/mqtt/mqtt-retain-on-tagoio-broker.md).
### How to Debug MQTT with TagoIO
You can view all your connections directly on the Live Inspector and inspect any
connection, payload, QoS, Last Will messages, and more.
The following actions trigger messages in the inspector:
- a client connects to the broker
- a client disconnects from the broker
- a client subscribes to any topic
- a client unsubscribes from any topic
- a client publishes to any topic
### Subscribing: Single Level
The single‑level wildcard is represented by `+`, which matches exactly one level
of a topic.
**Example:** If the client subscribes to `home/+/temperature`, the following
topics will match:
- `home/kitchen/temperature`
- `home/office/temperature`
- `home/livingroom/temperature`
Topics that **will not** match include:
- `home/kitchen`
- `home/kitchen/humidity`
- `home/office/ac/temperature`
### Subscribing: Multi Level
The multi‑level wildcard is represented by `#`, which matches any number of
levels.
**Example:** If the client subscribes to `home/#`, the following topics will
match:
- `home/kitchen`
- `home/kitchen/temperature`
- `home/office/ac/temperature`
- `home/upstairs/bedroom/ac/temperature`
---
## MQTT Retain on TagoIO Broker
:::danger[End of Life: August 2, 2027]
**Legacy MQTT is deprecated and will reach end of life on August 2, 2027.** On
that date the TagoIO MQTT Broker (`mqtt.tago.io`) will be shut down and devices
will no longer be able to connect. The broker was available exclusively for
Starter and Scale accounts in the US database region that upgraded before
April 15, 2026; accounts created or upgraded after that date do not have access
to this feature.
Please migrate before the end-of-life date to one of the following options:
- [TagoTiP MQTT](/docs/tagotip/transports/mqtt): supports all account types
and regions (US and EU).
- [TagoDeploy MQTT](/docs/tagodeploy/project/mqtt): a single-tenant TagoIO
instance with full support for the MQTT protocol.
Existing devices and actions will continue to function until August 2, 2027,
but no new features or bug fixes will be provided.
:::
TagoIO's MQTT broker does not natively support the standard MQTT **retain** feature. However, you can implement an equivalent workaround by using TagoIO Analysis and Actions to store the last message published to a topic and resend it when a new client subscribes.
This approach involves using the [Analysis](/docs/tagoio/analysis/) and [Actions](/docs/tagoio/actions/) features to store the last message published to a topic and then resend it when a new client subscribes.
## Setting Up Message on Subscribing
To build an MQTT **retain** workaround with TagoIO, you can either publish directly to a topic with an Action or use an Analysis script for more complex scenarios.
## Using Actions to Publish a Message to a Subscriber
1. **Create an Action**
- Navigate to the Actions section in your TagoIO dashboard and create a new action.
- Choose the event that will trigger this action. It could be a variable update, a scheduled time, or another custom trigger.
2. **Configure MQTT Publish**
- In the action configuration, select **"MQTT Publish"** as the action type.
- Specify the topic you wish to publish to and the message payload.

## Using Analysis for Advanced Scenarios
For scenarios where you need to publish for a large number of devices/topics and also require more control over the publishing process, you can use an Analysis script.
### 1. Create an Analysis
- Go to the **Analysis** section and create a new analysis.
- Choose **Node.js** as the environment.
### 2. Implement MQTT Publish Script
```js
const { Analysis, Services } = require("@tago-io/sdk");
async function mqttPushExample(context, scope) {
// Create your data object to push to MQTT
const myDataObject = {
variable: "temperature_celsius",
value: (myData.value - 32) * (5 / 9),
unit: "C",
};
// Options for the publish
const options = { qos: 0 };
// Publishing to MQTT
const MQTT = new Services({ token: context.token }).MQTT;
await MQTT.publish({
device: myData.device, // for immutable/mutable devices
message: JSON.stringify(myDataObject),
topic: "tago/my_topic",
options,
});
}
module.exports = new Analysis(mqttPushExample);
```
### 3. Trigger Analysis
- Configure an Action or another event to trigger the execution of your Analysis (e.g., a device update, a scheduled time, or any other event in your TagoIO application).
Refer to the [Analysis documentation](/docs/tagoio/analysis/) for details on creating scripts and integrating them with Actions and MQTT.
---
## MQTT Overview (Legacy)
:::danger[End of Life: August 2, 2027]
**Legacy MQTT is deprecated and will reach end of life on August 2, 2027.** On
that date the TagoIO MQTT Broker (`mqtt.tago.io`) will be shut down and devices
will no longer be able to connect. The broker was available exclusively for
Starter and Scale accounts in the US database region that upgraded before
April 15, 2026; accounts created or upgraded after that date do not have access
to this feature.
Please migrate before the end-of-life date to one of the following options:
- [TagoTiP MQTT](/docs/tagotip/transports/mqtt): supports all account types
and regions (US and EU).
- [TagoDeploy MQTT](/docs/tagodeploy/project/mqtt): a single-tenant TagoIO
instance with full support for the MQTT protocol.
Existing devices and actions will continue to function until August 2, 2027,
but no new features or bug fixes will be provided.
:::
## Overview
MQTT stands for MQ Telemetry Transport. It is an extremely simple and lightweight publish/subscribe messaging protocol designed for constrained devices and for use over low-bandwidth, high-latency, or unreliable networks.
## Data flow (example)

Caption: Device #1 publishes a payload (for example, "21") to the topic
"temperature" on the TagoIO Broker. Devices #2 and #3 subscribe to the same
topic and receive the published payloads.
TagoIO's MQTT broker is responsible for pushing data to clients whenever new
information is published on the specific topics they are subscribed to. Unlike a
full-featured MQTT implementation, it focuses mainly on ingesting sensor data
into our devices, so some standard features such as the native **Retain**
flag are not supported. A workaround that achieves similar functionality can be
found in the article
[MQTT Retain on TagoIO Broker](/docs/tagoio/integrations/networks/mqtt/mqtt-retain-on-tagoio-broker.md).
For example, a temperature sensor might publish a new value to the topic
`temperature` each time it receives an update. Devices that need to react to
these updates subscribe to that topic; when the sensor publishes, the broker
forwards the payload to all subscribed clients.

## Connection
To connect to our broker, use the following details. Note that there is a limit
on the number of connections, publications, and subscriptions you can make,
enforced based on your account plan. For more information, read about
[Rate Limits (Hard Limits)](/docs/tagoio/profiles/services/rate-limits-hard-limits.md).
> **Host:** `mqtt.tago.io`\
> **TCP/IP port:** 1883\
> **TCP/IP port over SSL:** 8883\
> **Username:** Token\
> **Password:** _Your Device‑Token_
If the [Device‑token](/docs/tagoio/devices/device-token.md) is removed from a
device, or if it is deleted, that device will be disconnected from the MQTT
broker.
## Security
Encryption across the network can be handled with SSL independently of the MQTT
protocol itself. Additional security can also be added through
application‑encrypted data that is sent and received. At TagoIO you can send
your data encrypted directly to [Analysis](/docs/tagoio/analysis.md), decrypt it
there, and then insert the data into your [device](/docs/tagoio/devices.md). This
procedure can increase security if your data is sensitive or if you simply want
an extra layer of protection.
## Data flow at TagoIO
When you first send data through MQTT to your device, you'll be able to
visualize the connection and message through the
[Live Inspector](/docs/tagoio/devices/live-inspector.md) on your device. Those
messages indicate that the connection is working, but nothing is being stored in
your device yet.
From this point, you need to create an [Action](/docs/tagoio/actions.md) with
trigger **MQTT** and type **Insert to Device**. Once configured, you
should see the data being stored in the Live Inspector.
If you don't send the data using the
[TagoIO data format](/docs/tagoio/devices/sending-data.md), you'll need to
normalize your data and change it to the correct format. Check out our
documentation on how to use a
[Payload Parser](/docs/tagocore/device/payload-parser.md).
## More Resources
Here are some additional documentation links on MQTT resources:
- [MQTT - Publishing and Subscribing](/docs/tagoio/integrations/networks/mqtt/mqtt-publishing-and-subscribing.md)
- [MQTT - Action Triggers by Topic](/docs/tagoio/actions/trigger-by-mqtt-topic.md)
---
## Orbiwise LoRaWAN™
This tutorial provides a step-by-step guide to integrate **Orbiwise** LoRaWAN™ with the TagoIO Platform. By following these instructions, you will enable uplink and downlink operations, allowing data exchange between your LoRaWAN devices and the TagoIO application.

## Prerequisites
Before you begin, ensure you have the following:
- Orbiwise account: If you don't have one, register here (https://orbiwise.com/).
- LoRaWAN device: Ensure your device is connected to Orbiwise and transmitting data.
## Step 1: Create Authorization in TagoIO
1. **Access Authorizations**
- Go to **Devices**.
- Click on the **Authorizations** link (https://admin.tago.io/devices/authorization) at the top of the page.
2. **Create a New Authorization**
1. Click the **Create Authorization** button.
2. Provide a meaningful name for the authorization (e.g., `Orbiwise-Integration`).
3. In the **Additional Parameters** section, enter your Orbiwise Username, Password and DASS URL from Step 2 in the following format:
```
USERNAME;PASSWORD;DASS_URL
```
Example: `tagoio;iot;;`
4. Click **Save**.
3. **Copy the Authorization Token**
- After creation, copy the authorization token. You will need this in later steps.

:::tip
You must enter the **username;password;host DASS url** separated by a semicolon.
Only one **Authorization** per application is necessary. Don't do it for each device.
:::
## Step 2: Add Integration to Orbiwise
1. Go to your Orbiwise console and create a new Application.
- Navigate through the menu: **Application > Add Application > Manage Apps > Push Settings**.
2. In the **Manage Apps** screen, click on **Push Settings** and fill in the following fields:
| Field | Value |
| --------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| Hostname | Use the endpoint format `https://orbiwise.middleware.REGION.tago.io`. Replace `REGION` with your deployment region (e.g., `us-e1`, `eu-w1`). |
| Port | `443` |
| Host Pass | Set its value to the authorization token you copied in Step 1. |
3. **Start Push**
- Make sure that you set the status of your Application to **<Start Push>**.

## Step 3: Add Device in TagoIO
### Ensure Device EUI Consistency
- When creating the device in TagoIO, use **exactly the same Device EUI** that is configured in Orbiwise.
- The Device EUI must match exactly to establish a successful connection between Orbiwise and TagoIO.
### Select the Appropriate Network and Device Type
1. Choose the **LoRaWAN** Orbiwise Network during the device setup process.
2. Select the correct device type from the available options. If your device is not listed, you can add support through connectors.
### Add Device Support via Connectors (If Necessary)
- If your device type is not available in the default list, enhance its compatibility by using connectors.
- For more information on available connectors and how to use them, refer to the [Connector Overview](/docs/tagoio/devices/payload-parser/connector/connector-overview.md).
For detailed instructions on adding devices, visit the [Adding Devices](/docs/tagoio/devices/) page.
### Finalizing Setup
1. **Power On Your Devices**
- Turn on your LoRaWAN device and gateway to initiate data transmission.
2. **Verify Data Reception**
- In TagoIO, navigate to the Device and into the **Data** tab to confirm that data is being received.
- Alternatively, use the [Live Inspector](/docs/tagoio/devices/live-inspector.md) within your device settings to monitor incoming data in real-time.
With data successfully flowing into TagoIO, you can start creating Dashboards, Analyses, and Actions to visualize and manage your device data effectively.
Learn how to perform [downlink for LoRaWAN](/docs/tagoio/integrations/networks/downlink-for-lorawan.md).
---
## Senet Network
Using this article, you will learn how to integrate a LoRaWAN device with the
Senet Network and TagoIO.
> Note: This setup is for the uplink operation (data sent from the device to
> your TagoIO account).
The integration is accomplished in two parts:
1. TagoIO Setup
2. Senet Portal Setup
## 1) TagoIO Setup
First, go to the [Devices](/docs/tagoio/devices/) module in our admin, look for
Senet, and select it.
### Create Device
Type a name for your device, select a
**[data storage type](/docs/tagoio/devices/)** and define a
**[Data Retention](/docs/tagoio/devices/data-management/data-retention-feature.md)**,
then click on _Create device_.

### Generate Service Authorization
You will need to generate an **Authorization** at TagoIO so that it can be added
into the Senet integration area.\
This authorization can be generated by accessing the
**[Service Authorization](https://admin.tago.io/devices/authorization)** page.
> The generation of an authorization code is crucial for being able to send
> downlinks since it allows TagoIO to authenticate downlinks.
## 2) Senet Portal Setup
Now, log in to **[Senet portal](http://portal.senetco.io/)** and go to your
devices.\
Choose the device to connect to TagoIO and click on the gear icon.

Go to **"Notification Target"**. Make sure the status is _Enabled_, select
**HTTP** in the 'forward to' field, and fill the fields accordingly:
| Field | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Name** | Enter a unique identifier (e.g., `tagoio-integration`). |
| **Forward To HTTP URL** | Use the endpoint format `https://senet.middleware.REGION.tago.io`. Replace `REGION` with your deployment region (e.g., `us-e1`, `eu-w1`). For a list of available regions, visit **[TagoIO Network Integration](/docs/tagoio/integrations.md)**. |
| **Headers** | Add a header key `Authorization` and set its value to the authorization token you copied in Step 1. |
| **Optional Data Fields** | Enable _RF Data_ and any other optional field you would like to receive in your device. |

### Setting up Downlink
To support downlinks from TagoIO to Senet, you need an additional step.
1. At the **[Senet portal](http://portal.senetco.io/)**, go to your
**[Account Settings](https://portal.senetco.io/#/user)**, generate a
_Developer API Key_ and copy it.
2. Now go back to the
**[Authorization](https://admin.tago.io/devices/authorization)** page where
you created your authorization in Step 1.
3. Click to edit the **Additional Parameters** of the Authorization.
4. Paste your Developer API Key and press _Save_.

Now you are all set; every time your device sends data to Senet, your data will
be available at TagoIO.
---
## SenRa Network
Using this article, you will learn how to integrate a LoRaWAN device with SenRa Network and TagoIO.
> Note: This setup is for the uplink operation (data sent from the device to your TagoIO account).
The integration is accomplished in two parts:
## 1) TagoIO Setup
1. Go to the [devices](/docs/tagoio/devices/) section, search for "SenRa", and select it.
2. Type a name for your device and click "Create device".

3. Access the device you just created, open the "Token" tab, and copy the `device-token`. You will use this token in the next step.
> Warning: You should not create Authorization when integrating with SenRa. Use the `device-token` instead.
## 2) SenRa Portal Setup
1. Log in to the [SenRa portal](http://portal.senetco.io/) and go to your devices.
2. Choose the device you want to connect to TagoIO and click on its gear icon.
3. In the device settings, navigate to **Notification Target**.
4. Ensure that the status is set to _Enabled_.
5. From the "forward to" dropdown, select **TagoIO**.
6. Paste the `device-token` you copied earlier into the **device-token** field and save your changes.
After completing these steps, every time your device sends data to SenRa, the data will be forwarded to your TagoIO account and available for use.
---
## Sigfox - Downlink
TagoIO is ready to receive data from and send data to Sigfox devices. The downlink is used to send data back to the device every time it checks in: the data to be sent should be prepared prior to that check-in. This article explains how to set up a downlink.
:::warning
It's essential to carefully follow all the steps outlined in the
[Sigfox network documentation](/docs/tagoio/integrations/networks/sigfox.md) in
order to send downlinks. The completion of Step D is required so that TagoIO can
accurately authenticate your downlink.
:::

Downlink Diagram
Information about uplink can be found in this
[tutorial](/docs/tagoio/integrations/networks/sigfox.md).
## Downlink Setup
The downlink sends responses from TagoIO to Sigfox, so the data transition works correctly. The downlink configuration is separated into two parts:
1. **TagoIO setup**
2. **Sigfox setup using the Sigfox Portal**
Further setup steps for each part are covered below.
### 1. TagoIO setup
1. Find your device that you just created by clicking on the
[Device](https://admin.tago.io/devices) (left menu), go to it, and click on
the tab **Configuration parameters**;
2. Create a parameter for your device:
- **Key**: type `downlinkData`
- **Value**: the message you want to send to your Sigfox device (must have
**8 bytes**)
- Click **Save**
> Example of 8 bytes `downlinkData`: `00000000ada1b2c3`
3. Create an **Authorization** and use it in the Sigfox setup later.
- If you already have created an authorization, you can just copy it.
> The **Authorization** should be generated by TagoIO by accessing the
> [Service Authorization](https://admin.tago.io/devices/authorization) page;
> no additional parameters are needed.
### 2. Sigfox setup
Go to the Sigfox portal and perform the following steps:
1. Click on **Device Type**;
2. After clicking **Device type**, the list of your devices will appear in
Sigfox, choose one;
3. Click in the **Callback** button;
4. Click the **New** button;
5. Select the option **Custom callback**;
6. Configure the downlink.
**CALLBACK CONFIGURATION**
- **Type:** `DATA` & `BIDIR`
- **Channel:** `URL`
- **URL pattern:**
```
https://sigfox.middleware.tago.io/downlink
```
- **Use HTTP Method:** `POST`
- **Send SNI:** Check the box "Send SNI".
- **Headers:** Add two rows in the Headers section inside the header and value
fields:
| Header | Value |
| ------------- | ----------------------------------------------------------- |
| device | `{device}` (Required) |
| authorization | `your_secret_code_here` (authorization generated by TagoIO) |
- **Content‑type:** `application/json`
- **Body:** Copy and paste the JSON text below into the body field. This JSON
will take care of the parse to prepare the data coming from your Sigfox module
to TagoIO.
```json
[
{
"variable": "device",
"value": "{device}",
"group": "{time}"
},
{
"variable": "data",
"value": "{data}",
"group": "{time}"
},
{
"variable": "seqNumber",
"value": "{seqNumber}",
"group": "{time}"
}
]
```
Your downlink integration is completed.
You can also send data from your **Dashboard** using the
[Input Form widget](/docs/tagoio/widgets/input-widgets/input-form/). A script to
perform a write in the parameter tab must be created using the
[Analysis](/docs/tagoio/analysis/).
---
## Sigfox using 'Advanced Data'
The Advanced Data option allows you to receive additional information such as
SNR, RSSI, STATION, and other metadata.
:::note
The type of information available will depend on your Data Plan contracted with
Sigfox.
:::
## Create the callback on the Sigfox Backend Portal
1. On the Sigfox Backend Portal, create a callback of type `uplink`.
2. Click on the Device Type and select your device from the list.
3. Click the Callback button, then click New and select the option Custom
callback.
## Configure the uplink
Configure the `uplink` callback with the following settings:
- Type: `SERVICE` & `ADVANCED_DATA`
- Channel: `URL`
- URL: https://sigfox.middleware.tago.io/uplink
- Use HTTP Method: `POST`
- Send SNI: Check the box "Send SNI".
- Headers: Add two rows in the Headers section (use the header and value
fields):
| header | value |
| ------------- | ------------------------------------- |
| device | `{device}` |
| authorization | `` |
- Content-type: `application/json`
- Body: Copy and paste the JSON text below
```json
[
{
"variable": "device",
"value": "{device}",
"group": "{time}"
},
{
"variable": "data",
"value": "{data}",
"group": "{time}"
},
{
"variable": "seqNumber",
"value": "{seqNumber}",
"group": "{time}"
},
{
"variable": "location",
"location": {computedLocation},
"group": "{time}"
}
]
```
Learn more about
[how to integrate with Sigfox](/docs/tagoio/integrations/networks/sigfox.md).
---
## Sigfox
Sigfox is a relatively low-cost wireless network technology that requires low
power to connect and is implemented by low-cost hardware modules.
This tutorial provides a step-by-step guide to integrate Sigfox with the TagoIO
platform. By following these instructions, you will enable uplink and downlink
operations, allowing data exchange between your Sigfox devices and the TagoIO
application.

Uplink is the process of sending data from the devices to TagoIO. Downlink is
used to send data back to the devices. Learn how to perform
[downlink for SigFox](/docs/tagoio/integrations/networks/sigfox/sigfox-downlink.md).
Follow these steps to set up the Sigfox backend and TagoIO.
## On this page
- Step 1: Create Authorization in TagoIO
- Step 2: Set Up Integration in Sigfox
- Step 3: Add Device in TagoIO
- Finalizing Setup
- Troubleshooting
## Step 1: Create Authorization in TagoIO
1. **Access Authorizations** Go to the _Devices_ section of your TagoIO account.
2. **Create a New Authorization**
- Click the **Create Authorization** button.
- Provide a meaningful name for the authorization (e.g.,
`Sigfox-Integration`).
- Click **Save**.
3. **Copy the Authorization Token** After creation, copy the authorization
token; you will need this in later steps.
> The Authorization should be generated by TagoIO by accessing the
> [Service Authorization](https://admin.tago.io/devices/authorization) page; no
> additional parameters are needed.
## Step 2: Set Up Integration in Sigfox
On the Sigfox Backend Portal, create a **callback** of type `uplink`. We will
use the **Data Uplink** option to obtain the basic information in the messages.
If your contract with Sigfox allows you to get more information like RSSI, SNR,
STATION, you can use the
[Advanced Data option](/docs/tagoio/integrations/networks/sigfox/sigfox-using-advanced-data.md).
1. **Create a new callback** Go to your Sigfox console and create a new callback
under the menu: `Device Type > Callback > New > Custom Callback`.
2. **Configure the Callback**
| Field | Description |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Type** | Data & Uplink |
| **Channel URL** | Use the endpoint format `https://sigfox.middleware.REGION.tago.io/uplink`. Replace `REGION` with your deployment region (e.g., `eu-w1`). |
| **HTTP Method** | POST |
| **Send SNI** | Check the box _Send SNI (Server Name Indication) for SSL/TLS connections_ |
| **Headers** | Add the following headers:• `authorization`: ``• `device`: `{device}`• `Content-Type`: `application/json` |
| **Body** | Copy and paste the JSON text below. |
```json
[
{
"variable": "device",
"value": "{device}",
"group": "{time}"
},
{
"variable": "data",
"value": "{data}",
"group": "{time}"
},
{
"variable": "seqNumber",
"value": "{seqNumber}",
"group": "{time}"
}
]
```
## Step 3: Add Device in TagoIO
To integrate your device with the TagoIO Platform, follow these guidelines:
1. **Ensure Device EUI Consistency**
- When creating the device in TagoIO, use the _exact same Device EUI_ that is
configured in Sigfox.
- The Device EUI must match exactly to establish a successful connection
between Sigfox and TagoIO.
2. **Select the Appropriate Network and Device Type**
- Choose the **Sigfox Network** during the device setup process.
- Select the correct device type from the available options. If your device
is not listed, you can add support through connectors.
3. **Add Device Support via Connectors (If Necessary)**
- If your device type is not available in the default list, enhance its
compatibility by using connectors.
- For more information on available connectors and how to use them, refer to
the
[Connector Overview](/docs/tagoio/devices/payload-parser/connector/connector-overview.md).
For detailed instructions on adding devices, visit the
[Adding Devices](/docs/tagoio/devices/) page.
## Finalizing Setup
1. **Power On Your Devices** Turn on your Sigfox device and gateway to initiate
data transmission.
2. **Verify Data Reception**
- In TagoIO, navigate to the _Device_ and into the _Data_ tab to confirm that
data is being received.
- Alternatively, use the
[Live Inspector](/docs/tagoio/devices/live-inspector.md) within your device
settings to monitor incoming data in real‑time.
## Troubleshooting
If you encounter issues with your Sigfox integration, check the callback logs in
the Sigfox Backend Portal. Common error codes and their solutions:
### Error 401 - Unauthorized
This error indicates an authentication problem:
- **Invalid Authorization Token**: Verify that you copied the correct
authorization token from TagoIO and pasted it exactly in the `authorization`
header (without extra spaces or characters).
- **Wrong Region**: Ensure the `REGION` in your callback URL matches your TagoIO
deployment region. For example, if your TagoIO account is in Europe, use
`eu-w1`; for US East, use `us-e1`.
- **Expired or Deleted Authorization**: Check if the authorization still exists
in your TagoIO account at the
[Service Authorization](https://admin.tago.io/devices/authorization) page.
### Error 400 - Bad Request
This error typically indicates a data format or processing issue:
- **Invalid JSON Body**: Verify that the JSON body in your callback matches the
format shown in Step 2. Ensure all brackets, quotes, and commas are correct.
- **Content-Type Header Missing**: Confirm that the
`Content-Type:
application/json` header is set in your callback
configuration.
- **Payload Parser Error**: If you're using a custom payload parser, there may
be an issue with your parsing script. Check the device's
[Live Inspector](/docs/tagoio/devices/live-inspector.md) to see detailed error
messages about payload parsing failures.
### No Data Received
If the callback succeeds (HTTP 200) but no data appears in TagoIO:
- **Device EUI Mismatch**: Verify that the Device EUI in TagoIO exactly matches
the device ID in Sigfox (case-sensitive).
- **Check Live Inspector**: Use the
[Live Inspector](/docs/tagoio/devices/live-inspector.md) to see if data is
arriving but being rejected or filtered.
---
Information about Downlink can be found at the
[Sigfox - Downlink](/docs/tagoio/integrations/networks/sigfox/sigfox-downlink.md)
documentation.
You also can
[create your own parse scripts](/docs/tagoio/devices/payload-parser/building-your-own-parser.md)
to process variables from the payload.
---
## The Things Network LoRaWAN™
This tutorial provides a step-by-step guide to integrate The Things Network
(TTN) with the TagoIO platform. By following these instructions, you will enable
uplink and downlink operations, allowing data exchange between your LoRaWAN
devices and the TagoIO application.
> If you're trying to integrate TTI or TTN v3, use our
> [TTI integration](https://community.tago.io/t/how-to-integrate-tagoio-with-ttn-v3/1026)
> instructions.

## Prerequisites
Before you begin, ensure you have the following:
- TTN v3 account: If you don't have one,
[register here](https://www.thethingsnetwork.org/get-started).
- LoRaWAN device: Ensure your device is connected to TTN and transmitting data.
If you need help doing that, watch this
[TTN video tutorial](https://www.youtube.com/watch?v=duwUwXt-hs8).
## Step 1: Create Authorization in TagoIO
### Create a New Authorization
1. Go to **Devices** in the TagoIO dashboard.
2. Click the **Create Authorization** button.
3. Provide a meaningful name for the authorization (e.g., `TTN-Integration`).
4. Click **Save**.

### Copy the Authorization Token
After creation, copy the authorization token. You will need this in later steps.
## Step 2: Configure TTN Integration
1. **Navigate to your TTN console** and open your application.
2. Go to **Integrations > Webhook > + Add Webhook**.
3. Select **Custom Webhook** from the list.

### Webhook Configuration
| Field | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Webhook ID** | Enter a unique identifier (e.g., `tagoio-integration`). |
| **Webhook Format** | JSON |
| **Base URL** | Use the endpoint format `https://ttn.middleware.REGION.tago.io`. Replace `REGION` with your deployment region (e.g., `us-e1`, `eu-w1`). For a list of available regions, visit [TagoIO Network Integration](/docs/tagoio/integrations.md). |
| **Additional Headers** | Add a header key `Authorization` and set its value to the authorization token you copied in Step 1. |
| **Enabled event types** | Enable the event types you expect to receive from your device. Set the path of all enabled event types to `/uplink`. |

## Step 3: Add Your Device to TagoIO
### Ensure Device EUI Consistency
- When creating the device in TagoIO, use **exactly the same Device EUI** that
is configured in TTN.
- The Device EUI must **match exactly** to establish a successful connection
between TTN and TagoIO.
### Select the Appropriate Network and Device Type
1. Choose the **LoRaWAN TTN** network during the device setup process.
2. Select the correct device type from the available options. If your device is
not listed, you can add support through connectors.
### Add Device Support via Connectors (If Necessary)
- If your device type is not available in the default list, enhance its
compatibility by using connectors.
- For more information on available connectors and how to use them, refer to the
**Connector Overview**.
For detailed instructions on adding devices, visit the
[Adding Devices](/docs/tagoio/devices/) page.
## Step 4: Finalize Setup
1. **Power On Your Devices**\
Turn on your LoRaWAN device and gateway to initiate data transmission.
2. **Verify Data Reception**
- In TagoIO, navigate to the Device and open the **Data** tab to confirm that
data is being received.
- Alternatively, use the
[Live Inspector](/docs/tagoio/devices/live-inspector.md) within your device
settings to monitor incoming data in real-time.
With data successfully flowing into TagoIO, you can start creating Dashboards,
Analyses, and Actions to visualize and manage your device data effectively.
---
## Bridging a Third-Party MQTT Broker
:::tip
For direct MQTT connectivity without an external broker, use
[TagoTiP MQTT](/docs/tagotip/transports/mqtt), which supports all account types
and regions (US and EU).
:::
TagoIO supports MQTT connections through the MQTT Relay command-line tool. The relay acts as a bridge between your MQTT Broker and the TagoIO platform, enabling integration and reliable data flow. Developed in Rust, the TagoIO MQTT Relay is fast, open-source, and highly scalable.

## What is TagoIO MQTT Relay?
The relay connects to your broker on predefined topics and redirects incoming
data to **[Devices](/docs/tagoio/devices/)** in your Profile using an
**[Authorization Key](/docs/tagoio/integrations/general/authorization.md)** from
your profile settings. It uses TagoIO's Integration Network and Connector
to process and store the data. In addition to receiving data, the relay can also
publish to topics, allowing bi-directional communication via standard TagoIO
tools such as the API and SDK.

## Installation
Learn how to use the relay by checking out the [TagoIO MQTT Relay GitHub repository](https://github.com/tago-io/mqtt-relay).
## How it works
### Data Flow
1. **MQTT Broker Connection**: The relay connects to your MQTT broker and
subscribes to predefined topics.
2. **Data Redirection**: Incoming data on these topics is forwarded to TagoIO
[Devices](/docs/tagoio/devices/).
3. **Network Integration**: The relay uses the Integration Network and
[Connector](/docs/tagoio/devices/payload-parser/connector/connector-overview.md)
to process the payload.
4. **Device Identification**: A Payload Parser exports a serial that TagoIO
uses to store the information in the correct device within your
[Profile](/docs/tagoio/profiles/).
## Customization
Given the diverse strategies for data transmission, the MQTT Relay supports a
broad range of solutions. You can customize how the Device is identified using
our [Network Integrations](/docs/tagoio/integrations.md). The Payload Parser
inside the Network can export a Serial so that the data is stored accurately
within your Profile.
## Demo video
### Key Features
The TagoIO MQTT Relay is equipped with the latest features for hosting and
deployment:
- **TLS and TLS Certificates Support**: Ensures secure communication.
- **Docker Support**: Simplifies deployment and scaling. Refer to the
[Docker Hub](https://hub.docker.com/r/tagoio/relay) page for more details.
- **Configurable**: Can be configured through a .toml file or environment
variables.
For detailed setup instructions, visit our
[GitHub repository](https://github.com/tago-io/mqtt-relay).
---
## Backups(My-account)
## Overview
The Profile Backup feature enables you to generate, download, and manage backups of the supported profile resources stored in TagoIO, so you keep your configuration data and a snapshot of the included resources of your IoT infrastructure.
Backups are retained for **12 months** and focus on **data preservation and export**. Resources are saved as structured JSON files and can be used to recreate your environment. Restoration options include:
- **TagoIO CLI** - run `tagoio backup restore` to interactively restore a backup to your profile.
- **TagoIO API** - read the exported JSON files and recreate resources programmatically using the API.
- **TagoIO Support** - for large-scale recovery needs, contact our support team. Note that this may involve an additional cost.
:::info[Availability]
Backups are available for paid plans: **Starter** and **Scale**.
:::
---
## Accessing Backups
Go to your profile name in the bottom-left corner of the screen and select **Backups**. You will be taken to the **Manage Backups** page.
---
## Creating a Backup
On the **Manage Backups** page, click **Create Backup**. A modal will list all resources included and excluded before you confirm.
### Resources Included
| Resource |
| -------------------------------- |
| All uploaded files and documents |
| Dashboard layouts and versions |
| Analysis scripts and versions |
| Device configurations |
| Actions, networks, connectors |
| Access management policies |
| Network configurations |
| Connector configurations |
| Profile secrets |
| Dictionary configurations |
| TagoRUN users |
| TagoRUN configuration |
| Service authorization tokens |
| Profile settings and allocations |
| Team members |
### Resources Not Included
| Resource |
| ------------- |
| Devices data |
| Entities data |
---
## Downloading a Backup
Find the backup in the list and click the **download icon** (⬇️) in the **Actions** column. You will be asked to verify your account password. The backup is downloaded as a **ZIP file** with the following structure:
```
backup-/
├── files/
│ ├──
│ └── devices/
│ └── /
│ └──
├── resources/
│ ├── access_management.json
│ ├── actions.json
│ ├── analysis.json
│ ├── connectors.json
│ ├── dashboards.json
│ ├── devices.json
│ ├── dictionaries.json
│ ├── networks.json
│ ├── profile.json
│ ├── run_users.json
│ ├── run.json
│ ├── secrets.json
│ └── service_authorization.json
└── versioning/
└── dashboard/
└──
```
- **files/** - Uploaded files and documents, organized by device.
- **resources/** - Profile resource configurations exported as JSON.
- **versioning/** - Historical versions of resources such as dashboards.
---
## Restoring a Backup
Backups are designed for **data preservation**, not automatic one-click restore. There is no restore option in the UI. To restore resources from a backup, choose one of the options below.
### Option 1: Manual Script-Based Restoration
1. Download and extract the backup ZIP file.
2. Read the JSON files for the resources you need to restore.
3. Use the TagoIO API to recreate resources programmatically.
- Example: Parse `devices.json` and call `POST /device` for each device.
### Option 2: TagoIO CLI
Make sure you have the [TagoIO CLI](https://docs.tago.io/cli) installed and authenticated, then run:
```bash
tagoio backup restore
```
The CLI fetches all available backups and displays them as a selectable list, each entry shows the creation date, file size, and backup ID. Use the arrow keys to pick the one you want to restore.
After selecting a backup, you will be prompted to confirm and authenticate with your **resources password**, **2FA method**, and **OTP code**. Once authenticated, the CLI downloads and extracts the backup, shows a summary table of all resources and their counts, and asks for a final confirmation before starting the restoration.
:::warning
**IDs are NOT restored.** New IDs will be generated for all resources. Any external references to resource IDs will need to be updated.
:::
### Option 3: Contact TagoIO Support
For large-scale restoration or complex recovery scenarios, contact TagoIO support. Our team can assist with full profile restoration, selective resource recovery, and migration between profiles or accounts.
:::important
Support-assisted restoration may involve an additional cost.
:::
### What Cannot Be Automatically Restored
- **Active tokens** - tokens are masked for security and will not be restored.
- **Run User passwords** - excluded for security and must be reset manually.
---
## Automated Backups via Actions
You can schedule automatic backups using TagoIO Actions with the `profile_backup` action type.
### Supported Triggers
| Trigger | Description |
| ------------- | ---------------------------------------------- |
| **Resource** | Fires when a specific resource event occurs |
| **Condition** | Fires based on data conditions |
| **Schedule** | Fires at a specific time (e.g., daily at 2 AM) |
| **Interval** | Fires at regular intervals |
### Setting Up
Navigate to **Actions** in your TagoIO Admin, create a new action, and select **Profile Backup** as the action type. Configure your desired trigger and activate the action.
:::tip
Schedule backups during low-activity periods and use daily or weekly schedules for production environments. Keep an eye on your daily backup limit to ensure scheduled backups don't get blocked.
:::
---
## Limits & Considerations
### General
- Backups are retained for **12 months** from the creation date.
- **Devices data** and **Entities data** are **not included** in backups.
- Backup generation time varies depending on the size of your profile data.
- During a restore, **all resource IDs are regenerated** - update any external references accordingly.
- The backup download URL has an **expiration time**, so complete the restore process promptly after initiating it.
For the daily backup creation limit per plan, see [Resource Limits](/docs/tagoio/my-account/billing/resource-limits).
For the API rate limits (RPM) that apply to backup operations, see [Rate Limits (Hard Limits)](/docs/tagoio/profiles/services/rate-limits-hard-limits).
---
## Account Plans
## Overview
TagoIO offers three account plans that allow you to use the platform's tools and capabilities for your application. Select your plan in the [My Billing](https://admin.tago.io/account/billing) page.
### Plan Types
- **Starter**: Basic plan for small projects.
- **Scale**: Intermediate plan with higher limits and additional services.
- **Enterprise**: Advanced plan for large organizations.
## Service limits
A certain limit of [Services](/docs/tagoio/profiles/services/) is already included in your plan.
> For more details about the features and pricing of each plan, access our [Pricing](https://tago.io/pricing) page.
For the **Starter** and **Scale** plans, you can change the limits of the Services and include Add-Ons such as Custom Domain & Whitelabel and Custom Mobile App.
Learn also about [Add-ons](/docs/tagoio/addons/).
---
## Allocating Services to Profiles
When you manage multiple profiles in your account, ensure each profile has the correct allocation of services so they operate efficiently. You can configure per-profile allocations on the Billing page under the [Resource Allocation](https://admin.tago.io/account/billing/allocation) tab.
The services within each profile can be limited so that when a profile exceeds its allocated limit, only that profile will have its service blocked. If the available services are insufficient for allocation, you can either increase the total service limits for your account or reduce the allocation for other profiles. Access the [Billing](https://admin.tago.io/account/billing) page to upgrade or change these service limits.
## How to allocate resources
You can easily allocate services to your profiles through your [Admin](https://admin.tago.io/) home page or through the '[Resource Allocation](https://admin.tago.io/account/billing/allocation)' tab on the Billing page. Simply click on the resource you wish to allocate and adjust the limits accordingly.

This interface provides all necessary information for each service, helping you make informed decisions about resource allocation. It displays the service amount allocated to each profile and, for deeper insights, you can view a profile's usage history for both past and current months by clicking on the chart icon.

## Monitoring allocated resources
You can easily monitor the amount of resources available in your account that can be allocated to your profiles, and verify the current service usage against the allocated limit for each profile. This monitoring can be done through the '[Resource Allocation](https://admin.tago.io/account/billing/allocation)' tab inside the Billing page.
On the left side, the 'Account Service Allocation' column shows the total service amounts available for distribution across your account. These are the limits you've set for your entire account, which can be increased by clicking the 'Upgrade account limits' button or through the [Billing](https://admin.tago.io/account/billing) page in the 'Services' tab.
On the right, 'Profile Usage Allocation' shows how much of each service has been used by each profile, highlighting which profiles are close to or have reached their limits. Clicking on a resource opens a new window for customizing its allocation.

Profiles are sorted by those that have reached their limits and those nearing their limits, helping you quickly identify which profiles may face service disruption and require immediate attention.
:::tip
You can also manage the limits of your account's services and allocate profiles directly from your mobile device through the [TagoIO mobile app](/docs/tagoio/my-account/managing-your-account-with-the-tagoio-mobile-app.md).
:::
---
## Common Billing Issues
## Why was a charge declined?
Declines can occur for many reasons determined by the cardholder's bank. Each bank uses a system that considers various signals, such as the cardholder's spending habits, account balance, and card information like the expiration date and CVV/CVC security number.
Since these signals change over time, a card that was previously successful may be declined for future charges.
:::info
Even if all card information is correct and the cardholder previously had a successful payment, a future charge can still be declined by the bank.
:::
## Finding out more information about a specific decline
TagoIO displays all information we receive from the cardholder's bank and the payment processor about a decline in the error message. Because of this, we do not have additional details beyond what is provided to us.
:::info
If the card information (card number, expiration date, CVV/CVC) is correct but there is still trouble checking out, the cardholder needs to contact their bank.
:::
The cardholder's bank is the entity declining the charge and sending the error. Therefore, only the bank can explain why the transaction was declined and how to resolve the issue.
## Error Messages
"Your card does not support this type of purchase."
- Some debit cards require a PIN to be entered. If the cardholder is using one of these cards, they'll need to use another card to make the purchase.
- Some cards have restrictions on international purchases. If the card was issued in a country other than where your business is located, this might be the problem. In this case, the cardholder needs to contact their bank.
- Some cards (often corporate cards or FSA cards) can only be used for certain business categories, like travel or healthcare.
"Your card is not supported."
- Some payment processors restrict card brands (i.e. Discover, Mastercard) based on the country your store is located in. For example, Stripe currently doesn't support Discover cards in Canada.
- The cardholder needs to use a different brand of card that would be supported for the country where your store is located.
"Your card has insufficient funds."
- The cardholder's bank is saying that they don't have enough funds in their account to cover the costs of the purchase.
- Sometimes cardholder's banks don't properly assess the risk of currency conversion for purchases. If they see that the purchase is in a different currency, the bank will decline the charge as insufficient funds so that the currency conversion or the currency conversion fee doesn't overdraw the account.
- If a cardholder sees this error message, they should contact their bank.
---
## Downgrading Plans & Services
You can schedule downgrades in your account for your plan, service limits, and add‑ons.
:::tip
If you change your mind any time before the end of the billing cycle, you can modify or cancel the downgrade.
:::
## Downgrade timing and modification
- All downgrades become effective on the next renewal date.
- Your current subscription and service limits remain active at their current level until the end of the billing period you already paid for.
- You can cancel or modify scheduled downgrades at any time until the end of your billing cycle.
## Multiple profiles (important)
If you have more than one profile, make sure you manually [reduce the resources for each profile](/docs/tagoio/my-account/billing/allocating-services-to-profiles.md) before your next billing cycle to avoid any disruption to your application.
## How to downgrade
To downgrade your billing, click on your account name at the bottom-left menu in your Admin, hover over "Profiles & Billing" and select **[Billing](https://admin.tago.io/account/billing)** option.
## Refunds
See our [Refund Policy](/docs/tagoio/my-account/billing/refund-policy.md).
---
## Billing
You can get an overview of your account's costs for the current month by clicking on the [Summary](https://admin.tago.io/account/billing) tab. This view helps you track the costs of sub-accounts (profiles) that you may use to bill your own customers.
## Views
This page consists of three views:
1. The account cost distributed by type of service and plan in a pie chart.
2. The account cost and the limits by type of service and plan in a table.
3. The allocated resources and the estimated cost of each profile.

:::info
The estimated cost of a profile does NOT include the cost of the current plan.
Also, the sum of all profiles plus the plan may not match the overall amount of your account if some resources are not allocated to any profile.
Discounts and credits are not included in the summary view.
:::
---
## Payment Methods
## Accepted payment methods
TagoIO accepts most major credit cards for payment.
## Invoice payments and bank transfers
Making payments by invoice through ACH or wire transfer is available on request for Starter and Scale yearly plans.
## Yearly plans and purchasing credits
TagoIO also offers a yearly plan with **flexible terms**. These terms allow companies to purchase credits that can be used for any service during the plan period.
## Contact to arrange payment
Please [contact us](https://tago.io/contact-us) to arrange payment by invoice or to purchase credits.
You may also reach out via email at **customercare@tago.io** with subject "Requesting information about yearly plans."
---
## Refund Policy
TagoIO offers refunds in certain situations if you forgot to cancel your plan or later decided to cancel it. If you believe you are entitled to a refund, follow the steps below.
## How to initiate the refund process
If you are entitled to a refund, complete these steps:
1. [Downgrade your plan](/docs/tagoio/my-account/billing/downgrading-plans-services.md) to the free plan to avoid any further charges.
2. Do not delete your account.
3. [Open a ticket requesting a refund](https://tago.io/contact-us).
## When are you entitled to a refund
You may be entitled to a refund in the following cases:
1. You were charged for your next **monthly** plan within the last **14 days** and had no service usage during that period (no use of Analysis, Data Input, Data Output, or additional storage).
2. You were charged for your next **yearly plan** within the last **14 days** (applies only to yearly plans).
:::info
When a payment method other than a credit card is used, there may be an additional bank or processing fee associated with the refund process.
:::
---
## Resource Limits(Billing)
TagoIO implements multi-tenancy as a cost-effective resource-sharing approach. This architectural choice brings many benefits but requires mechanisms to avoid service disruptions when other users sharing resources have spikes in consumption. To prevent such scenarios, TagoIO limits the number of resources individuals can create in their accounts. When these limits are reached, creating additional resources will be blocked to maintain platform stability and prevent resource exhaustion.
## Limits for each resource
The limits for each resource are determined by your specific plan. The table
below shows the limits for each plan.
:::info
Most of the limits below are applied per [Profile](/docs/tagoio/profiles).
:::
| Resource type / Plan | Free | Starter | Scale |
| --------------------------------------------------------------------------------------- | ----------: | ----------: | ----------: |
| [Actions](/docs/tagoio/actions/) | 5 | 100 | 200 |
| [Analysis](/docs/tagoio/analysis/) | 5 | 100 | 200 |
| [Dashboard](/docs/tagoio/dashboards/creating-dashboard-tabs.md) | 5 | 100 | 1,000 |
| [Entity](/docs/tagoio/entities.md) | 5 | 50 | 80 |
| [SQL Query](/docs/tagoio/tagosql/queries.md) | 3 | 15 | 50 |
| [Device](/docs/tagoio/devices/) | 5 | 100 | 10,000 |
| [Profile](/docs/tagoio/profiles) | 1 / account | 2 / account | 5 / account |
| [Team Member](/docs/tagoio/profiles/team-management-sharing-your-profile.md) | 0 | 1 | 5 |
| [TagoCore](https://tagocore.com/) | 10 | 10 | 10 |
| [TagoCore Cluster](https://tagocore.com/) | 3 | 3 | 3 |
| [Custom Connector](/docs/tagoio/devices/payload-parser/connector/connector-overview.md) | 3 | 20 | 50 |
| [Custom Network](/docs/tagoio/integrations/general/creating-a-network-integration.md) | 1 | 5 | 10 |
| [Device service authorization](/docs/tagoio/integrations/general/authorization.md) | 10 | 50 | 200 |
| [Secrets](/docs/tagoio/security/secrets.md) | 5 | 5 | 10 |
| [Access Management Policies](/docs/tagoio/tagorun/access-management/) | 5 | 30 | 100 |
| [Dictionary & Multi-language](/docs/tagoio/tagorun/getting-started/dictionaries.md) | 2 | 10 | 50 |
| [Backup](/docs/tagoio/my-account/backups) (daily creation limit) | N/A | 1 / day | 1 / day |
If you attempt to add additional resources after reaching your limit, a
notification will be shown indicating that you have run out of available slots.
If your requirements exceed these limits, consider upgrading to the **Scale**
plan or contacting us through support@tago.io. For more information about our
plans and services, check our [Pricing Page](https://tago.io/pricing).
## TagoSQL query limits
Beyond how many queries you can store, [TagoSQL](/docs/tagoio/tagosql/) limits what a single query may do:
| Limit | Free | Starter | Scale |
| ----------------------- | -----------: | ------: | ------: |
| Versions kept per query | 1 | 5 | 25 |
| Parameters per query | 3 | 10 | 50 |
| Max joined tables | 1 (no JOINs) | 3 | 5 |
| Result row cap | 100 | 1,000 | 10,000 |
| Fleet devices per query | 10 | 50 | 100 |
| Fleet time window | 7 days | 30 days | 90 days |
| Query timeout | 5s | 15s | 30s |
The two fleet rows govern
[multi-device queries](/docs/tagoio/tagosql/tables#data-from-many-devices) (`device_data_by_tag`):
how many devices one request may target, and how far back its mandatory time bound may reach.
The **result row cap** never truncates a result silently. A query whose written `LIMIT` exceeds the cap is
rejected when you store it, and a query with no `LIMIT` whose result turns out larger fails when it runs.
`OFFSET` is not capped. See [TagoSQL Errors](/docs/tagoio/tagosql/errors) for the exact messages.
:::info
Enterprise plans and TagoDeploy instances can have custom values. Your profile's effective numbers are
available on the profile limits endpoint (`GET /profile/{id}/limits`) under the `sql` group.
:::
For how many requests per minute the `/sql` routes accept, see
[Rate Limits](/docs/tagoio/profiles/services/rate-limits-hard-limits.md).
## Monitoring resources across your entire account
You can monitor the amount of resources you have used by accessing the profile
menu in the top left corner of your Admin page and selecting the
**[Hard Limits](https://admin.tago.io/limits/hard)** option.
---
## Setup Billing
To set up your billing, click on your account name at the bottom-left menu in your Admin and select **[Billing](https://admin.tago.io/account/billing)** option.

## Configure Billing Information
Once you are on the My Billing page, complete the **Billing Information** tab with the company name, address, and any other details you want printed on future invoices.
- Add credit card information to enable payments.
- Specify the email address that should receive invoices: you can use the email associated with your account or provide a different email.
## Select Services and Account Plan
After completing the Billing Information tab, open the **Services** tab and choose your **Account Plan**.
You may also set limits for any services where you think you'll need more than what is already included with your plan. These limits define the overall resources that you will have available to allocate to your profiles (if you have more than one profile). You can always increase or reduce these limits at any time later on.
### Add‑ons
Optional add‑ons can be selected to enhance your account, though they are not required for a basic setup.
---
## Upgrading Plans & Services
When you upgrade your plan from Free to a Starter or Scale plan for the first time, the date and hour of that upgrade define the billing cycle for your account. You will be charged for the full month at that time, and the plan will be automatically renewed until you cancel or downgrade it.
If you make additional upgrades to your plan during a billing cycle, we will apply a one-time prorated charge at the moment of the upgrade for the remaining days in that billing cycle.
For example, if you upgrade from the Starter to the Scale plan when there are 10 days remaining in the billing cycle, we will charge one-third of the Scale monthly price immediately (pro‑rated for the remaining days), minus the amount you already paid for the Starter plan. On the next billing cycle, we will charge the full price of the Scale plan and its provisioned services.
:::warning
Some services are not prorated if they are consumable. For example, if you increase the SMS limit in the middle of your billing cycle, you will be charged the full price (minus the amount already paid for the previous setting) because you then have the right to use all available SMS messages.
:::
To upgrade your billing, click on your account name at the bottom-left menu in your Admin, hover over "Profiles & Billing" and select **[Billing](https://admin.tago.io/account/billing)** option.
## How to auto-scale
If you want to automate upgrades of your account using an auto‑scaling feature, see the following tutorial:
- [How to auto-scale your profile limit](https://community.tago.io/t/how-to-auto-scale-your-profile-limits/1075)
---
## Deleting Your Account
## How to delete your account
If you need to cancel your account, go to Account Settings by accessing [My Account](https://admin.tago.io/account/general).
Steps:
- Click on your account name at the bottom-left menu in your Admin and select "My Account".
- Hover over "Profiles & Billing" and select "My Account".
- On the top right corner click "... Account Details" and click the "Delete my account" button.
- TagoIO will delete your account, including all profiles associated with it, and will send a confirmation email when the deletion is complete.
:::warning
All applications and data will be permanently deleted. This action cannot be undone.
:::
## Deleting a single profile, organization, or customer connection
If you only need to delete a single organization or a customer connection tied to a Profile (rather than your entire TagoIO account), you can delete that profile by opening the Profile and using the "More" tab inside your profile. This will remove only that profile instead of your whole account.
---
## Editing Accounts Details
## Accessing your account settings
To manage your account details, click the bottom-left menu in the TagoIO interface and hover over "Profiles & Billing" and select the option [My Account](https://admin.tago.io/account/general).
## What you can edit
From the Account settings page you can edit various personal and account preferences, including:
- Name
- Phone
- E-mail address
- Company name
- Time zone
- Decimal separator
- Notification preferences
- And other account-related settings
---
## Managing Your Account with the TagoIO Mobile App
The TagoIO App is your gateway to simplifying your IoT operations and managing all your IoT applications in one place. It enables you to monitor and control services and resources across all your profiles and receive real-time notifications and alerts on your smartphone so you stay informed about activities.

## Download
Download the TagoIO App from the App Store (iOS) or Google Play (Android).
[AppStore](https://apps.apple.com/us/app/tagoio/id1067391139) | [GooglePlay](https://play.google.com/store/apps/details?id=center.io.tago.admin&hl=en&gl=US)
---
## Resetting My Password
On the [signup page](https://admin.tago.io/signup), click the "Forgot your password?" link and enter your email address. We'll send instructions to that email to reset your password.
## Want to change your password?
To change your existing password:
- Click on your account name at the bottom-left menu in your Admin.
- Hover over "Profiles & Billing" and select "My Account".
- Go to the [Security](https://admin.tago.io/account/security) tab.
- Click the "Change password" button and follow the prompts.

:::tip
If you need to reset or change a password for yourself or your users in the TagoRUN application, manage those accounts via [User Management](/docs/tagoio/tagorun/access-management/).
:::
---
## Two-factor Authentication (2FA)
Two-factor authentication (2FA) adds an extra layer of security to your account by requiring more than just your password to sign in. It requires and provides another form of authentication that only you know or have access to.

## Available authentication methods
- App Authenticator: a six-digit code generated by an authentication app on your mobile device (for example, Google Authenticator, Authy).
- SMS: a six-digit code sent to your mobile phone via text message.
- E-mail: a six-digit code sent to your account e-mail address.
:::info
The SMS and E‑mail authentication methods are available only for Starter or Scale plans.
:::
## How 2FA codes work
The second factor is a six‑digit code. That code is either:
- generated by an authentication application on your mobile device (App Authenticator), or
- sent to you as a text message (SMS) or e‑mail.
Once a method is configured for your account, you will be asked to type the six‑digit code:
- when signing in, and
- when generating a new [Account Token](/docs/tagoio/profiles/account-token.md).
:::tip
You can choose the authentication method that suits you best when prompted by clicking the message "Can't access your two-factor device?"
:::
---
## Account Token
Account Tokens are the secret keys used between TagoIO and external applications. Any access from an account requires a valid token, so it is vital that tokens are kept secret and only shared with trusted parties.
:::warning
All sorts of requests can be performed with Account Tokens, so only provide this token to trusted applications.
:::
## Managing Account Tokens
Account tokens can be managed from your [account details](https://admin.tago.io/account) by selecting your profile. You can copy a token by clicking the **Copy** button.
More than one token can be created for the same profile. Permissions and expiration dates for each token may be configured as needed.
### Types of Permissions
- **Full**: full permission to manage all entities from your account, including read, create, update, and delete.
- **Write only**: create and update all entities from your account.
- **Read only**: read all entities from your account.
#### Entities Authenticated by Account Tokens
1. [Devices](/docs/tagoio/devices.md)
2. [Dashboards](/docs/tagoio/dashboards.md) and [Widgets](/docs/tagoio/widgets.md)
3. [Analysis](/docs/tagoio/analysis.md)
4. [Actions](/docs/tagoio/actions.md)
5. [Files](/docs/tagoio/files.md)
6. [Access Management](/docs/tagoio/tagorun/access-management.md)
7. [TagoRUN](/docs/tagoio/tagorun.md) and [Run users](/docs/tagoio/tagorun/getting-started/user-management.md)
8. [Dictionary](/docs/tagoio/tagorun/getting-started/dictionaries.md)
#### Expiration and Auto-Generated Tokens
The expiration field defines the lifespan of a token; it can be set to a temporary period or left infinite (never expires).
A new token is automatically generated each time you log in to your account, or when someone with whom you have shared your profile logs in. These auto‑generated tokens are created with an expiration time of 3 months and will be deleted automatically after that period or when the user logs out of the account manually.
## Token Format and Prefixes
Tokens issued by TagoIO carry a short prefix that identifies their type, so you can recognize a token at a glance in logs, screenshots, and configuration files:
| Prefix | Token type |
| ------ | --------------------- |
| `p-` | Profile token |
| `a-` | Analysis token |
| `u-` | Run user token |
| `n-` | Network token |
| `at` | Service authorization |
[Device tokens](/docs/tagoio/devices/device-token.md) are returned without a prefix, because they are provisioned into hardware and firmware where the token length is often fixed.
Tokens created before prefixes were introduced keep working, and the API accepts tokens with or without a prefix on every request.
---
## Audit Log
## Overview
The Audit Log is a chronological sequence of entries generated by activities performed by a Profile owned by your account. Audit logs show what happened before and after an event and help identify records associated with specific events.
You can monitor activities such as the addition and deletion of a [Device](/docs/tagoio/devices/), changes in [Access Management](/docs/tagoio/tagorun/access-management/), [Analysis](/docs/tagoio/analysis/), and other similar actions.
:::info
Only Starter or Scale plans are allowed to filter log entries. Check the Pricing page for plan details and available features.
:::
## Events
Each action executed by an agent on a resource is considered an event. Agents can be, for example, the TagoIO or TagoRUN system, your account, or another team member's account. Resources include Devices, Actions, Analysis, or Access Management policies.
TagoIO keeps a 6‑month log for each profile; events older than that will not be displayed.

:::info
Only the account that owns the profile can visualize the log. Team members added as Team Members cannot view logs from shared profiles.
:::
## Filtering events
The Audit Log can be filtered to focus on specific details. The following filters are available:
### Timezone
The log uses your account's timezone preference to display the timeline of events in ascending order. Changing the timezone will adjust the timeline, filter results, and any downloaded logs.
### Time interval
Use the **Time Interval** filter to inspect events that occurred within a specific range. Shorter ranges speed up the search; the maximum selectable period is 7 days.
### Event initiator
Filter by the agent that performed the action (you, a team member, TagoIO system, or TagoRUN system). Removed team members appear as "Unknown user".
### Resource type
Select a resource type to view only events related to that category (Devices, Actions, Analysis, etc.).
### Resource ID
Search for an event tied to a specific resource by its ID. If you choose a Resource Type first, the filter changes to a dropdown with all relevant IDs.
### Event messages
Each event includes a message describing the action performed on the resource. You can filter events based on these messages.

## Automation
You can access the Audit Log programmatically via our SDK, Analysis, and Actions to automate processes such as backing up log events.
## Downloading a copy
A PDF copy of the audit log entries can be downloaded by clicking the **Download** button in the top right corner. Only the activities displayed in the feed will be printed out.
---
## Creating a Sandbox
_Sandbox_ refers to an isolated testing environment that enables developers to run applications without affecting other applications that have critical devices or users connected.
:::tip
Use sandboxes to test new applications with the confidence that they will not impact others.
:::
## How to create a sandbox
To create a sandbox, just add a new [Profile](https://admin.tago.io/profile) and use it for your tests. Make sure that all critical applications are running in another profile.
For example, if any of your tests demand a higher level of services (limits) and lock that profile, it will not block the applications running in other profiles.
## Collaboration
Increase team collaboration and add more developers to your sandbox. To do this, share your profile with others by using the [Profiles & Teams](/docs/tagoio/profiles/team-management-sharing-your-profile.md) functionality.
## Notes
- Use a separate profile for all non-critical testing to avoid impacting production devices or users.
- Locking or changing limits on a sandbox profile will affect only that profile, not the applications in other profiles.
---
## Profiles
Profiles are similar to having different sub-accounts under your main account. You can create multiple profiles for people or organizations and share them with others. By doing this, you remain the owner of each "sub-account" while allowing others to access each profile separately.
## Benefits of Profiles
1. **Isolate applications from different customers**
If you have two profiles and one reaches its service limits, only that profile will be impacted or blocked; the other will remain unaffected.
2. **Grant restricted access for external individuals**
Allow external users limited access to specific applications and organizations.
3. **Track cost of individual applications and customers**
Visualize cost breakdown per Profile and Services on the [Statistics](/docs/tagoio/profiles.md) page.
## Switching Profiles
To switch profiles, click the current profile shown on the top left and select another one.

## Adding Profiles
Go to your account menu on the bottom left corner, hover over "Profiles & Billing" and select **[Profiles & Teams](https://admin.tago.io/profile)** from the list, and click on **Add Profile**.
After that, you can edit the profile to change the name, logo, and upgrade services & add‑ons for it. Each profile starts with certain services included; you can increase the limits as you scale up the applications that run on that profile.
## Renaming Your Profiles
Navigate to your **[Profiles & Teams](https://admin.tago.io/profile)** page and select the **Profile** tab. Click on the profile name and type a new one. It is possible to use the same name for multiple profiles.
### Sharing Profiles with Teams
Add others to your profiles by adding their e-mails in the team section. To invite someone, go to the specific profile, select the **Team** tab, and add the user's email. Make sure that the person already has a TagoIO account before inviting them. Read more about [Team management](/docs/tagoio/profiles/team-management-sharing-your-profile.md)
## Generating Account Tokens
[Account tokens](/docs/tagoio/profiles/account-token.md) are used to authenticate requests in the TagoIO API. You can use our API to make integrations and create devices, dashboards, send data, and more. To create an account token, follow these steps:
1. Click on the **Tokens** tab.
2. Add a name, permission, expiration, and click **Generate Token**.
3. Insert your password.
4. Click **Generate my token**.
## Transferring Profiles Between TagoIO Accounts
Once you request a transfer, our support team will assist you with the next steps. When you submit a profile transfer request, a support ticket is automatically created, and our team will contact you by email to confirm required information and prerequisites before proceeding with the transfer. The process is manual and may take a few hours to complete.
You can request the profile transfer by navigating to [Profiles](https://admin.tago.io/profile) > ... Profile Preferences > and clicking on "Transfer to another account", then typing the Account E-mail of the destination account in the field shown below:
:::info
Profile transfer is available for Starter and Scale accounts. Check the [pricing page](https://tago.io/pricing/) for more information.
:::
---
## Analysis Service
Every time an Analysis runs one script, its runtime duration (measured in 1-second increments) is counted against the limit in that specific Profile. This limit defines the total available runtime your Analyses can use inside TagoIO.
## Example
For example, if you have one Analysis that runs every day for 2 minutes and another that runs on only one day of the month for 5 minutes, you would need:
```
2 * 30 + 5 = 65 minutes/month
```
to run your Analyses. It is safer to add some extra time to account for time variation or for cases where your script becomes slower as it parses more data.
## Which scripts are counted
Only scripts that are configured to run on TagoIO are counted. Scripts that run from "External" do not count.

:::warning
If this limit is exceeded, no scripts in that Profile will be executed for the rest of the month.
:::
---
## Data Input Service
Each time a device sends data to TagoIO, the system counts one Data Input Transaction for each variable included in that payload.
These data input transactions are accounted against the Profile where your devices are located.
:::tip
You should estimate the maximum number of transactions per month to ensure your Profile has sufficient capacity.
:::
### Data Input (limit per month)
You should select a limit that you are sure your application will not exceed during a period of one month. When a profile hits its limit, inputs will be blocked up until the next counting cycle.
- Example: If your device sends the registers `temperature` and `humidity` in the same post every 2 minutes, the usage of Data Input Transactions will be:
`text
2 (registers) * 30 (requests per hour) = 60/hour
60 * 720 = 43,200 transactions/mo
`
Multiply the previous number by the number of devices and you will have the total number of data input transactions per month.
:::warning
TagoIO will deny requests from any devices linked to the Profile that is blocked.
:::
:::note
Rate limits are applied to interactions within the platform, including data sent from IoT devices via network protocols (MQTT, LoRaWAN, HTTP). Read more about our [Rate Limits](/docs/tagoio/profiles/services/rate-limits-hard-limits.md).
:::
## Viewing limits and usage history
You can visualize the limits and usage history of each service in the Profile tab, located under the [Profiles & Teams](https://admin.tago.io/profile) section of your account.

---
## Data Output Service
One transaction of Data Output is counted for each register read from a [device's](/docs/tagoio/devices/) data storage. Learn more in [Getting Data](/docs/tagoio/devices/getting-data.md).
You need to define the **Data Output limit per month** for each Profile where your applications are running.
## Example calculation
If a device reads one variable and the response contains 100 registers every 10 minutes (100 temperature data points), the operations will be:
- 100 registers × 6 reads per hour = 600 transactions per hour
- 600 transactions per hour × 720 hours per month = 432,000 transactions per month
## Limits and behavior
:::warning
If the limit is exceeded, any attempt to read data (GET) will be denied for the rest of the month.
:::
Accessing data from dashboards, including downloading a .csv file from widgets, does **not** count toward the general Data Output limit. Instead, dashboards consume a separate Data Output limit specifically for dashboards. For more details, see [Data Output for Dashboards](/docs/tagoio/dashboards/data-output-for-dashboards.md).
The system will also count Data Output when using the function "Export Data" from the [Device](/docs/tagoio/devices/).
## TagoSQL queries
[TagoSQL](/docs/tagoio/tagosql/) executions are metered against this same Data Output allocation:
- Every execution that reaches the database (a cache miss, or a run with `"test": true`) records the rows it returns as output usage, and is blocked once the monthly allocation is exhausted.
- **Results served from a query's cache are free** and consume no output. See [Caching](/docs/tagoio/tagosql/executing-queries.md#caching).
- A query that fails the plan row cap still counts the rows the database consumed, so fix the query rather than retrying it.
:::info
Request limits are applied to interactions within the platform, including activities such as reading and exporting data. Read more about our [Rate Limits](/docs/tagoio/profiles/services/rate-limits-hard-limits.md).
:::
---
## Data Records Service
A Data Record represents the variables stored and sent by devices. Each register consists of a variable and all metadata associated with it. The profile's data record limit defines the maximum storage capacity for the selected Profile at all times.
Fields such as [location](/docs/tagoio/devices/sending-data.md) that contain latitude/longitude as part of a variable should be considered part of the same register. The same rule applies to metadata fields.
## Metadata
All fields and [metadata](/docs/tagoio/devices/payload-parser/metadata.md) stored with a given variable are counted together as a single register. For example, all data stored with the variable "temperature" in the example below counts as only one register.
```json
[
{
"variable": "temperature",
"value": 71,
"unit": "F",
"time": "2019-06-30 01:58:11",
"group": "1561859891862",
"location": {
"lat": 35.770723,
"lng": -78.677328
},
"metadata": {
"color": "green"
}
}
]
```
Another simple example: Your device sends speed, temperature, and humidity with location every hour. As a result, 3 new registers will be added every hour.
## Notes
- The "variable" and its associated metadata (for example, [metadata](/docs/tagoio/devices/payload-parser/metadata.md)) are counted together as one register regardless of how many fields are present.
- Location stored inside the variable (lat/lng) is considered part of the same register.
- The data record limit is enforced per Profile and defines the maximum number of registers stored for that Profile.
## Additional considerations
- If the Data Record limit is exceeded, no data will be saved until the limit is increased or the number of registers is reduced. The API response will indicate that the limit was exceeded for that Profile.
- Location stored inside the variable (lat/lng) is considered part of the same register.
- When you reduce the number of registers, the Usage Statistics in your [Admin](https://admin.tago.io/) page may take some minutes to update to the new amount of data records.
- TagoIO will send warning Emails to you each time the storage exceeds 80%, 90%, and 100% of the limit.
---
## E-mail Service
Each time an Email is successfully sent by the [Action](/docs/tagoio/actions/)
or [Analysis](/docs/tagoio/analysis/) function, it is counted as one unit. This
limit defines the maximum number of Emails that can be sent from that Profile
during a one-month period. Unused Emails in a billing cycle will not be carried
over to the next cycle.
:::warning
As anticipated last year, starting on January 1st, 2025, TagoIO will no longer
offer native E-mail services. You can still send E-mails by integrating with
external providers such as AWS SES directly. Read more:
[External Email Integration Guide](/docs/tagoio/actions/defining-actions.md).
:::
## Limits and warnings
- If the monthly Email limit for a Profile is exceeded, no Email will be sent
from that Profile until either the limit is increased or the billing period
ends.
- TagoIO will send warning Emails each time a service reaches 80%, 90%, and 100%
of its limit.
---
## End-Users service
Each end-user added to the [User Management](/docs/tagoio/tagorun/getting-started/user-management.md) list is counted as an end‑user. End-users (also called RUN‑users) can be added automatically when they sign up in your RUN application.
## Limits and behavior
If any profile reaches the limit of end‑users allocated to it, the system will block the addition of new users.
When a profile hits its allocated end‑user limit, no additional users can be added under that profile until the limit is increased.
## Increasing end-user capacity
Increase the number of end‑users allowed in your application by [Allocating Resources to profiles](/docs/tagoio/my-account/billing/allocating-services-to-profiles.md).
For more information about pricing and FAQs, see the [Pricing Page](https://tago.io/pricing).
---
## File Storage Service
Use the [Files](/docs/tagoio/files) module to store any type of file (reports, videos, logos, background images, and more). File service usage is measured by the total size of all files stored across your profiles.
If any profile reaches its allocated storage limit, the system will block adding new files to that profile.
## Increasing the limit
You can increase the File service limit by [Allocating Resources to profiles](/docs/tagoio/my-account/billing/allocating-services-to-profiles.md).
## Pricing and FAQ
More information about pricing and frequently asked questions is available on the [Pricing Page](https://tago.io/pricing).
---
## Services Overview
## Overview
TagoIO offers a broad range of services that can be provisioned according to your application's needs. By selecting which services each profile will use during the month, you can track usage and know exactly how much you will be charged.
You can view limits and usage history for each service in the **Usage Statistics** tab, located under the Profiles section of your TagoIO account. If you have more than one profile, you must allocate the resources for each profile (see [Allocating Services to profiles](/docs/tagoio/my-account/billing/allocating-services-to-profiles.md)).
Currently, the following services are available for selection on your account. Some services are reset monthly.
## Services and Usage
| Service | Unit | Usage counted | Definition |
| -------------------- | -------------: | ------------------- | ------------------------------------------------------------------------------------------- |
| Data Input | Variable | Monthly | Each variable sent by your devices or posted to the Device's data storage. |
| Data Output | Variable | Monthly | Each variable received by your devices or retrieved ('get') from the Device's data storage. |
| Data Storage | Variable | Maximum at any time | Each variable stored in a Device. |
| File Storage | Size (MB) | Maximum at any time | Total aggregate size of all files stored in the Files module. |
| Analysis for Scripts | Execution time | Monthly | Total script runtime executed by the Analysis module (minimum 1 second). |
| Push Notifications | Message | Monthly | Messages sent out. |
| E-mail | Message | Monthly | Messages sent out. |
| SMS | Message | Monthly | Messages sent out. |
| End user (RUN user) | User | Maximum at any time | End users registered in your RUN applications. |
| TagoAI Requests | Request | Monthly | Each message sent to TagoAI using the TagoIO default provider. |
## Service Alerts
TagoIO will send warning emails and push notifications when a service usage exceeds 80%, 90%, or 100% of the limit set for a specific profile. This helps you stay informed before reaching your quota.
:::tip
You can add scripts in the [Analysis](/docs/tagoio/analysis/) to automatically upgrade your plan or to increase the limit for a service to avoid service blockages.
:::
## Where to view usage
Usage limits and history: see the Usage Statistics tab under the [Profiles](https://admin.tago.io/profile) page or in the home of your [TagoIO admin](https://admin.tago.io/).
---
## Notification Service
Each push notification delivered to a device logged into the TagoRUN mobile app counts as one Notification unit.
## Overview
- The service limit defined in each Profile determines the maximum number of notifications that can be sent during a one-month period (based on your billing cycle).
- Each push notification is limited to 140 characters.
- Messages will appear in the mobile app and the browser when end users receive notifications through your RUN application.
## How push notifications consume the limit
- Notifications sent to Run users do not count toward the limit on their own.
- Notifications sent to your Admin do not count toward the limit.
- When a Run user is logged into the TagoRUN mobile app, the push notification delivered to that device counts toward the limit.
:::warning[The limit is consumed per Mobile App logged-in device]
If a user is logged into the TagoRUN mobile app on 10 devices, a single push notification to that user consumes 10 units from the limit, one per device.
:::
## Limits and behavior
- If the Notification service limit is exceeded, no push notifications will be delivered to the TagoRUN mobile app for that Profile until the limit is increased or the billing period ends. Notifications that do not count toward the limit, such as those sent to your Admin or shown to Run users in the browser, continue to work as normal.
- Push notifications not used during the period will not be carried over to the next month.
More information about pricing and FAQs can be found on the [Pricing Page](https://tago.io/pricing).
---
## Rate Limits (Hard limits)
When executing requests to TagoIO, there is a limit on the number of requests that can be made during a given time period. These limits are applied in short intervals every minute to provide a reliable, scalable API for developers and to help prevent cyber attacks on our servers, such as DDoS.
Rate limits apply to interactions within the platform, including, but not limited to, API endpoints, data sent from IoT devices via network protocols (MQTT, LoRaWAN, HTTP), data exports, dashboard visualizations, user interactions, and more. These limits are also known as hard limits.
:::note
Hard limits are used to prevent high-frequency usage beyond a healthy acceptable level; this is different than [TagoIO services](/docs/tagoio/profiles/services/) that are reset monthly. You still need to make sure you have enough resources to run your application.
:::
:::info
TagoIO also enforces limits on the number of resources you can have and the amount of data displayed on your dashboards. Read more about [Resource Limits](/docs/tagoio/my-account/billing/resource-limits.md) and [Data Output for Dashboards](/docs/tagoio/dashboards/data-output-for-dashboards.md).
:::
## How it works
Rate limits, or hard limits, are enforced based on your account's plan. Each account tier has different rate limits for each API resource. The maximum number of requests allowed is based on a time interval, with a retry window when requests exceed the limit.
**The limit for each request is applied every minute.** If a user reaches their rate limit during a specific minute, they must wait until the next minute for the limit to reset before making additional requests. So if you reach the maximum rate at 12:45:09 PM, the next request can only be done after 12:46:00 PM.
The system aggregates all requests performed inside a Profile for each route. Therefore, if you reach the limit for a route, all future requests to that Profile and route will be denied during the period. For some specific routes, such as the Post and Get data from devices, you can configure the rate limit for each device separately.
:::tip
Check out the [Control Tower add‑on](/docs/tagoio/addons/control-tower.md).
:::
### Request Rate Limits
The following table lists the API resources for which the hard limit applies, the corresponding endpoint route, functions associated with the route, and limits for each plan. The values are given in RPM: requests per minute.
**Maximum number of requests per minute allowed in each Profile**
| Resource | Route | Function | Free | Starter | Scale |
|---|---|---|---|---|---|
| **DEVICE** | POST /Device | Create Device | 10 | 250 | 5 000 |
| | DEL /Device/:device_id | Delete Device | 10 | 250 | 5 000 |
| | GET /device | List Devices | 50 | 100 | 500 |
| | GET /device/:device_id | Get Device | 100 | 1 000 | 5 000 |
| | GET /device/token/:device_id | Get Device Token | 100 | 1 000 | 5 000 |
| | GET /device/:device_id/data_amount | Get Device Data Amount | 100 | 1 000 | 5 000 |
| | GET /device/:device_id/chunk | Get Device Chunk | 100 | 1 000 | 15 000 |
| | GET /device/params | Get Device Params | 100 | 1 000 | 5 000 |
| | GET /device/params/:device_id | Get Device Params | 100 | 1 000 | 5 000 |
| | PUT /device/:device_id | Edit Device | 50 | 250 | 2 000 |
| | POST /device/token | Create Device Token | 50 | 250 | 2 000 |
| | DEL device/token/:token | Delete Device Token | 50 | 250 | 2 000 |
| | POST /device/:device_id/convert | Convert Device | 50 | 250 | 2 000 |
| | POST /device/:device_id/empty | Empty Device | 50 | 250 | 2 000 |
| | POST /device/:device_id/chunk/copy | Export Device Chunk | 50 | 250 | 2 000 |
| | DEL /device/:device_id/chunk/:chunk_id | Delete Device Chunk | 50 | 250 | 2 000 |
| **DATA** | GET /data | Get Device Data [*] | 250 | 5 000 | 15 000 |
| | POST /data | Send Device Data [*] | 250 | 5 000 | 15 000 |
| | PUT /data | Edit Device Data | 60 | 250 | 1 000 |
| | DEL /data | Delete Device Data | 60 | 250 | 1 000 |
| | GET /data?query=aggregate | Get Device Data with Query Aggregate | 5 | 10 | 100 |
| **SQL** | GET /sql | List SQL Queries | 60 | 100 | 500 |
| | GET /sql/:sql_id | Get SQL Query | 60 | 100 | 500 |
| | GET /sql/:sql_id/version/:version | Get SQL Query Version | 60 | 100 | 500 |
| | GET /sql/tables | Get SQL Table Catalog | 60 | 100 | 500 |
| | POST /sql | Create SQL Query | 30 | 75 | 150 |
| | PUT /sql/:sql_id | Edit SQL Query | 30 | 75 | 150 |
| | DEL /sql/:sql_id | Delete SQL Query | 30 | 75 | 150 |
| | POST /sql/:sql_id/execute | Execute SQL Query, fresh run [**] | 30 | 60 | 120 |
| | POST /sql/:sql_id/execute | Execute SQL Query, served from cache [**] | 300 | 600 | 1 200 |
| **ACCOUNT** | PUT /account | Edit TagoIO Account | 50 | 100 | 500 |
| | GET /account | Get TagoIO Account | 50 | 100 | 500 |
| | ... | ... | ... | ... | ... |
| **ANALYSIS** | POST /analysis | Create Analysis | 60 | 100 | 1 000 |
| | PUT /analysis/:analysis_id | Edit Analysis | 60 | 100 | 1 000 |
| | DEL /analysis/:analysis_id | Remove Analysis | 60 | 100 | 1 000 |
| | POST /analysis/:analysis_id/upload | Analysis Upload Script | 60 | 100 | 1 000 |
| | GET /analysis | Get Analysis List | 60 | 1 000 | 2 000 |
| | ... | ... | ... | ... | ... |
| **ACTION** | POST /action | Create Action | 60 | 100 | 1 000 |
| | PUT /action/:action_id | Edit Action | 60 | 100 | 1 000 |
| | DEL /action/:action_id | Remove Action | 60 | 100 | 1 000 |
| **DASHBOARD** | POST /dashboard | Create Dashboard | 60 | 100 | 1 000 |
| | PUT /dashboard/:dashboard_id | Edit Dashboard | 60 | 100 | 1 000 |
| | DEL /dashboard/:dashboard_id | Remove Dashboard | 60 | 100 | 1 000 |
| | ... | ... | ... | ... | ... |
| **FILES** | POST /files | Create Files | 60 | 100 | 200 |
| | PUT /files | Edit Files | 60 | 100 | 200 |
| | DEL /files | Remove Files | 60 | 100 | 200 |
| | ... | ... | ... | ... | ... |
| **PROFILE** | POST /profile | Create Profile | 60 | 100 | 200 |
| | PUT /profile/:profile_id | Edit Profile | 60 | 100 | 200 |
| | DEL /profile/:profile_id | Remove Profile | 60 | 100 | 200 |
| | ... | ... | ... | ... | ... |
| **NOTIFICATION** | POST /notification | Create Notification | 60 | 100 | 1 000 |
| | PUT /notification/:notification_id/:btn_id | Edit Notification | 60 | 100 | 1 000 |
| | ... | ... | ... | ... | ... |
| **RUN NOTIFICATION** | POST run/notification | Create RUN Notification | 60 | 100 | 1 000 |
| | ... | ... | ... | ... | ... |
| **RUN** | POST /run/users | Create Run User | 60 | 100 | 1 000 |
| | PUT /run/users/:user_id | Edit Run User | 60 | 100 | 1 000 |
| | ... | ... | ... | ... | ... |
| **AM** | POST /am | Create Access Management | 60 | 100 | 200 |
| | PUT /am/:am_id | Edit Access Management | 60 | 100 | 200 |
| | ... | ... | ... | ... | ... |
| **TAGS** | GET /tags/keys/:type | Get Tags Resource | 60 | 100 | 1 000 |
| **TOKEN INFO** | GET /info | Get Token Info | 60 | 5 000 | 10 000 |
| **INTEGRATION** | POST /integration/network | Create Integration Network | 60 | 100 | 500 |
| | ... | ... | ... | ... | ... |
| **AUTHORIZATION** | POST /serviceauth | Create Authorization Code | 60 | 100 | 200 |
| | ... | ... | ... | ... | ... |
| **DICTIONARY** | POST /dictionary | Create Dictionary | 60 | 100 | 200 |
| | ... | ... | ... | ... | ... |
| **TEMPLATE** | POST /template | Create Template | 60 | 100 | 100 |
| **TCORE** | POST /tcore/instance | Create TCore | 60 | 100 | 100 |
| | ... | ... | ... | ... | ... |
| **MQTT** | Publish-Publish | MQTT payload to TagoIO broker | 250 | 5 000 | 15 000 |
| | Subscribe-Receive | MQTT payload from TagoIO broker | 250 | 5 000 | 15 000 |
| | Connection-Connect to TagoIO broker | 20 | 100 | 200 |
| **BACKUP** | POST /backup | Create Backup | N/A | 5 | 5 |
| | GET /backup | List Backups | N/A | 50 | 100 |
| | GET /backup/:backup_id/url | Download Backup | N/A | 10 | 20 |
| | DEL /backup/:backup_id | Delete Backup | N/A | 10 | 20 |
> [*] You may also be able to adjust the limit individually **per device** depending on your plan. Check out our [Control Tower add‑on](/docs/tagoio/addons/control-tower.md).
> [**] A [TagoSQL](/docs/tagoio/tagosql/) execution counts against the fresh-run limit only when it reaches the database. Results served from the query's cache use the much higher cached allowance. Each stored query can also set its own `rate_limit_rpm` so one heavily-exposed query cannot exhaust the profile's budget. See [Executing Queries](/docs/tagoio/tagosql/executing-queries.md).
:::tip
If, for some special reason, your requirements exceed the limits in the table, consider upgrading your plan to **[Scale](https://admin.tago.io/account/billing)**. If you are already on the Scale plan, take a look at the enterprise version [TagoDeploy](https://tago.io/deploy), or please contact us through support@tago.io.
:::
## Response code
When a hard limit has been exceeded, TagoIO will return an **HTTP 429 "Too Many Requests"** error. You can also examine the response body to check the remaining retry window in seconds (`Retry-After : seconds`). The following error will be returned in the response body:
```json
{ "status": false, "message": "Too many requests (Retry-After: 8)" }
```
If you need help adapting your project to these hard limits, try our [Community](https://community.tago.io/).
## Rate limits usage monitoring
You can monitor the hard limits for each function by accessing the profile menu located in the top left corner of your [Admin](https://admin.tago.io/limits/hard) page and selecting the **Hard Limits** option. If the [Control Tower](/docs/tagoio/addons/control-tower.md) add‑on is active in your account, you will have the ability to view historical and detailed statistics of your usage for Hard Limits, providing insights into your requests per minute.
---
## SMS Service
Each time an SMS message is successfully sent by an [Action](/docs/tagoio/actions/) or [Analysis](/docs/tagoio/analysis/) function, it is counted as one SMS unit. The service limit defined inside each [Profile](/docs/tagoio/profiles) determines the maximum number of SMS messages that can be sent during a one-month period (based on your billing cycle).
Each SMS unit is limited to 140 characters.
:::warning
As anticipated, starting on **November 1st, 2024**, TagoIO will no longer offer built-in SMS services. You can still send SMS by integrating directly with external providers such as Twilio or AWS. [Read more](https://community.tago.io/t/tagoio-will-soon-stop-providing-sms-and-email-services/1646)
:::
## Limits and usage
- If the SMS service limit is exceeded, no SMS for that Profile will be sent until the limit is increased or the billing period ends.
- SMS pricing is only valid for: United States, Australia, Germany, United Kingdom, Canada, Mexico, Brazil, Colombia, Japan, and Chile. For other countries, contact us.
- SMS messages not used during the period will not be carried over to the next month.
- For more information on pricing and FAQs, see the [Pricing Page](https://tago.io/pricing/).
## Short-number behavior
The short number used to send messages is randomly selected by our SMS provider, Amazon SNS. Companies that use the same provider might share the same short number of origin, so users may receive SMS from other services that use the same sender number.
Analysis is able to integrate with any SMS service; you can register a unique number for your own origin if desired.
---
## TagoAI Service
Each profile receives a monthly allowance of requests to the free model, shared by the Admin [TagoAI features](/docs/tagoio/tago-ai/features.md) in that profile, and a second allowance of the same size reserved for [AI Chat in TagoRUN](/docs/tagoio/tago-ai/tagorun-ai-chat.md).
A free model request is counted against the profile allowance when a user:
- Sends a message through [TagoAI Chat](/docs/tagoio/tago-ai/chat.md).
- Generates or refines an [AI Dashboard](/docs/tagoio/dashboards/ai-dashboard/).
A message sent by a portal user through AI Chat in TagoRUN counts against the **TagoRUN AI requests** allowance instead. Portal traffic never consumes the Admin allowance, and the reverse.
## Monthly free model allowance
| Plan | Admin requests per profile, per month | TagoRUN AI requests per profile, per month |
| ------- | ------------------------------------: | -----------------------------------------: |
| Free | 10 | 10 |
| Starter | 50 | 50 |
| Scale | 150 | 150 |
Both allowances reset at the beginning of each calendar month in UTC. Unused requests do not carry over.
When the profile uses up one allowance, new requests to the free model from that surface are refused until it resets; the other allowance is unaffected. A portal user whose profile exhausted the TagoRUN allowance sees an error with the code RA07.
Other model paths remain available:
- TagoIO catalog models can run when the account has enough [AI Credits](/docs/tagoio/tago-ai/ai-credits.md) and the profile is within its [spend cap](/docs/tagoio/tago-ai/ai-credits.md#set-a-monthly-profile-spend-cap).
- Connected-provider models can run according to the provider's limits and billing.
TagoIO notifies the account by email and an Admin notification when a profile reaches 80%, 90%, and 100% of either monthly allowance.
## Rate limits
Each monthly allowance limits the total number of free model requests from its surface in a calendar month. Separate rate limits can temporarily restrict how quickly requests are sent.
A profile can therefore receive a rate limit error even when free model requests remain. Wait briefly and send the request again.
## Catalog models
Requests to TagoIO catalog models do not consume the free model allowance. They are charged by usage against the account's [AI Credits](/docs/tagoio/tago-ai/ai-credits.md).
A request is refused if the balance cannot cover it or the profile has reached its monthly spend cap. The free model remains available while the profile has free model requests remaining.
What you spend is reported in the [AI usage statistics](/docs/tagoio/tago-ai/ai-usage.md), by period, profile, and model.
## Connected-provider models
Requests to models from a [connected AI provider](/docs/tagoio/tago-ai/ai-provider.md) do not consume the free model allowance or AI Credits. The connected provider bills the usage directly.
The TagoIO free model remains available on these profiles and continues to use the monthly allowance.
---
## Usage Policy
When executing API requests to TagoIO, either posting (data input) or getting (data output), your account may be subject to usage limits configured in your [My Billing](https://admin.tago.io/account/billing) settings.
## How limits work
- Limits are based on the number of transactions allowed for a given time period (for example, per hour or per minute).
- A global (higher-level) limit is also applied to all accounts to help prevent attacks on TagoIO servers (for example, DDoS attacks) and to provide a way to limit usage from applications.
> Check our documentation on [Rate Limits](/docs/tagoio/profiles/services/rate-limits-hard-limits.md).
## Token access and restrictions
TagoIO limits access for both [Account-Token](/docs/tagoio/profiles/account-token.md) and [Device-Token](/docs/tagoio/devices/device-token.md) according to the transaction-per-hour limits configured in your account. Ensure your token usage patterns comply with these per-hour transaction limits to avoid throttling.
Other services (for example: data storage, SMS, email, and Analysis) are also limited according to your plan settings. Learn more in the [TagoIO Services](/docs/tagoio/profiles/services/) documentation.
---
## Team Management - Sharing your profile
You can grant access to other TagoIO accounts to any of your [Profiles](/docs/tagoio/profiles/) by using the Team feature. By adding other accounts to your team, they will have access to all applications that run on that Profile.

## Invitation notification
The invitation can only be sent to email addresses that already have a TagoIO account. If the invited user doesn't have an account, the invitation email will guide them to create one.
Once you invite a team member, they can accept the invitation by clicking on the link in the email or via the push notification displayed in the Admin panel. The invitation is valid for 7 days; after that it expires and another one must be sent. You will receive an email and push notification if the invitation was accepted or declined.
After accepting, the new member can access the shared profile by clicking on the profile selection button. Shared profiles are marked with a tag.
## Plan limits
The number of members you can invite to a Profile depends on your current plan. Check the list of features and limits available for each plan [here](/docs/tagoio/my-account/billing/resource-limits.md).
## Removing access
You can remove any granted access at any time by removing the user's email from the team members list.
:::info
Only the owner of the Profile is able to invite members.
:::
---
## Secrets
Secrets are encrypted environment variables used to store sensitive information such as passwords and API keys. Each secret consists of a key (the secret's name) and a value (the sensitive data). Secrets keep their contents hidden once encrypted, making them safer than regular environment variables for confidential data.
Unlike regular [Environment Variables](/docs/tagoio/analysis/environment-variables.md), secrets are encrypted so their contents remain hidden after encryption. This security feature is essential for protecting sensitive data within TagoIO. Secrets are especially useful when scripts or services need access to confidential information without exposing it to users or developers. You can retrieve secrets from the Analysis context in your [Analysis](/docs/tagoio/analysis/) code and use them in some [Actions](/docs/tagoio/actions/).
To start using secrets, open your [Admin](https://admin.tago.io/) panel, and click on the "Secrets" button in the sidebar menu. Don't forget that you must [grant access to the Secrets to the Analysis](/docs/tagoio/security/secrets#granting-your-analysis-access-to-secrets) you want to use them in.
## Creating a Secret
To create a secret, open the "Secrets" page in the Admin panel and click on "+ New Secret". For every secret you create, you can define:
- **Key**: the name used to reference the secret.
_Keys cannot be edited once the secret is created, so choose your keys carefully._
- **Value**: the secret data (encrypted and hidden). This field can contain plain text or a structured value depending on the chosen secret type.
- **Tags**: metadata to organize and filter secrets. [Tags](/docs/tagoio/devices/tags-system.md) are used across various assets within TagoIO and allow you to grant Analysis access to specific secrets via Access Management policies.
### Types of Secrets
Secrets can be stored in several formats, each suited for different use cases:
#### Text
A secure storage option for text-based data that allows both letters and numbers. This secret can hold up to **2048 characters**.
#### SMTP Secret
Used exclusively for securely storing information required to configure an action that sends emails through external email providers.
- Supports TLS connections on port **587**.
- Refer to the [Defining Actions](/docs/tagoio/actions/defining-actions.md) documentation for more details about this action.
#### AWS SQS Secret
Used exclusively for securely storing information required to communicate with Amazon SQS.
To obtain these values, log into your AWS Management Console and navigate to the SQS service. From there, you can retrieve:
- `_aws_region_`
- `_queue_url_`
- `_access_key_id_`
- `_secret_access_key_`
These credentials are then entered into the corresponding fields of the secret.
## Using Secrets in Analysis
Secrets are accessible through the `_context.environment_` parameter, allowing you to pass variable values directly into your script's context.
You can also overwrite a Secret's value in an Analysis by creating an environment variable with the same Secret Key.
Example:
```python
# Retrieve a secret value inside an analysis
secret_value = _context.environment_.get('MY_SECRET_KEY')
```
## Granting Your Analysis Access to Secrets
Before using secrets as environment variables in your script, ensure that your analysis has the necessary permissions to access the information within your secret. This is achieved by:
1. Assigning tags to your secret.
2. Creating an Access Management policy that grants your analysis permission to read those tags.
Refer to the [Access Management](/docs/tagoio/tagorun/access-management/) module for detailed instructions on creating policies and managing permissions.
---
---
## Security and Compliance
At TagoIO, we understand that the foundation of trust with our customers is built on the assurance of information security and adherence to legal standards. Our commitment to data privacy and security is embedded in every part of our business. Our proactive approach to security, compliance policies, and dedication to legal obligations are at the core of our operations, providing you with the confidence that your information is in safe hands.
Learn below about our efforts to protect your and your users' data.
## ISO 27001 Certification
TagoIO is proud to be ISO 27001 certified, a reflection of our dedication to security. This internationally recognized standard specifies the requirements for establishing, implementing, maintaining, and continually improving an information security management system (ISMS) within the context of our organization. Our certification reflects our systematic approach to managing sensitive company and customer information so it remains secure.
## General Data Protection Regulation (GDPR)
TagoIO is committed to upholding the principles of the GDPR and to processing the personal data of our EU customers and users with the utmost care and respect for privacy. We have implemented data protection policies and procedures that align with GDPR requirements, giving our users control over their personal information and fostering transparency in the way that data is handled.
If you wish to know how we process and protect personal data that is subject to the GDPR and other E.U. laws, you can download our Data Processing Agreement (DPA) here: [https://tago.io/dpa](https://tago.io/dpa)
## TagoIO Security Portal
For a detailed overview of our security practices, you should visit our [Security Portal](https://security.tago.io/). Here, you will find information on the measures we take to safeguard your data, including encryption and access control, security policies, and other documents.
---
## AI Credits
AI Credits are prepaid funds on your account that pay for AI models charged by usage. You load money onto the account first, and each request to a TagoIO catalog model debits the balance based on the model price and the size of the request.
Credits are separate from your TagoIO plan. Only paid models from the [AI model catalog](/docs/tagoio/tago-ai/ai-models.md) spend them. The free model never touches your balance (it runs on the TagoIO side within its monthly allowance, on every profile), and a profile running [its own AI provider](/docs/tagoio/tago-ai/ai-provider.md) is billed by that provider instead.
:::info
Free chat is not unlimited: the free model has a monthly request quota per profile. See [TagoAI Service](/docs/tagoio/profiles/services/tago-ai-service.md) for the plan numbers, and the [AI model catalog](/docs/tagoio/tago-ai/ai-models.md) for the free model.
:::
The financial terms at a glance:
| Term | Value |
| -------------- | --------------------------------------------------------------------------------------- |
| Purchase range | 10.00 to 1000.00 per purchase |
| Service fee | Added to the card charge, not credited to the balance; the Admin shows the current rate |
| Expiration | One year after the purchase that created the credits |
| Currency | USD or EUR, fixed when the wallet is created |
| Spend cap | Optional, per profile, per calendar month (UTC) |

## The wallet
Your credit balance belongs to the account, not to a profile. Every profile in the account draws from the same balance, and the only per-profile control is a [monthly spend cap](#set-a-monthly-profile-spend-cap). There are no per-profile balances or allocations.
The wallet page shows:
- The current balance and its currency.
- How many purchases still hold unspent credits, and the date the next batch expires.
- The automatic top-up configuration.
- A history of every movement: purchases, spending, expirations, and adjustments issued by TagoIO support.
### Currency
The wallet currency is set when the wallet is created, based on your account region: USD for US accounts and EUR for EU accounts. It cannot be changed afterwards, and TagoIO does no currency conversion. Prices, purchases, caps, and card charges all use that single currency.
## Buy credits
Before buying credits, add a default card under [Payment Methods](/docs/tagoio/my-account/billing/payment-methods.md). Purchases always charge the default payment method saved on your account.
To buy credits:
1. Open the AI wallet.
2. Select **Top up**.
3. Enter an amount from **10.00 to 1000.00**.
4. Review the credit amount, service fee, and total card charge.
:::warning
The service fee is added to the card charge and is not added to your AI Credits balance. For example, buying 30.00 in credits adds 30.00 to the wallet and charges the card 30.00 plus the displayed fee. Review the total card charge before confirming the purchase.
:::
5. Confirm the purchase.
After the payment succeeds, the purchased amount appears in the wallet balance. The service fee appears in the payment breakdown but is not added to the credit balance.
### If the purchase result does not appear
If the page closes, times out, or does not show a result:
1. Return to the AI wallet and check the balance and transaction history.
2. If the purchase appears, do not start another purchase.
3. If the Admin presents a retry option for the pending purchase, use that option instead of starting a new purchase. Retrying the same purchase cannot charge your card twice.
:::warning
Starting a new purchase creates a separate payment. Only use the retry action associated with the original purchase.
:::
## Credit expiration
Credits expire **one year after the purchase that created them**. Spending always consumes the credits closest to their expiration date first, so the oldest credits leave the wallet before newer credits.
The wallet page shows the next expiration date. Expired credits are removed from the balance and appear in the history as an expiration entry. Expired credits are not refundable.
## Automatic top-up
Automatic top-up buys credits for you when the balance runs low. You set:
- **Threshold**: the balance below which a top-up is triggered.
- **Amount**: how much to buy, within the same range as a manual purchase (see [Buy credits](#buy-credits)). The purchase service fee applies the same way.
For example, if the threshold is 20.00 and the top-up amount is 50.00, TagoIO schedules a 50.00 purchase after the balance falls below 20.00.
A scheduled job checks wallets every few minutes, so a top-up happens shortly after the balance dips below the threshold, not at the instant of the request that crossed it. A request that arrives before the top-up lands can still fail for insufficient credits.
### When a top-up fails
Automatic top-up uses the saved default payment method. When the charge fails, for example because the card was declined or removed:
- The balance stays where it is, and requests to catalog models keep failing for insufficient credits.
- Automatic top-up stays enabled, and TagoIO tries again on later checks while the balance remains below the threshold.
- TagoIO does not email you when a charge fails.
- Successful top-ups appear in the wallet history; a failed charge does not create a history entry.
Keep a valid card on the account so top-ups can go through.
## Set a monthly profile spend cap
A spend cap limits how much a single profile can spend in a calendar month. It does not reserve part of the account balance for that profile; a capped profile still competes for the same balance as every other profile.
To configure a cap:
1. Open the AI wallet.
2. Find the profile spend cap settings.
3. Select a profile.
4. Enter the monthly cap, or clear it to remove the cap.
5. Save the setting.
The counter resets at the beginning of each calendar month in UTC. When a profile reaches its cap, its requests to catalog models are refused until the month rolls over or you raise the cap.
### Where the cap applies
| Environment and model path | Does the profile spend cap apply? |
| ------------------------------------- | --------------------------------- |
| TagoIO catalog model on TagoIO Cloud | Yes, on AI Credits spending |
| Free model | No; the monthly allowance applies |
| Own-provider model on TagoIO Cloud | No; your provider's limits apply |
| Instance-provider model on TagoDeploy | Yes, based on the catalog value |
## Resolve wallet and spending errors
| Message | What to do |
| ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| Insufficient AI credits | The account balance cannot cover the request. Top up the balance in the AI wallet, or enable [automatic top-up](#automatic-top-up). |
| This profile has reached its monthly AI spend cap | The profile hit its monthly cap. Raise it in the [spend cap settings](#set-a-monthly-profile-spend-cap) or wait for the next calendar month. |
| Top-up amount supports at most 2 decimal places | The purchase amount has more than two decimal places. Enter an amount with at most two. |
| Cannot top up AI credits without a payment method | No default card is saved on the account. Add one under [Payment Methods](/docs/tagoio/my-account/billing/payment-methods.md). |
## TagoDeploy behavior
TagoDeploy does not use an AI Credits wallet.
| Capability | TagoIO Cloud | TagoDeploy |
| ------------------ | ---------------------------------- | ------------------------------------- |
| AI Credits balance | Available | Not available |
| Credit purchases | Available | Not available |
| Automatic top-up | Available | Not available |
| Provider billing | TagoIO or your configured provider | The instance provider account |
| Usage statistics | Report charged spend | Report catalog value as a usage meter |
| Profile spend cap | Enforced on AI Credits spending | Enforced against the catalog value |
On TagoDeploy, paid-model usage runs on the instance's own provider credentials and is priced from the instance [catalog](/docs/tagoio/tago-ai/ai-models.md) for metering and cap enforcement. TagoIO bills nothing for it; the instance's provider account pays for the actual usage.
---
## AI models and pricing
The model catalog lists the AI models available to your account and the input and output rates for each model. On TagoIO Cloud, catalog models use [AI Credits](/docs/tagoio/tago-ai/ai-credits.md). On TagoDeploy, the catalog rates act as usage meters rather than TagoIO charges (see [TagoDeploy billing](#tagodeploy-billing)).
## How a request is billed
How a request is billed depends on the model path used by the profile:
- **Free**: the default model. It is a small model suited to quick questions and light tasks. It spends no credits, and each profile gets a monthly allowance of free model requests based on its plan ([TagoAI Service](/docs/tagoio/profiles/services/tago-ai-service.md)).
- **AI Credits**: any paid model in the catalog. The request debits [AI Credits](/docs/tagoio/tago-ai/ai-credits.md) from the account balance at the catalog price.
- **Your own provider (BYOK)**: a profile configured with [its own AI provider](/docs/tagoio/tago-ai/ai-provider.md). Requests to your provider's models go through your provider account and are billed by that provider, so they spend no credits and no free allowance. The free model stays available on these profiles too; it runs on the TagoIO side and counts against the monthly allowance as usual.
Every combination, in one table:
| Profile configuration | Model path | Who bills the usage | Uses AI Credits | Uses free allowance |
| --------------------- | -------------------- | --------------------------- | --------------- | ------------------- |
| TagoIO default | Free model | No additional charge | No | Yes |
| TagoIO default | TagoIO catalog model | TagoIO, at the catalog rate | Yes | No |
| Own provider | Provider model | Your provider | No | No |
| Own provider | Free model | No additional charge | No | Yes |
### TagoDeploy billing
TagoDeploy does not use an AI Credits wallet. Models run through the instance's configured provider credentials, and that provider bills the actual usage.
The catalog still assigns a price to each model. TagoDeploy uses that price to report usage and enforce profile spend caps, but TagoIO does not charge the displayed amount. See [TagoDeploy behavior](/docs/tagoio/tago-ai/ai-credits.md#tagodeploy-behavior).
:::tip
The free model is intentionally small. For demanding work such as long analyses, dashboard generation, or large debugging sessions, we recommend a catalog model or your own provider.
:::
## Reading the catalog
Each model in the catalog shows:
- **Name and description**: what the model is and what it is good at.
- **Input price** and **output price**, per 1 million tokens, in your wallet currency.
- **Reasoning effort levels** the model supports.
Input tokens can include more than the text entered in the current prompt. Depending on the feature, the request can include previous conversation messages, resource information, and instructions TagoIO adds to complete the task. Output covers what the model writes back.
A model that costs 3.75 per 1M input tokens costs 0.00000375 per input token, so most single requests cost a fraction of a cent. Longer conversations generally use more input tokens, because previous messages are included as context for later requests.
### Catalog prices
The catalog displays the final TagoIO rate for each model. No additional model service fee is added when the request is billed.
Model availability and prices can change. Check the catalog in the Admin before choosing a model or estimating future costs.
### Regional availability
The models available in the catalog can differ between US and EU accounts. Check the catalog in the Admin for the models and prices available to your account.
## Reasoning effort
Some models support multiple reasoning effort levels. A higher level allows the model to spend more computation on the request, which can help with complex tasks but can also increase response time and token usage.
The available levels depend on the model, and `auto` is always available. When you select `auto`, TagoIO chooses a level appropriate for the request. The free model supports `auto` only.
## Choosing a model
Requests that do not name a model use the profile's configured default model, or the free model when the profile has none configured. Choose a catalog model when the task justifies it: long analysis scripts, large payload debugging, or dashboard generation where quality matters more than the credit cost.
Before using a TagoIO catalog model, make sure the account has enough [AI Credits](/docs/tagoio/tago-ai/ai-credits.md) and that the profile is within its monthly spend cap. If either condition prevents the request, no model usage is charged.
Models connected through your own provider do not use AI Credits. Your provider's billing and limits apply instead.

:::info
The catalog changes over time as models are added, repriced, or retired. Check the current prices in the Admin rather than relying on a rate you noted earlier.
:::
---
## Using your own AI provider
By default, TagoAI runs on a model and provider selected by TagoIO. If you want direct control over which model handles your data, for compliance, cost, or model-preference reasons, you can connect your own provider to a profile.

## Supported providers
You can choose from:
- **Use TagoIO default**: TagoIO handles the model and provider. No configuration needed.
- **OpenAI**
- **Anthropic**
- **AWS Bedrock**
- **Google Gemini**
- **OpenRouter**
## Connect your provider
The setting is per profile, so different profiles in your account can use different providers or stay on the TagoIO default.
:::warning
Changing or removing a provider resets model selections that the new provider cannot serve. This affects the defaults for chat, AI Dashboards, and TagoRUN.
Record the current model settings before switching providers. After saving the new provider, review the model settings for all three features.
:::
1. Go to **Profile Settings → Services → AI Provider**.
2. Pick a provider from the **Select Provider** dropdown.
3. Create the provider secret. The Admin displays the credential fields required by the selected provider.
4. Complete the required fields and save the secret.
5. Save the AI Provider settings.
6. Select a model and send a test request to verify the connection.
From that point on, requests to the models of your provider go through your account with it. The free model is the one exception: it always runs on the TagoIO side, whatever provider is connected.
## Model selection with your own provider
The model picker only shows models available on your connected provider.
You can also register a custom model by the model ID expected by the connected provider. TagoIO validates the model when you save it: a successful validation confirms that TagoIO can identify the model through the configured provider. It does not confirm that every future request will succeed, because provider quotas, permissions, availability, and rate limits can change.
## How your keys are stored
Provider credentials are stored using [TagoIO Secrets](/docs/tagoio/security/secrets.md) and are not displayed again in the Admin after they are saved.
## Request limits and billing
When a profile uses its own AI provider:
- Requests go through your provider account and are billed by that provider directly.
- Requests never spend [AI Credits](/docs/tagoio/tago-ai/ai-credits.md), including requests that would otherwise use a catalog model.
- The profile's [TagoAI request limits](/docs/tagoio/profiles/services/tago-ai-service.md) do not apply: you manage rate limits and quotas with your provider. The free model is the exception: it always runs on the TagoIO side and counts against its monthly allowance.
When a profile uses **Use TagoIO default**, the free model costs nothing and catalog models from the [AI model catalog](/docs/tagoio/tago-ai/ai-models.md) spend AI Credits.
## Switching back to the default
To stop using your own provider, open the same settings page, select **Use TagoIO default**, and save. TagoAI will resume using the TagoIO-managed provider on the next request. The switching warning above applies here too: model selections your provider was serving are reset.
---
## AI usage statistics
AI usage statistics show what your account spends on AI and how many requests it makes. You can read them for the whole account or for a single profile, and break them down by model over any period within the available [time windows](#select-a-reporting-period).

Usage covers every AI request, not only the paid ones. Free and own-provider requests appear in the request counts with a spend of zero, so you can see total activity next to what it costs.
## What you can see
- **Summary**: total spend and total requests for the selected period.
- **Activity over time**: spend and requests per hour, day, or month, so you can spot when usage grew.
- **By model**: spend and requests per model, which shows where the money goes.
Statistics report **spend and request counts only**. Token counts are not exposed. To reason about token cost, use the per 1M token rates in the [model catalog](/docs/tagoio/tago-ai/ai-models.md).
A request appears in the statistics when it finishes, and the wallet debit for a catalog model lands at the same time. A request that was interrupted or failed partway is still recorded, with the tokens it actually consumed.
## Filters
Use the filters to answer different questions:
- **Profile**: which profile generated the usage?
- **Model**: which model received the requests?
- **Billing type**: was the request free, charged to AI Credits, or sent to your own provider?
- **Surface**: did the request come from chat, AI Dashboard generation, or AI Chat in TagoRUN? The dashboard surface counts generate and refine requests only; loading data on a dashboard is not an AI request.
## Select a reporting period
The maximum range depends on the selected periodicity:
| Periodicity | Maximum range |
| ----------- | ------------- |
| Hour | 1 month |
| Day | 3 months |
| Month | 12 months |
Ranges are measured in whole calendar months rather than fixed day counts. Asking for a longer range than the periodicity allows returns an error naming the limit, so switch to a coarser periodicity to cover more time.
To request a custom range, set both the start and end dates. If either date is missing, the statistics use the default range for the selected periodicity.
### Default ranges
When no dates are selected, the range ends at the current moment:
- Hourly statistics cover the previous 24 hours.
- Daily statistics cover the previous 3 calendar months: the range starts on the same day of the month, three months earlier.
- Monthly statistics cover the previous 12 calendar months, on the same rule.
On August 27, for example, daily statistics with no dates cover May 27 up to the current moment. The current hour, day, or month can contain partial data.
## Compare usage with wallet history
On TagoIO Cloud, the usage statistics explain where [AI Credits](/docs/tagoio/tago-ai/ai-credits.md) were spent. The wallet history explains how credits entered or left the wallet.
Use:
- **Usage statistics** to review requests and spending by profile, model, and feature.
- **Wallet history** to review purchases, expirations, support adjustments, and spending debits.
On TagoDeploy instances, spend is a meter rather than a debit: it reports the catalog value of paid-model usage, but no credits exist and TagoIO charges nothing for it. See [TagoDeploy behavior](/docs/tagoio/tago-ai/ai-credits.md#tagodeploy-behavior).
---
## TagoAI Chat
Use TagoAI Chat to ask questions about your TagoIO resources, investigate problems, and create or edit resources from the Admin. Before sending a message, choose the model and the permission mode appropriate for the task.
This page covers the chat in the Admin. For the assistant your portal users see, read [AI Chat in TagoRUN](/docs/tagoio/tago-ai/tagorun-ai-chat.md).
## Starting a chat
Open TagoAI from the star icon in the sidebar, or from the top bar of the [Analysis](/docs/tagoio/analysis/) and [Dashboards](/docs/tagoio/dashboards/) pages. You can start a new chat at any time using the **New Chat** button.
Each chat keeps its own history, so you can split unrelated tasks into separate conversations and come back to them later.
## Choose a model
The selected model determines the capabilities and billing path used by the chat:
- The free model uses the profile's monthly [free model allowance](/docs/tagoio/profiles/services/tago-ai-service.md).
- A TagoIO catalog model uses [AI Credits](/docs/tagoio/tago-ai/ai-credits.md).
- A model from a [connected provider](/docs/tagoio/tago-ai/ai-provider.md) is billed by that provider.
Some models also let you choose a [reasoning effort](/docs/tagoio/tago-ai/ai-models.md#reasoning-effort). Higher levels can improve results on complex tasks but can increase response time and usage.
New chats start with the profile's configured default chat model. You can select another model if it is available in the profile's [chat model menu](/docs/tagoio/tago-ai/features.md#control-the-chat-model-menu).
## Page context
When you open TagoAI from a supported Admin page, it receives context about the resource currently open. For example:
- From a dashboard, it can identify the dashboard you are viewing.
- From an Analysis page, it can inspect the script currently open.
This lets you ask questions such as "why is this widget empty?" or "refactor this function to handle missing payloads" without manually identifying the resource.
## Expanding and retracting the panel
The chat panel sits on the right side of the Admin. Use the expand control in the top-right corner of the panel to give the chat more room, or collapse it when you need the full screen for your work.
## Chat history
TagoAI keeps your previous chats so you can revisit what you asked before. The history panel groups chats by recency and lets you search across them.
Chat history is capped at **20 chats per profile**. When you reach the limit, the oldest chat is deleted first as new ones are created. If you want to preserve a conversation long-term, copy the relevant parts out before it rolls off.
## Modes
TagoAI has three modes that control what it is allowed to do on your platform. It opens in **Edit Mode**. You can switch modes from the bottom of the chat panel before sending a message.
:::warning
In Edit Mode and Full Access, TagoAI can apply changes without asking you to confirm each action. Use Read Only first when you want to review the proposed approach before allowing changes.
:::
### Read Only
Read Only mode only fetches information. TagoAI can list your devices, inspect dashboards, review Analysis scripts, and read data, but it will not create or change anything.
Use Read Only when you are exploring, asking questions, or want to be sure the assistant will not touch your resources.
### Edit Mode
Edit Mode allows TagoAI to edit existing resources and create new ones. It can draft an Analysis script, build a dashboard, register a device, or update an Action. Each action it takes appears in the conversation.
Edit Mode does not delete anything. It is the recommended mode for most day-to-day work.
### Full Access
Full Access adds deletion to what Edit Mode already allows. TagoAI can remove devices, dashboards, analyses, and other resources as part of completing a task.
:::warning
Full Access can delete resources in your profile. Deletions in TagoIO are not recoverable from the Admin, so only use Full Access when you understand what you are asking for and have a recent [backup](/docs/tagoio/my-account/backups.md) if the data matters.
:::
## Tips for better results
- Be specific about the device, dashboard, or analysis you want to act on. Names and IDs help.
- Use Read Only to ask for a proposed plan. Switch to Edit Mode only after you have reviewed the plan.
- Keep one task per chat so the history stays useful when you come back.
---
## TagoAI Features
TagoAI powers AI features across TagoIO. This page covers each feature, how requests are billed, and how to set the default model each feature runs on.
## AI features
| Feature | Where it appears | Who selects the model | Main purpose |
| ------------------ | ------------------------------------------- | --------------------------------------------------- | -------------------------------------------- |
| Chat | TagoIO Admin | The chat user, within the profile's chat model menu | Ask questions and work with TagoIO resources |
| AI Dashboards | Dashboard creation and editing in the Admin | Profile default | Generate and refine dashboard layouts |
| AI Chat in TagoRUN | TagoRUN portal | Profile default | Read-only assistant for your portal users |
### Chat
[Chat](/docs/tagoio/tago-ai/chat.md) is the assistant panel in the Admin console, and it is available only there; RUN users use [AI Chat in TagoRUN](/docs/tagoio/tago-ai/tagorun-ai-chat.md). It answers questions about TagoIO and acts on your resources: inspecting devices, building dashboards, writing Analysis code. You pick a model and reasoning effort per chat, and control what it may change with the Read Only, Edit Mode, and Full Access modes.
### AI Dashboards
[AI Dashboards](/docs/tagoio/dashboards/ai-dashboard/) generate a dashboard layout from a natural language description and the devices and variables available to the profile.
AI Dashboards use a full-page generated layout rather than the configurable widget grid used by Normal and Blueprint dashboards. The feature is experimental and is not supported in TagoRUN. See [Access and Limits](/docs/tagoio/dashboards/ai-dashboard/access-and-limits.md).
### AI Chat in TagoRUN
[AI Chat in TagoRUN](/docs/tagoio/tago-ai/tagorun-ai-chat.md) is the assistant inside the profile's TagoRUN portal, for your RUN users. You turn it on per portal, give it a system prompt about your application, and decide through Access Management which users get it and which devices, entities, dashboards, users, and TagoSQL queries it may read for them. It is read-only. It runs on the model configured for TagoRUN on the profile, or the free model when none is configured, and RUN users cannot change it.
## How feature requests are billed
Billing depends on the model used, not on the feature:
- The free model uses the profile's monthly [free model allowance](/docs/tagoio/profiles/services/tago-ai-service.md). TagoRUN chats draw from a separate allowance of the same size, so portal traffic never consumes the Admin's requests.
- TagoIO catalog models use [AI Credits](/docs/tagoio/tago-ai/ai-credits.md).
- Models from a [connected provider](/docs/tagoio/tago-ai/ai-provider.md) are billed by that provider.
All requests appear in the [AI usage statistics](/docs/tagoio/tago-ai/ai-usage.md), identified by feature.
## When a request is refused
The reason depends on the selected model:
- **Free model**: the profile has no free model requests remaining in the pool the request draws from, or is temporarily rate limited. Admin requests draw from the profile allowance; TagoRUN chats draw from the separate TagoRUN AI requests allowance, and a portal user sees the code RA07 when it is exhausted.
- **TagoIO catalog model**: the account has insufficient AI Credits, or the profile cannot spend more under its monthly [spend cap](/docs/tagoio/tago-ai/ai-credits.md#set-a-monthly-profile-spend-cap). A portal user sees the code RA05.
- **Connected-provider model**: the provider rejects the request because of credentials, availability, quotas, or rate limits.
Disabling AI on the profile prevents all TagoAI features from sending new requests.
## Configure default models
Go to **Profile Settings → AI models** to select a default model and reasoning effort for:
- Chat.
- AI Dashboard generation.
- TagoRUN.

A new chat starts with the chat default, but the chat user can choose another model when the profile's chat menu allows it. AI Dashboard and TagoRUN requests always run on their configured defaults; they cannot be overridden per request.
## Control the chat model menu
By default, chat users can select any model available to the profile. Turn on **Curate the menu** to limit the models and [reasoning effort](/docs/tagoio/tago-ai/ai-models.md#reasoning-effort) levels they can select.
For example, you can allow the free model and one catalog model while hiding more expensive models from chat users.

Menu curation affects chat only. It does not change the default models used by AI Dashboards or TagoRUN. On a profile with its own AI provider, the menu changes; see [Model selection with your own provider](/docs/tagoio/tago-ai/ai-provider.md#model-selection-with-your-own-provider).
---
## TagoAI
TagoAI adds AI assistance to the TagoIO Admin, [AI Dashboards](/docs/tagoio/dashboards/ai-dashboard/), and TagoRUN portals. It can answer questions about TagoIO, inspect your resources, generate Analysis code, and create or edit platform resources according to the permissions you select.
Use this section to configure AI for a profile, choose models, manage costs, and work safely with the assistant.
## Choose what you want to do
- [Use TagoAI Chat](/docs/tagoio/tago-ai/chat.md) to ask questions or work with resources in the Admin.
- [Review TagoAI features](/docs/tagoio/tago-ai/features.md) to configure Chat, AI Dashboards, and TagoRUN.
- [Set up AI Chat in TagoRUN](/docs/tagoio/tago-ai/tagorun-ai-chat.md) for your portal users, and [write its system prompt](/docs/tagoio/tago-ai/tagorun-system-prompt.md).
- [Choose an AI model](/docs/tagoio/tago-ai/ai-models.md) and understand how requests are billed.
- [Manage AI Credits](/docs/tagoio/tago-ai/ai-credits.md) for TagoIO catalog models.
- [Connect your own AI provider](/docs/tagoio/tago-ai/ai-provider.md).
- [Review AI usage](/docs/tagoio/tago-ai/ai-usage.md) by profile, model, and feature.
## Work safely with TagoAI
TagoAI can inspect, create, edit, or delete resources depending on the selected mode.
Start in Read Only when you want TagoAI to investigate a problem or propose a plan. Edit Mode can apply changes directly, and Full Access can delete resources. Review the [TagoAI Chat modes](/docs/tagoio/tago-ai/chat.md#modes) before allowing changes.
TagoAI acts within your own account permissions. It cannot access or change anything your user could not already reach on its own, and it cannot grant itself more access than you have.
## Disabling TagoAI
TagoAI is controlled per profile. An account can have multiple profiles, and each one is enabled or disabled on its own, so turning it on or off in one profile does not affect the others.
When it is disabled, the assistant does not appear in that profile and no data is sent to any AI provider. On profiles in the EU region, it is off by default.
You will find the toggle to enable or disable TagoAI under **Profile Settings → Services → AI Provider**.
## Security
**Action logging:** Actions TagoAI takes on your account are recorded with the account that ran them, the time, and what changed, so you can review what happened after the fact. Read more in [Audit Log](/docs/tagoio/profiles/audit-log.md).
**On-screen data is context, not commands:** When you open TagoAI from a page, it reads what is on that screen to understand your question. It is built to treat that content as data to reason about, not as instructions to follow. Even so, when a task involves content you do not control, such as device payloads from the field, run it in Read Only first and review what the assistant proposes before switching to a mode that can make changes.
**Review what it produces:** TagoAI can write Analysis code and build resources for you. You are responsible for reviewing its output before you rely on it. Read generated code and check configuration the way you would review a change from a teammate, especially before running it in production.
## Privacy and data handling
**What TagoIO stores:** TagoIO keeps your chat history so you can revisit previous conversations; it is capped per profile and the oldest chats are deleted as new ones are created (see [Chat history](/docs/tagoio/tago-ai/chat.md#chat-history)). Actions the assistant takes are recorded in the [Audit Log](/docs/tagoio/profiles/audit-log.md).
**What model providers see:** By default, TagoIO selects the model and provider that meet the privacy and security evaluations we have in place. The providers we use operate under agreements that prevent your data from being used to train their models.
If you want direct control over which model and provider process your data, you can configure your own AI provider for a profile. Your provider keys are stored using [TagoIO Secrets](/docs/tagoio/security/secrets.md) and are not displayed again in the Admin after they are saved. When you bring your own provider, your data is processed under your own agreement with that provider, and its data-handling terms are your responsibility. See [Using your own AI provider](/docs/tagoio/tago-ai/ai-provider.md).
No data is sent to any AI provider while TagoAI stays disabled on a profile (see [Disabling TagoAI](#disabling-tagoai)). TagoAI is identified as an AI assistant wherever it appears in the Admin.
---
## TagoIO MCP
The TagoIO MCP Server bridges AI‑powered development tools with your TagoIO IoT platform, enabling intelligent workflows that understand and interact with your real device data, configurations, and platform resources.

## What is a Model Context Protocol (MCP)?
Model Context Protocol (MCP) is an open standard that connects AI assistants to external data sources and tools. When integrated with TagoIO, it allows AI models in your development environment to directly access your IoT data, analyze patterns, generate code, and provide context‑aware assistance based on your actual platform configuration.

## Core capabilities
Core capabilities of the MCP Server include:
### Device Management & Data Access
The MCP server provides access to the following parts of your TagoIO environment, subject to the token's permissions:
- Query device configurations, metadata, and real-time sensor readings
- Access historical data from both Devices and Entities
- Retrieve user information, actions, and analysis scripts
- Generate statistical reports with aggregations, trends, and custom analytics
### Intelligent Development Support
Beyond data access, the integration enhances your development workflow:
- Context-Aware Code Generation: AI generates TagoIO Analysis - scripts with knowledge of your actual device structure and data formats
- Smart Debugging: Identify tag relationships, data dependencies, and configuration issues
- Automated Documentation: Generate reports and documentation based on your platform's current state
### Platform Integration
The TagoIO MCP Server works with popular AI platforms and development environments:
- AI Platforms: Claude Desktop, ChatGPT, and other MCP-compatible assistants
- Development IDEs: Cursor, Windsurf, VS Code with AI extensions
The MCP Server works with popular AI platforms such as Claude Desktop, ChatGPT, and other MCP-compatible assistants, and with IDEs like Cursor, Windsurf, and VS Code with AI extensions.
## Installation and Setup
For complete installation instructions and configuration options, see the [TagoIO MCP Server GitHub repository](https://github.com/tago-io/mcp-server).
## Real-World Use Case
### For IoT Application Developers
#### Rapid Prototyping
Generate boilerplate code for new Analysis scripts with proper error handling and TagoIO-specific patterns:
> "Create an Analysis script that monitors temperature sensors and sends SMS alerts when readings exceed thresholds"
#### Data Pipeline Debugging
Trace data flow issues across devices and scripts:
> "Show me all devices with tag value TEST sending data to the 'temperature' variable in the last hour and identify any gaps"
#### Performance Optimization
Analyze resource usage and optimize data retention strategies:
> "Which devices are consuming the most data transactions this month?"
### For Platform Administrators
#### Tag Monitoring
Identify devices with data transmission issues:
> "List all devices that haven't sent data in the last 24 hours"
#### User Management
Track platform access and new user activity:
> "Show me all users created in the past week"
### For Data Analysts
#### Trend Analysis
Perform complex queries across multiple data sources:
> "Compare energy consumption patterns between weekdays and weekends for all smart meters"
#### Anomaly Detection
Identify unusual patterns in sensor data:
> "Find all temperature spikes that deviate more than 2 standard deviations from the weekly average"
---
## AI Chat in TagoRUN
AI Chat lets TagoRUN users ask questions about your application and interact with the resources you authorize. You configure the assistant's model, application instructions, suggested questions, and permissions.
Enabling AI Chat does not grant access to your devices or other resources. A user needs permission to use the chat, and the assistant needs additional permissions before it can retrieve application data. The chat is read-only: the assistant can look things up and summarize, and it cannot create, change, or delete anything.

## Before you start
Prepare:
- A TagoRUN application and a RUN user for testing.
- Access to the application's TagoRUN settings, profile settings, and Access Management.
- AI enabled for the profile. See [TagoAI](/docs/tagoio/tago-ai/).
- A test device with known data if you want to verify device access.
Start with a test user before granting AI access to a larger audience.
## Choose the TagoRUN model
1. Open **Profile Settings → AI models**.
2. Under **Default models**, locate **TagoRUN**.
3. Select a **Model**.
4. Select an **Effort** level supported by that model.
5. Click **Save**.
This selection applies to chats through the profile's TagoRUN application. RUN users do not select their own model.
When no model is selected, chats run on the free model. The free model has its own monthly allowance of **TagoRUN AI requests**, separate from the allowance used by the Admin chat, so portal traffic never consumes your own requests. The numbers per plan are in [TagoAI Service](/docs/tagoio/profiles/services/tago-ai-service.md#monthly-free-model-allowance).
Model selection also determines how requests are billed. Review [AI models and pricing](/docs/tagoio/tago-ai/ai-models.md) before making the assistant available to users.

## Give the assistant application context
1. Open **TagoRUN → AI**.
2. In **System Prompt**, describe your application and how the assistant should help its users.
3. Click **Save** beside the system prompt.
Include information such as:
- What the application does.
- What device names, variables, and statuses mean.
- How to explain readings and answer common questions.
- Relevant instructions from your application documentation.
- How to respond when information is missing or an operation is unavailable.
For example, explain whether `battery_level` refers to a tracking device's battery or a vehicle's battery. The assistant should not have to infer application-specific meanings from variable names.
The system prompt guides responses. It does not grant access to resources. See [Writing a system prompt for TagoRUN AI](/docs/tagoio/tago-ai/tagorun-system-prompt.md).
## Grant permission to use AI Chat
Create a policy for the users who should have access.
1. Open **Access Management** and click **Add Policy**.
2. Enter a descriptive policy name.
3. Under **Targets**, select **Run User**.
4. For the initial test, match your test user by **ID**.
5. Add an **Allow** permission for **AI**.
6. Under **WITH RULES**, select **Access**.
7. Save the policy.
The **Access** rule allows the targeted user to use AI Chat, and it is what makes the chat appear in the portal for that user. By itself, it does not authorize the assistant to read device data.
For a larger audience, use the appropriate target selection described in [Defining Targets](/docs/tagoio/tagorun/access-management/defining-targets.md).
## Allow access to application data
Choose the access approach that fits the information the assistant needs:
| Approach | Use it when |
| ---------------------- | ------------------------------------------------------------------------------- |
| Direct resource access | The assistant may access the selected resources through the allowed AI actions. |
| A saved TagoSQL query | You need to control the variables, rows, or fields returned to the assistant. |
### Example: allow the assistant to read a device
For direct device reading, configure both the AI action and the device permission.
In the policy for your test user:
1. In the **AI** permission, keep **Access** selected and add **Read device data**.
2. Add another **Allow** permission for **Device**.
3. Select the device rule **AI access**.
4. Restrict the permission to the test device.
5. Save the policy.
The two permissions serve different purposes:
| Permission | Purpose |
| ------------------------- | ------------------------------------------------------------------- |
| **AI → Read device data** | Allows the assistant to perform device-reading operations. |
| **Device → AI access** | Makes the selected device available to the assistant for that user. |
The same pattern applies to entities (**Read entity data** with **Entity → AI access**), dashboards (**Read dashboards** with **Dashboard → AI access**), and users (**Read users** with **Run User → AI access**).
The device's **Dashboard access** rule is separate from **AI access**. Making a device available in a dashboard does not make it available to AI.

:::warning
If the assistant should see only selected variables or fields, use a restricted TagoSQL query instead of also granting broad direct-device AI access. Query restrictions do not narrow a separate direct-device permission.
:::
See [Restrict AI results with a saved TagoSQL query](/docs/tagoio/tagosql/session-context.md#restrict-ai-results-with-a-saved-tagosql-query) for queries that return different results for each RUN user.
## Add suggested questions
Suggestions give users a starting point for a conversation. Tapping one sends its prompt as the first message of a new chat.
Configure them under **TagoRUN → AI → Suggestions**, separately from the profile's model settings.
1. Add a suggestion.
2. Choose an icon and enter a short label.
3. Enter the question or instruction associated with the suggestion.
4. Configure visibility conditions if the suggestion is intended for a specific audience.
5. Save the suggestions.
For example:
| Label | Suggested question |
| ---------------------- | ---------------------------------------------------------- |
| Battery status | Show the latest available battery readings for my devices. |
| Understanding readings | What does battery_level mean in this application? |
Only offer data-related suggestions to audiences whose policies support the requested operation.
### Show suggestions to selected users
Use **Visibility Conditions** to match user tags.
For example, a condition with tag key `user_level` and tag value `supervisor` can target a suggestion to that audience. These are example tag values, not built-in roles.
Add the condition, click **Confirm**, and save the suggestions. A suggestion with several conditions is shown when at least one of them matches a tag on the user.
Visibility conditions control which suggestions appear. They do not grant permission to perform the suggested action. Configure authorization through Access Management even when a suggestion is hidden from other users.

## Enable and test AI Chat
1. Open **TagoRUN → AI** and enable AI Chat.
2. Sign in to the TagoRUN application as your test user.
3. Select the star-shaped AI icon in the sidebar to open the chat.
4. Ask a question covered by your system prompt.
5. If you configured device access, request a reading from the test device.
Settings take effect on the next message. No deployment step is needed, and users do not need to sign out.
Check both allowed and restricted requests:
| Test | What to verify |
| -------------------------------------------------------------- | ------------------------------------------------------------------------ |
| Ask about an application term explained in the system prompt. | The answer matches your documentation. |
| Request data from an authorized device. | The answer matches the stored reading, including its unit and timestamp. |
| Request data from a device without AI access. | The assistant does not retrieve that device's data. |
| Sign in as a user outside a suggestion's visibility condition. | The suggestion is not displayed. |
Use a RUN user for these tests rather than relying only on an administrator's view.
## Troubleshooting
| Symptom | What to check |
| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| The user cannot access AI Chat. | Check that AI Chat is enabled, the user is signed in and not anonymous, and an applicable policy grants **AI → Access**. |
| Chat works, but device data is unavailable. | Check **AI → Read device data**, **Device → AI access**, and the policy's user and device selections. |
| A suggestion is missing. | Check its visibility conditions against the user's tags. |
| The assistant misunderstands application terms. | Add the missing definitions or examples to the system prompt. |
| Requests are refused regardless of the question. | The error code says why: RA07 means the profile used its monthly TagoRUN AI requests on the free model; RA05 means AI Credits or the spend cap block the request. See [TagoAI Features](/docs/tagoio/tago-ai/features.md#when-a-request-is-refused). |
---
## Writing a system prompt for TagoRUN AI
The system prompt gives the TagoRUN assistant application knowledge and instructions for responding to users.
Use it to explain your application's terminology, describe supported workflows, and provide documentation the assistant can use in its answers.
Configure it under **TagoRUN → AI → System Prompt**, then click **Save**. The field accepts up to 30,000 characters, and users never see it.
TagoIO adds its own instructions before yours. The assistant already knows it is the assistant of a portal, treats the session as read-only, never names the platform or shows internal identifiers, and reports data it was not granted as unavailable. Your prompt can set its name, tone, and domain and add any knowledge you want; it cannot override those rules.
## What to include
Write instructions that help the assistant answer the questions your users actually ask.
### Application context
Describe:
- What the application monitors or manages.
- Who uses it and what they need to accomplish.
- How users refer to devices, equipment, locations, and other resources.
- What important variables and statuses mean.
Be specific about names that can be misunderstood. For example, explain whether a "device" represents a sensor, a gateway, or an entire machine.
### Response guidelines
Specify how the assistant should communicate:
- The language and level of technical detail appropriate for your users.
- When to ask a clarifying question.
- When to include units, timestamps, or the requested time range.
- How to explain missing data or unavailable operations.
- Whether to summarize first and provide details afterward.
Prefer concrete instructions such as "include the measurement timestamp" over broad instructions such as "always be accurate."
### Application documentation
Paste relevant excerpts from your documentation, such as:
- Definitions of measurements and statuses.
- Instructions for common tasks.
- Explanations of application-specific calculations.
- Approved troubleshooting procedures.
- Guidance on when to contact an application administrator.
Give each excerpt a descriptive heading. Include the information needed to answer the question directly rather than assuming that a documentation URL makes its contents available to the assistant.
Only include procedures and capabilities that your application actually supports.
:::warning
Do not put passwords, tokens, provider credentials, or customer-confidential records in the system prompt. Include only reference material appropriate for the assistant's audience.
Use Access Management and restricted queries to enforce data access. Instructions in the system prompt are not a substitute for authorization.
:::
## Example: a fleet-monitoring assistant
The following example describes a sample application. Replace its terminology and procedures with your own before using it.
```text
You assist users of Fleet Monitor, an application for monitoring tracking
devices installed in vehicles.
Application context
- Users may refer to a tracking device as a tracker or a device.
- battery_level describes the tracker's battery, not the vehicle's battery.
- A stored reading describes a measurement at its recorded time. It does
not necessarily describe the device's condition right now.
How to respond
- Use clear, concise language suitable for fleet operators.
- Answer the user's question first, then add supporting details.
- For data-based answers, include the device name, measurement unit when
available, and measurement timestamp.
- Ask for clarification when the requested device or time period is
ambiguous and that information is needed to answer.
- Do not invent readings, units, thresholds, device names, or completed
actions.
- If a request cannot be completed, explain what information or access is
missing when that is known.
- Do not describe missing data as a zero reading.
- Do not claim that an operation succeeded without a successful result.
Application reference: battery readings
battery_level reports the battery measurement received from the tracker.
Use the unit returned with the reading. Do not assume a percentage when
the unit is unavailable.
Application reference: checking a battery reading
Check the timestamp before interpreting a battery measurement. If no
recent measurement is available, explain that the current battery
condition cannot be determined from the available data.
Application reference: questions outside this documentation
If the application reference does not explain a procedure, say that the
procedure is not documented here. Ask the user to contact their
application administrator rather than inventing navigation steps.
```
These instructions guide the assistant's behavior. Test the resulting answers rather than treating the prompt as a guarantee.
## Keep instructions separate from permissions
Describe how the assistant should handle information it is allowed to retrieve.
Do not rely on instructions such as "only show the current customer's data" to implement customer isolation. Enforce that restriction through resource permissions or the SQL of an authorized saved query.
Similarly, a prompt that mentions a device or dashboard does not make that resource available to the assistant.
## Test the prompt
Start with a user who has chat access but no data-reading permissions.
Ask questions that the application reference should answer, such as:
- "What does battery_level mean?"
- "Does a missing reading mean the battery is empty?"
- "How do I know whether a reading is recent?"
Then test with a user who has the intended data permissions:
- Compare a data-based answer with the actual stored values.
- Ask an ambiguous question and check whether clarification is useful.
- Ask about an unsupported procedure and check that the assistant does not invent steps.
- Ask for an unauthorized resource and verify that the request does not retrieve its data.
Revise the prompt when the assistant lacks application context, uses the wrong terminology, or gives more detail than the audience needs. Changes apply to the next message sent.
## Maintain the application reference
Keep a copy of the system prompt with your application documentation.
Review it when variable meanings, workflows, business rules, or support procedures change. Remove obsolete instructions instead of adding conflicting corrections at the end.
Include enough context to answer common questions, and remove material that does not help users complete a supported task.
---
## Creating a Policy
You can create individual policies to securely grant access to specific resources in your account for pre-defined targets.
You create [Targets](/docs/tagoio/tagorun/access-management/defining-targets.md) and decide which resource types they can access by assigning [Permissions](/docs/tagoio/tagorun/access-management/defining-permissions.md).
To do this, navigate to the [Access module](https://admin.tago.io/am) and click **Add Policy**.

---
## Defining Permissions
You need to set up Permissions to define which types of resources your Targets will be allowed or denied access to.
The resources available to grant permission depend on the Target you select, and the rules available depend on the resource selected. For more information, see [Defining Targets](/docs/tagoio/tagorun/access-management/defining-targets.md).

## Adding resources and how rules are applied
You can add as many resources as needed. An OR operation is applied between permissions: as you add more permissions, more resources will be included in the verification process.
:::warning
Denial permissions will override any permissions granting access to the same resource.
:::
## AI
The **AI** resource controls the [AI Chat in TagoRUN](/docs/tagoio/tago-ai/tagorun-ai-chat.md) for Run user Targets. It has no individual resources to match, so its permission always applies to the whole feature. Its rules are:
| Rule | What it grants |
| -------------------- | ------------------------------------------------------------------------ |
| **Access** | The user sees and can use the AI chat. Required for every other rule. |
| **Read device data** | The assistant can list devices and read their data on the user's behalf. |
| **Read entity data** | The assistant can read entity schemas and data on the user's behalf. |
| **Read dashboards** | The assistant can list dashboards on the user's behalf. |
| **Read users** | The assistant can list TagoRUN users on the user's behalf. |
The read rules say what kind of resource the assistant may read. Which resources it may read is decided by the **AI access** rule on the resource permission itself.
### AI access on resources
**Device**, **Entity**, **Dashboard**, **Run User**, and **SQL Query** permissions for Run user Targets have an **AI access** rule next to their existing rules. It marks the matched resources as readable by the assistant for the targeted users, and matches by ID, tag, or Any like any other rule.
**AI access** is separate from the rules the portal uses. **Dashboard access** on a device lets the user see it in dashboards and says nothing about the assistant. Grant both when the user should have both.
A complete grant to read device data therefore needs two permissions: **AI** with **Access** and **Read device data**, and **Device** with **AI access** on the devices in question. A grant missing either half reads nothing.
## SQL Queries
The **SQL Query** resource grants actions on [TagoSQL stored queries](/docs/tagoio/tagosql/). Run user Targets can be granted **Access** (list and view queries), **Execute** (run them), and **AI access** (let the AI chat discover and run them on the user's behalf); analysis Targets can additionally be granted **Create**, **Edit**, and **Delete**.
:::warning
Granting execution of a query grants its full result set. The policy decides **which** queries a caller may run, never which rows or columns come back.
:::
To scope what each Run user sees within a single query, see [Session Context](/docs/tagoio/tagosql/session-context). The same scoping applies when the AI chat runs the query; see [Restrict AI results with a saved TagoSQL query](/docs/tagoio/tagosql/session-context.md#restrict-ai-results-with-a-saved-tagosql-query).
---
## Defining Targets
You can define multiple **Targets** in each policy that are used inside the [Access Management](https://admin.tago.io/am) module.
:::tip
Targets are to whom or to what you are giving the permissions.
:::
Currently, you can select [Run Users](/docs/tagoio/profiles/services/end-users-service.md) and [Analysis](/docs/tagoio/analysis/) as Targets.

## How multiple Targets work
You can add more Targets of the same type as needed. Multiple Targets are combined using a logical OR, so adding more targets makes the resulting options more inclusive. For example:
- "Apply the permissions below for the users using this tag OR this tag OR ..."
## Target Matching Options
Selecting a Target can be performed in several ways:
1. **ID**: select a specific target from the list.
2. **Tag**: match using tags from your target. Learn more about the [Tags System](/docs/tagoio/devices/tags-system.md).
- Tags are composed of two fields: **Tag key** and **Tag value**.
- You can manually edit the tags for each user, or run [scripts](/docs/tagoio/analysis/) to set them. Learn more about [User Management](/docs/tagoio/tagorun/getting-started/user-management.md).
- For example, Joe Doe has a Tag key `user_level` and Tag value `supervisor`. If you create a policy with Targets using the same tags, the selected permissions will be granted for Joe Doe.
3. **Tag Match**: the match is done automatically by searching for those Tag Keys of the selected target.
- Run User: tag keys presented in your users will be listed.
- Analysis: tag keys presented in your [Analysis](/docs/tagoio/analysis/) will be listed.
:::tip
The **Tag Match** option reduces the number of policies you need to create, as you can create a single Policy that would grant access to all users or analysis that match the same tag keys.
:::
4. **ANY**: all will be automatically included.
---
## Access Management
Access Management (AM) lets you securely grant access to specific resources in your TagoIO account. You create [Targets](/docs/tagoio/tagorun/access-management/defining-targets.md) (Users or Analysis) and assign [Permissions](/docs/tagoio/tagorun/access-management/defining-permissions.md) that determine which resources those Targets can access.

## Examples
Some examples of how AM can be used:
1. Share one specific dashboard only with users that contain certain tags.
Example: Share the dashboard "Regional Sales View" with users who have the tag `level = supervisor`.
2. Share all dashboards with certain tags only with users that contain certain tags.
Example: Share dashboards that contain the tag `state = NY` or `state = MA` with users that have the tag `region = northeast`.
3. Share all my dashboards with all users.
For this, you just need to set the field in Targets like `Run User = Any`, and the `Permissions = Any` to share everything with anyone.
4. Allow an Analysis to do a bulk device upload.
Example: An Analysis with the tag `script = device_upload`, and the `Permission = device` with rules `Create` and with field `Any`.
5. Allow an Analysis to get data from specific devices.
Example: An Analysis with the tag `script = device_upload`, and the `Permission = device` with rules `Read` and with field `Tag` and the tag key `device_id` and the tag value `1234567890`.
_By using the option `Any` in the matching fields, you expose all of that resource without restrictions._
---
## Import & Export for Dictionary
The Dictionary provides a quick and easy way to import and export a Dictionary's key/value pairs via the two buttons at the top of a Dictionary page.
## Overview
All data in a Dictionary can be exported into a CSV or JSON file. Exported files are useful for:
- Editing in another program (such as Excel or another editor)
- Sending for translation to another language by keeping the keys and editing only the values
- Keeping a backup to revert any changes to a known state

:::info
The import and export functionality is available for users in the Scale plan.
:::
## Exporting a Dictionary
- Use the Export button at the top of the Dictionary page to download all key/value pairs as a CSV or JSON file.
- Choose CSV or JSON based on how you plan to edit or process the data.
## Importing a Dictionary
- A CSV or JSON file can be imported into a specific language in the Dictionary after being edited (e.g., translated).
- Importing will overwrite the values for all existing keys in the selected language.
- Any new keys and values present in the imported file will be added to the selected language.
## Behavior when adding new keys
- When you add new keys to a language via import, the Dictionary will also add those keys with empty values to all other languages in the Dictionary.
## CSV & JSON Samples
TagoIO provides sample CSV and JSON files accessible from the Import and Export buttons for convenience and reference. In the Dictionary UI the menu includes options such as:
- Import CSV
- Import JSON
- Get CSV sample
- Get JSON sample
### CSV and JSON file requirements
**CSV**
- Can optionally contain a header if the first row has _Key_ on the first column and _Value_ on the second column.
- Only the first two columns will be used; values in other columns are ignored.
- The key should follow the same format as on the TagoIO Admin: all caps, no spaces (use underscores), only alphanumeric characters.
- When using a text editor to edit the file, it's preferable to enclose each field in quotation marks (e.g. `"SOME_KEY","Value for the key"`). Quotation marks are required if the value contains a comma (e.g. `"SOME_KEY","Value for the key, extra content"`).
**JSON**
- Should be an array of objects containing a `key` and a `value` only; anything else is ignored.
- The key should follow the same format as on the TagoIO Admin: all caps, no spaces (use underscores), only alphanumeric characters.
---
## Using Dictionaries & Multi-language
Use the Dictionary to deploy applications to end-users (RUN users) in any language. All you have to do is to create a [dictionary](https://admin.tago.io/dictionaries), use the `#SLUG.KEY#` format in the texts that will be substituted, and then select the languages that should be available for your end-users.

End-users can select the language in their applications.
:::info
End-users are able to switch languages within their accounts. By default, the system will use the language identified by their browsers. If the language is not available in the application, the system will default to the **Fallback language**.
You can create multiple dictionaries and use them in your applications with the Free/Starter plans. However, to use multiple languages in your Run environment other than the **Fallback language**, you need to upgrade to the Scale plan. Check out our Pricing page [Pricing](https://tago.io/pricing).
:::
## Importing and exporting dictionaries
To make the process easier and faster, you can import and export dictionary files in any language. Learn more in the Import & Export for Dictionary documentation: [Import & Export for Dictionary](/docs/tagoio/tagorun/dictionaries/import-export-for-dictionary.md).
An auto‑complete feature is available to find Slugs and Keys as shown below.
## How to use dictionary keys
### 1) Create a Dictionary
First, create the Dictionary by clicking on the Dictionaries button in the sidebar menu in your Admin.
You will be asked to select a language that will be the first one made available; you may edit or add more languages later. You will also need to create a Slug for the dictionary which can also be edited later.
Populate the dictionary with Key & Value pairs for substitution, then save it. Example for English:
| Key | Value |
| ------ | ----------------------- |
| TITLE1 | Temperature Information |
| TITLE2 | Humidity Information |
| ... | ... |
Add another language (e.g., French) by changing the values:
| Key | Value |
| ------ | ------------------------------- |
| TITLE1 | Informations sur la température |
| TITLE2 | Informations d'humidité |
| ... | ... |
> Only UPPERCASE letters without a space are accepted as **Key**.
### 2) Use `#SLUG.KEY#` wherever you want the substitution to be applied
After creating the Slug and Keys, use them anywhere in your application.
For example, if you created a dictionary with a Slug `DIC1` and want to use the Key `TITLE1` as a widget title, input `#DIC1.TITLE1#`.
:::tip
Fields that accept Dictionary have auto‑complete enabled: click the icon on the right or type `#` to get the list of available Slugs and Keys.
:::
You can also substitute variable names, values, tooltips, messages sent by email, SMS, push notifications, etc.
Some widgets (e.g., **Map** and **Image Marker**) offer a _Custom Texts_ option where you can enter text directly or use `#` to substitute it with your dictionary.
### 3) Configure the language for your RUN
Dictionary can also be applied in your [RUN](/docs/tagoio/tagorun/) application so that you can configure additional texts used in common places (e.g., 'Save' button, sign-in form, tooltips).
If you haven't done so, create a dictionary to be used as a baseline by accessing the **[RUN] >> Main Configuration** section. The system can create a dictionary with all available Keys for editing or use it as a basis for other languages.

> This dictionary will also contain the Keys used in your TagoRUN mobile app.
### 4) Enable the languages
Make sure that the languages you want end-users to choose from are enabled so they can select them.
You can enable languages here: [Dictionaries](https://admin.tago.io/dictionaries).
:::tip
Enabling multiple non‑fallback languages for your Run environment requires the Scale plan.
:::
## Enable languages
- After creating dictionaries and translations, enable the languages you want end-users to choose from within your application.
- Note: enabling multiple non-fallback languages for your Run environment requires the Scale plan.
---
## Custom Settings
Custom Settings is a feature that lets you create unique visualization preferences for your end-users that extend beyond TagoRUN's default settings. After you configure custom settings, they will appear in your end-users' Account Settings.
With Custom Settings you can tailor preferences to specific needs: for example, selecting preferred units for visualizing temperature and distance, or choosing decimal, date, and time formats. These settings can be used together with [Dynamic Formulas](/docs/tagoio/widgets/general/formula.md) to ensure displayed data matches user expectations.

## How it works
To create your Custom Settings, first navigate to your [Run](https://admin.tago.io/run) and click on the Custom Settings tab. From there, you can configure the following fields:
**1. Name:** the name of the field that will be displayed for your end-users.
**2. Type:** determines the type of field that the user will interact with.
**3. Tooltip:** the text that will display when the user hovers over the field.
**4. Required:** if you set the field as required, the user will not be able to save without selecting a preference.
**5. Field Options:** allows you to specify the options that the user can select. You must define a Value to be used with Dynamic Formulas and a Label that will be displayed for your end-users. You can also set a value as Default, which will be applied to new users. At least one option must be set as the default.
Keep in mind that the Values set in the Field Options are supposed to be unique since they are used as identifiers.
After saving the options for a dropdown field, it's essential not to change the Value from Field Options. If you need to modify the Value or remove an option, you should update your users' preferences using the [SDK](/docs/tagoio/analysis/sdk/) or an [Analysis](/docs/tagoio/analysis/).
Keep in mind that editing the options for a field will not automatically update the preferences that your users have already saved.
Once you have created your Custom Fields and deployed your updated RUN, your users can select their preferences by navigating to the Visualization Preferences tab on their Account page.
## Configuring your Custom settings
After setting up your Custom Fields, the next step is to configure your widgets to use Dynamic Formulas. This feature allows you to apply formulas to your Custom Fields created through Custom Settings.
Read more about the [Dynamic Formulas](/docs/tagoio/widgets/general/formula.md).
---
## Dictionaries
The innovative dictionary feature from TagoIO enables you to improve your application by editing any displayed messages and providing your solution in multiple languages. Use dictionaries to substitute texts across dashboards, widgets, variables, and the RUN application (including the mobile app).

## Translate text messages to any language
Any text present in dashboard frames, inside widgets, variables, and your RUN application (including the mobile app) can be replaced using the dictionary. Create your own dictionary by editing fields (key & value) for each language you want to make available to end users.
Learn how to [set up the dictionary](/docs/tagoio/tagorun/dictionaries/using-dictionaries-multi-language.md).
## Customize texts for the same language
You can also customize text values for the same language to adapt wording for different dashboards, contexts, or user groups. For example, if you wish to change the default message shown in the tooltip **'Enter full screen'** in your RUN application, edit the value associated with the key `ENTER_FULLSCREEN` to something like _Click here to switch to full screen_.
Because each dictionary has a unique slug, you can substitute the same data with different texts using the format `#slug.key#`. For instance, show a title in a widget for a certain group of users using `#DIC1.TITLE#`, and another title for other users using `#DIC2.TITLE#`. In this case, the values associated with the key `TITLE` would be different for dictionaries `DIC1` and `DIC2`.
Refer to [Customize texts for the same language](/docs/tagoio/tagorun/dictionaries/using-dictionaries-multi-language.md) for details.
---
## End-User Agreements
End-User Agreements is a feature designed to help you manage and track legal agreements or terms of service that users must accept before accessing the platform. This feature ensures compliance and provides a transparent method for maintaining records of user consent. Once implemented, users are prompted to review and accept these agreements during their initial login or whenever new agreements are introduced.
## Creating Agreements
Use this section to create and configure end-user agreements (terms of service, privacy policies, etc.) that users must accept before using the platform. Creating an agreement typically involves providing the agreement text, configuring visibility, and setting when the agreement must be accepted (for example, at first login or when a new version is introduced).

**Steps to create a new agreement**
1. Navigate to the **[Run module](https://next-admin.test.us-e1.tago.io/run/agreements)** user agreements section.
2. Click on the **_+ New Agreement_** button.
3. Define the following fields:
- **Name:** Enter a descriptive name for the agreement.
- **Document Link:** Provide the URL where the full agreement text is hosted.
- **Checkbox Label:** Customize the text that appears next to the acceptance checkbox.

## Accepting Agreements
This section covers how end users are prompted to review and accept agreements during login and how accepted agreements are recorded for compliance and auditing purposes.
- End‑users encounter the agreement acceptance process in two scenarios:
1. During their first login to the platform.
2. When new agreements are added to the system.
- The agreement modal displays the terms that require acceptance. Users must check the customized checkbox to indicate their acceptance before they can proceed.
- If a user does not accept, they will be logged out.
## Updating Agreements
This section describes how to update existing agreements and how to require users to re‑accept when a new version is published.
- You can edit an existing agreement's **name** and **checkbox label**; however, the **document link cannot be changed**.
- To update the agreement text, create a new agreement with the revised terms. The system will automatically prompt existing users to accept the new agreement upon their next login.
- To disable an agreement, toggle off the **_Enable Agreement_** option. This prevents the agreement from appearing for end users.
- Previous agreements cannot be deleted; they remain in the system for record-keeping purposes.
## Monitoring Agreements
This section explains methods for tracking which users have accepted which agreements and when they accepted them, to maintain records of user consent.
### Global agreement overview
Navigate to the **Agreements tab** (Run module → Signup Page). Here you can view which agreements are active and their creation dates.
### Individual user agreement status
1. Navigate to the **Users module**, select a user, then go to the **Agreements tab**.
2. View information about all accepted agreements, including:
- Agreement name
- IP address
- User Agent
- Acceptance timestamp
---
## Language Preferences
The TagoIO admin interface used for developing applications is displayed only in English. However, users who access your RUN application can view it in any language you configure in your [Dictionary](/docs/tagoio/tagorun/getting-started/dictionaries.md). These users are referred to as "RUN users."
## Most commonly used languages
Some of the most commonly used languages for RUN applications include:
- English
- Spanish
- German
- French
- Italian
- Japanese
- Portuguese
## How end-users choose a language
:::info
Make sure that your current plan allows the deployment of multi-language for your end-users. See [pricing page](https://tago.io/pricing)
:::
---
## Navigation bar
The navigation bar is a section of your Run portal that lets end users easily navigate between pages and access their account information. The bar appears at the top of the Run portal sidebar for convenient visibility and can be customized to match your brand by adding your logo, choosing colors for the bar, text, and icons, and adding extra items to the user menu.
## Your logo & colors
You can add your company's logo by accessing the [Run module](https://admin.tago.io/run) and inserting the URL of your image in the **Main Configuration** section. This will apply the logo to the navigation bar and to signup pages. For more advanced customization, you can set a distinct logo for the navigation bar or implement dynamic logos based on [Run User Tags](/docs/tagoio/devices/tags-system.md)
### Color scheme
The color scheme for the bar, text, and icons can be configured in the **Theme** section of your Run. Discover more about [Run Themes](/docs/tagoio/tagorun/getting-started/run-theme.md) and their capabilities.
### Implementing dynamic logos
Create a personalized experience for different user groups or roles by displaying unique logos in the navigation bar with the help of the [Tags System](/docs/tagoio/devices/tags-system.md).
1. Navigate to the **Run module** → _Navigation Bar_ → _Navigation Bar Logo_ and enable the **Use dynamic logo** option.
2. A new **Tag Match** field will appear, allowing you to specify a [tag key](/docs/tagoio/devices/tags-system.md) that corresponds to the custom logo URL.
3. Existing end-user tags will be displayed here. If you haven't created a tag containing your logo image URL, you can do so by accessing the **User** module and adding the appropriate tag to your end-users.
4. For users without a matching tag key, the logo specified in the **Default Logo URL** field will be displayed for consistent branding across all user experiences.
### User menu customization
The user menu is available to signed-in users and works as a central hub for accessing their account settings, preferences, and signing out from the Run portal. You can configure it to have additional items, ranging from simple text (such as a version number for your application) to incorporating external links or links to specific dashboards within your application.
To add new items to your user menu:
1. Navigate to your **Run module** and select the **Navigation Bar** tab.
2. Click _Add new item_ and configure the following options:
- **Label:** the name of the option that will be displayed for your end‑users.
- **Icon:** choose the icon along with its color, which will be displayed next to the option.
- **Type:** determines the type of field the user will interact with.
- **Alignment:** choose the text alignment direction for the item.
- **Visibility options** (eye icon): allows you to restrict certain items to specific users using the [Tag System](/docs/tagoio/devices/tags-system.md) associated with the [Access Management](/docs/tagoio/tagorun/access-management/) feature.
Once you have configured your user menu and deployed your updated Run, your users can see the menu items that they have permission to view when signed in.
---
## Notifications for Users using Analysis
The Analysis feature is a core tool in TagoIO and supports sending Notifications to TagoRUN users. This article describes how to send a notification using Analysis, how to use notification buttons, and how to handle user decisions within your notifications. For more details about Analysis and notification buttons, see [Analysis](/docs/tagoio/analysis/).

## Customized Notifications
When sending a notification using Analysis, the primary interactive feature is buttons. You can include up to four buttons in a single notification. Each button can be configured to perform one of several behaviors:
- Redirect to a TagoIO page.
- Redirect to an external link.
- Trigger an Analysis when pressed.
You also have a few customizable options:
- Set the button label.
- Set the button color.
## How to Setup an Analysis
We provide you with an Analysis template that will have a code for a demonstration. The following analysis does send a notification to all your users with given tags.
Follow these instructions to set up your analysis and send the notifications. You can make any change to the analysis after it is installed in your account, to properly fit your needs.
1. Go to your account settings by clicking on the lower left corner of TagoIO page and enter [My Account](https://admin.tago.io/account/).
2. Select your Profile in the sidebar.
3. Go to the Tokens Section, create a new token with **Expire In** set to _1 year_ and copy the Token.
4. Install the [Analysis Template](http://admin.tago.io/template/5d51de52171477001b203458).
5. Go to the Environment Variables of your analysis.
6. Replace the field value `"Your Account Token Here"` with the token you copied in step 3.
7. Fill the field values for **tag key** and **tag value** to match at least one of the tags of your users in the User Management.
8. If you didn't set up a tag for any of your users, go to [User Management](https://admin.tago.io/usermanagement), enter one of your users, and create a tag key and value in the Tags section.
9. Press **Save** and **Run** to start your analysis and send a notification to your users.
With this all up and running, you can open the script of your analysis and start to change it as needed. The code is fully commented and detailed to guide you.
You can also create an [Action](/docs/tagoio/actions/) to trigger the Analysis or set it to run every X minutes.
:::tip
You can learn more about the notification object in the [Notification Object](https://js.sdk.tago.io/interfaces/NotificationCreate.html) SDK documentation.
:::
---
## Notifications for Users
You can push notification messages directly to the users registered in your TagoRUN application. This article explains how to send notifications to your users using Actions or Analysis.
Notifications for users are similar to your account's regular notifications, but they include additional customization options.
:::info
Users registered in your TagoRUN account can only receive notifications sent by your application.
:::
## How to send notifications
Before sending notifications, ensure you have users registered in your account's User Management.
There are two primary ways to send notifications to users in TagoRUN:
1. Notifications using Actions
See [Actions](/docs/tagoio/actions/) for details on configuring Actions to send user notifications.
2. Notification using Analysis
See [Analysis](/docs/tagoio/tagorun/getting-started/notifications-for-users-using-analysis.md) for details on sending notifications from an Analysis script.
> By pushing notifications using Analysis, you can add custom buttons allowing for more interaction.
---
## TagoRUN Overview
The Run module allows you to create and deploy your solution using a customized logo, colors, URL, signup page, email messages, and more.
Run works synchronized with the [Access](/docs/tagoio/tagorun/access-management/) and [User management](/docs/tagoio/tagorun/getting-started/user-management.md) modules. When you configure and deploy your solution, a domain will be created for your application so that users can sign up.

:::info
By default, your URL may be a subdomain of run.tago.io (for example, mycompany.tago.run) or a fully customized URL using your current company domain (for example, portal.mycompany.com).
:::
With Run you can set up three main things:
1. Your Run (Application portal)
2. Your Sign up page
3. E-mail templates
## Your Run (Application portal)
Go to the [Main Configuration](https://admin.tago.io/run) and Domain tabs to configure the title and subtitle of your application, add a favicon (browser icon) and logo, and edit the colors used in the navigation bar.
---
## Run Theme
You have the freedom to personalize the look and feel of the RUN interface by crafting your application with a unique color palette and custom assets. To get started, access the [Run module](https://admin.tago.io/am) and navigate to the Theme tab. The available customization options are described below.
## 1. Signup page
Within the Signup page, you can customize colors, the background, and the displayed logo. You may choose an image, a video, or a solid color for the background. Any adjustments applied to the Signup page will also extend to the Sign-in page.
:::tip
You can upload the image files for the background and logo on [TagoIO's file storage service](/docs/tagoio/files).
:::
## 2. Navigation bar & Sidebar
The Navigation bar and Sidebar are the main components used to route users through the application.
### 2.1 Navigation bar
The Navigation bar is the vertical bar on the left that displays the brand logo, sidebar, notifications and the account menu. (Remaining content not visible in the provided screenshot.)
#### 2.2 Sidebar
The Sidebar contains the vertical menu housing your [sidebar buttons](/docs/tagoio/tagorun/getting-started/sidebar.md) and the dashboard list. Personalizing the background color ensures that icons and text automatically adapt their colors for optimal contrast and readability.
#### 2.3 Account Menu
The account menu allows users to access account‑related features and is located on the navigation bar. You have the option to add new items into this menu, such as text entries, links to URLs, or your own dashboards.
## 3. Page Header & Background
The page header displays the title of the module you are using in TagoRUN. It is available on desktop platforms; on mobile devices it is replaced by the navigation bar.
The page background reflects the module that you are using. Since dashboards have their own color settings, modifying these colors will change the appearance for notifications and account settings. Icons and text automatically adjust their colors to maintain contrast and readability. However, if you change a dashboard title, color, or icon, those changes take precedence over the default styling.
## 4. Buttons
You can customize the buttons shown on the platform, tailoring their appearance to match your overall theme.
## 5. General Elements
General elements such as form fields, dropdowns, loading indicators, and other UI components can be customized to maintain visual consistency across the application.
---
## Security and Protection for RUN users
With TagoRUN, you can add actions and methods to increase your application's security and protect your Run users. These controls help your application comply with data-privacy and security laws by providing end-user safeguards to prevent inappropriate access to personal data.
By accessing the [Run](https://admin.tago.io/run) module and selecting the "Security and Protection" tab, you can implement and customize:
- How account delete requests will be processed
- Two-Factor Authentication (2FA) options for end users
- Password creation requirements (strength rules)
- User session duration and management
- Protection against automated (robot) activities using captcha
## Delete request process
This section configures how your application handles end‑user requests to delete their account. The available actions include sending a notification to the profile owner, immediate deletion, or triggering an analysis to perform custom cleanup tasks. Choose the action that best fits your privacy and audit requirements.
You can choose to enable the following options:
1. **Send an e-mail to the profile owner**: you can receive an e-mail and manually process the request through the [User Management](/docs/tagoio/tagorun/getting-started/user-management.md) module.
2. **Immediately delete user account**: you can consent to TagoIO to process the request and immediately delete the user account.
3. **Run an Analysis**: you can implement a script to analyze and process the requests using [Analysis](/docs/tagoio/analysis/) and our [SDK](/docs/tagoio/analysis/sdk/).
:::note
**TagoIO will never remove any Run User data without your consent.** To allow the immediate deletion of an end-user, you must activate the option "Immediately delete user account".
:::
## Two-Factor Authentication (2FA) for End-Users
Enable 2FA to add an extra layer of security for user logins. Available methods in the Run UI include:
- App Authenticator (recommended for most users)
- SMS (note that SMS messages may incur billing)
- Email (note that emails may incur billing)
Once 2FA is activated, users can navigate to their **Account Settings** to configure their preferred authentication method, similar to the [Two‑Factor Authentication](/docs/tagoio/my-account/two-factor-authentication-2fa.md) setup available on Admin.
You can also enforce 2FA if any method is enabled.
:::info
_Notice that each e‑mail and SMS sent to Run users will be counted as 1 transaction from the respective service._ Make sure to allocate the right amount of these resources to your profile. Read more about [Allocating Resources to profiles](/docs/tagoio/my-account/billing/allocating-services-to-profiles.md).
:::
## Password strength
Configure password rules to meet your security policy:
- Require upper and lower case letters
- Require a mix of letters and numbers
- Require special characters (examples: !, &, ...)
- Set a minimum password length (e.g., 8 characters)
:::tip
Just like other features within TagoRUN, you can customize the appearance and sentences used for these features on mobile and on the web through the [Run Theme](/docs/tagoio/tagorun/getting-started/run-theme.md) and [Dictionaries & Multi‑language](/docs/tagoio/tagorun/dictionaries/using-dictionaries-multi-language.md).
:::
## User session management
Set the session expiration to control how long a user stays logged in before re‑authenticating. The UI provides a selector for duration (for example, 3 Months).
You can set the session expiration to occur after a specific number of hours, days, weeks, or months, depending on your security requirements and user convenience.
## Enabling Captcha
Enable captcha to reduce automated or bot activity on signup and other user actions. The UI offers a checkbox to request a captcha test and enables captcha protection when active.
:::tip
**Captcha protects your application from automated actions** such as web scraping, spamming, and brute-force password attacks so that only genuine human users can proceed.
:::
---
## Sidebar
The sidebar on the left side of your Run application lets end users navigate between dashboards. You can create buttons for quick access, not only to open dashboards but also to open other pages or to connect a new Wi‑Fi device.
## Customizing the sidebar
You can customize your sidebar settings by accessing the Run module and selecting the Sidebar tab.
:::tip
It is possible to modify the appearance of your sidebar with your own color palette. Learn more about [Run Themes](/docs/tagoio/tagorun/getting-started/run-theme.md).
:::
### Dashboard Search Bar and Sorting Options
The Dashboard Search feature enhances the end‑user experience by providing a search functionality and sorting options for dashboards within the Run portal panel.

To enable the search bar, toggle the option **"Enable dashboard search."** A new section will be added to the Run portal.
You can customize the search bar section label by entering the desired text; if left blank, the default label will be **"Dashboards."** You can also use a KEY defined in your dictionary to dynamically translate the text to your end-users' language. Learn more about [Using Dictionaries & Multi-language](/docs/tagoio/tagorun/dictionaries/using-dictionaries-multi-language.md).

### Logo Button Configuration
You can define what happens when an end user clicks on your logo or what message is displayed when the mouse hovers over it.
#### 1. Tooltip
To display a tooltip over your logo, write text in the **Button tooltip** field.

Notice that you can use a KEY defined in your [Dictionary](/docs/tagoio/tagorun/getting-started/dictionaries.md) to display text that will be substituted according to the languages available for your end users.
#### 2. Button Action
By default, when an end user clicks on your logo, they are redirected to your Run URL domain. You can customize what happens after clicking on it. The actions can be:
1. **Open Dashboard**: opens the selected dashboard (the user must have access).
2. **Show iFrame**: opens an external link inside the Run application; the page must be embeddable.
3. **Show HTML file content**: displays a custom HTML file that can be hosted in [Files](/docs/tagoio/files).
4. **Open external link**: opens a URL outside of Run.
You can also add the action **"Open Setup Wi‑Fi Device."** To enable this option, navigate to the **Mobile Features** tab in the Run module and activate **Setup Wi‑Fi device**.
### Sidebar Buttons
Buttons allow end users to quickly navigate between dashboards, pages, or scan a new Wi‑Fi device. Dashboards used as buttons are automatically hidden from the dashboard list on the sidebar, keeping the interface cleaner.
#### Label
Define the text displayed under the icon.
#### Icon
Select an icon from our list or add your own using a URL to an SVG file. You can also change its color to match your project style.
#### Action
When you create a new button, it is disabled by default and will not be shown in Run. Choose one of the following actions:
1. **Disabled**: removes the button from the application.
2. **Open dashboard**: opens the selected dashboard (requires user access). See [Access Management](/docs/tagoio/tagorun/access-management/).
3. **Show iFrame**: opens an external link inside Run; must be embeddable.
4. **Show HTML file content**: displays a custom HTML file hosted in [Files](/docs/tagoio/files).
5. **Open external link**: opens a URL outside of Run.
6. **Open Setup Wi-Fi Device**: initiates the process of connecting a Wi-Fi device (enable via Mobile Features tab).
#### Platform
Choose where to display the button: Desktop, Mobile, or both.
#### Break Line
Customize how many buttons appear per line. The slider lets you move a button to a new line or place multiple buttons on the same line. You can have up to three buttons arranged side by side.
#### Visibility Conditions
By default, every button is visible to all users. To restrict visibility (e.g., an "Admin Settings" button only for administrators), set a visibility condition using user tags. A button with conditions is shown when at least one condition matches a tag on the user.
## AI Chat button
When [AI Chat in TagoRUN](/docs/tagoio/tago-ai/tagorun-ai-chat.md) is enabled, users who are granted access see a star icon in the sidebar that opens the chat. The icon is not a sidebar button and has no settings on this tab: the Run module **AI** tab and Access Management decide who sees it.
---
## Signup Fields
Inside your Sign up page, you can add custom fields that appear in addition to the form's required fields. The field name you create is copied to the User tag key, and the value entered by the user during sign up is copied to the User tag value.
Mapping summary:
| Signup field | Tag created for the User |
| --------------- | ------------------------ |
| Field name | Tag key |
| Input from user | Tag value |
## Customization
The "Signup fields" section lets you configure additional fields that will appear on the Sign up page and be stored as user tags. Additional fields will show up as tags for the user. Learn more here (see the Sign up page documentation).
To create customized fields, you must provide four pieces of information:
- **Field name**: This becomes the tag key.
- **Label above the field**: Text that guides the user on what to enter.
- **Type**: Choose one of three types:
- _Text_: The user can type anything.
- _Number_: Only numeric input is allowed.
- _Hidden_: The field is invisible to the user; a tag will be created automatically with the key from the Field name and the value from the Field Placeholder.
- **Required**: Indicates whether the user must fill in this field before signing up. Hidden fields are not affected by the required option.
Notes:
- The Field name you enter becomes the User tag key.
- The value the user types during sign up becomes the User tag value.
---
## TagoRUN Mobile App
The TagoRUN mobile app is designed for end users to access applications created by companies and developers. It is easy to use and supports a high level of customization.
Download the TagoRUN app from the [AppStore](https://apps.apple.com/br/app/tagorun/id1475966647) or [GooglePlay](https://play.google.com/store/apps/details?id=io.tago.run&hl=en).
:::tip
You also have the option to launch your own custom-branded mobile app, allowing your users to engage with your brand directly on their devices.
To learn how to publish a personalized mobile app under your company's branding, visit the [Custom Mobile App](/docs/tagoio/addons/custom-mobile-app.md) documentation.
:::
---
## User Engagement
You can use third-party analytics and engagement tools to track and interact with your RUN users. The Integration module in the RUN session enables easy setup with popular solutions such as [Google Analytics](/docs/tagoio/tagorun/integrations/google-analytics.md) and [Zoho SalesIQ](/docs/tagoio/tagorun/integrations/zoho-salesiq.md).

Some tools provide more than analytics and insights: they can add chat, bots, calls, remote screen access, and other engagement features to your RUN application.
## Cookie compliance
Make sure you comply with cookie and privacy laws that apply to your users' region. Some tools provide built-in options to present DISCLAIMERS and collect user consent.
## Integrations / Tutorials
Follow one of the tutorials below to integrate your application with:
- Google Analytics: See [Google Analytics](/docs/tagoio/tagorun/integrations/google-analytics.md)
- Zoho SalesIQ: See [Zoho SalesIQ](/docs/tagoio/tagorun/integrations/zoho-salesiq.md)
---
## User Management
You can view and control all users that sign up in your application under the User Management module.

## Overview
When you create and deploy your solution using TagoRUN, all users that sign up will be listed in the [User Management](https://admin.tago.io/usermanagement) area. From there you can:
- Grant or remove access
- Change a user's password
- Edit user information
- Edit tags associated with a user
- Delete a user completely
## Tags
Each user has a Tags tab. Tags can be provided in three ways:
- Manually (by an administrator)
- Programmatically (by script)
- By the user during the [Signup process](/docs/tagoio/tagorun/getting-started/signup-fields.md)
> Note: All customized fields defined in your Signup Page under the Run module are added in the Tags here.
The fields `tag_key` and `tag_value` from the users can be used in the Targets when granting access to your application. Refer to the Targets documentation (e.g., "Defining Targets") for instructions on using these fields.
:::warning
Accessing data from dashboards using TagoRUN, even when downloading it as a .csv from widgets, is counted towards your Data Output for Dashboards. See [Data Output for Dashboards](/docs/tagoio/dashboards/data-output-for-dashboards.md) for quota and billing details.
:::
## Anonymous User
You can create an Anonymous User to allow access to dashboards in your TagoRUN without having to sign in with credentials. This user is required if you want to share your dashboard with anyone using a URL. Read more about [Sharing Dashboards](/docs/tagoio/dashboards/sharing-dashboards.md).
The anonymous user behaves as a normal user, except for not having a password. You can still use Tags to grant access to resources through Access Management policies.
Only one anonymous user can be created per Profile.
## Exporting Users
Click the **'Export Users'** button on the top right side of the module to download a .csv file containing data of all your users. The file also includes all Tags, user ID, subscription date, and activation status.
---
## TagoRUN - Branding and Deploying Applications
Use the [Run](https://admin.tago.io/run) module to deploy your own IoT platform for your end-users.
The options available in your RUN will depend on your current plan and the add-ons selected. Even the Free plan offers a high level of customization.
## Deploying
Your own platform is deployed in seconds, no need to build and pay for an additional server. Your customers will access the domain assigned for you.
## Branding
Your customers will engage with your brand as they sign up and access the application that contains your logo, colors, and personalized e‑mails.
1. **Company logo**
2. **Customized sub-domain**
1. Your application will be part of the domain `run.tago.io`. For example: `myiot.run.tago.io`
2. Complete custom domain. For example: [iot.mycompany.com](https://iot.mycompany.com/)
3. **Browser favicon**
4. **Navigation bar colors**
5. **Signup page edition**
1. A small reference to TagoIO is added on the button of the Signup page.
6. **Side bar button inclusion**
7. **E‑mail template configuration**
8. **Geolocation and Wi‑Fi setup**
9. **Google Analytics and CRM integrations**
:::tip
Unlock the [Custom Domain & Whitelabel](/docs/tagoio/addons/custom-domain/) add-on to remove any reference to TagoIO brand and use your own domain and e‑mail.
:::
## How it works with mobile App
Your users can download the free [TagoRUN Mobile App](/docs/tagoio/tagorun/getting-started/tagorun-mobile-app.md). They will simply need to enter your Run domain the first time they log in. For example, if your Run is `mycompany.run.tago.io`, they will enter "mycompany", and from there the mobile App will present the colors, logo, and buttons that you saved in the Run configuration.
Learn also about other [Add‑on](/docs/tagoio/addons/).
---
## Google Analytics
You can integrate your RUN application with Google Analytics to track users and obtain actionable insights. Google Analytics lets you analyze in-depth details about your RUN users and provides data to help improve your solution. The tool can provide valuable insights to help you improve your solution based on real data.

All you need from Google Analytics is the [Measurement ID](https://support.google.com/analytics/answer/12270356?hl=en#:~:text=A%20Measurement%20ID%20is%20an,same%20as%20your%20destination%20ID) to add to your RUN setup.
## How to integrate Google Analytics with TagoIO
To integrate, follow these steps:
1. Create a Google Analytics account at https://analytics.google.com/.
2. Set up an Analytics property and a data stream for your website and/or app. You can follow Google's setup guide: [Set up Analytics for a website and/or app - Analytics Help](https://support.google.com/analytics/answer/9304153).
3. Copy the Measurement ID from the web stream details in Google Analytics.
4. Back to TagoIO, go to your [RUN setup page](https://admin.tago.io/run), under **Integrations > User Engagement**, paste the key into the _Google Analytics ID_ field and save.
You are ready to track the visitors (your RUN users) using Google Analytics!
Notes:
- The Measurement ID is required in the RUN setup to enable analytics collection.
- If you installed tags recently and data collection does not appear active, wait up to 48 hours and verify your tag installation in Google Analytics.
---
## Zoho SalesIQ
You can easily integrate your RUN application with Zoho SalesIQ to track and engage users. All you need to do is use your SalesIQ key in the RUN setup.
## Steps to integrate
1. Create an account at https://www.zoho.com/salesiq/
2. Copy the key (only the key value shown after `widgetcode:` in the SalesIQ script).

3. Enable Visitor Tracking for your RUN URL.
4. Then, back to TagoIO, in your [RUN](https://admin.tago.io/run) setup page, under **Integrations > User Engagement**, paste the key in the Zoho ID field.
You are now ready to track your RUN users using SalesIQ!
You will be able to know who is accessing your RUN, when, for how long, and what type of browser and devices were used. Also, you can enable Chat, Bots, remote screen access, analytics, and add functions to comply with GDPR (cookies disclaimer).
---
## Notifications
TagoIO will notify you when something needs your attention. You may need to
perform an action depending on the type of notification.
## Types of notifications
There are four types of notifications:
- Applications\
Sent by a customized script triggered by an Analysis. Examples: "Two trucks
are out of route", "Your refrigerator temperature is higher than 36 °F".
- Other users\
Displayed when another user shares something with you. Example: "John wishes
to share a Dashboard with you."
- TagoIO system\
Used by the TagoIO backend to notify users about the system or your account.
Example: "There is a new application in the Healthcare industry that you may
be interested in."
- TagoRUN User\
Notifications sent to users registered in your account's User Management. Only
your account (or authorized users) can send notifications to these users.
Learn more in
[Run Users Notifications](/docs/tagoio/tagorun/getting-started/notifications-for-users.md).
## Viewing notifications
When you log in to your account, you can see all your notifications in the
bottom-left corner of the admin page with a blinking blue dot indicating unread notifications.

## Filtering and searching
On the Notifications window, you can:
- Filter by read/unread status.
- Filter by the type of notification.
- Use the search field to refine results.
### Notifications from Other Users
These notifications are usually related to authorization requests asking you to
accept dashboards or profiles. In these cases, you need to first accept the
request before the shared item is available to you.
> The user who sent the request can track the status of the request by checking
> the "Share" tab of each item (Dashboard/Profile).
After you accept or decline a notification, the system removes the dark blue unread icon.
TagoIO automatically removes all ignored notifications that are older than two weeks.
### Notifications from Applications
Application notifications can give you the option to be redirected to a
dashboard that can contain more information about the event. The content of the
notification and the button that links to the dashboard (which is optional) is
defined by the developer.
To learn how to create an Application notification from an Analysis, check our
[SDK documentation](https://js.sdk.tago.io/).
---
## Single Sign-On (SSO)
With Single Sign-On (SSO), your users can sign in to a RUN application using credentials from their existing identity provider (IdP) instead of separate RUN credentials. RUN supports two SSO protocols: **SAML** and **OIDC**. You can use providers such as Okta, Auth0, Microsoft Azure AD, AWS Cognito, Google Workspace, and others.

## Overview
SSO lets your RUN application delegate authentication to an external identity provider. Your users keep one set of credentials across multiple services, and you get centralized access control without managing passwords inside RUN.
RUN offers two protocols for SSO. Choose the one that best fits your identity provider and your organization's setup.
## SAML vs OIDC
[**SAML**](/docs/tagoio/tagorun/single-sign-on-sso/saml-sso.md) is an established enterprise standard supported by most identity providers. If your organization already uses SAML for other applications, this is likely the fastest path to get SSO working.
[**OIDC**](/docs/tagoio/tagorun/single-sign-on-sso/oidc-sso.md) (OpenID Connect) is a more recent protocol built on OAuth 2.0. RUN makes OIDC simpler to configure, with built-in claim mappings and auto-generated callback URLs that reduce the back-and-forth between RUN and your IdP.
Both protocols achieve the same result: your users sign in through the IdP, and RUN receives the identity information it needs.
:::note
You cannot have both SAML and OIDC active at the same time. Enabling one will disable the other.
:::
## Plan requirements
SSO is available on **Scale plans** only.
## How it works
Whichever protocol you choose, the setup follows a similar pattern:
1. **Configure an application on your Identity Provider.** Create a SAML or OIDC application in your IdP and obtain the required credentials.
2. **Manage users and permissions on the IdP side.** Set up which users and groups have access to your RUN application.
3. **Import the IdP configuration into RUN.** Enter the provider details in the corresponding SSO settings page.
4. **Complete the integration.** Register any required URLs (such as callback or metadata URLs) back in your IdP.
Once enabled, only users registered in the IdP and granted permission to access the application can sign in to RUN with their IdP credentials.
## RUN users and mappings
When a user signs in through SSO for the first time, RUN automatically creates a new RUN user with the tag `source: sso`. This applies to both SAML and OIDC.
You can use this tag to identify which RUN users were created through SSO, filter them in the user list, or set up automations. For example, you can create a [Trigger by Resource](/docs/tagoio/actions/trigger-by-resource) action that runs whenever a new RUN user is created with the `source: sso` tag.
Mapping values are automatically updated every time a user signs in. If you change a claim or attribute value on the IdP side (for example, updating a user's name), the RUN user will reflect the new value on their next login.
---
## OIDC SSO
OIDC SSO lets your users sign in to a RUN application through any OpenID Connect-compatible identity provider. You can configure it in **RUN Settings > Integrations > SSO OIDC**.
## Supported providers
You can use any OpenID Connect-compatible provider, including Auth0, Okta, AWS Cognito, Microsoft Azure AD, and Google Workspace.
## Setup process
The configuration steps vary depending on your identity provider, but the overall process is similar. For a step-by-step walkthrough using Auth0, see the [Auth0 OIDC SSO setup guide on the TagoIO Community](https://community.tago.io/t/how-to-set-up-oidc-single-sign-on-on-tagoio-with-auth0). If you are using a different provider, the Auth0 guide is still a useful reference since the steps are often comparable.
Setting up OIDC SSO involves four steps:
1. **Configure an OIDC application on your Identity Provider.** Create an OAuth 2.0 / OpenID Connect application in your IdP and obtain the Issuer URL, Client ID, and Client Secret.
2. **Manage users and permissions on the IdP side.** Set up which users and groups have access to your RUN application.
3. **Import the IdP configuration into RUN.** Fill in the Provider Settings form and configure the Claim Mappings (see sections below).
4. **Register the Callback and Logout Redirect URLs.** Copy the auto-generated URLs from RUN and add them to your IdP's allowed callback and redirect settings.
## Configuration fields
When you open the SSO OIDC settings in RUN, you will see the following fields:
| Field | Description |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Issuer URL** | The OpenID Connect discovery URL from your IdP (e.g., `https://dev-xxx.us.auth0.com`). Where you find this varies by provider. In Auth0 it is under Application Settings; in Okta it is in the Authorization Server metadata. |
| **Client ID** | The application identifier from your IdP. |
| **Client Secret** | The secret key from your IdP. RUN stores this value encrypted. |
| **Scopes** | The OpenID Connect scopes to request. Default: `openid profile email`. |
| **Callback URLs** | Auto-generated by RUN. Copy these and register them in your IdP as allowed callback URLs. |
| **Logout Redirect URLs** | Auto-generated by RUN. Copy these and register them in your IdP as allowed logout redirect URLs. |
## Claim mappings
RUN maps attributes from your identity provider to RUN user fields. Two mappings are required:
- **E-mail** (required)
- **First Name** (required)
- **Last Name**
Most providers send these by default with the `openid profile email` scopes. If your IdP uses non-standard claim names, you can adjust the mapping for each field.
To map additional IdP claims, click **Edit other claim mappings**. Custom claim values are stored as tags on the RUN user, which you can then use for access management and other features within your application.
## Passing query parameters to your IdP
Some identity providers support additional features through query parameters, such as invite links or custom login hints. RUN forwards any query parameters from your application URL to the IdP during the authentication flow.
For example, if your provider supports invite codes, you can share a link like:
```
https://myapp.tago.run/login?invite=INVITECODE
```
RUN will pass the `invite` parameter along to the IdP. Check your provider's documentation for the specific query parameters it supports.
## Enable or disable OIDC SSO
Use the **Enabled** toggle at the top of the SSO OIDC settings page to activate or deactivate OIDC SSO. Click **Save** to apply your changes.
---
## SAML SSO
SAML SSO lets your users authenticate with a RUN application through a SAML-compatible identity provider. You can configure it in **RUN Settings > Integrations > SSO SAML**.
## Supported providers
You can use any SAML 2.0-compatible identity provider, including Okta, Auth0, Microsoft Azure AD, OneLogin, PingIdentity, and Google Workspace.
## Configuration guide
The configuration steps vary depending on your identity provider, but the overall process is similar across providers:
1. Create a SAML application in your IdP and configure the required settings (such as the Assertion Consumer Service URL and Entity ID provided by RUN).
2. Add and manage users on the IdP side, including group membership and permissions.
3. Import the IdP metadata or configuration details into RUN under **SSO SAML** settings.
4. Enable SSO and verify that your users can sign in through the IdP.
For a step-by-step walkthrough, see the [Auth0 SSO setup guide on the TagoIO Community](https://community.tago.io/t/how-to-set-up-single-sign-on-sso-on-tagorun-with-auth0/1727). If you are using a different provider, the Auth0 guide is still a useful reference since the steps are often comparable.
---
## Errors
The errors below are specific to TagoSQL queries. Standard API errors (authentication, rate limiting,
per-endpoint status codes) behave as they do everywhere else; see the
[TagoIO API reference](https://api.docs.tago.io/).
## Query problems (`400`)
Fix the query or the request:
| Trigger | Example message |
| --------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| SQL syntax error or disallowed construct | `Invalid query` |
| Inactive query executed | `SQL query is inactive` |
| Written `LIMIT` above the plan row cap | `LIMIT exceeds the plan row cap (100). Lower it and paginate with OFFSET` |
| Result exceeds the row cap (query had no `LIMIT`) | `Result exceeds the 100-row limit. Add LIMIT (and OFFSET to paginate)` |
| Missing, extra, duplicate, or gapped `$n` parameter | Message naming the `$n` token |
| Parameter value invalid for how the query uses it | `Invalid value for parameter $1` |
| Fleet query without the required filters | `device_data_by_tag requires a variable = '' filter` |
| Fleet time bound older than the plan window | `time bound exceeds the 90-day window allowed on your plan` |
| Fleet tag filter matching too many devices | `Tag filter matches more than 100 devices. Narrow the tags or paginate with after_device` |
## Session context (`400`)
Raised by [session functions](/docs/tagoio/tagosql/session-context.md). Some are checked when you save the query, others when it
runs:
| Trigger | Message |
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `session_user_tag` called with the wrong number of arguments | `session_user_tag takes exactly one argument, the literal tag key` |
| A malformed `COALESCE` around a session function | `COALESCE with a session function takes exactly two arguments: the session function first, a non-empty string fallback second` |
| The profile owner runs a session function that has no fallback | `session_user_tag('customer') has no value for profile execution` |
| An analysis runs a query that uses session context | `This query uses session context and requires a Run User` |
| A user's tag key resolves to more than one value | `User tag 'customer' resolves to multiple values` |
| A session query run outside stored query execution | `Session functions are only available on stored query execution` |
A session function in a disallowed position, with a non-literal argument, or wrapped in a malformed
`COALESCE` is rejected when you save the query. Whether the running caller is allowed (owner, Run user, or
analysis) is decided when the query runs.
## Plan limits (`402`)
Raised by upgrading the plan (or lowering usage): the stored-query cap, the per-query parameter cap, JOINs
on the Free plan (`JOINs require a Starter plan or above`), too many joined tables, and an exhausted monthly
data output allocation.
## Not found (`404`)
| Trigger | Example message |
| ------------------------------------------------------- | -------------------------- |
| Query id not on your profile (including someone else's) | `SQL Query can't be found` |
| Version not in the query's history | `404` |
| Device or entity not owned or nonexistent | `Device 'x' not found` |
| Device exists but has never stored data | `Device 'x' has no data` |
| Fleet tag filter matching no device | `No device found with tag` |
Ownership failures are always `404`, never `403`: a resource you cannot access is indistinguishable from one
that does not exist.
These not-found errors apply to literal values written in the query text. A value resolved from the user
session that matches nothing returns an empty `200` result instead, so a resolved value never appears in an
error message. See [Session Context](/docs/tagoio/tagosql/session-context.md#empty-results-when-the-session-has-no-data).
## Timeout (`408`)
The query exceeded your plan's execution timeout: `Query timed out (15s limit)`. Narrow the time range, add
tighter filters, or reduce the number of joined tables.
---
## Executing Queries
Running a saved query is one request: you send its id, optionally with values for its parameters, and you
get a table back. A query must be [stored](/docs/tagoio/tagosql/queries.md) before it can run: store it once, then execute it by
id. Storing is what validates the query against your plan, records its version history, and gives it the
identity an [Access Management](/docs/tagoio/tagorun/access-management/) grant matches. Full request and
response schemas are in the [TagoIO API reference](https://api.docs.tago.io/) under **SQL Queries**.
## Run a stored query
```
POST /sql/{id}/execute
profile-token:
{ "params": [{ "key": "$1", "value": "40" }] }
```
The body is optional: a bare `POST` runs the query with its stored defaults and the cache enabled. Values
you send override the stored defaults per parameter (see [Parameters](/docs/tagoio/tagosql/parameters.md)).
:::tip
While developing, add `"test": true` to run the query with the cache out of the picture: no cached result is
read, and the run leaves the cache untouched for everyone else. Test mode is not a limit bypass, so rate
limits and usage metering still apply.
:::
## Exploring and iterating
There is no unsaved execution. To explore, store a scratch query and iterate on it with `PUT /sql/{id}`:
each content change snapshots a new [version](/docs/tagoio/tagosql/queries.md#versioning), so nothing is lost while you refine
it. Combine with test mode to keep the cache untouched while you experiment.
## Query many devices
The fleet function `device_data_by_tag` returns the latest reading per device across every device matching
your tags (see [Available Tables](/docs/tagoio/tagosql/tables.md#data-from-many-devices)). Last temperature of every sensor, in
one request:
```sql
SELECT device, device_name, variable, value, time
FROM device_data_by_tag('type', 'sensor') AS f
WHERE variable = 'temperature' AND time > $1
ORDER BY device
```
Store it once, then execute with the time bound as a parameter
(`{ "params": [{ "key": "$1", "value": "2026-07-11T00:00:00Z" }] }`).
One row per device comes back, each carrying that device's newest reading. Swap `temperature` for
`location` or `battery` to build the other fleet widgets.
Fleet queries return up to your plan's device cap per request. For larger fleets, page with the
`after_device` body field: order by `device`, then pass the last device id you received to fetch the next
page.
```json
{
"params": [{ "key": "$1", "value": "2026-07-11T00:00:00Z" }],
"after_device": "LAST_DEVICE_ID_FROM_PREVIOUS_PAGE"
}
```
Each page is cached independently, so a dashboard paging a warm fleet stays on cache hits.
## What you get back
```json
{
"status": true,
"result": {
"columns": [
{ "name": "variable", "type": "string" },
{ "name": "value", "type": "number" },
{ "name": "time", "type": "timestamp" }
],
"rows": [{ "variable": "temperature", "value": 41.2, "time": "2026-03-30T10:00:00+00:00" }],
"row_count": 1,
"execution_ms": 12,
"served_from_cache": false
}
}
```
`columns` gives you the typed header, ready for a table or chart; `rows` holds the data keyed by column
name. Results are never silently shortened: `row_count` is the true size of what you asked for, and a query
whose result would exceed your plan's
[row cap](/docs/tagoio/my-account/billing/resource-limits#tagosql-query-limits) fails with a clear error
instead.

## Caching
For queries that run often with the same inputs, like a dashboard widget or a public report, enable the
cache on the stored query (`cache_enabled` plus a `cache_ttl_seconds`). Then:
- Repeat executions with the same parameter values are served from the cache: fast, free of
[data output](/docs/tagoio/profiles/services/data-output-service.md#tagosql-queries) usage, and allowed
far more requests per minute than fresh executions.
- Each distinct set of parameter values gets its own cache entry, so different inputs never mix. In a query
with session functions, the resolved session values join the cache identity the same way, so results are
cached per user.
- Editing the query drops its cached results automatically.
- When many callers hit an expired entry at once, only one execution runs against the database and everyone
shares its result.
The response's `served_from_cache` field tells you which path a result took.

## Soft rate limit
Each plan caps how many fresh executions per minute a profile can run, and cached reads get a much higher
allowance. A stored query can also set its own `rate_limit_rpm`, so one heavily-exposed query cannot exhaust
the whole profile's budget. The per-plan numbers and the over-limit response are in
[Rate Limits](/docs/tagoio/profiles/services/rate-limits-hard-limits.md).

## Who can execute
- A **profile token** can execute any of the profile's queries.
- An **Analysis or Run-user token** can execute a stored query when granted the **Execute** permission
through an Access Management policy, matched by the query's id or tags. The query then runs under the
owning profile's plan and scope.
A grant authorizes running the query as-is: it returns exactly what the query selects, no more and no less.
The policy never filters the rows that come back, so write the query for its audience. See
[Session Context](/docs/tagoio/tagosql/session-context.md) for scoping one query per user. Policies themselves are set up in
[Access Management](/docs/tagoio/tagorun/access-management/).
---
## Getting Started(Tagosql)
Each recipe below is a complete query. Copy one, replace the parts written in capitals with your own device
id, variable name, or tag, and run it. You do not need to know SQL beforehand: the first time a keyword
appears, it is explained in one sentence.
:::tip
Ready-made examples for many other tasks are collected at [snippets.tago.io](https://snippets.tago.io).
:::
## Read the last values of one device
The most common question: what has this device been sending?
```sql
SELECT variable, value, time
FROM device('DEVICE_ID') AS d
ORDER BY time DESC
LIMIT 20
```
- `SELECT` lists the columns you want back. Use `SELECT *` to get all of them.
- `FROM device('DEVICE_ID') AS d` says which device to read. The `AS d` part is a short nickname for that
device, and it is required.
- `ORDER BY time DESC` sorts newest first (`DESC` for descending, `ASC` for ascending).
- `LIMIT 20` stops after 20 rows, which keeps the result small and fast.

## Filter one variable over a time window
Now narrow the question down to a single variable and a period. This is the shape most charts need.
```sql
SELECT value, time
FROM device('DEVICE_ID') AS d
WHERE variable = 'temperature' AND time > $1
ORDER BY time DESC
LIMIT 100
```
`WHERE` keeps only the rows that match a condition, and `AND` chains conditions together. The `$1` is a
placeholder: instead of writing the start date into the query, you send the value when you run it, so the
same saved query can cover today, last week, or last month. Save a default with the query and override it
per run. See [Parameters](/docs/tagoio/tagosql/parameters.md) for the details.
## List your devices and their configuration
This one does not read sensor data at all. `devices()` lists your devices themselves, which is what you
want for an overview of the fleet: what exists, what is active, when each device last sent data.
```sql
SELECT id, name, active, last_input, params
FROM devices() AS d
WHERE active = true
ORDER BY last_input DESC
LIMIT 100
```
The `params` column carries each device's Configuration Parameters, so one query gives you your devices with
their configuration attached: firmware version, sampling interval, or whatever keys you store there. The
full column list is on the [Available Tables](/docs/tagoio/tagosql/tables.md#your-device-inventory) page.
## Read the latest value across a whole fleet
To see every device at once, filter by tag instead of naming a device. `device_data_by_tag` returns the
newest matching reading for each device carrying the tag, so a hundred devices come back as a hundred rows
in one request.
```sql
SELECT device, device_name, value, time
FROM device_data_by_tag('device_type', 'sensor') AS f
WHERE variable = 'temperature' AND time > $1
ORDER BY device
```
Fleet queries always need a variable and a starting time, which is what keeps them fast. Swap
`temperature` for `location` to build a map widget, or for `battery` to build a health table.
## Show each Run user only their own data
If a TagoRUN portal shows the same report to many users, you do not need one query per user. A session
function fills in who is running the query, so each user sees only their own rows.
```sql
SELECT value, time
FROM device_data_by_tag('customer', COALESCE(session_user_tag('customer'), 'acme')) AS f
WHERE variable = 'temperature' AND time > $1
ORDER BY device
```
When a Run user tagged `customer = globex` runs this, they get the Globex devices; another user gets theirs.
The `COALESCE(..., 'acme')` part is the value used when you test the query yourself as the profile owner.
[Session Context](/docs/tagoio/tagosql/session-context.md) covers the rules and what happens when a user matches nothing.
## Next steps
- Save the query you like so widgets and analyses can run it by id. See [Queries](/docs/tagoio/tagosql/queries.md).
- Run it and read the result, including caching and fleet pagination, in
[Executing Queries](/docs/tagoio/tagosql/executing-queries.md).
- Look up any table or column in [Available Tables](/docs/tagoio/tagosql/tables.md).
---
## Parameters
Parameters let one query serve many inputs. Write `$1`, `$2`, ... where a value would go, store defaults
with the query, and override any of them at execution time. Parameter values never become part of the SQL
text: they are bound separately, so a value can never change what the query does.
```sql
SELECT variable, value FROM device($1) AS d
WHERE value > $2
ORDER BY time DESC LIMIT 50
```
Here `$1` picks which of your devices to query and `$2` is the threshold. Execute it with different values
each time without touching the query.

## Where you can use them
- **In place of values**: comparisons (`value > $1`), `IN` lists, `BETWEEN` bounds, and `LIKE`/`ILIKE`
patterns.
- **As the device or entity id**: `device($1)` and `entity($1)` let one stored query target different
resources per execution. The id goes through the same ownership check as a literal id; a device you do
not own fails with `404`.
## Supplying values
Parameters travel as an array of `{ key, value }` pairs, and all values are strings. The server converts
each one based on where it is used (number, timestamp, boolean, text):
```json
{
"params": [
{ "key": "$1", "value": "DEVICE_ID" },
{ "key": "$2", "value": "30" }
]
}
```
- **Stored defaults** must cover every placeholder the query uses.
- **At execution time** you can override any subset: values you send win, stored defaults fill the rest.
## Paginating with parameters
`LIMIT` and `OFFSET` take fixed numbers in the query text, and a stored query has fixed text, so a classic
skip pattern (`LIMIT 100 OFFSET 200`) would need one stored query per page. Page through a **parameter
cursor** instead: keep the `LIMIT` fixed and pass the last timestamp you saw as `$1`.
```sql
SELECT variable, value, time FROM device('DEVICE_ID') AS d
WHERE time < $1
ORDER BY time DESC LIMIT 100
```
```json
{ "params": [{ "key": "$1", "value": "2026-07-17T10:00:00Z" }] }
```
The cursor form is also the faster one: a deep `OFFSET` makes the database scan and discard every skipped
row on each page, while the cursor jumps straight to the right position. Prefer it for anything beyond a few
pages. Fleet queries (`device_data_by_tag`) paginate over devices with the `after_device` body field
instead; see [Executing Queries](/docs/tagoio/tagosql/executing-queries.md#query-many-devices).
## Rules
- Only `$n` placeholders, numbered `1..N` with no gaps. Named (`:name`) and anonymous (`?`) styles are
rejected.
- Dates accept ISO format (`2026-01-01` or `2026-01-01T00:00:00Z`); booleans accept `true`/`false`/`1`/`0`.
- An invalid value returns a `400` naming the placeholder (for example, `Invalid value for parameter $1`);
the value itself is never echoed back.
---
## Queries
Once a query gives you the rows you want, save it so you never have to paste it again. A saved TagoSQL query
is a resource on your profile, like a device or an analysis: it has a name, tags, a version history, and its
own settings. Store the query once; widgets, analyses, and applications execute it by id.
New to TagoSQL? Start with [Getting started](/docs/tagoio/tagosql/getting-started.md) for query examples to save.

## Creating a query
```
POST /sql
profile-token:
{
"name": "hot sensors",
"description": "temperatures above a threshold",
"query": "SELECT variable, value, time FROM device('DEVICE_ID') AS d WHERE value > $1 ORDER BY time DESC LIMIT 100",
"params": [{ "key": "$1", "value": "30" }],
"cache_enabled": true,
"cache_ttl_seconds": 60
}
```
:::info
The query is validated when you store it: syntax, allowed SQL, and your plan's caps (including the
[row cap](/docs/tagoio/my-account/billing/resource-limits#tagosql-query-limits)) are all checked before
anything is saved. A query that stores cleanly will not fail structurally when executed later.
:::
### What a query holds
| Field | Meaning |
| ------------------- | ------------------------------------------------------------------------------------------------------ |
| `name` | Display name (required). |
| `description` | Optional free text. |
| `query` | The TagoSQL statement. |
| `params` | Default values for the query's placeholders. See [Parameters](/docs/tagoio/tagosql/parameters.md). |
| `cache_enabled` | Turns the result cache on (off by default). See [Executing Queries](/docs/tagoio/tagosql/executing-queries.md#caching). |
| `cache_ttl_seconds` | How long a cached result stays fresh (up to 24 hours). |
| `rate_limit_rpm` | Optional per-query rate cap, useful when one query is exposed to many consumers. |
| `active` | An inactive query cannot be executed. |
| `tags` | Your own `{ key, value }` labels, also usable to grant execution through Access Management. |
| `session_context` | Read-only. `true` when the query uses a [session function](/docs/tagoio/tagosql/session-context.md). Not accepted in a body. |
## What SQL you can write
TagoSQL accepts a read-only subset of SQL, checked when you store the query. A query is a single `SELECT`
statement, and every table in a `FROM` clause must use one of the
[table functions](/docs/tagoio/tagosql/tables.md) with an alias.
**Allowed**: `SELECT` (with aliases, `DISTINCT`, `*`), the aggregates `COUNT`/`AVG`/`SUM`/`MIN`/`MAX`,
`JOIN`/`LEFT JOIN`/`RIGHT JOIN` with `ON` (Starter plan or above), `WHERE` with the usual comparison and
logical operators, `IN`, `BETWEEN`, `IS NULL`, `LIKE`/`ILIKE`, `GROUP BY`, `HAVING`, `ORDER BY`,
`LIMIT`/`OFFSET`, and arithmetic expressions such as `value * 1.8 + 32`.
**Not allowed**: subqueries, CTEs (`WITH`), `UNION`, window functions, type casts (`::type`), any write
operation, and database system functions.
This example correlates two devices by time with a `JOIN`:
```sql
SELECT a.value AS sensor_value, b.value AS actuator_value
FROM device('SENSOR_ID') AS a
JOIN device('ACTUATOR_ID') AS b ON a.time = b.time
WHERE a.variable = 'temperature'
ORDER BY a.time DESC
LIMIT 50
```
How many tables one query may join depends on your plan, and the inventory tables cannot be joined at all.
See [Available Tables](/docs/tagoio/tagosql/tables.md) for the per-table rules and
[Resource Limits](/docs/tagoio/my-account/billing/resource-limits#tagosql-query-limits) for the numbers.
## Managing queries
Queries are managed like any other TagoIO resource: list them (with field selection, name and tag filters,
ordering, and pagination), fetch one, edit, and delete. Editing re-validates the query and drops its cached
results. Field-level details and every list parameter are in the
[TagoIO API reference](https://api.docs.tago.io/) under **SQL Queries**.
## Versioning
Every save snapshots the query, so you can always see what it looked like and go back:
- Creating a query stores version 1. Each edit that changes the `query` or `params` stores the next
version; renames and other metadata edits do not create versions.
- Fetch any past revision with `GET /sql/{id}/version/{version}`. It returns that snapshot's
`{query, params}`.
- **To restore**, fetch the snapshot you want and save its content back with a regular edit. The restored
content becomes the newest version, so the history never rewrites itself.
How many versions are kept depends on your plan (see
[Resource Limits](/docs/tagoio/my-account/billing/resource-limits#tagosql-query-limits)); beyond the cap,
the oldest snapshots are recycled.
For every table and column a query can reference, see [Available Tables](/docs/tagoio/tagosql/tables.md).
---
## Session Context
Scope one stored query to the session that executes it. Write the query once; each session that runs it
sees only its own data, and no identity value passes in the request. The scoping lives in the query text, so
the session cannot widen it at run time.
Session functions resolve values from the session executing the query. A Run user executing it resolves to
their own id and tags. A profile owner testing the query gets the authoring fallback, and an execution
without a user session, like an analysis, is rejected.
A shared dashboard is the typical use: one query behind a widget, where each Run user sees only their own
devices. The [AI Chat in TagoRUN](/docs/tagoio/tago-ai/tagorun-ai-chat.md) is the other: one query granted to
the assistant, where each user's questions are answered from their own data only.
:::warning[The query result is not filtered by Access Management]
Granting execution of a query grants its full result set. A policy decides **which** queries a caller may
run, never which rows or columns come back: the query runs with the profile's data access, and no permission
in the policy (device, entity, or any other resource) applies to the rows returned. A Run user with a deny
rule on a device still receives that device's data if a granted query selects it.
The query itself is the security boundary. Write it so it returns only what its audience may see, and treat
the result set as visible to everyone the policy targets. Session context is how one stored query does that
per user, instead of authoring one query per audience.
:::
## A worked example
Two customers, Acme and Globex, share one dashboard. Each customer's Run users must see only their own
devices' temperatures.
**1. Tag the devices.** Give every device a `customer` tag: Acme's devices get `customer = acme`, Globex's
get `customer = globex`.
**2. Tag the Run users with the same key.** Give each Run user a `customer` tag for the customer they belong
to: Acme's Run users get `customer = acme`, Globex's get `customer = globex`.
**3. Write the query.** Store one query that reads the `customer` tag from the Run user who runs it:
```sql
SELECT time, value
FROM device_data_by_tag('customer', COALESCE(session_user_tag('customer'), 'acme')) AS d
WHERE variable = 'temperature' AND time > $1
ORDER BY device
LIMIT 100
```
`session_user_tag('customer')` is the value of the `customer` tag on the Run user who runs the query. The
`COALESCE` gives it a fallback of `acme` for running the query as the profile owner while you build it (see
[Providing a fallback while authoring](#providing-a-fallback-while-authoring)).
**4. Grant it.** Grant the Run users execution through
[Access Management](/docs/tagoio/tagorun/access-management/). They run it by id and never see or edit the
SQL.
The same stored query returns different rows depending on who runs it:
- A Run user tagged `customer = acme` runs it. The server fills the `customer` value with `acme`, so only
Acme's devices come back.
- A Run user tagged `customer = globex` runs the same query. The fill is `globex`, so only Globex's devices
come back.
- The profile owner runs it to test. The owner has no `customer` tag, so the `COALESCE` fallback `acme`
applies and Acme's devices come back.
Access Management still decides **which** query a Run user may run. The session value decides **which rows**
that run returns. They are independent: a grant lets a Run user run the query as written, and the session
value inside it narrows the result to that Run user.
## Restrict AI results with a saved TagoSQL query
Use a saved TagoSQL query when the [AI Chat in TagoRUN](/docs/tagoio/tago-ai/tagorun-ai-chat.md) should receive
only selected variables or fields from your devices.
For example, an assistant may need battery readings without receiving location data or other variables stored
on the same devices.
:::warning
A query granted to the assistant exposes the query's full result. Device-level deny rules do not filter its
rows.
Review the SQL before granting access. The query must return only data appropriate for its audience.
:::
### Example: battery readings for the current customer
Give the RUN users and their devices matching `customer` tags, then save this query:
```sql
SELECT device, device_name, variable, value, unit, time
FROM device_data_by_tag('customer', COALESCE(session_user_tag('customer'), 'acme')) AS f
WHERE variable = 'battery_level'
AND time > $1
ORDER BY device
```
The query restricts:
- **Devices:** the device `customer` tag must match the executing RUN user's `customer` tag.
- **Variable:** only `battery_level` is selected.
- **Fields:** only the six columns listed in `SELECT` are returned.
- **Time:** the reading must be later than `$1`.
`device_data_by_tag` returns the latest qualifying reading per device, rather than the device's complete
reading history.
Store a default value for `$1` using an ISO 8601 timestamp within your plan's permitted lookback period. The
assistant can override that time parameter from the question. Choose a period containing known readings when
testing. See [Parameters](/docs/tagoio/tagosql/parameters) for parameter configuration.
### Grant the query to the assistant
In the policy for the RUN users, add an **Allow** permission for **SQL Query** with the **AI access** rule,
matched to this query. The assistant discovers the query by its name and description, runs it as the user, and
works from its result. It never sees the SQL and cannot read anything the query does not select.
### How customer scoping works
The server resolves `session_user_tag('customer')` from the RUN user executing the query. The assistant does not
choose the customer value.
The `acme` fallback applies only when the profile owner tests the query. A RUN user without the required tag
receives an empty result, not Acme's data. See [Providing a fallback while authoring](#providing-a-fallback-while-authoring).
### Verify the restriction
Test through AI Chat with users belonging to different customers and with a user who has no `customer` tag.
Also request a variable excluded by the query, such as location data. Check that no other AI permission or
authorized query provides an unintended route to that information.
## Session functions
These functions read from the session running the query:
| Function | Fills in |
| ------------------------- | ----------------------------------------------------------------------------- |
| `session_user_id()` | The id of the user running the query. Takes no arguments. |
| `session_user_tag('key')` | That user's value for the tag `key`. Takes one argument, the literal tag key. |
The tag key is a literal you write; the value resolves from the session. `session_user_tag('customer')` is
the session's value for the `customer` tag. No session value is ever passed in the request body, which keeps
the contract visible in the query text.
The tag key must be a non-empty single-quoted string; expressions, column references, `$n` placeholders, and
empty strings are rejected when you save. Function names are case-insensitive, may appear together and more
than once in one query, and do not count toward the parameter cap. Full error messages are on the
[Errors](/docs/tagoio/tagosql/errors.md#session-context-400) page.
A query using either function carries a read-only `session_context: true`, computed from the query text and
returned by info, create, and edit (the list projection does not include it). Query editors use it to mark
"this query resolves per session" without reading the SQL; it is never accepted in a request body.
## Where they are allowed
A session function stands in for a value the query would otherwise hard-code. It is allowed in the value slot
of a tag selector (`device_tag`, `devices_tag`, `entity_tag`, `entities_tag`, and the `device_data_by_tag`
pairs) and in the id slot of `device(...)` and `entity(...)`. The tag key itself stays a literal.
```sql
device_data_by_tag('customer', COALESCE(session_user_tag('customer'), 'acme'))
device_tag('owner', COALESCE(session_user_id(), '68b8a48a474dbd307bd84da3'))
```
Anywhere else (a `WHERE` operand, the `SELECT` list, `LIMIT`) is rejected when you save the query. Scoping by
a tag value is almost always the right choice; a raw user id used directly as a device id rarely matches
anything.
They also resolve only on stored query execution (`POST /sql/{id}/execute`). Run any other way, a session
query is rejected with `Session functions are only available on stored query execution`.
## Providing a fallback while authoring
While authoring a query you run it as the profile owner, and the owner session usually has no `customer` tag
to fill in. Use `COALESCE` (the standard SQL clause) to give the session function a fallback value for
running and iterating:
```sql
COALESCE(session_user_tag('customer'), 'acme')
COALESCE(session_user_id(), '68b8a48a474dbd307bd84da3')
```
TagoSQL accepts `COALESCE` here in one shape: the session function first, then one non-empty string as the
fallback. That is enough to test with a real tag value.
:::warning[Owner only]
The fallback applies only when the profile owner runs the query. A Run user never gets it: the value always
comes from that Run user's own session, so one customer is never shown another's rows through a shared
fallback. A Run user who lacks the tag gets an empty result, not the fallback.
:::
## Empty results when the session has no data
When the resolved session has access to nothing, the result is empty: zero rows, HTTP `200`, billed as
normal output. Never an error, never the fallback. This covers a session whose tag value matches no devices
and a session with no tag at all. An untagged Run user sees an empty dashboard, not an error, and no resolved
value ever appears in an error message.
This is an exception the session values carry: the same misses with literal values written in the query text
keep their regular `404` errors (see [Errors](/docs/tagoio/tagosql/errors.md#not-found-404)). The empty result reports
`columns: []` and `rows: []`. One error still stands: a tag matching more devices
than the fan-out device cap keeps the same `400` telling you to narrow the tags or paginate, a resource limit
rather than an access question.
## Caching
Resolved session values join the cache key alongside the `$n` parameter values. Two sessions share a cached
result only when every resolved value is identical, so caching is effectively per session. Changing a tag a
session resolves does not invalidate earlier cached results; the old key stops being produced and ages out.
See [Executing Queries](/docs/tagoio/tagosql/executing-queries.md#caching).
---
## Available Tables
TagoSQL exposes your data through virtual table functions. Every table in a `FROM` clause must use one of
them (plain table names are rejected) and must have an alias. Pick by what you want back:
| You want | Use | Example |
| ----------------------------------------------- | ----------------------------------- | ----------------------------------------------- |
| The sensor readings stored on **one device** | `device('id')` | `FROM device('62a1...') AS d` |
| The rows stored in **one entity** | `entity('id')` | `FROM entity('62a1...') AS e` |
| The latest reading per device, **many devices** | `device_data_by_tag('key','value')` | `FROM device_data_by_tag('type','sensor') AS f` |
| Your **device inventory** (names, tags, status) | `devices()` | `FROM devices() AS d` |
| Your **entity inventory** | `entities()` | `FROM entities() AS e` |
The two groups answer different questions. In a data table (`device`, `entity`, `device_data_by_tag`) one row
is one stored data point. In a list table (`devices`, `entities`) one row is one device or one entity.
`device` and `entity` also have a tag form (`device_tag('key','value')`, `entity_tag('key','value')`) that
picks the **first** resource matching that tag instead of naming an id: when several resources match, the
first one ordered by resource id wins, so the same tag always resolves to the same resource, which is not
necessarily the one you had in mind. Use the id form when you need a specific one, and `device_data_by_tag`
when you want **all** matching devices.

## Data from one device
`device('id')` and `device_tag('key','value')` read the time-series data stored on a device.
| Column | Type |
| ------------ | -------------------------------- |
| `id` | string |
| `variable` | string |
| `value` | auto (number / string / boolean) |
| `unit` | string |
| `group` | string |
| `lat` | number |
| `lng` | number |
| `metadata` | json |
| `time` | timestamp |
| `created_at` | timestamp |
The `value` column stores numbers, strings, and booleans. In a `WHERE` clause its type follows what you
compare it against: `value > 30` treats it as a number, `value = 'open'` as a string, `value = true` as a
boolean. In the output, `value` comes back as the type it was stored with.
The tag form reads the data of the **first** device carrying the tag, ordered by device id, so a tag matching
several devices always resolves to the same one but not necessarily the one you had in mind. Use `device('id')`
when the device matters, and `device_data_by_tag` when you want every matching device.
```sql
SELECT d.variable, d.value, d.time
FROM device_tag('device_type', 'gateway') AS d
WHERE d.variable = 'uptime'
ORDER BY d.time DESC
LIMIT 20
```
## Data from one entity
`entity('id')` and `entity_tag('key','value')` read the rows stored in one of your
[Entities](/docs/tagoio/entities/). Columns are dynamic: each entity exposes exactly the fields defined in
its own schema, with their native types. `SELECT *` returns that entity's columns, and you can only
reference columns the entity actually has. `entity_tag` follows the same pick-first rule as `device_tag`: it
reads the first entity carrying the tag, ordered by entity id, so name the id when the entity matters.
```sql
SELECT e.*
FROM entity_tag('category', 'inventory') AS e
ORDER BY e.created_at DESC
LIMIT 10
```
## Data from many devices
`device_data_by_tag('key','value', ...)` is the fleet function: one query returns the **latest reading per
device** across every active device carrying ALL the tags you list (1 to 5 pairs, AND-combined). Built for
dashboards that show a whole fleet at once, like a map with each device's last location or a table with each
device's last temperature.
```sql
SELECT device, device_name, value, time
FROM device_data_by_tag('org_id', 'XY', 'type', 'sensor') AS f
WHERE variable = 'temperature' AND time > $1
ORDER BY device
```
It exposes the same columns as [data from one device](#data-from-one-device), plus two extras: `device` (the
device id) and `device_name`.
A few rules keep it fast even against hundreds of devices; breaking one is a clear `400`:
- Filter on **one variable** (`variable = '...'`) and give a **recent time bound** (`time > `),
both as plain `AND` conditions. How far back the bound may reach depends on your plan (see
[Resource Limits](/docs/tagoio/my-account/billing/resource-limits#tagosql-query-limits)).
- The tag filter may match at most your plan's device cap per request. For larger fleets, page with the
`after_device` body field (see [Executing Queries](/docs/tagoio/tagosql/executing-queries.md#query-many-devices)).
- No aggregates or grouping, and no JOINs with other tables. Devices that never stored data are skipped.
## Your device inventory
`devices()` and `devices_tag('key','value')` list your devices themselves (not their data). Useful for
reports over the fleet: how many devices are active, which ones stopped sending data, which carry a tag.
| Column | Type |
| ----------------- | --------- |
| `id` | string |
| `name` | string |
| `description` | string |
| `active` | boolean |
| `visible` | boolean |
| `type` | string |
| `tags` | json |
| `network` | string |
| `connector` | string |
| `last_input` | timestamp |
| `created_at` | timestamp |
| `updated_at` | timestamp |
| `chunk_retention` | string |
| `chunk_period` | string |
| `params` | json |
`devices_tag('key','value')` returns **all** devices carrying the tag, not just the first one, and it carries
the same columns as `devices()` including `params`.
```sql
SELECT d.id, d.name, d.last_input
FROM devices_tag('device_type', 'sensor') AS d
WHERE d.active = true
ORDER BY d.name
LIMIT 100
```
### The params column
`params` carries the device's
[Configuration Parameters](/docs/tagoio/devices/configuration-parameters-for-devices) as a json array, one
element per parameter, in the same shape [List device params](/docs/api/list-device-params) returns:
`{"id", "key", "value", "sent"}`, ordered by `key`. A device with no parameters gets an empty array (`[]`).
```sql
SELECT id, name, params FROM devices() AS d WHERE d.active = true LIMIT 100
```
```json
{
"id": "62a1b3c4d5e6f7a8b9c0d1e2",
"name": "Warehouse sensor",
"params": [
{ "id": "64f2a1b3c4d5e6f7a8b9c0d1", "key": "firmware_version", "value": "1.2.0", "sent": false },
{ "id": "64f2a1b3c4d5e6f7a8b9c0d2", "key": "sampling_interval", "value": "300", "sent": true }
]
}
```
- Element `id` is the parameter row id, so you can pass it straight to
[Create and edit device params](/docs/api/create-and-edit-device-params) or
[Delete device param](/docs/api/delete-device-param).
- A device with two parameters under the same key returns them as two separate elements, matching the REST
API.
- The column is part of the devices column set, so `SELECT *` includes it, and `GET /sql/tables` reports it
typed `json`.
:::warning[Selectable only]
Using `params` in `WHERE`, `GROUP BY`, `ORDER BY`, or inside an aggregate fails with a `400`. Filter by the
other device columns (`name`, `tags`, `active`, and the rest) and read the parameters from the result.
:::
## Your entity inventory
`entities()` and `entities_tag('key','value')` list your entities.
| Column | Type |
| ------------ | --------- |
| `id` | string |
| `name` | string |
| `tags` | json |
| `created_at` | timestamp |
| `updated_at` | timestamp |
`entities_tag('key','value')` returns every entity carrying the tag.
```sql
SELECT e.id, e.name, e.updated_at
FROM entities_tag('category', 'inventory') AS e
ORDER BY e.name
LIMIT 50
```
Inventory tables are single-table-only: they cannot be joined with any other table.
## What SQL is supported
The allowed statement shape, operators, aggregates, and JOIN rules are on the
[Queries](/docs/tagoio/tagosql/queries.md#what-sql-you-can-write) page.
## Discovering your schema
You do not need to memorize any of this. The schema discovery endpoint (`GET /sql/tables`, see the
[TagoIO API reference](https://api.docs.tago.io/)) returns the full catalog above plus the list of your own
devices and entities, and can resolve the columns of a specific entity. Query editors use it in the browsing
panels and in editor autocomplete, which behave differently:
- **The Tables and Functions panels.** Clicking a table family, a column, or a function copies its
identifier (or snippet) to your clipboard, so you can paste it where you want it. The panels never touch
the query you are writing.
- **Editor autocomplete.** Autocomplete is the path that writes into the editor. A completion in a `FROM` or
`JOIN` clause inserts the table function together with its alias, which the parser requires.
A profile token can always read the catalog. An analysis can read it too, when a policy grants it any action
on the **SQL Query** resource.
### The functions catalog
Alongside `tables` and `resources`, the response carries a `functions` array: everything callable inside a
query that is not a table function. It is built from the allowlist and is identical for every caller.
```json
"functions": [
{ "name": "count", "kind": "aggregate", "args": ["column"], "description": "Row count" },
{ "name": "avg", "kind": "aggregate", "args": ["column"], "description": "Average of a numeric column" },
{ "name": "sum", "kind": "aggregate", "args": ["column"], "description": "Sum of a numeric column" },
{ "name": "min", "kind": "aggregate", "args": ["column"], "description": "Smallest value of a column" },
{ "name": "max", "kind": "aggregate", "args": ["column"], "description": "Largest value of a column" },
{ "name": "session_user_id", "kind": "session", "args": [],
"description": "Id of the user executing the query, filled by the server",
"example": "COALESCE(session_user_id(), '...')" },
{ "name": "session_user_tag", "kind": "session", "args": ["key"],
"description": "The executing user's value for a tag key, filled by the server",
"example": "COALESCE(session_user_tag('key'), '...')" }
]
```
- `kind` is `aggregate` or `session`. The [session functions](/docs/tagoio/tagosql/session-context.md) scope a query to the
session running it.
- `example` is present only on the session entries: it shows the COALESCE authoring fallback. `COALESCE` is
not a standalone function here, because it is accepted only wrapping a session function.
- Table functions are not repeated in this array; they are the `tables` catalog above.
---
## TagoSQL
TagoSQL lets you ask a question about your data and get back a clean table of answers. Instead of pulling
raw data and filtering it yourself, you write a few lines that say which device, which variable, which
period, and how many rows you want. TagoIO does the work and returns exactly those rows.
:::info
Every query is read-only and limited to your own profile. A query can never reach data that is not yours,
and it can never change anything.
:::

## Your first query
Copy this, replace `DEVICE_ID` with the id of one of your devices, and run it. You get that device's last
10 readings.
```sql
SELECT variable, value, time
FROM device('DEVICE_ID') AS d
ORDER BY time DESC
LIMIT 10
```
You can find a device id on the device page in TagoIO, or let the query editor list your devices for you.
## Where to use it
| Context | Support |
| -------------------- | --------------------------------------------------------------------------------------------------- |
| **Admin** | Yes: write, save, and run queries with your profile token. |
| **Analysis** | Yes: run a saved query when an Access Management policy grants it **Execute**. |
| **TagoRUN** | Yes: Run users run a saved query through the same kind of grant. Custom widgets read data this way. |
| **Custom Dashboard** | Yes: list and run saved queries from the shell over the postMessage SQL bridge. |
| **TagoIO API** | Yes: the `/sql` routes create, manage, and execute queries. |
## What you can build with it
- **Dashboards.** Feed a table or chart widget with rows that are already filtered, sorted, and summed, so
the widget only has to draw them.
- **Reports.** Answer questions like "how many alarms did each site raise last month" in one query instead
of exporting data and working on it afterwards.
- **Fleet views.** Show the last reading of every device carrying a tag, such as the newest temperature for
all your sensors, in a single request.
- **Portals and applications.** Save a query once and let an analysis, a custom widget, or a TagoRUN portal
run it by id.
## How it works
1. **Write** the query. Start from the examples in [Getting started](/docs/tagoio/tagosql/getting-started.md) and change the
device, variable, or period.
2. **Save** it. A saved query is a resource on your profile with a name, a version history, and its own
settings. See [Queries](/docs/tagoio/tagosql/queries.md).
3. **Execute** it whenever you need the data, from a widget, an analysis, or your own application. See
[Executing Queries](/docs/tagoio/tagosql/executing-queries.md).
## Next steps
- [Getting started](/docs/tagoio/tagosql/getting-started.md) walks through the queries most accounts need, one recipe at a time.
- [Queries](/docs/tagoio/tagosql/queries.md) covers saving a query, what it holds, and
[which SQL is accepted](/docs/tagoio/tagosql/queries.md#what-sql-you-can-write).
- Look up the specifics as you need them: every [table and column](/docs/tagoio/tagosql/tables.md),
[parameters](/docs/tagoio/tagosql/parameters.md), [per-user results](/docs/tagoio/tagosql/session-context.md), and [errors](/docs/tagoio/tagosql/errors.md).
- Plan limits live with the rest of the platform's:
[Resource Limits](/docs/tagoio/my-account/billing/resource-limits#tagosql-query-limits) for what one query
may do, and [Rate Limits](/docs/tagoio/profiles/services/rate-limits-hard-limits.md) for requests per
minute.
- For endpoint-level detail (request and response schemas, every field and status code), use the
[TagoIO API reference](https://api.docs.tago.io/) under the **SQL Queries** section.
---
## ABS Telemetry
Devices from ABS Telemetry can be easily integrated with TagoIO. The integration allows uplink communication between the device and TagoIO while parsing messages to extract commands and variables in real time.

You will need to set up your device using the ABS portal and your TagoIO account.
## ABS Setup
You need to download the ABS software configurator (available here: [ABS software configurator download](https://abs-telemetry.com/downloads/)).
Open the file `Configurator_cel.exe` and follow these steps.
The first step is to configure the basic working parameters. At the main configurator window, be sure to select:
| host type | 99 |
After that, go to the **Extended Commands** tab and press the **HTTP** button.
Add _TagoIO Host_ in the **HTTP URL** field.
Change the method to **POST** and go to **Send Commands**, and you will have to configure some commands:
| Command | Description |
| ------------------------------------------------------------------------ | ------------------------------------------------------------ |
| `_extcmd=postcfg info` | Return configured values |
| `_extcmd=postcfg erase` | Delete POST settings |
| `_extcmd=postcfg Content-type,text/plain;Device-Token,_device_token_;;;` | Set the two parameters required to configure POST for TagoIO |
> Replace the text `device_token` with the value of your [device‑token](/docs/tagoio/devices/device-token.md).
Learn more information about the [ABS devices configuration](http://www.abstelemetria.com/abs-cel-io/#tab-id-2).
## TagoIO Setup
Add a **[device](https://tago.io/devices)** in your account at TagoIO. Go to _Devices_ and click on **'Add Devices'**, filter network by **'Cellular'** and pick your device from the list.

> Currently, the following device is integrated with TagoIO: **ABS CEL IO**
Click on **'Create Device'**, and you will have confirmation that the device was created.
Turn your device on, and wait for the data to arrive and be stored in your **[device](/docs/tagoio/devices/)**; it will be ready to be shown in the dashboards. You can start to build your **[Dashboard](/docs/tagoio/dashboards/)** by adding graphs, tables and more.
Enjoy your ABS devices with TagoIO!
---
## Adeunis with LoRaWAN™

TagoIO supports most Adeunis devices by providing integration with several
LoRaWAN™ networks. TagoIO parses incoming payloads and extracts the measured
variable values in real time, making all device data readily available for
display or processing in your Analysis.
See [Analysis](/docs/tagoio/analysis/) for working with and visualizing your
incoming device data.
:::tip
You can check whether a [parser](/docs/tagocore/device/payload-parser.md) script
was added to your device under the tab **Payload Parser** in your device page.
:::
## Adding a device
To add an Adeunis device to TagoIO:
1. Go to [Devices](https://tago.io/devices).
2. Click **Add Devices**.
3. Filter the network by the LoRaWAN™ provider you will connect with.
4. Pick your Adeunis device from the list.
The exact configuration steps will vary depending on the LoRaWAN™ provider you
choose.
## LoRaWAN™ providers
Select one of the following provider-specific integration guides to continue
your setup:
- [TTN (The Things Network)](/docs/tagoio/integrations/networks/the-things-network-lorawan.md)
- [Loriot](/docs/tagoio/integrations/networks/loriot-lorawan.md)
- [Everynet](/docs/tagoio/integrations/networks/everynet-lorawan.md)
- [Senet](/docs/tagoio/integrations/networks/senet-network.md)
- [Orbiwise](/docs/tagoio/integrations/networks/orbiwise-lorawan.md)
- [machineQ](/docs/tagoio/integrations/networks/machineq-lorawan.md)
Each provider link points to the corresponding TagoIO tutorial for configuring
Adeunis devices with that network.
After you have completed the setup above, you will be ready to move forward.\
Now, just turn your Adeunis device on and wait for the data to be stored on your **device**.\
Every time the device sends data, the parser will be executed, the
extracted data will be stored in the device, and it will be ready to be shown in
dashboards.
You can start building your [dashboards](/docs/tagoio/dashboards/) immediately.\
Also, you can create [notifications](/docs/tagoio/actions/) and more advanced
[scripts](/docs/tagoio/analysis/creating-analysis.md) as needed.
---
## Adeunis with Sigfox
TagoIO supports most Adeunis devices by providing an integration with the Sigfox
backend. Incoming messages from Adeunis devices are parsed in real time so
measured variables are extracted and made available to TagoIO for display in
widgets and for processing.

All data sent by your device will be readily available to be displayed in
widgets and processed in your [Analysis](/docs/tagoio/analysis/).
:::info
You can check if a parser script was added to your device under the tab "Payload
Parser".
:::
## Adding a device
To add an Adeunis device in TagoIO:
1. Go to [Devices](https://tago.io/devices).
2. Click on "+ New Device".
3. Filter the list by network and select "Sigfox".
4. Pick your specific Adeunis device from the list.
### Integrate your Adeunis with TagoIO
1. Turn on your Adeunis device and wait for the data to reach your TagoIO Device.
2. Each time the device sends data, the parser will be executed automatically,
extracting the measured variables and sending them to the device.
3. The extracted data is then available in widgets, dashboards, and can be
processed further.
You can start building **dashboards** immediately:
[Dashboard Overview](/docs/tagoio/dashboards/).
You can also create **notifications** or write advanced **scripts** to process the data:
- Notifications: [Notifications](/docs/tagoio/actions/)
- Scripts:
[Creating Analysis Scripts](/docs/tagoio/analysis/creating-analysis.md)
If you have questions about configuring your account for Sigfox and TagoIO, read
the article about [SigFox](/docs/tagoio/integrations/networks/sigfox.md).
---
## Beagle Bone Black

## Overview
This simple tutorial uses the BeagleBone Black (BBB) board to demonstrate key principles for integrating your solution with TagoIO. Beyond connecting the BBB to the cloud, you will learn how to structure and reuse this code in your own application.
The circuit uses a single digital input connected to a normally open switch on connector **P8 pin 19**. A 2.2 kΩ resistor pulls the line low (0 V) when the switch is closed, which gives a clean signal for the BBB's GPIO pin.
### Adding the Device
1. Log in to your TagoIO account and navigate to the **[Devices](https://tago.io/devices)** section.
2. Click the blue **Add Device** button, give it the name `dev01`, and click **Save**.
3. All devices must use a valid token when accessing TagoIO. The token is automatically generated upon creation. Open the device's **General information** page, click **QR Code** or **Tokens**, and copy the token to insert into your BBB code later.
### Building the Dashboard
1. In the left sidebar, open **Dashboards** and click the plus icon (`+`).
2. Name the dashboard (e.g., `BBB Dashboard`) and click **Save**.
3. Add a widget: Choose the **Display** widget, and configure it to show the variable `switch`.
4. Select your device (`dev01`), and click **Select as new option** then **Save**.
5. The widget will update in real time once data starts flowing from the BBB.
### Sending e‑mail Action
1. Add an action to send an email notification when the switch state changes to closed.
2. Configure the action: choose **Send e-mail**, enter the destination address and subject, and optionally a message such as "Hi, the switch on your BBB is closed!".
3. To avoid duplicate notifications, set up hysteresis by defining **Set Trigger** (closed) and **Reset Trigger** (open). This ensures only one email per state change.
## Example: sending a digital input status
In this example, the BBB will send the status of a digital input to TagoIO. The input status will be visualized on a dashboard. Using the [Actions](/docs/tagoio/actions/) capability, you can configure the system to send an email whenever the switch changes to the closed state.
### Sending data from BBB
TagoIO uses JSON over HTTP for device communication. For example, to report that the switch is closed:
```json
{ "variable": "switch", "value": "closed" }
```
#### Python Code
The following code was developed on a Debian distribution with Python 2.7.9 but works on any Linux distribution and Python version.
Before running the code, install the `tago` Python library:
```bash
sudo pip install -U tago
```
Create a file `bbb_switch.py` with the following content (replace the token placeholder):
```python
from tago import Tago
import Adafruit_BBIO.GPIO as GPIO
PIN = "P8_19"
GPIO.setup(PIN, GPIO.IN)
LOW = 0
HIGH = 1
# Initial level state
Level = GPIO.input(PIN) and HIGH or LOW
MY_DEVICE_TOKEN = '### INSERT YOUR TOKEN HERE ###'
my_device = Tago(MY_DEVICE_TOKEN).device
send_close = {
'variable': 'switch',
'value' : 'closed'
}
send_open = {
'variable': 'switch',
'value' : 'open'
}
def send_data(data_to_insert):
response = my_device.insert(data_to_insert)
print data_to_insert
print response
while True:
if Level == LOW:
if GPIO.input(PIN):
send_data(send_close)
Level = HIGH
elif GPIO.input(PIN) == LOW:
send_data(send_open)
Level = LOW
```
#### Tips for your code
1. Import the TagoIO library: `from tago import Tago`.
2. Replace `MY_DEVICE_TOKEN` with the token created for your device.
3. Prepare a JSON payload, e.g., `data_to_insert = {'variable': 'switch', 'value': 'closed'}`.
4. Send data: `result = my_device.insert(data_to_insert)`.
5. Check the API response to handle errors and confirm success.
### Running the application
Open your dashboard on TagoIO, then run the script on the BBB:
```bash
python bbb_switch.py
```
The widget will display the variable's value in real time. Press the switch button: you should see "closed" appear, followed by an email notification. Release the button to see "open". Repeat as needed.
Feel free to extend this example for your own applications: add more variables, use different programming languages, or integrate additional sensors.
---
## Connecting ZEN15 Power using SmartThings Hub
This tutorial shows how to bring electric power and energy measurements from the Zen15 sensor into your TagoIO account using a Samsung SmartThings hub.

The Zooz [Zen15](https://www.getzooz.com/zen15-power-switch/) Power Switch connects to the SmartThings hub using Z‑Wave wireless technology. The Zen15 can control loads and measure power and energy in real time so data can be displayed and processed by TagoIO.
## On this page
1. SmartThings Setup
2. TagoIO Setup
## SmartThings Setup
Assume that you already have your devices, hubs, and locations registered in the SmartThings portal.
- Make sure your ZooZ device is registered at the [SmartThings portal](https://support.smartthings.com/hc/en-gb/articles/360052390111-Devices-in-SmartThings).
- If you don't have the locations and hubs registered, access this [tutorial](https://support.smartthings.com/hc/en-us/articles/360052390151-SmartThings-Enabled-Hubs).
> SmartThings Hub demands a manual installation of a handler when using Zooz sensors.
> Click here for the [tutorial](https://www.thesmartesthouse.com/blogs/the-smartest-blog/how-to-install-a-custom-device-handler-in-smartthings). The code to be inserted in the handler to enable the Hub to find the Zen15 sensor can be found [here](https://github.com/krlaframboise/SmartThings/blob/master/devicetypes/krlaframboise/zooz-power-switch.src/zooz-power-switch.groovy).
### Configuring SmartThings to send data to TagoIO
1. Sign in to the SmartThings portal.
2. Click on **My Device Handlers** and create a new Device Handler.
3. Set up your Device Handler:
- **Name:** insert a name for your device handler
- **Namespace:** insert the preferred namespace
- **Capabilities:** select _Power Meter_ and _Energy Meter_
- Click **Create**.
4. After the Device Handler is created, a code is automatically generated. Remove all the generated code and replace it with our TagoIO handler. [Copy the code](https://github.com/tago-io/zooz-zen15/blob/master/smart_things_conf.rb) and paste it into your device handler.
5. Click **Save** and then click **Publish > For Me**.
6. Install the Device Handler on your devices:
- Click the **Simulator** button.
- Select your location and click **Set Location**.
- In the section "Choose a device to test with", select the device you want to install the handler (you can only install the handler on one device at a time).
- Click **Install**.
## TagoIO Setup
In your TagoIO account, go to [Device](https://admin.tago.io/devices) and click **Add Device**.
Select the Zen15 device type and fill in the required fields.
> The **Device ID** is obtained by following these steps:
>
> - In the SmartThings portal, select the **My devices** tab and find the device you just installed.
> To get the Device ID, go to the URL of the browser and copy the text as shown below.
>
> ```
> https://graph.api.smartthings.com/api/smartapps/installations/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
> ```
>
> - Paste it into the **Device ID** field.
Your Zen15 is now ready to send data to TagoIO. To visualize the incoming data, create a [dashboard](/docs/tagoio/dashboards/).
---
## Displaying Locations on a Map with TTN LoRaWAN
If you have a custom payload format decoder at TTN (The Things Network), you can send your payload variables directly to TagoIO.
To display locations on a Map, your data needs to be sent inside a "location" field with the format shown below:
```json
{
"variable": "speed",
"value": 10,
"location": {
"lat": 42.2974279,
"lng": -85.628292
}
}
```
TagoIO converts latitude and longitude fields to the specific format required, so you don't need to perform any conversions. To accomplish this, you only need to have the variables named `latitude` and `longitude` or `lat` and `lng`. TagoIO will automatically read these variables and create a new variable called `location`.
After your device is sending latitude and longitude information to TagoIO:
- Create a dashboard.
- Add a [Map widget](/docs/tagoio/widgets/map-and-location/map-widget/).
- Look for the device and add the variable `location` inside the Map widget.
---
## LoRaWAN Publication of Environmental Measurements with a LoRa-E5 Module
This tutorial explains how to implement a LoRaWAN publication on The Things Network (TTN) with a TagoIO integration using a MicroPython driver for a Grove LoRa‑E5 module. It was originally posted in French at https://stm32python.gitlab.io/fr/docs/Micropython/grove/lora-e5 and credits Julien Terrier and Yannick Marietti.
To learn more about LoRa and LoRaWAN, consult this page (link to a general LoRa/LoRaWAN overview). For a deeper technical explanation, read the document by Sylvain Montagny (University of Savoie Mont Blanc) (link to Sylvain Montagny's LoRa/LoRaWAN document). Additional references are available at the end of the full tutorial.
## Required materials and assembly
1. For the connected device (the NUCLEO‑WB55 board used in this tutorial), a Grove LoRa‑E5 module from Seeed Studio was chosen. The module uses the STM32WLE5JC System on Chip (SoC) from STMicroelectronics.
2. For the LoRaWAN gateway, the Things Indoor Gateway (TTIG) was chosen.
Minimum items:
- A TTIG LoRa‑WiFi gateway
- A Grove LoRa‑E5 module
Our connected device will consist of the following components:
1. A basic Grove extension board with its power switch set to the 3.3 V position
2. A NUCLEO‑WB55 board
3. A Grove BME280 module (environmental sensor)
**Assembly steps**
- Place the Grove extension board onto the NUCLEO board.
- Connect the BME280 module to an I²C connector on the Grove extension board.
- Mount the Grove LoRa‑E5 module on a UART connector (D9).
- Connect the Grove extension board to your computer using the NUCLEO board USB port.
**Obtaining the DevEUI and DevAddr of the LoRa‑E5 module**
The Grove LoRa‑E5 module communicates over UART with AT commands. To query its unique identifiers, use a MicroPython script that sends AT commands and prints the responses.
Below is a minimal `main.py` example that you can copy to your computer and flash onto the NUCLEO board (see flashing instructions in the next section).
```python
# Purpose of the script: Sending / Receiving messages via UART in command line mode
# This script is useful for communicating with modules equipped with AT firmware.
from machine import UART, const
DELAY_TIMEOUT = const(1000) # Duration (in milliseconds) during which the UART waits to receive a message
BAUDRATE = const(9600) # Communication speed, in bauds
UART_NUMBER = const(2) # Identifier of the NUCLEO‑WB55 board's UART to be used
RX_BUFF = const(512) # Size of the reception buffer (received messages will be truncated to this number of characters)
EOC = "\r\n" # Command termination to validate sending
uart = UART(UART_NUMBER, BAUDRATE, timeout=DELAY_TIMEOUT, rxbuf=RX_BUFF)
@micropython.native
def Reception(uart_object):
message_received = uart_object.read()
if not (message_received is None):
print("Message received: \r\n" + message_received.decode("utf-8"))
irq_uart = uart.irq(Reception, UART.IRQ_RXIDLE, False)
@micropython.native
def Emission():
print("Enter your command")
while True:
message_sent = input()
uart.write(message_sent + EOC)
print("Sent: " + str(message_sent))
Emission()
```
After flashing this script, open a serial terminal (e.g., PuTTY) to the board's UART.
Type `AT` and press ENTER; you should see:
```
Message received:+AT:OK
```
Then type `AT+ID` to retrieve the identifiers:
```
Message received:
+ID:DevAddr,44:10:BA:4A
+ID:DevEui,2C:F7:F1:20:44:10:BA:4A
+ID:AppEui,00:00:00:00:00:00:00:00
```
Keep the `DevEui` and `DevAddr` values for later use when configuring TTN.
## Firmware and MicroPython driver
To use the LoRa‑E5 module, this tutorial uses a MicroPython firmware built for the STM32WB series (stm32wb5x) with LoRa API support. The LoRa‑E5 MicroPython driver source and related documentation are available here: https://stm32python.gitlab.io/fr/docs/Micropython/grove/lora-e5
You will need the appropriate firmware and driver for the STM32WLE5JC-based module and instructions for flashing the NUCLEO board with the MicroPython image. (Refer to the linked MicroPython Grove LoRa‑E5 documentation for specific build and flashing steps.)
**Flashing the firmware**
1. Download the latest `stm32wb5x-micropython.bin` from the official repository.
2. Connect the NUCLEO board via USB, put it into DFU mode (hold the BOOT0 button while powering on).
3. Use STM32CubeProgrammer or `dfu-util` to flash the binary onto the board.
**Joining a private LoRaWAN network**
Once the firmware is running and you have obtained the DevEui/DevAddr, use the following script to configure the module for OTAA join with TTN:
```python
# Importing different drivers
import machine
from stm32_LoRa import *
from utime import sleep_ms
UART_WB55 = const(2)
devAddr = "44 10 BA 4A" # Obtained with AT+ID
appEui = "00 00 00 00 00 00 00 00"
appKey = "55 10 67 98 B2 15 EE 4E D0 33 19 DC 65 27 88 AB" # Obtained from TTN
loRa = LoRa(9600, UART_WB55, DataReceiveCallback=None)
status = loRa.setIdentify(DevAddr=devAddr, AppEui=appEui, AppKey=appKey)
def PrintLoRaParameters():
identify = loRa.getIdentify()
if identify != -1:
print("#####################################################################")
print("########## INITIALIZE ########")
print("#####################################################################")
print("LORA_DRIVER_VERSION: " + loRa.getDriverVersion())
print("#### " + loRa.getMode() + " ####")
print("#### AppKey: " + identify["AppKey"])
print("#### DevEUI: " + identify["DevEui"])
print("#### AppEUI: " + identify["AppEui"])
print("#### DevAddr: " + identify["DevAddr"])
else:
print("#### = Read identify fail. Reboot!")
sleep_ms(2000)
machine.reset()
def JoinNetwork():
joinStatus = False
tryJoin = 0
while not joinStatus:
print("#### = Try join n°" + str(tryJoin+1))
status = loRa.join()
if status == -1:
print("#### = Join Fail, retry in 10 seconds.")
tryJoin += 1
else:
joinStatus = True
print("#### = Join success.")
PrintLoRaParameters()
JoinNetwork()
```
After running this script (e.g., via PuTTY), you should see a "Join success" message indicating that the module has joined TTN.
**Publishing BME280 measurements**
Once the device is connected to TTN, use the following script to read data from the BME280 sensor and send it in a compact hexadecimal payload:
```python
# Importing different drivers
import machine
from stm32_LoRa import *
from utime import sleep_ms
UART_WB55 = const(2)
devAddr = "44 10 BA 4A"
appEui = "00 00 00 00 00 00 00 00"
appKey = "55 10 67 98 B2 15 EE 4E D0 33 19 DC 65 27 88 AB"
def DataReceived(Port=0, DataReceived=b""):
print("#### = Data received")
print(f"Data received on PORT: {Port}, Size = {len(DataReceived)}, Data = {[hex(x) for x in list(DataReceived)]}")
loRa = LoRa(9600, UART_WB55, DataReceiveCallback=DataReceived)
status = loRa.setIdentify(DevAddr=devAddr, AppEui=appEui, AppKey=appKey)
def PrintLoRaParameters():
identify = loRa.getIdentify()
if identify != -1:
print("#####################################################################")
print("########## INITIALIZE ########")
print("#####################################################################")
print("LORA_DRIVER_VERSION : " + loRa.getDriverVersion())
print("#### " + loRa.getMode() + " ####")
print("#### AppKey: " + identify["AppKey"])
print("#### DevEUI: " + identify["DevEui"])
print("#### AppEUI: " + identify["AppEui"])
print("#### DevAddr: " + identify["DevAddr"])
else:
print("#### = Read identify fail. Reboot!")
sleep_ms(2000)
machine.reset()
def JoinNetwork():
joinStatus = False
tryJoin = 0
while not joinStatus:
print("#### = Try join n°" + str(tryJoin+1))
status = loRa.join()
if status == -1:
print("#### = Join Fail, retry in 10 seconds.")
tryJoin += 1
else:
joinStatus = True
print("#### = Join success.")
def GetSendData():
TEMPO = const(600000) # 10 minutes
from time import sleep_ms
from machine import I2C
import bme280
i2c1 = I2C(1)
sleep_ms(1000)
print("I2C addresses used: " + str(i2c1.scan()))
sensor = bme280.BME280(i2c=i2c1)
trySend = 0
NB_BYTES = const(5)
loRaFrame = [0x00] * NB_BYTES
while True:
bme280data = sensor.values
temp, press, humi = bme280data[0], bme280data[1], bme280data[2]
print("="*40)
print(f"Temperature: {temp:.1f} °C")
print(f"Pressure: {press} hPa")
print(f"Relative Humidity: {humi}%")
temp = int(temp * 10)
press = int(press * 10)
humi = int(humi * 2)
loRaFrame[0] = (temp >> 8) & 0xFF
loRaFrame[1] = temp & 0xFF
loRaFrame[2] = (press >> 8) & 0xFF
loRaFrame[3] = press & 0xFF
loRaFrame[4] = humi
print("#### = Send data.")
sendStatus = loRa.sendData(loRaFrame, Port=1, NeedAck=False)
if sendStatus == -1:
print("#### = Join fail.")
trySend += 1
if trySend > 5:
machine.reset()
else:
print("#### = Send success.")
trySend = 0
loRa.enterLowPowerMode()
sleep_ms(TEMPO)
pyb.wfi()
PrintLoRaParameters()
JoinNetwork()
GetSendData()
```
**Important parameters**
- `appEui` must remain `00 00 00 00 00 00 00 00`.
- `appKey` is obtained when creating the application in TTN.
- The `devAddr`, `appEui`, and `appKey` values must be correctly filled for a successful join.
**Optional: Configuring communication channels for TTN**
To optimize channel usage, send the following AT commands to the module (e.g., via the AT command script above):
```
AT+CH=3,867.1,DR0,DR5
AT+CH=4,867.3,DR0,DR5
AT+CH=5,867.5,DR0,DR5
AT+CH=6,867.7,DR0,DR5
AT+CH=7,867.9,DR0,DR5
```
These commands can be integrated into the initialization of the `stm32_LoRa.py` driver.
## Credits and original post
This tutorial is based on the original French post found at the stm32python GitLab pages (link above). All credit for the MicroPython driver and original instructions go to Julien Terrier and Yannick Marietti.
---
## Markdown Guide
## Emphasis
Use Markdown to emphasize text:
```markdown
**bold**
_italics_
~~strikethrough~~
```
Additional emphasis examples:
- Italic using underscores: `_italic_`
- Strikethrough with four tildes: `~~~~strikethrough~~~~`
## Headers
Examples of header levels:
```markdown
# Big header
## Medium header
### Small header
#### Tiny header
```
## Lists
Unordered list:
```markdown
- Generic list item
- Generic list item
- Generic list item
```
Ordered list:
```markdown
1. Numbered list item
2. Numbered list item
3. Numbered list item
```
## Links
Inline link example:
```markdown
[Text to display](http://www.example.com)
```
## Quotes
Block quote example:
```markdown
> This is a quote.
> It can span multiple lines!
```
## Images
Need to upload an image? [TagoIO Files](/docs/tagoio/files) can help you with that.
Example Markdown for an image:
```markdown

```
## Tables
Basic Markdown table example:
```markdown
| Syntax | Description |
| --------- | ----------- |
| Header | Title |
| Paragraph | Text |
```
Additional tables from the legacy guide:
```markdown
| Column 1 | Column 2 | Column 3 |
| -------- | -------- | -------- |
| John | Doe | Male |
| Mary | Smith | Female |
```
_Or without aligning the columns..._
```markdown
| Column 1 | Column 2 | Column 3 |
| -------- | -------- | -------- |
| John | Doe | Male |
| Mary | Smith | Female |
```
## Displaying code
Inline code example:
Use backticks for inline code: `var x = 1;`
Fenced code block example (JavaScript):
```javascript
function greet() {
console.log("Hello, world!");
}
```
Additional code examples from the legacy guide:
Inline code:
```markdown
`var example = "hello!";`
```
Spanning multiple lines:
```markdown
var example = "hello!";
alert(example);
```
References:
- See the TagoIO Files interface for uploading images: [TagoIO Files](/docs/tagoio/files)
---
## MQTT with Sensor Tag
This example shows how to use the SensorTag Bluetooth module (Texas Instruments CC2650) to send data to TagoIO. No code modification is required on the SensorTag itself; because the SensorTag is used with MQTT, only the configuration setup is necessary.
The SensorTag communicates with a mobile device over Bluetooth. Texas Instruments provides a mobile app that converts that Bluetooth communication into MQTT and forwards the data to the cloud over cellular or Wi‑Fi.
## Connectivity Diagram

## Learn more
Learn more about the SensorTag CC2650 and how to get started: [SensorTag CC2650 and how to get started](https://www.ti.com/tool/CC2650STK)
## What this example does
- All sensor data from the SensorTag will be posted to TagoIO.
- Sensor data can be visualized on a TagoIO [dashboard](/docs/tagoio/dashboards/) in real time.
- This example uses the hardware version tested by the authors.
## Sensors included
The tested SensorTag hardware contains the following 10 sensors:
- Light
- Digital microphone
- Magnetic sensor
- Humidity
- Pressure
- Accelerometer
- Gyroscope
- Magnetometer
- Object temperature
- Ambient temperature
## Notes and limitations
- There is no native way to control the SensorTag's LED or buzzer using MQTT's subscribe method.
- Only configuration is required on the TagoIO side to receive and process MQTT messages from the SensorTag (via the mobile app).
## On this page
- Adding the Device
- Setup the SensorTag to send data to TagoIO
- Creating an MQTT Action to save data
### Adding the Device
1. Go to **Devices** in your TagoIO account and click on the **Add Device** button.
2. Select the connector type **MQTT** and look for the _SensorTag_ device.
3. When the device is created, open its details page and copy the generated token.
### Setup the SensorTag to send data to TagoIO
1. Download the official _SensorTag_ app for Android or iOS (make sure it matches your CC2650 model).
2. Open the app, press the power button on the SensorTag so that it starts communicating with your mobile device.
3. In the app, tap **Cloud configuration** and ensure the Bluetooth module is enabled; then select your device from the list.
4. Tap **All** (or the "+" icon at the top right if you have multiple tags) and choose **Advanced Configuration**.
5. In the _Advanced cloud config_ section:
- **Broker Address:** `mqtt.tago.io` (without `https://`)
- **Broker Port:** `1883`
- **Username:** `tagoio`
- **Password:** `` (the token you copied earlier)
- **Publish Topic:** `data`
- **Subscribe Topic:** leave as is (not used)
- **Publish rate:** `5000` (you may adjust later)
6. Turn the switch on to start pushing data to TagoIO.
7. If the configuration is correct and you have a network connection, sensor data will begin arriving in your account. You can view it in the Live Inspector tab of the device details. Note that this data is not yet stored in a bucket; you need to create an MQTT Action to save it.
### Creating an MQTT Action to save data
1. Go to **Actions** and click **Create new**.
2. Set the trigger type to **MQTT Topic**.
3. Choose the action type **Insert data into device**.
4. Select your SensorTag device, set the topic to `data`, and save the action.
Once the action is in place, all incoming MQTT messages on the `data` topic will be stored in the device's bucket for further analysis or visualization.
---
## PDF Service Generator
Learn how to create PDF documents using the TagoIO PDF service generator. Make an HTTP POST request to the PDF service endpoint and the response will be a base64‑encoded PDF. The base64 response can be sent by an Analysis or saved in Files. See the Analysis and Files documentation for examples on sending or saving the response ([Analysis](/docs/tagoio/analysis/), [Files](/docs/tagoio/files)).
## Notes
- You can generate a PDF with HTML size up to 256 KB.
- The service returns the PDF content as a base64 string.
## Endpoint
Use the following endpoint with an HTTP POST request:
```text
POST https://pdf.middleware.tago.io
```
## Headers
| KEY | TYPE | REQUIRED |
| ------------- | ------------------------ | -------- |
| authorization | your analysis token here | yes |
Learn how to get your Analysis token: [Analysis token](/docs/tagoio/analysis/).
## Body Description
| KEY | TYPE | REQUIRED |
| ---- | ------ | -------- |
| html | string | yes |
### Optional Parameters
| KEY | TYPE | DESCRIPTION |
| ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| format | string | Paper format. If set, it takes priority over `width` or `height`. Defaults to `'A4'`. Options: `Letter`, `Legal`, `Tabloid`, `Ledger`, `A0`-`A6`. |
| printBackground | boolean | Prints background graphics. Defaults to `false`. |
| displayHeaderFooter | boolean | Displays header and footer. Defaults to `false`. |
| headerTemplate | string | HTML template for the print header. Should be a valid HTML markup with classes: `date`, `title`, `url`, `pageNumber`, `totalPages`. |
| footerTemplate | string | HTML template for the print footer. Same format as `headerTemplate`. |
| scale | number | Scale of the webpage rendering. Defaults to `1`. Must be between `0.1` and `2`. |
| landscape | boolean | Paper orientation. Defaults to `false`. |
| pageRanges | string | Page ranges to print, e.g., `'1-5,8,11-13'`. Defaults to empty string (print all pages). |
| margin | object | Paper margins. Example: `{ top:'1.5cm', right:'1.5cm', left:'1.5cm', bottom:'1.5cm' }`. |
## Response
The response body will contain the generated PDF as a base64‑encoded string in the `result` field, and a boolean `status` indicating success.
| KEY | TYPE | DESCRIPTION |
| ------ | ------- | --------------------------------------- |
| status | boolean | `true` if successful, `false` if error. |
| result | string | Base64 string of the PDF document. |
You can decode and save it as a PDF file, or use Analysis to send it via email and Files to store it.
## Using the PDF Service in Analysis
You can also generate PDFs directly from TagoIO Analysis by following this example: [analysis-example-generatepdf-report](https://github.com/tago-io/analysis-example-generatepdf-report).
---
## Queclink GPS
Queclink GPS can be integrated with TagoIO through middleware that translates the device's TCP/IP protocol. The middleware enables uplink communication between the device and TagoIO while parsing messages to extract commands and variables in real time.

> Downlink messages must be accomplished using SMS messages or Queclink tools; they cannot be sent from TagoIO.
## Queclink Setup
Configure your GPS so it sends data to the TagoIO middleware. Follow the Queclink manual to set the protocol type, host URL, and port as shown below:
| Parameter | Value |
| --------: | --------------------------- |
| Protocol | TCP/IP |
| Host URL | queclink.middleware.tago.io |
| Port | 50005 |
## TagoIO Setup
Add a device in your account at TagoIO. Go to [Devices](https://admin.tago.io/devices) and click on **'Add Devices'**, filter network by **'Cellular'** and pick your device from the list.
### Devices currently integrated with TagoIO
- GL505
- GV300
- GV300 CAN
- GV75
- GV55\*
_contact us for updates_
Once the device is created, you will receive a confirmation. Turn your GPS on and wait for data to be stored on your [Device](/docs/tagoio/devices/).
### Building Dashboards
Create a dashboard by adding gauges, maps (add the variable **'location'**), tables, and more.
You can also set up notifications to send push notifications, SMS, or emails: [Notifications](/docs/tagoio/tagorun/notification.md). Advanced analysis scripts are available here: [Scripts](/docs/tagoio/analysis/creating-analysis.md).
### GPS Configuration
For detailed GPS configuration settings, refer to the Queclink manual: [GPS configuration](http://www.queclink.com/product).
---
## RadioBridge Devices
TagoIO is fully integrated with RadioBridge devices connected over Sigfox or LoRaWAN and with the RadioBridge backend. Read more about [Radio Bridge](https://radiobridge.com/).

All data sent by your device will be readily available to display in widgets or
to be processed by your Analysis.
:::info
You can perform both uplink and downlink operations.
:::
## Step 1:
First, go to your TagoIO account, and if this is your first device connected
between Radio Bridge and TagoIO, you will need to create an
[Authorization](/docs/tagoio/integrations/general/authorization.md).
Copy the **Authorization** to use in the next step.
### Step 2:
After configuring your device on RadioBridge console following the developer's
documentation, set up a Callback API.\
Go to your RadioBridge Console and open the Callback API page. Here, enter the
TagoIO callback URL and set an Authorization Header of your choice.
- **Callback URL:**
[https://radiobridge.middleware.tago.io/uplink](https://radiobridge.middleware.tago.io/uplink)
- **Header Authorization Code:** Enter the one you generated at Step 1.
### Step 3:
Copy the **Unique API URL** and return to the TagoIO Authorization page by
clicking [here](https://radiobridge.com/).\
Press the Pencil button to edit the authorization you created previously in
Step 1. Paste the **Unique API URL** and press save.
### Step 4:
Add the device at TagoIO. Go to [Devices](https://admin.tago.io/devices), click
on **Add Devices**, search for the RadioBridge category, and pick your device
from the list. If you can't find it, select **Custom RadioBridge** to add your
device.
Then follow the directions to integrate your device with TagoIO and start
building your own application.\
When completed, click on **Create Device**.
Turn your Radio Bridge device on, and wait for the data to be stored on your
device. Every time the [device](/docs/tagoio/devices/) sends data, it will be stored in the device's data and be ready to be shown in the dashboard.\
You can start editing the installed dashboard.
Also, you can create
[notifications](/docs/tagoio/tagorun/notification.md) and more advanced
[scripts](/docs/tagoio/analysis/creating-analysis.md) as needed.
:::tip
Depending on your type of device, a Parser may be automatically added. You may
want to edit the [parse function](/docs/tagocore/device/payload-parser.md) in
your device if necessary.
:::
---
## Raspberry Pi

Visit this [tutorial (Community)](https://community.tago.io/t/raspberry-pi-tagoio-using-sdk-or-mqtt-getting-started/991) to learn how to communicate a Raspberry Pi board with TagoIO using the TagoIO SDK or the MQTT protocol.
More tutorials like this are available in the [Forum inside our Community](https://community.tago.io/).
---
## Sens'it with Sigfox
TagoIO is fully integrated with Sens'it v2 and v3 devices. This integration uses
the Sigfox backend and parses incoming messages to extract measured variables in
real time. All data sent by your device will be available to display in widgets
or to process in your Analysis.

Sens'it + Sigfox + TagoIO Connectivity Diagram
## Prerequisites
- Sens'it device (v2 or v3)
- Sigfox developer access (to activate the devkit)
- TagoIO account with access to add devices
## Activate the Sens'it devkit
1. Activate your Sens'it as a devkit. As a developer, you must request access to
the Sigfox Cloud to use devkit mode.
2. Request access from the Sigfox Buy website: https://buy.sigfox.com/ and
follow their instructions to activate the devkit.
3. Once the devkit is activated, the device will be able to send data through
the Sigfox network for testing.
## Add the device to TagoIO
1. In TagoIO, go to [Devices](https://admin.tago.io/devices).
2. Click on "+ New Device".
3. Filter the network by "Sigfox" and choose your Sens'it device from the list
(select v2 or v3 accordingly).
4. After adding the device, incoming data will be parsed by TagoIO and become
available for widgets and Analysis.
## Dashboard Setup
After adding the device, click on the newly created dashboard in the left menu
and associate it with your Sens'it device. Turn the device on; data will arrive
automatically into the bucket and be displayed in widgets.
## Data Storage
Every time the device sends data, the parser is executed, the extracted data is
sent to the device's data, and it will be ready to be displayed in dashboards or processed in [Analysis](/docs/tagoio/analysis/).
## Custom Parsing (v2)
If you are using Sens'it v2, you may want to edit the **parse function** in your
device to extract variables from more modes. Check the complete payload
documentation: [Payload Parser](/docs/tagocore/device/payload-parser.md) and the
Sigfox developer guide for detailed information.
## Advanced Features
You can create **notifications** and advanced **scripts** as needed:
- Notifications: [Notifications](/docs/tagoio/tagorun/notification.md)
- Scripts / Analysis: [Analysis Overview](/docs/tagoio/analysis/)
## Notes
- The integration supports both Sens'it v2 and v3 devices.
- Ensure your Sigfox devkit is properly activated before registering the device
in TagoIO.
- For any specific Sigfox backend configuration or Sigfox Cloud settings,
consult Sigfox documentation or the Sigfox Cloud console:
[Sigfox](/docs/tagoio/integrations/networks/sigfox.md).
---
## TagoIO Academy
TagoIO Academy is a video learning library built into the TagoIO Admin. It collects short, focused tutorials that walk you through the platform, one feature at a time, so you can learn how something works and put it to use without leaving the Admin. Watch, learn, and work smarter.
## How to find it
The Academy lives in the main navigation menu of the Admin, alongside entries like What's new, Profiles, Billing, and Documentation. Click **Academy** to open it.
It opens as a drawer over your current screen, so you can browse and watch a tutorial without losing the page you were working on. Close the drawer to return to exactly where you left off.

## How it works
When the drawer opens, it shows the full catalog of tutorials. From there you can:
- **Search** by typing in the _What would you like to learn?_ box to find a tutorial by name or topic.
- **Filter by category** using the category selector to narrow the list to a single area, such as Devices, Actions, or Secrets.
- **Browse the cards**, each of which shows a thumbnail, the title, the length, the language, and the category.
Click any card to open the tutorial. The video page includes the player, the duration, language, and category, a short _What's it about?_ summary, and the instructor. Use **Back to Academy** to return to the catalog and pick another tutorial.
## What you can learn
The catalog covers features across the platform, including [Devices](/docs/tagoio/devices/), [Actions](/docs/tagoio/actions/), [Entities](/docs/tagoio/entities/), [Files](/docs/tagoio/files/), Access Management, [Secrets](/docs/tagoio/security/secrets.md), Custom Widgets, the Audit Log, adding team members, [TagoRUN](/docs/tagoio/tagorun/), and TagoTiP. Most tutorials run only a few minutes, so you can watch one before you start building.
## Why use the Academy
- **Learn in context.** Everything is one click away inside the Admin, so you can watch a tutorial and apply it on the same screen.
- **Get up to speed fast.** Tutorials are short and focused, which makes the Academy a quick way to onboard new team members or ramp up on a feature you have not used before.
- **Pairs with the documentation.** Watch the overview here to see a feature in action, then come back to these docs for the full details and step-by-step instructions.
---
## TouchTAG LoRaWAN™ Everynet
## Overview
TagoIO integrates with TouchTAG devices through a complete connector to the Everynet back-end server. The system decrypts and parses incoming payloads, extracting all measured variables in real time.
TouchTAG is a disposable, action-driven IoT device: a very low-cost, compact, and lightweight geotag with additional sensor functionality.

## Add a TouchTAG device to TagoIO
To add your TouchTAG device to TagoIO:
1. Go to the [Devices](/docs/tagoio/devices/) section in TagoIO.
2. Click on "+ New Device".
3. Select the connector "LoRaWAN Everynet" (select the LoRaWAN Everynet connector).
4. Search for "TouchTag" and create the device.
After creating the device, generate an **Authorization** for it (one per application).
[Generate Authorization](/docs/tagoio/integrations/general/authorization.md)
## Notes
- The connector handles decryption and parsing of the TouchTAG payloads so measured variables are available in real time in TagoIO.
- When configuring devices, ensure you select the correct connector ("LoRaWAN Everynet") and the correct device type ("TouchTag") so the parsing rules are applied.
- After creating your device, generate an **Authorization** for it (one per application).
[Generate Authorization](/docs/tagoio/integrations/general/authorization.md)
- Turn your TouchTAG device on and wait for data to arrive. Each time the device sends data, the parser is executed, extracted data is sent to the device, and it becomes available in dashboards.
- You can create **notifications** and more advanced **scripts** as needed.
## References
- Refer to the [Devices](/docs/tagoio/devices/) documentation for general instructions on creating and managing devices in TagoIO.
- Learn how to configure your account for Everynet:
[Everynet configuration guide](/docs/tagoio/integrations/networks/everynet-lorawan.md)
---
## Area Chart Widget
Charts are an effective way to visualize your data and discover insights. The Area Chart widget supports color customization, applying [Formula](/docs/tagoio/widgets/general/formula.md), and customizing the line shape (step or smooth), among other options.

This widget also accepts features like [metadata](/docs/tagoio/devices/payload-parser/metadata.md) and [groups](/docs/tagoio/devices/grouping-variables.md), which can be set in your variable data.
## 1. 'Data Sources' Field
This field allows you to set the device and variable that will be used by this widget.
### 1.1 'Data Sources' for Normal Dashboards
From the option **'Data Sources'** on the right menu, select one device from your list of devices and the variable that contains the data.

Click on the **Settings icon** to edit specific options for this variable, such as formulas, color, and more. Click on the **close icon** to remove this variable from the widget's data.
### 1.2 'Data Sources' for Blueprint Dashboards
From the option **'Data Sources'** on the right menu, add the [Blueprint device](/docs/tagoio/devices/blueprint-devices-entities.md) and input the name of the variable that contains the information.
When using a [Blueprint dashboard](/docs/tagoio/dashboards/blueprint-dashboard.md), the field **Variable** will list variables to be picked using the currently selected blueprint device.
## 2. Composing X-axis and Y-axis
In the area chart widget, it is possible to customize how the data will be displayed in the horizontal and vertical axis.

### 2.1 X-axis
By default, the X‑axis of the chart will be the time of the values, and you can customize the range of time.
If you need to group your data through the X‑axis even if they don't have the same time, select a variable that contains the group; all data will then be grouped by **groups**.
Data is ordered by time ascending, but it is possible to change it to groups:

It is also possible to define the X‑axis time range dynamically, using another variable data. The variable payload should look like this:
```json
{
"variable": "time_range",
"value": "Any value",
"metadata": {
"start_date": "2021-06-17T00:00:00.000Z",
"end_date": "2021-06-18T00:00:00.000Z"
}
}
```
The `start_date` specifies the start of the range, and the `end_date` specifies the end of the range. In this case, the date and format should be in ISO 8601.
### 2.2 Y-axis
The Y‑axis will automatically find the best scale for your data; it is not necessary to customize it unless you want to do so.
## 3. Filtering data
It is possible to pre‑set some date filters to be displayed, and these filters can be in minutes, hours, days, weeks, months, or custom by choosing a date in the calendar.

## 4. Increasing performance
For charts with a large number of data points, enable the **Aggregate** feature to improve widget performance and make the data easier to visualize. Learn more about the [Aggregate feature](/docs/tagoio/widgets/general/data-analytics.md).

---
## Calendar Heatmap Widget
The Calendar Heatmap widget renders a single variable as a calendar-style grid: hours of the day as rows and days, weekdays, or weeks as columns. Each cell is colored by the aggregated value of the records that fall inside it, making time patterns easy to spot, like a machine that always runs hot in the afternoon or anomalies that cluster on weekends.

The widget reads two kinds of data, chosen by a configuration toggle:
- **Raw values**: regular sensor telemetry. The widget buckets and aggregates the records and scales the colors to your data automatically.
- **Pre-scored values**: anomaly scores from 0 to 1, produced by your own [Analysis](/docs/tagoio/analysis/) scripts or another scoring pipeline. The color scale locks to the 0 to 1 range and the widget gains anomaly-specific features: severity tiers, expected ranges, and contributor breakdowns.
## Data source
The widget uses exactly one variable. From the **Data Sources** option, select the device and the variable that holds the data. On [Blueprint dashboards](/docs/tagoio/dashboards/blueprint-dashboard.md), add the Blueprint device and type the variable name.
The widget fetches data over a configurable time window of 1, 2, 4, or 8 weeks (**Time window**, default 4 weeks).
## Data interpretation
The **Data interpretation** section controls how records become cells.
- **Values are pre-scored (0-1)**: enable when the variable already holds anomaly scores between 0 and 1. Disable to aggregate raw sensor values.
- **Aggregation**: how multiple records inside the same cell combine: Average, Minimum, Maximum, Sum, Count, Last, or 95th percentile. Raw mode defaults to Average; pre-scored mode defaults to Maximum, so a single bad hour is never averaged away.
- **Scale minimum / maximum** (raw mode only): clamp the color scale instead of auto-scaling to your data's range.
- **Max data points**: how many records to fetch before bucketing. Leave empty to fetch the maximum your profile allows; raise it for long windows with frequent data to avoid truncation.
### Raw data format
Raw mode reads plain telemetry. No metadata is required:
```json
{
"variable": "temperature",
"value": 25.94,
"unit": "°C",
"time": "2026-07-08T14:00:00.000Z"
}
```
`value` must be numeric and `time` must be a valid timestamp; records failing either are ignored. The first record with a `unit` sets the unit shown in cells and tooltips.

### Pre-scored data format
In pre-scored mode, `value` is the anomaly score from 0 (normal) to 1 (critical), and `time` marks the start of the scored interval. Everything else is optional `metadata` that enriches the cell's hover card:
```json
{
"variable": "anomaly_score",
"value": 0.87,
"unit": "°C",
"time": "2026-07-08T14:00:00.000Z",
"metadata": {
"severity": "critical",
"observed": 25.94,
"expected_min": 22.55,
"expected_max": 26.55,
"contributors": [{ "variable": "temperature", "weight": 0.82 }]
}
}
```
| Metadata key | Type | What it does |
| ------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `severity` | `"normal"`, `"low"`, `"elevated"`, `"warning"`, `"high"`, or `"critical"` | Overrides the tier derived from the score, affecting the cell color, the tier word in the hover card, and the summary counts. |
| `observed` | number, in the source units | Shows an "Observed" row in the hover card, formatted with the record's `unit`. |
| `expected_min` / `expected_max` | numbers, in the source units (send both) | Shows an "Expected" range row. When `observed` is also present, the hover card draws a small sparkline of the observed value against the expected band. |
| `contributors` | array of `{ "variable": string, "weight": number }`, weight from 0 to 1 | Shows the top contributing variables as bars in the hover card. The single highest contributor weight across the grid appears as the "Top driver" percentage in the anomaly summary. |
The `severity` scale has five tiers; `warning` and `high` both map to the **High** tier, so sending `severity: "warning"` shows the "High" tier word and color.
Extra lineage keys such as `model_id`, `model_version`, `run_id`, and `interval` are accepted and ignored by the widget, so you can keep provenance on your scored records.
:::info
When several records fall into one cell, the metadata shown comes from the record the aggregation selected. Selecting aggregations (Maximum, Minimum, Last, 95th percentile) carry the chosen record's metadata; averaging aggregations (Average, Sum, Count) only show metadata when the cell holds a single record.
:::
## Visualization options
The **Visualization** section controls the grid's look:
- **Palette**: Traffic, Neutral alarm, Heat, or Color-blind safe. Tier palettes color cells in five discrete severity steps; the Heat palette is a continuous gradient.
- **Grid mode**: Daily (one column per day), Day of week (columns Mon through Sun), or Weekly (one column per week).
- **Hour bin**: group rows into 1, 2, or 3-hour blocks.
- **Week starts on**: Monday or Sunday.
- **Density**: Comfortable, Compact, or Chunky cell sizing.
- **Legend**: place the legend on any side of the grid or hide it, with start, center, or end alignment.
- **No-data cell style**: how empty cells render (dashed, hollow, hatch, or dot).
- **Show header stat**: adds a headline stat above the grid, either the peak cell or the count of critical hours.
- **Show anomaly summary** (pre-scored mode only): adds a footer highlighting the worst cell and the top driver's contribution as a percentage. Per-tier cell counts render in the legend by default, moving into the footer only when the legend is hidden or the Heat palette is used.
The widget colors itself from its own palette and the widget's background, so it renders consistently on the Admin, on [TagoRUN](/docs/tagoio/tagorun/), and when [embedded](/docs/tagoio/widgets/general/embedding-widgets-to-your-website.md), including on custom widget background colors.
## Severity filter
Enable **Show severity filter to viewers** to give dashboard viewers a slider that hides cells below a chosen severity floor, letting them focus on the worst hours. The **Severity filter start value** sets where the slider starts; with the slider hidden, the same value acts as a fixed floor. The filter is a viewing aid: it never changes stored data, and reloading the dashboard resets it to the configured start value.
## Interacting with the grid
Hovering a cell shows a detail card with the value or score, the date and hour range, and, in pre-scored mode, the severity tier plus any metadata extras (observed value, expected range, contributors). Click a cell to pin its card while you compare it against others; press Escape or click elsewhere to unpin. The grid is keyboard accessible: focus it and use the arrow keys to move between cells, Enter or Space to pin, and Escape to unpin.

## Custom text
The **Custom text** option overrides the widget's built-in labels, such as severity tier names, legend text, and hover card rows. Each field accepts a literal string or a dictionary expression like `#NS.KEY#`, which resolves per language through the [TagoRUN dictionary](/docs/tagoio/tagorun/dictionaries/using-dictionaries-multi-language.md).
## Timezone
Cells bucket by hour, so the timezone matters: the **Timezone** section sets which timezone the grid uses to place records into days and hours. It defaults to your account timezone.
---
## Horizontal Bar Widget
Charts are a useful way to visualize your data and look for insights. You can customize colors, apply [Formula](/docs/tagoio/widgets/general/formula.md), decide whether bars are stacked, and configure other visual options.

This widget also accepts features such as [metadata](/docs/tagoio/devices/payload-parser/metadata.md) and [groups](/docs/tagoio/devices/grouping-variables.md), which can be set in your variable data.
## 1. 'Data Sources' Field
This field allows you to set the device and variable that will be used in this widget.
### Editing Variable Options
Click on the **cog** icon next to a variable to edit its options, such as formulas or color. Click on the **close** icon to remove that variable from the widget's data.
### Dashboard Types
The available options in the _Data Sources_ field depend on whether you are using a normal dashboard or a blueprint dashboard.
#### 1.1 'Data Sources' for Normal Dashboards
From the _Data Sources_ option on the right‑hand menu, select one device from your list of devices and then choose the variable that contains the data.
#### 1.2 'Data Sources' for Blueprint Dashboards
From the _Data Sources_ option on the right‑hand menu, add a **Blueprint device** and input the name of the variable that holds the information.
When using a [Blueprint dashboard](/docs/tagoio/dashboards/blueprint-dashboard.md), the field **Variable** will list variables to be picked using the currently selected blueprint device.
## 2. X-axis and Y-axis
In the horizontal bar chart widget you can customize how data is displayed on both axes.
### 2.1 X‑axis
By default, the X‑axis shows the time of each value, and you can set a custom time range.
If you need to group data that does not share the same timestamp, select a variable that contains the grouping key; all values will then be grouped by **groups**.
You can also define the X-axis time range dynamically using another variable's metadata:
```json
{
"variable": "time_range",
"value": "Any value",
"metadata": {
"start_date": "2021-06-17T00:00:00.000Z",
"end_date": "2021-06-18T00:00:00.000Z"
}
}
```
The `start_date` and `end_date` must be in ISO 8601 format.
### 2.2 Y‑axis
On the horizontal axis you can adjust scaling behavior, apply metric prefixes or abbreviations, change the step value (the distance between ticks), and more.
By default the widget automatically selects an appropriate scale for your data; manual adjustments are only needed if you have specific requirements.
## 3. Filtering Data
You can pre‑set date filters that will be shown when the widget loads. Filters can be expressed in minutes, hours, days, weeks, months, or a custom range chosen from a calendar picker.
## 4. Increasing performance
For charts with a large number of data points, enable the **Aggregate** feature to improve widget performance and make the data easier to visualize. Learn more about the [Aggregate feature](/docs/tagoio/widgets/general/data-analytics.md).
---
## Line Chart Widget
Charts are an effective way to visualize your data and discover insights. The Line Chart widget supports color customization, applying Formula, and changing the line shape (step or smooth), among other options.

This widget also accepts features like [metadata](/docs/tagoio/devices/payload-parser/metadata.md) and [groups](/docs/tagoio/devices/grouping-variables.md), which can be set in your variable data.
## On this page
1. 'Data Sources' Field
2. Composing X-axis and Y-axis
3. Filtering data
4. Increasing performance
5. Forecast
6. Time bands and value bands
## 1. 'Data Sources' Field
This field allows you to set the device and variable that will be used by the widget.
### 1.1 'Data Sources' for Normal Dashboards
From the option **Data Sources** on the right menu, select one device from your list of devices and the variable that contains the data.
### 1.2 'Data Sources' for Blueprint Dashboards
From the option **Data Sources** on the right menu, add the [Blueprint device](/docs/tagoio/devices/blueprint-devices-entities.md) and input the name of the variable that contains the information.
When using a [Blueprint dashboard](/docs/tagoio/dashboards/blueprint-dashboard.md), the field **Variable** will list variables to be picked using the currently selected blueprint device.
## 2. Composing X-axis and Y-axis
In the line chart widget, it is possible to customize how the data will be displayed in the horizontal and vertical axis.
### 2.1 X-axis
By default, the X‑axis of the chart will be the time of the values, and you can customize the range of time.
If you need to group your data through the X‑axis even if they don't have the same time, select a variable that contains the group; all data will then be grouped by **groups**.
Data is ordered by time ascending, but it is possible to change it to groups.

It is also possible to define the X‑axis time range dynamically, using another variable data. The variable data should look like the following payload:
```json
{
"variable": "time_range",
"value": "Any value",
"metadata": {
"start_date": "2021-06-17T00:00:00.000Z",
"end_date": "2021-06-18T00:00:00.000Z"
}
}
```
The `start_date` specifies the start of the range, and the `end_date` specifies the end of the range. In this case, the date and format should be in the ISO 8601 format.
### 2.2 Y-axis
In the vertical axis, it is possible to customize the scaling behavior: apply metric prefixes and abbreviations, change the step value (represents the difference between each tick), and more.
The Y‑axis will automatically find the best scale for your data; it is not necessary to customize it unless you want to do so.
## 3. Filtering data
It is possible to pre‑set some date filters to be displayed, and these filters can be in minutes, hours, days, weeks, months, or custom by choosing a date in the calendar.

## 4. Increasing performance
For charts with a large number of data points, enable the **Aggregate** feature to improve widget performance and make the data easier to visualize. Learn more about the [Aggregate feature](/docs/tagoio/widgets/general/data-analytics.md).
## 5. Forecast
Each data source in a Line Chart can overlay a forecast: a dashed projection that continues from the last real data point, plus a shaded confidence band that widens with the forecast horizon. The forecast values come from a second variable, typically written by an [Analysis](/docs/tagoio/analysis/) script that runs your forecasting model.

### Configuring the forecast
In the widget's edit page, open the data source's options and fill in the **Forecast** group:
- **Forecast device** and **Forecast variable**: where the forecast records live. On Blueprint dashboards, select the Blueprint device association instead.
- **Points**: how many forecast records to read. Defaults to the widget's max points.
- **Line style** (dashed or dotted) and **Dash gap**: the projection line's appearance.
- **Band opacity**: the confidence band's fill opacity.
- **Formula**: transforms the forecast values, useful when the base variable also has a [Formula](/docs/tagoio/widgets/general/formula.md) applied. The formula transforms the projection and its confidence bounds together, so the dashed line continues the solid line without a jump at the seam.
### Forecast data format
Each record holds one future point. The projection center goes in `value`, and the confidence bounds go in the record's [metadata](/docs/tagoio/devices/payload-parser/metadata.md):
```json
{
"variable": "forecast_temperature",
"value": 27.31,
"unit": "°C",
"time": "2026-07-10T18:05:00.000Z",
"metadata": {
"lower": 26.42,
"upper": 28.2
}
}
```
- `time` must be in the future relative to the base variable's data: points at or before the base variable's latest record are not drawn. Because these records carry future timestamps (immutable storage rejects far-future data), the device holding them must use a **mutable** storage type, or a [**hybrid**](/docs/tagoio/devices.md#hybrid-immutable--mutable) device with the forecast variable routed to its mutable side.
- `metadata.lower` and `metadata.upper` are absolute values (not offsets) and both must be present for the confidence band to draw at that point. Records without them still draw the projection line.
- The projection line appears in the chart's hover tooltip like any other series; the confidence band does not.
## 6. Time bands and value bands
Line Charts can also shade time windows (X-axis) and value zones (Y-axis) driven by variable data, with optional recoloring of the series inside them. See [Time Bands & Value Bands](/docs/tagoio/widgets/general/chart-bands.md).
---
## Multiple Axes in Chart Widgets
All chart-type widgets in TagoIO can be configured to display more than one Y‑axis. The widgets that support this feature include:
- [Area Chart](/docs/tagoio/widgets/charts/area-chart-widget.md)
- [Line Chart](/docs/tagoio/widgets/charts/line-chart-widget.md)
- [Horizontal Bar](/docs/tagoio/widgets/charts/horizontal-bar-widget.md)
- [Vertical Column](/docs/tagoio/widgets/charts/vertical-column-widget.md)
- [Multiple Charts](/docs/tagoio/widgets/charts/multiple-charts-widget.md)
## Adding multiple axes to your chart
To add multiple axes to a chart widget:
1. Open the widget's edit page.
2. Under the **Options** section, open the **Y‑axis** subsection.
3. Click the Multiple Axes button to add and configure additional Y‑axes.

After clicking the Multiple Axes button you can associate one or more variables with each Y-axis. By default all variables are automatically assigned to the **y1** axis unless you change it. Only variables that have been added in the widget's **Data Sources** section can be selected here.
Once you have configured the axes, click **Confirm** to preview the changes and then **Save** to apply them.
Notes:
- After enabling multiple axes, you can assign different groups to specific Y‑axes so each groups scales appropriately.
- Use the Y‑axis settings to configure labels, units, and axis position (left/right) for each axis. You can also hide a label if desired.
---
## Multiple Charts Widget
Charts are an effective way to visualize your data and find insights. The Multiple Charts widget lets you customize colors, apply Formula (/docs/tagoio/widgets/general/formula), change line shapes (step or smooth), choose whether groups are stacked, and more.
When using multiple charts widget, each variable produces a different chart. In
the same widget it is possible to have each chart as an
[Area Chart](/docs/tagoio/widgets/charts/area-chart-widget.md),
[Line Chart](/docs/tagoio/widgets/charts/line-chart-widget.md),
[Horizontal Bar](/docs/tagoio/widgets/charts/horizontal-bar-widget.md), or
[Vertical Column](/docs/tagoio/widgets/charts/vertical-column-widget.md).

This widget also accepts features like
[metadata](/docs/tagoio/devices/payload-parser/metadata.md) and
[groups](/docs/tagoio/devices/grouping-variables.md), which can be set in your
variable data.
## 1. 'Data Sources' Field
This field allows you to set the device and variable that will be used in this
widget.
Click on the **settings icon** to edit specific options for this variable, such as
the chart type, formulas, and more. Click on the **close icon** to remove this
variable from the widget's data.
### Normal Dashboards
From the option **'Data Sources'** on the right menu, select one device from your
list of devices and the variable that contains the data.
### Blueprint Dashboards
From the option **'Data Sources'** on the right menu, add the
[Blueprint device](/docs/tagoio/devices/blueprint-devices-entities.md) and input the
name of the variable that contains the information.\
When using a blueprint dashboard, the field **Variable** will list variables to be picked using the currently selected blueprint device.
## 2. Composing X-axis and Y-axis
In the multiple charts widget, it is possible to customize how the data will be
displayed in the horizontal and vertical axis.
### 2.1 X-axis
By default, the X‑axis of the chart will be the time of the values, and you can
customize the range of time.\
If you need to group your data through the X‑axis, even if they don't have the
same time, select a variable that contains the group; all data will then be
grouped by **groups**.
Data is ordered by time ascending, but it can also be sorted by groups:
It is also possible to define the X-axis time range dynamically, using another
variable's data. The variable payload should look like:
```json
{
"variable": "time_range",
"value": "Any value",
"metadata": {
"start_date": "2021-06-17T00:00:00.000Z",
"end_date": "2021-06-18T00:00:00.000Z"
}
}
```
The `start_date` specifies the start of the range, and the `end_date` specifies
the end of the range. The date must be in **ISO 8601** format.
### 2.2 Y-axis
In the vertical axis you can customize the scaling behavior: apply metric
prefixes and abbreviations, change the step value (the difference between each
tick), and more.\
The Y‑axis will automatically find the best scale for your data; it is not
necessary to customize it unless you want a specific presentation.
## 3. Filtering data
It is possible to pre‑set date filters that will be displayed in the widget.
These filters can be set in minutes, hours, days, weeks, months, or custom by
choosing a date in the calendar.
## 4. Increasing performance
For charts with a large number of data points, enable the **Aggregate** feature to improve widget performance and make the data easier to visualize. Learn more about the [Aggregate feature](/docs/tagoio/widgets/general/data-analytics.md).
:::warning
The greater the factor, the higher the performance gain, but as the factor
increases, the data becomes less recognizable.
:::
---
## Pie Widget
Using data from multiple variables, the Pie widget automatically produces a pie chart where each slice represents a variable's data.

This widget also accepts features like [metadata](/docs/tagoio/devices/payload-parser/metadata.md) that can be set in your variable data.
## Creating your own
To add the Pie widget to your dashboard, choose the Pie widget from the widget list and customize it to your preference. You can edit it using the options located on the right side of the widget.
### Data Sources Field
This field allows you to set the device and variable that will be used in this widget.
Click on the settings icon to edit specific options for this variable, such as formulas or color. Click on the close icon to remove this variable from the widget's data.
#### Normal Dashboards
From the option **Data Sources** on the right menu, select one device from your list of devices and the variable that contains the data.
#### Blueprint Dashboards
From the option **Data Sources** on the right menu, add the _Blueprint device_ and input the name of the variable that contains the information.
When using a [Blueprint dashboard](/docs/tagoio/dashboards/blueprint-dashboard.md), the field **Variable** will list variables to be picked using the currently selected blueprint device.
---
## Semi Donut Widget
Using data from multiple variables, the Semi Donut widget automatically produces a semi-donut chart in which each slice represents a variable's data.

This widget also accepts features like [metadata](/docs/tagoio/devices/payload-parser/metadata.md), which can be set in your variable data.
## Creating your own
To add the widget to your dashboard, select the Semi Donut widget from the widget list and customize it to your preference. You can edit it using the options located on the right side of the widget editor.
### 1. 'Data Sources' Field
This field allows you to set the device and variable that will be used in this widget. It is dependent on the type of dashboard you are using; the difference is explained below.
> Click on the settings icon to edit specific options for this variable, such as formulas, color, and more. Click on the close icon to remove this variable from the widget's data.
##### 1.1 'Data Sources' for Normal Dashboards
From the option **Data Sources** on the right menu, select one device from your list of devices and the variable that contains the data.
##### 1.2 'Data Sources' for Blueprint Dashboards
From the option **Data Sources** on the right menu, add the [Blueprint device](/docs/tagoio/devices/blueprint-devices-entities.md) and input the name of the variable that contains the information.
:::info
When using a [Blueprint dashboard](/docs/tagoio/dashboards/blueprint-dashboard.md), the field **Variable** will list variables to be picked using the currently selected blueprint device.
:::
---
## Semi Pie Widget
Using data from multiple variables, the Semi Pie widget automatically produces a Semi Pie chart where each slice represents a variable's value.

This widget also accepts features like [metadata](/docs/tagoio/devices/payload-parser/metadata.md), which can be set in your variable data.
It works for both Normal and Blueprint dashboards.
## Creating your own
To add the Semi Pie widget to your dashboard, choose the Semi Pie widget from the widget list and customize it to your preference. You can edit it using the options located on the right side of the widget.
### Data Sources Field
This field allows you to set the device and variable that will be used in this widget. Click on the settings icon to edit specific options for this variable, such as formulas, color, and more. Click on the close icon to remove this variable from the widget's data.
#### For Normal Dashboards
From the option 'Data Sources' on the right menu, select one device from your list of devices and the variable that contains the data.
#### For Blueprint Dashboards
From the option **'Data Sources'** on the right menu, add the [Blueprint device](/docs/tagoio/devices/blueprint-devices-entities.md) and input the name of the variable that contains the information.
When using a [Blueprint dashboard](/docs/tagoio/dashboards/blueprint-dashboard.md), the field **Variable** will list variables to be picked using the currently selected blueprint device.
---
## Vertical Column Widget
Charts help you visualize your data and gain insights. The Vertical Column Widget lets you customize colors, apply [Formula](/docs/tagoio/widgets/general/formula.md), choose whether groups are stacked, and more.

This widget also supports features such as [metadata](/docs/tagoio/devices/payload-parser/metadata.md) and [groups](/docs/tagoio/devices/grouping-variables.md), which can be set in your variable data.
## 1. 'Data Sources' Field
This field allows you to set the device and variable that will be used by the widget.
**Editing options for the selected variable**
- Click on the **settings icon** to edit specific options such as formulas, color, and more.
- Click on the **close icon** to remove this variable from the widget's data.
The available variables depend on the type of dashboard you are using; the difference is explained below.
### 1.1 Data Sources for Normal Dashboards
From the option **'Data Sources'** in the right‑hand menu, select one device from your list and then choose the variable that contains the data.
### 1.2 Data Sources for Blueprint Dashboards
From the option **'Data Sources'** in the right‑hand menu, add the **Blueprint device** and input the name of the variable that contains the information.
When using a [Blueprint dashboard](/docs/tagoio/dashboards/blueprint-dashboard.md), the field **Variable** will list variables to be picked using the currently selected blueprint device.
## 2. Composing X-axis and Y-axis
In the vertical column chart widget you can customize how data is displayed on both axes.
### 2.1 X‑axis
By default, the X‑axis shows the time of each value, and you can set a custom time range.
If you need to group data that does not share the same timestamp, select a variable that contains the grouping key; all values will then be grouped by **groups**.
Data is ordered by time ascending by default, but it can also be sorted by groups.
You can define the X‑axis time range dynamically using another variable. The payload should look like this:
```json
{
"variable": "time_range",
"value": "Any value",
"metadata": {
"start_date": "2021-06-17T00:00:00.000Z",
"end_date": "2021-06-18T00:00:00.000Z"
}
}
```
The `start_date` specifies the start of the range, and the `end_date` specifies the end. Both dates must be in ISO 8601 format.
### 2.2 Y‑axis
You can customize the scaling behavior on the vertical axis, apply metric prefixes or abbreviations, change the step value (the difference between each tick), and more.
The Y‑axis will automatically find the best scale for your data; manual adjustments are only needed if you have specific requirements.
## 3. Filtering data
You can pre‑set date filters to be displayed on the chart. Filters can be set in minutes, hours, days, weeks, months, or custom by choosing a date from the calendar.
## 4. Increasing performance
For charts with a large number of data points, enable the **Aggregate** feature to improve widget performance and make the data easier to visualize. Learn more about the [Aggregate feature](/docs/tagoio/widgets/general/data-analytics.md).
---
## Custom Widget Parameters
Parameters allow you to send a list of keys and values to your custom widget.
One common use for parameters is to change how your code behaves. For example, you can point two widgets to the same link but configure each to exhibit different behaviors by using different parameters.

Each parameter has a unique key and a value. The key is used in your code to retrieve the parameter's value.
Note: You can specify up to 20 parameters.
## Usage in code
Before reading this section, you should read the [Custom Widget tutorial](https://help.tago.io/portal/en/community/topic/custom-widget-iframe) to understand how the widget code works.
In your code, the parameters are received in the `onStart` function, for example:
```javascript
window.TagoIO.onStart((widget) => {
const display = widget.display;
// Your code here. Parameters are provided to the widget when it starts
});
```
For details on how to access specific parameter values inside your widget code, refer to the Custom Widget tutorial or the rest of the Custom Widget documentation.
Parameters are exposed as an array on `display.parameters`. You can retrieve them like this:
```javascript
window.TagoIO.onStart((widget) => {
const display = widget.display;
const parameters = display.parameters;
// Example: accessing the first two parameters by index
const parameter1 = parameters[0];
const parameter2 = parameters[1];
// Your code here. Use the retrieved parameters as needed
});
```
---
## Custom Widget Overview
Custom Widgets are flexible components that let you build any interface or visualization needed for your application. They can be created with plain web technologies (HTML, CSS, and JavaScript) or with client-side frameworks, provided the final build outputs HTML, CSS, and JavaScript.
The widget must include at least some JavaScript functionality provided via our library ([our library](https://github.com/tago-io/custom-widget)).
## Supported technologies
- HTML, CSS, JavaScript (minimum requirement: include JavaScript from our library)
- Client-side frameworks (e.g., React, Vue, Angular) are supported as long as the framework outputs HTML, CSS, and JavaScript in the build process
## Examples
Below are example outputs from Custom Widgets (charts and a map) to illustrate what you can build:





## Notes
- Ensure your widget bundle includes any required JavaScript and CSS files so the widget works when embedded.
- If you use a framework, configure the build process to produce static HTML/CSS/JS assets that the TagoIO widget loader can consume.
## Creating your own
The sky's the limit when using this widget. You can build your widget using Angular, React, Vue.js, or even plain old JavaScript.
To understand how to write the code for your Custom Widget, click [here](https://community.tago.io/t/custom-widget-iframe/842).
## Data Sources
Due to their versatility, Custom Widgets allow you to use data from different sources in TagoIO.
You can use data stored in your **device's variables** by configuring your devices and the variables in the **Data Sources** section of the widget's configuration.
You can also use data from **resources** such as devices and users, just like on the [Device List](/docs/tagoio/widgets/tables/device-list-widget.md) and [User List](/docs/tagoio/widgets/tables/user-list-widget.md) widgets, by configuring the tag filters in the **Device Filters** and/or **User Filters** sections of the widget's configuration. It's possible to build your custom logic by mixing data from different data sources, such as getting data from variables and combining them with Devices/User.
## Main Configuration
After you've coded your Custom Widget, you need to host it and provide a link to the location where you've hosted it.
:::tip
You can use our [TagoIO Files](/docs/tagoio/files/) to host your custom Widget.
:::

## Parameters
Parameters allow you to send a list of keys and values to your Custom widget.
One use of these parameters is to change the way your code behaves. You can have two widgets pointing to the same link, but exhibiting different behaviors.

Read more about [Custom Widget Parameter](/docs/tagoio/widgets/custom-widget/custom-widget-parameters.md).
## User Control
You may choose to run an Analysis once you send data from your widget. To do so, inform the desired Analysis in the Run analysis when sending data field.

---
## ECharts Custom Widget Tutorial
In this tutorial, we will guide you through creating a Ring Gauge custom widget that displays multiple variables simultaneously in an interesting format.
To achieve this, we use the [ECharts library](https://echarts.apache.org/), a highly customizable charting library that simplifies the development of interactive and visually appealing data visualizations. Using ECharts can greatly speed up development and help ensure the custom widget is both efficient and aesthetically pleasing.

This tutorial is structured into three detailed sections to support a smooth learning experience:
1. Installing Dependencies and Setting Up Your Development Environment: Walk through the initial setup, including installing necessary dependencies and configuring your environment so you have everything needed to get started.
2. Key Components of Your Custom Widget: Explanation and examples of the main building blocks of the widget, including `widget.tsx` and `widget.view.tsx`, to implement core functionality and visual layout.
3. Deploying your Custom Widget Code to TagoIO Files: Steps to deploy your widget code to the TagoIO Files section so it can be used within the platform.
## Installing Dependencies and Setting Up Your Development Environment
We will walk you through the initial setup, including installation of required dependencies and configuration of your development environment. This ensures you have the tools and libraries needed for building and testing the Ring Gauge widget.
### Prerequisites
Before you begin, ensure you have the following:
- **A TagoIO account**: You need an active TagoIO account to access the dashboard and deploy custom widgets. Click [here](https://admin.tago.io/signup) to create your account.
- **Basic knowledge of TagoIO**: Familiarize yourself with TagoIO's platform and understand the basics of creating and managing dashboards.
- **Node.js and npm**: Ensure you have Node.js and npm (Node Package Manager) installed on your machine.
- **React knowledge**: Basic familiarity with React is necessary as you will be using it to create custom widgets.
### Download the Project Repository
First, download the project repository from GitHub. This repository contains the template and initial setup for creating custom widgets using React, HTML, and CSS.
1. [Custom Widget Repository](https://github.com/tago-io/custom-gauge-tutorial)
You can clone it with Git or download it as a ZIP file and extract it to your desired location:
```bash
git clone https://github.com/tago-io/custom-widget-typescript-template.git
```
### Install Dependencies
Next, install all the dependencies used in the project. These dependencies are essential for the project to function correctly.
Using NPM, run the following command to install all dependencies:
```bash
npm install
```
The libraries used include:
- **TagoIO Custom-widget**: provides the necessary tools and functions to create custom widgets for TagoIO dashboards.
- **Luxon**: a library for working with dates and times in JavaScript, along with its TypeScript type definitions (`@types/luxon`).
- **ECharts**: a flexible charting library for creating interactive charts.
### Port Forward to Allow TagoIO to Access Your Code
To view your custom widget in the TagoIO dashboard during development, you need to set up port forwarding. Port forwarding lets TagoIO access your local development server.
1. Launch Visual Studio Code and open your project folder.
2. Click on **Terminal** → **New Terminal**.
3. In the terminal, type `npm start` and press Enter.
The command will start the local server hosting your custom widget. Copy the port number from the terminal output (e.g., `localhost:3000`).
4. Above the terminal window, click on **Ports**, then **Forward Port**.
5. Enter the port number you noted in step 3 and press Enter.
6. Right‑click on the newly created port forward, select **Change Visibility**, and set it to **Public**.
7. Copy the URL provided; you will need to add this URL to the custom widget in your TagoIO dashboard.
### Create the Custom Widget in Your Dashboard
Once you have set up port forwarding, create the custom widget so that you can see what you're developing:
1. Go to your TagoIO admin panel.
2. Select a dashboard where you would like to add the custom widget.
3. Create a new widget and, in the **URL & Parameters** section, add your local development server URL.
4. Click on **Configure Parameters** and enable `show_time`. When this is enabled, it will display the last time the variable was received.
By following these steps, you will have your custom widget up and running in your TagoIO dashboard.
## Key Components of Your Custom Widget
This section covers the main components required to build the widget and how they interact:
- `widget.tsx`: contains the widget logic, data handling, and integration points with TagoIO.
- `widget.view.tsx`: contains the UI rendering using ECharts (or other front-end code) and handles the visual layout.
### `widget.view.tsx`: Managing State and Callbacks
The `widget.view.tsx` file handles two main tasks:
1. **State Management**: keeps the internal state in sync with TagoIO, both in the Admin and RUN environments.
2. **Callbacks**: provides functions that the presentational component can use from the Custom Widget Library.
Key callbacks include:
- `window.TagoIO.ready()`: starts communications with TagoIO.
- `window.TagoIO.onStart()`: creates the widget's structure and prepares it to receive data.
- `window.TagoIO.onRealtime()`: receives device data to display on the widget.
- `window.TagoIO.onSyncUserInformation()`: receives user data for formatting.
At the end of the `useEffect` function, these properties are returned to the widget itself located within `widget.tsx`.
### `widget.tsx`: Rendering and Data Conversion
The `widget.tsx` file is in charge of showing the custom widget's interface. It uses the widget's configuration settings and data from variables and resources, and employs handlers provided by the view component.
Key responsibilities:
1. **Define Widget Properties**: include:
- `data`: The data fetched from the API.
- `showTime`: A parameter to determine if the time should be displayed.
- `userSettings`: User-specific settings such as date and time format.
2. **Create the Widget Component**: handles rendering logic, memoizes data, formats it according to user settings, initializes the chart, and manages window resize events.
3. **Handle Loading State**: displays a "loading" message if data is not available.
4. **Render the Widget**: ensures the gauge chart occupies the full available space.
## Deploying your Custom Widget Code to TagoIO Files
Once your widget is ready and tested locally, this section explains how to upload and deploy the widget files to the TagoIO Files area so your custom widget becomes available in the platform.
### Creating the Custom Widget Folder and Getting Your Profile ID
1. **Create a Folder in TagoIO Files**
- Go to your TagoIO Admin panel → **Files** module → create a new folder (e.g., `ring-gauge-widget`).
2. **Retrieve Your Profile ID**
- Click on your account image in the top right corner → **My Account** → select the profile you are using → click **More** to find your profile ID and save it.
### Modify the `package.json` File for Your Use Case
Open the project folder's `package.json` file, locate the `build` line, and replace:
- The placeholder profile ID with your own.
- The default folder name with the folder you created in TagoIO Files.
Example snippet (replace placeholders accordingly):
```json
"scripts": {
"build": "react-scripts build --profileId YOUR_PROFILE_ID --outputPath dist/your-folder-name"
}
```
### Building and Deploying Your Custom Widget Code
1. In your terminal, run:
```bash
npm run build
```
2. After the build completes, open the `dist` folder.
3. Copy all `.js`, `.css`, and `.html` files (ignore any files ending with `.map`) into the folder you created in TagoIO Files.
### Updating the Widget URL
Finally, paste the new URL of your widget (the one pointing to the files you just uploaded) into the custom widget configuration in your dashboard. This replaces the local development server URL and completes the deployment process.
With these steps completed, your Ring Gauge custom widget is fully deployed and ready for use within TagoIO dashboards.
---
## Card Widget
Use Card to display the last value of a variable, its unit, and a mini‑chart. Card accepts links so that when users click on it, it can redirect them to another website, dashboard, or tab.

This widget also accepts features like [metadata](/docs/tagoio/devices/payload-parser/metadata.md), which can be set in your variable data.
## Creating your own
To add a Card to your dashboard, choose the Card widget from the widget list and customize it to your preference. Edit the widget using the options located on the right side of the widget.
### 1. Data Sources field
This field allows you to set the device and variable that will be used in this widget.
For **Normal dashboards** you select one device from your list of devices and then pick the variable that contains the data.
For **Blueprint dashboards** you add a [Blueprint device](/docs/tagoio/devices/blueprint-devices-entities.md) and add the name of the variable.
When using a Blueprint dashboard, the Variable field will suggest a list of variables to be picked using the currently selected blueprint device.
### 2. Color options
You can configure colors for the text, background, and mini‑chart based on conditions that use the value of the selected variable.
The colors can be controlled by parameters defined in the widget configuration or by metadata stored within the variable.
### 3. Using colors from metadata
To let the Card use colors defined in the variable's metadata, switch the option on the **Visualization** tab, _Override colors with the conditions defined here_, to **False**.
Inside the metadata you can define the following fields:
| Field | Description |
| ------------------ | ----------------------- |
| `color` | Text color |
| `background_color` | Widget background color |
| `chart_color` | Mini‑chart color |
Example JSON:
```json
{
"variable": "current",
"value": 5.3,
"unit": "A",
"metadata": {
"color": "white",
"background_color": "blue",
"chart_color": "green"
}
}
```
> TIP: You can enter colors using `#HEX` or `RGB` values instead of a color name.
```json
{
"variable": "current",
"value": 5.3,
"unit": "A",
"metadata": {
"color": "#FFFFFF",
"background_color": "blue",
"chart_color": "rgb(44, 144, 223)"
}
}
```
### 4. Mini‑chart
A mini‑chart can be added at the bottom of the widget to display up to the last **30 points** (values).
The chart is optional and its number of points is configurable. It does not show absolute values; it only depicts the trend of the variable.
### 5. Clickable link
If you want the user to be redirected to another website, dashboard, or tab, add a valid `https://` link in the field inside the **User Control** tab.
---
## Clock Widget
Clock gauges allow you to visualize the time in a specific timezone. The widget can show the timezone, weekday, hour, minutes, and seconds.

The outer circle contains 60 dots, with each dot representing one second. When all of the dots turn blue, a minute has passed.
After a full minute, the dots are reset back to empty.
## Time format
This widget displays either the 24-hour or the 12-hour format, based on your account's preferences.
## Timezone
By default, the clock will display the timezone set in your account. To change to a different timezone, head over to the **Timezone** tab of your widget and set a new one there.
---
## Display Widget
Display is a widget that shows the last value of a variable, regardless of the format of the value (string or number).

You can select one or more variables; each variable will have its own box and will display its most recent value simultaneously.
This widget also accepts features like [metadata](/docs/tagoio/devices/payload-parser/metadata.md) that can be set in your variable data.
## Creating your own
To add the widget to your dashboard, choose the Display widget from the widget list and customize it to your preference. Edit the widget using the options located on the right side of the widget editor.
This widget works for both Normal and Blueprint dashboards.
**Data Sources field**
The _Data Sources_ field allows you to specify which device or entity provides the data for this widget.
- In a **Normal dashboard**, select one device or an entity from your list, then choose the variable that contains the data.
- In a **Blueprint dashboard**, add a Blueprint Device or Entity and input the name of the variable that holds the information. The field will suggest a list of variables to be picked using the currently selected blueprint device.
After adding a variable, click the settings icon to edit specific options for that variable, such as formulas, color, and more. Click the close icon to remove the variable from the widget's data.
**Note:** The _Data Sources_ field is dependent on the type of dashboard you are using; the differences are explained above.
---
## Icons Widget
With icons, you can create highly personalized widgets for your applications. The examples below illustrate a few possible styles, but the customization options are extensive.

Users can add up to 9 variables to this widget. Each variable will display its latest value.
## Creating your own
To add the Icons widget to your dashboard, choose the Icon widget from the list and customize it to your preference. Edit the widget using the options located on the right side of the widget editor.
### 1. 'Data Sources' Field
Use the "Data Sources" field to select which device and variable(s) will feed the Icon widget. This determines which variable values are displayed for each icon instance.
- **Normal Dashboards**: Select one device from your list of devices and choose the variable that contains the data.
- **Blueprint Dashboards**: Add the Blueprint device and input the name of the variable that contains the information.
When using a [Blueprint dashboard](/docs/tagoio/dashboards/blueprint-dashboard.md), the field **Variable** will list variables to be picked using the currently selected blueprint device.
Click on the settings icon to edit specific options for this variable, such as formulas, color, and more. Click on the close icon to remove this variable from the widget's data.
### 2. Conditions
The widget changes its content based on two types of conditions, **Icon conditions** and **Color conditions**, based on variable data. If it satisfies any condition set, then it will change the color of the content.
- The priority order between conditions is top to bottom; a condition in the top row always has priority over one below.
- It is possible to show your own images in the widget by adding their icon URL.
---
## Note Widget
## Overview
Note allows you to create text to be displayed in your dashboard. It accepts [markdown](/docs/tagoio/tutorials/markdown-guide.md) formatted text, enabling you to use bold, italic, and list formatting.
Although you can use the Note Widget to add images to your dashboard, we strongly recommend using the [Image Widget](/docs/tagoio/widgets/media-widgets/image-widget.md) instead, because it provides an auto-resize image feature.

---
## Tile Widget
Tiles allow you to simplify how users interact with your dashboards by providing a more intuitive and visually appealing set of options.
Use the Tile widget to display clickable images that redirect users to another website, dashboard, or tab.

## Creating your own
To add it to your dashboard, choose the Tile widget from the list and customize it to your preference. You can edit it.
- **Image link**: Provide a URL for the image that will be displayed in the tile. Acceptable formats include `.svg`, `.png`, `.jpg`, and `.jpeg`. Only HTTPS URLs are accepted.
- **Visualization options**: Under the _Visualization_ tab you can:
- Add text at the bottom of the widget.
- Set the image level from 0 % to 100 %.
- **Clickable link**: In the _Options_ tab add a valid HTTPS URL if you want the tile to redirect to another website, dashboard, or tab. Leave this field empty if the tile should not be clickable.
- The widget works for both Normal and Blueprint dashboards.
---
## Angular Widget
Angular gauges allow you to display your variable in an angular gauge meter.

This widget also accepts features like [metadata](/docs/tagoio/devices/payload-parser/metadata.md), which can be set in your variable data.
## Creating your own
To add the Angular widget to your dashboard, choose the Angular widget from the widget list and customize it to your preference. You can edit the widget using the options located on the right side of the widget.
### Data Sources Field
The **Data Sources** field lets you specify which device and variable will feed data into the gauge.
- **Normal Dashboards**: Select a device from your list, then choose the variable that contains the data.
- **Blueprint Dashboards**: Add the [Blueprint device](/docs/tagoio/devices/blueprint-devices-entities.md) and input the name of the variable that contains the information. The field will suggest a list of variables to be picked using the currently selected blueprint device.
> The field is dependent on the type of dashboard you are using; the differences are explained above.
---
## Cylinder Widget
Cylinders are simple and useful widgets that visualize values relative to a defined range (with maximum and minimum limits) using a cylinder gauge.

This widget also accepts features like [metadata](/docs/tagoio/devices/payload-parser/metadata.md) that can be set in your variable data. (Refer to the Metadata documentation for details.)
## Creating your own
To add the Cylinder widget to your dashboard, choose the Cylinder widget from the widget list and customize it to your preference. You can edit the widget using the options located on the right side of the widget.
### Data source configuration
The **Data Sources** field allows you to select the device and variable that will feed data into this widget.
- For normal dashboards, choose a device from your list and then pick the variable that contains the data.
- For Blueprint dashboards, add the Blueprint device and enter the name of the variable.
> This widget works for both Normal and Blueprint dashboards.
### Level labels
Level labels appear as dashed lines inside the cylinder and can represent limits or divisions. They can be set dynamically by variable data, allowing you to highlight thresholds directly on the gauge.
---
## Dial Widget
Dials are one of the simplest and most useful widgets. They make it easy to visualize values relative to a defined range (with maximum and minimum limits).

This widget also accepts features like [metadata](/docs/tagoio/devices/payload-parser/metadata.md), which can be set in your variable data.
## Creating your own
To add the Dial widget to your dashboard, choose the Dial widget from the list and customize it to your preference. You can edit it by using the options located on the right side of the widget.
### 'Data Sources' Field
This field allows you to set the device and variable that will be used in this widget.
> This field is dependent on the type of dashboard you are using; the difference is explained below.
#### 1.1 'Data Sources' for Normal Dashboards
From the option **'Data Sources'** on the right menu, select one device from your list of devices and the variable that contains the data.
#### 1.2 'Data Sources' for Blueprint Dashboards
From the option **'Data Sources'** on the right menu, add the [Blueprint device](/docs/tagoio/devices/blueprint-devices-entities.md) and input the name of the variable that contains the information.
:::info
When using a [Blueprint dashboard](/docs/tagoio/dashboards/blueprint-dashboard.md), the field **Variable** will list variables to be picked using the currently selected blueprint device.
:::
---
## Data Range & Format
Data Range & Format is where you define the display format and the value range used by the gauge.
## Range Limits

In the Range Limits section you set the minimum and maximum values displayed on the gauge. There are two types of range limits:
- Fixed: The minimum and maximum values of the gauge are static and never change.
- From Metadata: The minimum and maximum values of the gauge can change based on a metadata property in a variable.
## Fixed
If you want to set a static range on your gauge, choose the Fixed type. Simply specify the minimum and maximum values to define the range shown in the widget.
## From Metadata
If you want to set a dynamic range on your gauge, this is the desired type. This type defines the range based on a metadata property of a variable.
The variable that contains the metadata can be selected in the **Get metadata from** field. The variable that you choose in this field must contain a metadata field with the following schema:
```json
{
"variable": "range",
"value": "50",
"metadata": {
"limit_inferior": 0,
"limit_superior": 100
}
}
```
You can choose one of 3 options in the **Get metadata from** field:
1. **From original variable**: The metadata property will be acquired from the variable that contains the gauge's value.
2. **From variable used in formula**: The metadata property will be acquired from the variable that is being used in the formula. This option will only be enabled if your gauge is using [Formula](/docs/tagoio/widgets/general/formula.md), and the Formula is being used with a variable.
3. **From another variable**: The metadata will be acquired from a variable of your choice. If you select this option, a variable selector will appear.
---
## Grain Bin Widget
The Grain Bin widget produces a grain-bin illustration so you can easily track variable information.

This widget also accepts features like [metadata](/docs/tagoio/devices/payload-parser/metadata.md) that can be set in your variable data.
## Creating your own
To add the Grain Bin widget to your dashboard, choose the Grain Bin widget from the widget list and customize it to your preference. Edit the widget using the options located on the right side of the widget configuration panel.
### Data Sources Field
This field allows you to set the device and variable that will be used in this widget.
> This field is dependent on the type of dashboard you are using; the difference is explained below.
#### 1.1 Normal Dashboards
From the option 'Data Sources' on the right menu, select one device from your list of devices and the variable that contains the data.
#### 1.2 Blueprint Dashboards
From the option 'Data Sources' on the right menu, add the [Blueprint device](/docs/tagoio/devices/blueprint-devices-entities.md) and input the name of the variable that contains the information.
> When using a [Blueprint dashboard](/docs/tagoio/dashboards/blueprint-dashboard.md), the field **Variable** will list variables to be picked using the currently selected blueprint device.
---
## Gauge Overview
Gauge contains a collection of metric widgets. You can choose among several types of gauges that present the last value of a variable in different formats.
## Dial
Dials are one of the simplest and most useful widgets. They make it easy to visualize values relative to a defined range (maximum and minimum limits).

Learn more about the [Dial Gauge](/docs/tagoio/widgets/gaude-and-meters/dial-widget.md).
## Angular
Angular allows you to show your variable in an angular gauge meter.

Learn more about the [Angular Gauge](/docs/tagoio/widgets/gaude-and-meters/angular-widget.md).
## Solid
Solid gauges display your variable in a solid gauge meter.

Learn more about the [Solid Gauge](/docs/tagoio/widgets/gaude-and-meters/solid-widget.md).
## Clock
Clock gauges allow you to visualize the time in a certain timezone. You can see the selected timezone, weekday, hour, minutes and seconds.

Learn more about the [Clock Widget](/docs/tagoio/widgets/displays/clock-widget.md).
## VU Meter
VU Meter gauges show the variable in a velocimeter gauge.

Learn more about the [VU Meter Gauge](/docs/tagoio/widgets/gaude-and-meters/vu-meter-widget.md).
---
## Solid Widget
Solid gauges allow you to display a variable in a solid gauge meter.

This widget also accepts features like [metadata](/docs/tagoio/devices/payload-parser/metadata.md), which can be set in your variable data.
## Creating your own
To add the Solid widget to your dashboard, choose the Solid widget from the widget list and customize it to your preference. Edit the widget using the options located on the right side of the widget.
This widget works for both Normal and Blueprint dashboards. The **Data Sources** field allows you to specify the device and variable that will be used in this widget.
- For Normal dashboards, select a device from your list of devices and then choose the variable containing the data.
- For Blueprint dashboards, add the Blueprint device and input the name of the variable; note that the variable selector will use the tags set for your Blueprint Device.
---
## VU Meter Widget
VU Meter gauges display the variable in a velocimeter-style gauge.

This widget also accepts features like [metadata](/docs/tagoio/devices/payload-parser/metadata.md) that can be set in your variable data.
## Creating your own
To add the VU Meter widget to your dashboard, choose the VU Meter widget from the widget list and customize it to your preference. You can edit the widget using the options located on the right side of the widget.
### 'Data Sources' Field
This field allows you to set the device and variable that will be used in this widget.
> This field is dependent on the type of dashboard you are using; the difference is explained below.
#### 1.1 'Data Sources' for Normal Dashboards
From the option **Data Sources** on the right menu, select one device from your list of devices and the variable that contains the data.
#### 1.2 'Data Sources' for Blueprint Dashboards
From the option **Data Sources** on the right menu, add the [Blueprint device](/docs/tagoio/devices/blueprint-devices-entities.md) and input the name of the variable that contains the information.
:::info
When using a [Blueprint dashboard](/docs/tagoio/dashboards/blueprint-dashboard.md), the field **Variable** will list variables to be picked using the currently selected blueprint device.
:::
---
## Time Bands & Value Bands
Chart bands shade regions of a chart based on data from one of your variables. They are useful for marking anomaly windows, operating zones, alert thresholds, or any range worth calling out visually:
- **Time bands** shade windows along the X-axis, spanning the chart's full height. Use them to highlight when something happened, like an anomaly detected between 8:00 and 9:30.
- **Value bands** shade zones along the Y-axis, spanning the chart's full width. Use them to highlight value ranges, like a "Hot" zone between 30 and 40 degrees.
Both are driven by data: you point the band at a device variable, and the records of that variable define where the bands sit, their colors, and their labels. Writing new records moves the bands, with no widget edit needed. This pairs naturally with anomaly detection or scoring pipelines built on [Analysis](/docs/tagoio/analysis/), which can write band records as they detect events.

## Supported widgets
The [Line Chart](/docs/tagoio/widgets/charts/line-chart-widget.md) widget supports both band types.
## Adding bands to your chart
In the widget's edit page, use the **Anomaly time bands** and **Anomaly value bands** sections. Each accepts one band configuration per widget:
1. Select the **Device** (or Blueprint device) and the **Variable** that holds the band records.
2. Optionally set **Points** (how many of the variable's latest records to read; defaults to the widget's max points) and the **Shade opacity**.
3. For value bands on charts with multiple Y-axes, pick which **Axis** the zone belongs to. "Default axis" applies it to all series.
Bands always read the latest records of the variable, regardless of the chart's visible time range, so a band defined last week still shades this week's chart.
## Time bands
Each record of the band variable draws one shaded window. The window bounds, color, and label come from the record's [metadata](/docs/tagoio/devices/payload-parser/metadata.md):
```json
{
"variable": "anomaly_windows",
"value": 87,
"time": "2026-07-10T12:00:00.000Z",
"metadata": {
"start_date": "2026-07-10T08:00:00.000Z",
"end_date": "2026-07-10T09:30:00.000Z",
"color": "#F43F5E",
"label": "Anomaly",
"recolor": true
}
}
```
- `start_date` and `end_date` are ISO 8601 dates. Omit either one for an open-ended band that extends to the chart's edge. A record with neither is skipped.
- `color` fills the band and `label` names it inside the chart.
- `recolor: true` also repaints the metric lines inside the window with the band's color, making the affected stretch of the series stand out. Recolor applies to every series on the chart; a specific variable can keep its own color by enabling its **Skip anomaly-band recolor** option in the data source settings.
- A record that resolves no color (no `metadata.color` and no matching threshold condition) is skipped.
### Threshold conditions
Instead of hardcoding a color on every record, a time band can derive its color and label from the record's numeric `value` (a score). Open the band's **Thresholds** drawer and define rules, each with a condition, a comparison value, a color, and a label:
| Condition | Meaning |
| --------- | -------------------- |
| `<` | Less than |
| `>` | Greater than |
| `=` | Equal |
| `!` | Not equal |
| `><` | Between (inclusive) |
| `*` | Anything (catch-all) |
The first matching rule, in the order listed, sets the band's color and label. When a record carries its own `metadata.color` or `metadata.label`, those win over the matched rule. A record whose `value` is not numeric only matches a `*` rule.
In the example above, a rule like "Greater than 80 = red, 'Anomaly'" colors the window from the score of 87 without any color in the record's metadata.
## Value bands
Each distinct zone comes from a record of the band variable. The zone bounds are plain numbers on the Y-axis:
```json
{
"variable": "temperature_zones",
"value": 0,
"time": "2026-07-10T12:00:00.000Z",
"metadata": {
"limit_inferior": 30,
"limit_superior": 40,
"color": "#F97316",
"label": "Hot",
"recolor": true
}
}
```
- `limit_inferior` and `limit_superior` set the zone's lower and upper bound. Omit either for an open-ended zone that extends to the axis edge. A record with neither is skipped.
- `color` is required; a record without it is skipped. `label` names the zone.
- `recolor: true` repaints the parts of the series that fall inside the zone. On charts with multiple Y-axes, recolor only affects series bound to the band's configured axis.
- The record's `value` is not used.
- Records with the same pair of bounds update the same zone, latest record wins, so you can move or recolor a zone by writing a new record. Records with different bounds draw separate zones.
## Composing with other features
Bands compose with [Reference Lines](/docs/tagoio/widgets/general/reference-lines.md) and the Line Chart's forecast feature on the same chart: a single widget can show a forecast projection, an alert threshold line, a shaded operating zone, and highlighted anomaly windows at once.

---
## Data Analytics
Transform your raw sensor data into actionable insights using visual calculations. Data Analytics enables operations such as averaging, summing, finding maximum and minimum values, and more, directly on your widget variables. This feature also lets you filter data, apply mathematical computations, visualize data as numbers, analyze trends, and perform other data transformations.
Data Analytics is currently available for the widgets listed below, with support for additional widgets to be added in the future:
- [Display](/docs/tagoio/widgets/displays/display-widget.md)
- [Line Chart](/docs/tagoio/widgets/charts/line-chart-widget.md)
- [Area Chart](/docs/tagoio/widgets/charts/area-chart-widget.md)
- [Horizontal Bar](/docs/tagoio/widgets/charts/horizontal-bar-widget.md)
- [Vertical Column](/docs/tagoio/widgets/charts/vertical-column-widget.md)
- [Multiple Charts](/docs/tagoio/widgets/charts/multiple-charts-widget.md)
- [Icon](/docs/tagoio/widgets/displays/icons-widget.md)
## Available Functions
The following table lists the calculations you can perform:
| Function | Description |
| ------------- | -------------------------------------------------------------------------------------------------------------- |
| Last Value | Default for widgets with a single value; represents the last value of the variable. |
| All Data | Default for widgets with multiple values; shows all data of this variable or widget. |
| Last Location | Finds the last value with valid location data. |
| Count | Counts the number of values for a variable over a selected period. |
| Average | Calculates the average value over a selected period. |
| Sum | Computes the sum of values over a selected period. |
| Max | Identifies the maximum value over a selected period. |
| Min | Identifies the minimum value over a selected period. |
| Conditional | Finds values that meet a specific condition (e.g., greater than, less than, not equal) over a selected period. |
| Aggregate | Performs a calculation (e.g., average, max, min, sum) based on an interval (e.g., daily, weekly, monthly). |
### Types of Functions
#### Basic calculations
These functions perform fundamental mathematical operations on data within a specified period, up to one month. All functions are considered basic calculations except for the Aggregate function. When applied, these calculations process all data within the chosen timeframe. Note that these calculations are limited to data in a period of 1 month.
#### Data aggregation operations
This includes the **Aggregate** function, which aggregates data over an interval and applies mathematical operations. Unlike basic calculations, there is no period restriction; the calculation will be applied to all available data by default or to the period configured in the [Dashboard Period Preset](/docs/tagoio/dashboards/dashboard-global-time-filter-and-period-presets.md).
Available intervals:
- Per minute
- Per hour
- Daily
- Weekly
- Monthly
- Quarterly
- Yearly
When applying an interval, the system uses the first data point as a starting reference. For example, if your data starts on January 1st and the selected interval is _Weekly_, then the data between January 1st and January 7th will be aggregated.
### How to apply functions to your data
1. Open the widget in **Edit Mode**.
2. Select a variable in **Data Sources**.
3. Click the cog icon to access the **Data Analytics** tab.
4. Choose a function from the list above.
5. For basic calculations, define a period (up to one month).
- The period can be set directly in the Data Analytics tab, on the X‑axis, or via global preset periods using the [Dashboard Period Presets](/docs/tagoio/dashboards/dashboard-global-time-filter-and-period-presets.md).
6. For aggregation functions, set a default interval or create a preset interval by navigating to the **Preset Interval** option after entering the widget in Edit Mode.
7. The system will prompt you to define **Cache settings**, which are mandatory for maintaining dashboard responsiveness and performance.
- You can also set an expiration time to prevent rapid consumption of your [Dashboard Data Output](/docs/tagoio/dashboards/data-output-for-dashboards.md).
---
## Displaying Units
You can set up your widget to present the unit of a variable (e.g., °C, °F, km, mm) in different ways.
Under the **"Formula"** tab, select the option that indicates the origin of the unit that works best for you.

## 1. Unit from the original variable
This option uses the selected variable's **`unit`** field to display alongside the value in the widget.
If the variable doesn't have a unit, it will be left empty.
## 2. Unit from variable used in Formula
When you set a [Formula](/docs/tagoio/widgets/general/formula.md) in your widget where the source of the formula is based on another variable, this option will use what is stored in the **`unit`** of that variable to present in the widget.
This is helpful when you need to build an application that changes the output dynamically.
For example, users can select the scale they want to see a temperature at among the options: °C, °F, or K.
## 3. Fixed Unit
If you select this option, you just need to enter the unit that will be displayed. The entered unit will be shown even if the variable contains another unit stored in the [Device](/docs/tagoio/devices/).
---
## Embedding Widgets to Your Website
Embedded widgets are a convenient way to display your data from TagoIO in another application. By generating the embedding code on a [widget](/docs/tagoio/widgets/), you can add that widget to your website or any other application.

## Example
Below is a simple example of using embedded widgets in your own website.
## How to Generate the Embedding Code
1. **Open the dashboard** that contains the widget you want to embed, and click the gear icon on that widget.

2. In the widget settings, navigate to the **Embed Widget** tab, click **Generate Code** and then click **Copy**.

3. Paste the copied code into your website's HTML where you want the widget to appear. The data from TagoIO will update in real-time for anyone who views the page!
---
## Formula
The Formula feature enables users to modify the visualization of data in specific widgets. Located inside the configuration tab of most widgets, a Formula can be applied to all fields of a stored register, including variable, value, unit, group, location, and metadata.
## How it works
The system receives input data from a variable in your [Device](/docs/tagoio/devices/), applies a formula to generate a temporary output value, and displays that output in the widget. This output is only for visualization and is not saved as a variable in device data storage.
Example flow:
- Input value: 10
- Formula: $value$ \* 2 + 5
- Output shown in widget: 25
Example formula (text):
```text
$value$ * 2 + 5
```
## Formula configuration in widgets
The Formula configuration is available inside most widget configuration panels. It produces a temporary, visual-only value and does not alter the stored device data.

Typical elements in the Formula panel include:
- Formula mode: Disabled | Simple | Dynamic
- Source: Fixed | Variable
- The formula expression input (for example: ($VALUES$ - 273.15) \* 9/5 + 32)
- Unit origin and related unit fields
- "Apply a formula to modify the visualization of each variable" help text and a "Learn more" link
Example formula (temperature conversion):
```text
($VALUES$ - 273.15) * 9/5 + 32
```
## Notes
- The output generated by a Formula is temporary and only used by the widget for display purposes.
- The original variable and device data remain unchanged by a Formula.
- Formula expressions can reference stored fields like value, unit, group, location, and metadata.
### Source options
When configuring a formula you can choose between two source types:
- **Fixed**: enter the formula directly into the field.
Example: `$value$ * 2 + 5`
- **Variable**: store the formula in a variable's `value` field (and optionally a unit).
Example JSON for a temperature conversion variable:
```json
{
"variable": "equation_f_c",
"unit": "°C",
"value": "($value$-32)*5/9"
}
```
### Operators
The following operators are available in Formula expressions:
| Operator | Name | Example |
| -------------- | ---------------------- | ------------------------------- |
| +, - | add, subtract | `$value$ + 1000` |
| /, \* | divide, multiply | `$value$ / 2.14` |
| to | unit conversion | `$value$ inch to cm` |
| ? : | conditional expression | `$value$==1 ? $VALUE$ ON : OFF` |
| >>, << | arithmetic shifts | `$VALUE$ >> 1` |
| sqrt, sin, log | math operations | `sqrt ($value$)` |
More operators can be found on [Math.js](https://mathjs.org/docs/expressions/syntax.html).
### Date & Time Operations
The `Date()` function formats date and time values dynamically.
**Syntax**
```text
Date(source, format_string)
```
- **source**: the value containing the date/time (variable, metadata field, or system value).
- **format_string**: desired output format using supported tokens or preference variables (`DATE_FORMAT`, `TIME_FORMAT`).
Special variables are replaced at runtime based on the current TagoRUN user's settings.
| Formula Example | Description | Example Output |
| ------------------------------------------------------------ | ---------------------------------------------- | -------------------- |
| `Date($METADATA.shipped_at$, "$DATE_FORMAT$ $TIME_FORMAT$")` | Formats a metadata date using user preferences | 07/23/2024 14:30 |
| `Date($VALUE$, "$DATE_FORMAT$ HH:mm:ss")` | Custom time with user date format | 23/07/2024 18:45:10 |
| `Date($VALUE$, "YYYY-MM-DDTHH:mm:ss[Z]")` | ISO 8601 format for timestamps | 2024-07-23T18:45:10Z |
| `Date($TIME$, "HH:mm")` | Time only, 24‑hour format | 14:30 |
### Formula application examples
To visualize a temperature in Fahrenheit when the device stores Celsius:
```text
$VALUE$ celsius to fahrenheit
```
or
```text
($VALUE$ × 9/5) + 32
```
More options are available in the dropdown list of the Formula field.
### Units for output variables
After converting a value, you can set the unit that appears next to it. Use the **Unit Origin** field to define where the displayed unit comes from. Learn more about [Display Units](/docs/tagoio/widgets/general/displaying-units.md).
### Minimum and Maximum limits
The formula does not adjust the widget's data range. The Min and Max values are still controlled by the settings in the **Data Range** tab.
## Dynamic Formulas
Dynamic Formulas allow formulas to adapt based on user preferences or browser settings. To use them:
1. Create a new visualization preference in **TagoRUN** via **[Custom Settings](/docs/tagoio/tagorun/getting-started/custom-settings.md)**.
2. In the widget's configuration, enable **Dynamic Formula** and select:
- **Source**: user preference or browser preference.
- **Option**: the specific custom setting created earlier.
- **Condition**: optional value to check before applying the formula.
- **Formula Type**: Fixed or Variable.
- **Formula**: the expression to apply.
Dynamic Formulas are especially useful for letting users choose units (Celsius, Fahrenheit, Kelvin) or other display options that automatically adjust across dashboards.
---
## Frame Settings
The Frame Settings allow you to adjust the appearance of your widgets by customizing the corners, padding, and shadows, and by toggling the widget's header visibility. These options help your widgets integrate with your dashboard's design.

## Accessing Frame Settings
To access the Frame Settings, open the widget's configuration page and find the "Frame Settings" section. There you will find options to customize the appearance of the widget.
### Customizing Rounded Corners
The **Rounded Corners** option lets you soften the edges of your widgets. You can adjust the radius for all corners simultaneously or modify each corner individually by toggling the lock icon button.
### Adjusting Frame Padding
**Frame Padding** controls the space around the widget's frame. By adjusting the padding values, you can position your widgets closer together or further apart. Similar to corner customization, you can set padding for all sides at once or adjust each side separately using the lock icon button.
### Disabling Shadow
If you prefer a flat design or plan to combine widgets, you can disable the shadow effect by enabling the **Disable Shadow** option. This will remove the shadow from the widget, providing a cleaner look.
### Header Visibility Options
By default, widget headers are set to "show always". You can change this behavior to better suit your dashboard's design:
1. **Show on hover**: The header (including buttons) appears only when the user hovers over the top part of the widget.
2. **Show only buttons**: Only the configured header buttons and options menu are displayed, without any header background or title.
These settings currently apply to TagoIO web applications; we're working to extend this functionality to mobile platforms.
---
## Reference Lines
All chart-type widgets in TagoIO can be configured to display reference lines within the graph. These lines can serve as thresholds, upper and lower bounds, medians, or any other value that enhances the data visualization experience.
## Supported widgets
The widgets that support this feature include the [Area Chart](/docs/tagoio/widgets/charts/area-chart-widget.md), [Line Chart](/docs/tagoio/widgets/charts/line-chart-widget.md), [Horizontal Bar](/docs/tagoio/widgets/charts/horizontal-bar-widget.md), [Vertical Column](/docs/tagoio/widgets/charts/vertical-column-widget.md), and [Multiple Charts](/docs/tagoio/widgets/charts/multiple-charts-widget.md).
## Adding reference lines to your chart
You can set reference lines by navigating to your widget's edit page and accessing the `Reference Lines` option.
:::info
Up to **5** reference lines per widget are allowed.
:::
### Defining a new line
1. Click **Add Reference Line** (or the equivalent button in the UI).
2. Choose the _source_ of the value:
- **Fixed**: Enter a static numeric value that will be displayed as a horizontal or vertical line.
- **Variable**: Select one of your device's variables; the line's value will update dynamically based on that variable.

### Fixed vs Variable source
- When using a **fixed** source, you can manually set a _Label_ and _Color_ for the line.
- If the source is a **variable**, the label and color are automatically taken from the variable's metadata. For more information about how to configure metadata, see the [Metadata](/docs/tagoio/devices/payload-parser/metadata.md) article.

### Multiple axes
If your chart uses multiple Y‑axes, you can associate a reference line with a specific axis by clicking the **axis dropdown** inside the reference line options and selecting the desired axis. This ensures the line aligns correctly with the chosen scale.
### Applying formulas
You can apply a formula to the reference line's value. This is especially useful when:
- The dataset variables already have a [Formula](/docs/tagoio/widgets/general/formula.md) applied, or
- You want to use a **Dynamic Formula** for custom unit conversions.
The formula will be evaluated each time the chart refreshes, keeping the reference line in sync with your data.
---
## Widget Cache system
The caching system optimizes the performance of your dashboards by storing widget data and any computations performed using [Data Analytics](/docs/tagoio/widgets/general/data-analytics.md), so future requests are served faster by reusing results from earlier requests, speeding up load times and improving the overall responsiveness of your dashboard. Because less data needs to be queried, caching also reduces Dashboard data output consumption.
:::info
The caching system is mandatory when using [Data Analytics](/docs/tagoio/widgets/general/data-analytics.md).
:::
## How It Works
When a widget is first loaded by any user, its data is fetched and stored in cache storage on TagoIO servers. Subsequent accesses to the same widget will retrieve the pre-fetched data from the cache, whether accessed through the [Admin](https://admin.tago.io/) or [Run portal](/docs/tagoio/tagorun). Each widget's cache is enabled individually and is automatically refreshed after a specified expiration time (for example, 30 seconds or 1 hour).
For example, if you configure a widget to cache data for 10 minutes, the first time anyone accesses the Dashboard (via the Admin or Run portal) with the same visualization settings, the cache system will store the data on our servers for 10 minutes. Anyone accessing the dashboard within that 10‑minute window will immediately receive the cached data without waiting for the API to query it again, resulting in faster response times. After the 10‑minute period, if the dashboard is being viewed, the widget will reset its cache and update with fresh data.
This feature is particularly beneficial when widgets display complex or resource-intensive data, such as results from [Data Analytics](/docs/tagoio/widgets/general/data-analytics.md) functions.
## Enabling the cache system for your widget
All widgets support the caching system; you can enable it by entering your widget in Edit mode and navigating to the **Cache** tab. Once enabled, you can define the expiration time, which specifies how long the cached data will be retained on our servers. You can set periods in seconds, minutes, or hours.
To enable or configure caching for a specific widget, use the widget settings in the Dashboard editor. For step‑by‑step instructions, see the Dashboard editor in your Admin. This feature is part of each widget's settings.

:::note
If you need to refresh data before the cache expires (for example, after changing the widget configuration or a Data Analytics function), you can clear the cache manually from the widget while in Edit mode. This is only available on the Admin Console.
:::

---
## Widget Header
The widget header enables you to add customized buttons to the header of your widget to improve the flow of your dashboard.
## Header buttons (UI fields)
The header buttons panel includes fields to configure each custom button:
- Icon: Icon displayed on the button.
- Button text: The label shown on the button.
- Color: Button color.
- Layout: Button style (e.g., Fill, Outline).
- When clicked: Action performed when the button is clicked (for example, "Open widget").
- Resource: Target resource for the action (for example, a specific Form).
Example entry shown in the UI:
- Icon: plus/add icon
- Button text: Add data
- Color: (blue sample color)
- Layout: Fill (or Outline)
- When clicked: Open widget
- Resource: Form #1
Additional UI controls:
- Add and remove buttons (+ / −)
- Cancel and Confirm buttons for the header configuration dialog

### Additional configuration notes
- The widget header is enabled for all widgets. You can configure it by selecting the **Header** tab in your widget configuration.
- A maximum of **3 buttons** can be added per widget.
- **Icon**: Optional icon displayed to the left of the button text. If there isn't enough room, only the icon will appear.
- **Button text**: No character limit, but longer text consumes more space; keep it short for better layout.
- **Color**: The button's background color; the text color is automatically adjusted for optimal contrast.
- **When clicked**: Choose an action (e.g., _Open widget_, _Navigate to URL_, etc.). You can select one of three options.
### Recommended practices
If you use the **Open widget** action, consider hiding that widget in a Hidden tab. This keeps it out of view while still allowing users to open it via the header button.
---
## Widgets Overview
# Widgets
Dashboards are composed of widgets that can display and collect data from users. TagoIO provides many widgets to handle your data in real time, from simple dials to tables, maps, videos, and forms that allow user input.

:::info
You can add up to 100 widgets in a single dashboard. This total includes widgets across all tabs. Each widget can display a maximum of 10,000 data points (variables).
:::
## Widget Categories
The following widgets are provided out of the box (icons and labels shown):
### Chart Widgets
- [Line Chart](/docs/tagoio/widgets/charts/line-chart-widget.md) - Display data trends over time
- [Area Chart](/docs/tagoio/widgets/charts/area-chart-widget.md) - Filled area charts for data visualization
- [Horizontal Bar](/docs/tagoio/widgets/charts/horizontal-bar-widget.md) - Horizontal bar charts
- [Vertical Column](/docs/tagoio/widgets/charts/vertical-column-widget.md) - Vertical column charts
- [Multiple Charts](/docs/tagoio/widgets/charts/multiple-charts-widget.md) - Multiple chart types in one widget
- [Pie Chart](/docs/tagoio/widgets/charts/pie-widget.md) - Pie charts for proportional data
- [Semi Pie](/docs/tagoio/widgets/charts/semi-pie-widget.md) - Semi-circle pie charts
- [Semi Donut](/docs/tagoio/widgets/charts/semi-donut-widget.md) - Semi-circle donut charts
- [Calendar Heatmap](/docs/tagoio/widgets/charts/calendar-heatmap-widget.md) - Hour-by-day heatmap for time patterns and anomaly scores
### Gauge & Meter Widgets
- [Dial](/docs/tagoio/widgets/gaude-and-meters/dial-widget.md) - Traditional dial gauges
- [Angular](/docs/tagoio/widgets/gaude-and-meters/angular-widget.md) - Angular gauge meters
- [Solid](/docs/tagoio/widgets/gaude-and-meters/solid-widget.md) - Solid gauge displays
- [VU Meter](/docs/tagoio/widgets/gaude-and-meters/vu-meter-widget.md) - VU meter style gauges
- [Cylinder](/docs/tagoio/widgets/gaude-and-meters/cylinder-widget.md) - Cylindrical level indicators
- [Grain Bin](/docs/tagoio/widgets/gaude-and-meters/grain-bin-widget.md) - Grain bin level visualization
### Display Widgets
- [Display](/docs/tagoio/widgets/displays/display-widget.md) - Simple value display
- [Card](/docs/tagoio/widgets/displays/card-widget.md) - Card-style data display
- [Tile](/docs/tagoio/widgets/displays/tile-widget.md) - Tile-based data visualization
- [Icons](/docs/tagoio/widgets/displays/icons-widget.md) - Icon-based status display
- [Clock](/docs/tagoio/widgets/displays/clock-widget.md) - Time and date display
- [Note](/docs/tagoio/widgets/displays/note-widget.md) - Text notes and annotations
### Input Widgets
- [Input Form](/docs/tagoio/widgets/input-widgets/input-form/) - Data input forms
- [Input Control](/docs/tagoio/widgets/input-widgets/input-control-widget.md) - Control elements
- [Keypad](/docs/tagoio/widgets/input-widgets/keypad-widget) - Number input keypad
- [Push Button](/docs/tagoio/widgets/input-widgets/push-button-widget.md) - Action buttons
- [Step Button](/docs/tagoio/widgets/input-widgets/step-button-widget.md) - Increment/decrement buttons
### Table Widgets
- [Dynamic Table](/docs/tagoio/widgets/tables/dynamic-table-widget.md) - Dynamic data tables
- [Static Table](/docs/tagoio/widgets/tables/static-table-widget.md) - Static data tables
- [Entity Table](/docs/tagoio/widgets/tables/entity-table-widget.md) - Entity data tables
- [Device List](/docs/tagoio/widgets/tables/device-list-widget.md) - Device listing widget
- [User List](/docs/tagoio/widgets/tables/user-list-widget.md) - User listing widget
### Map & Location Widgets
- [Map](/docs/tagoio/widgets/map-and-location/map-widget) - Interactive maps with device locations
- [Heat Map](/docs/tagoio/widgets/map-and-location/heat-map-widget.md) - Heat map overlays
- [Image Marker](/docs/tagoio/widgets/map-and-location/image-marker-widget.md) - Image-based location markers
### Media Widgets
- [Image](/docs/tagoio/widgets/media-widgets/image-widget.md) - Image display
- [Video](/docs/tagoio/widgets/media-widgets/video-widget.md) - Video player
- [Compose](/docs/tagoio/widgets/media-widgets/compose-widget.md) - Rich content composition
## Widget Features
### Common Features
Check the Widgets > General documentation for common features that can be used across almost all widgets.
---
## Input Control Widget
The Input control allows users to submit values to variables through a form.
## Editing the form
The Input Control is a dynamic widget. You can customize its colors, layout, text, and icons.

To edit a specific section or element in the widget, go to the structure section and click on the desired element.
> This widget works for both **Normal** dashboards and **Blueprint** dashboards.
### Sections
Sections form the main structure of the form; each section can contain its own fields.
You can show a divider between sections, duplicate or remove sections, and hide captions if desired.
### Fields
Fields are associated with a variable and a device using the **Data Sources** setting.
When a field is submitted, the data is sent to the device's data storage.
You can also assign an analysis to each field; when the field is submitted, that analysis will run automatically.
#### Data Sources Field
The `Data Sources` field allows you to set the device and variable that will be used in this widget.
- **Normal Dashboards**: Select one of your devices from the list and choose the variable that contains the data.
- **Blueprint Dashboards**: Add the Blueprint device and input the name of the variable that holds the information.
_When using a Blueprint dashboard, the Variable field will suggest a list of variables to be picked using the currently selected blueprint device._
### Field Types
The Input Control supports two types of fields: **Switch** and **Text**.
Unlike the Input Form widget, each field has its own button that sends data when pressed.
- **Switch**: Displays a simple toggle button that changes the variable's value to `true` or `false`. You can customize the text shown for each state.
- **Text**: Provides a standard text input; whatever is typed into it will be sent as the field's value.
---
## Adding Image Selector Field on Forms
The Image Selector field type allows you to present an input where the user can select one or more images that correspond to predefined values.
## Example: Select the Device

Users need to click an image. The value you entered in the widget options will be stored in your data [device](/docs/tagoio/devices/).
## Widget options
Common options for the Image Selector field:
- Type
- Image Select
- Label for variable
- Example: "Select the furniture" (this is the label shown for the form field)
- Default value(s) comes from
- Options such as "the variable (last value)"
- Use values from
- Static (configurable) or other value sources
- Is it a required field?
- Yes / No
- Allow multiple selections
- Yes / No
Notes:
- Configure the values that each image represents in the widget options; those exact values are what will be stored when a user selects an image.
- If you allow multiple selections, users can choose more than one image; otherwise only one image can be selected.
- **Use Values From**:
- _Static_: Define a label (header text), value (sent to the variable), URL (must be HTTPS), and description (shown below the image). These settings create each selectable image.
- _Dynamic_: Provide a device variable that contains an array of objects. Each object should include `metadata.description`, `metadata.url` (HTTPS), and optionally `metadata.value`. The widget will use the object's `value` field if present; otherwise it uses the variable's value.
- In the configuration panel you can add or remove images using the **+** and **-** buttons.
---
## Creating Dynamic Dropdown Selection using Forms
Dynamic Dropdown allows users to populate a dropdown list with dynamic items coming from a variable. The example below shows how a Dynamic Dropdown can present selectable country values that are updated dynamically.

The items for a Dynamic Dropdown are sourced from a variable. Because of that, you can change the available values dynamically: for example, developers can create inputs where users insert values that will later appear in the Dynamic Dropdown.
You will typically use this feature when you need the dropdown options to change often. For example, imagine you need to add supervisors to your application and later attach those supervisors (clients) to a device. Using a Dynamic Dropdown you can change the client options simply by adding or removing entries from the [device's data storage](/docs/tagoio/devices/).
### Setting up a Dynamic Dropdown
1. **Create an Input Form widget**
- Choose the _Input Form_ widget type and set its output variable.
2. **Configure the field as a dropdown**
- In the form editor, select **Field Type: Dropdown**.
- Change **Use values from** to **Dynamic**. The previous static options will disappear.
3. **Specify the source variable**
- Enter the name of the variable that will supply the options (e.g., `supervisor_options`). This variable should exist in your device's data storage.
4. **Populate the variable with options**
- Add entries to the device using the Device Emulator or directly via API. Example JSON:
```json
{
"variable": "supervisor_options",
"value": "John Doe"
}
```
- After saving, the dropdown will display the new option.
5. **Use metadata for custom labels**
- If you want the stored value to differ from what is shown in the dropdown (e.g., a code vs. a name), include a `metadata` field:
```json
{
"variable": "supervisor_options",
"value": "12433",
"metadata": {
"label": "John Doe"
}
}
```
- The dropdown will display "John Doe" while the stored value remains `12433`.
---
## Field Types for Input Form
The field defines the relationship between the user-inputted data and the Device data for certain widgets.
:::info
Fields can be of many types, depending on the widget they are used with.
:::
## 1. Text
A typical text input will appear. The value saved for this field will be whatever the user typed into it.

## 2. Address
This field displays a text input integrated with Google Maps to search for an address. The value for this field will be the complete selected address, and it will also include the location coordinates within it. See [location coordinates](/docs/tagoio/devices/sending-data.md) for details.

:::info
For the **Input Form Widget**, it is possible to enable a map under the input.
:::
## 3. Calendar
It will display a calendar field that allows the user to enter a date, or a date range, as a value through a calendar popup. When receiving the user answer in your **device**, you will be able to get it in the value parameter and the metadata parameter. The latter one will be in date string format as `start_date` and `end_date`.

For example, the input above will produce the following payload:
```json
{
"variable": "location",
"value": "Start: 2021-06-04T03:00:00Z, End: 2021-06-11-T02:59:59Z",
"metadata": {
"end_date": "2021-06-11T02:59:59Z",
"start_date": "2021-06-04T03:00:00Z"
}
}
```
## 4. Checkbox
A traditional checkbox will appear and the value will be set as `true` (checked) or `false` (not checked).

## 5. Clock set
It will display a step button to select the hour range without the date.

## 6. Device
It will display a dropdown field with all of an account's devices. The value of the field will be the id of the selected device.
:::info
You can customize the devices shown by the list through tags. Configuring tags will show only the devices with a tag correspondence.
:::

## 7. Dropdown
It displays a dropdown menu with options that you define. The value of the field will be the selected option, and these options can be defined as static or dynamic with the dropdown being populated with data coming from a variable. Learn more about **Creating Dynamic Dropdown selection using Forms.**

## 8. Dropdown Multiple
It displays a dropdown menu with options that you define. The value of the field will be the multiple options selected, and these options can be defined as static or dynamic with the dropdown being populated with data coming from a variable.

## 9. Email
It displays a field to submit multiple emails, separated by a comma.

## 10. Entity & Entity Multiple
It will display a dropdown using values contained in your Entities. You are able to select which field from your entity should be displayed in the dropdown, and which field from your entity should be submitted when pressing the Submit button.
The difference between the Entity and Entity Multiple is that the last one allows you to select multiple values in the dropdown, instead of just one.

## 11. File Upload
It will display an option to upload files to TagoIO. These files will be stored in the files section under the folder `devices/your_device_id/` by default. It is possible to restrict the number of files per upload, allow users to use their mobile camera, and more.
:::info
You can customize the path, however. For example, if you want to save the files in a folder called `images` and a subfolder called `icons`, you would need to type `devices/your_device_id/images/icons`
:::
The variable that receives this data keeps the file name in the value parameter, and in the metadata it keeps the file's parameter. So sending a file will produce variable data like this:
```json
{
"variable": "file_variable",
"value": "file_name.png",
"metadata": {
"file": {
"md5": "md5_data",
"path": "devices/your_device_id/file_name.png",
"url": "your_file_url"
}
}
}
```
## 12. Filtered Variables
It will display a dropdown field, with options that change dynamically based on other fields. In the example below, the Food field changes based on the Food Type field. Learn more about **Filtered Variables**

## 13. Image / Video Select
Whenever you need to choose a value by using images instead of entering text, the image Field is the best option in form widgets. By entering media URLs into the options, the field will be filled with these images or videos and you will be able to select one or many of them. Learn more about Adding Image Selector field on Forms.

## 14. Incremental Step
It will display a step button to select a numerical value.

## 15. Radio
A traditional Radio input will appear. Once selected, you will be able to define its options with their labels and values. The value of the field will be one of the selected options.

## 16. Validation
This field type does not represent a form input value. Instead, the variable assigned to this field expects to receive text data that displays as a message above the form. The validation message supports four predefined types: warning, info, danger, and success. You can specify the message type by including a type property in the metadata object.
:::info
By default, validation messages are displayed to all users viewing the widget. To target specific users, capture the User Session ID using the Capture User field and include the session_id parameter in the metadata object.
:::
| Parameter | Type | Required | Description |
| ------------- | ------- | -------- | ------------------------------------------------------------------------------------ |
| type | string | No | Message type: warning, info, danger, or success |
| color | string | No | Custom color override. If not specified, color is determined by the `type` parameter |
| session_id | string | No | User session ID to target specific users. If omitted, message displays to all users |
| show_markdown | boolean | No | Enables markdown rendering in the message text |
Example:
```json
{
"variable": "validation",
"value": "Your **data** has been successfully updated!",
"metadata": {
"type": "success",
"color": "green",
"session_id": "USER_SESSION_ID",
"show_markdown": true
}
}
```

## 17. Other fields
• **Barcode:** It will display a field to type the barcode or scan it when using it through mobile or computer.
• **Capture user:** It will capture user information, such as e-mail or name, so that you can use it in the Analysis later.
• **Capture blueprint:** It will capture the blueprint device from the blueprint dashboard.
• **Hidden:** For submitting data without user input, this variable will receive a static value, set by the field edit screen.
• **Number:** It will display a text input, but it validates if a number was typed.
• **Password:** It will display a password input.
• **QR Code:** It will display a field to scan a QR Code when using it through mobile or computer.
---
## Filtered Variables
Filtered Variables is a field type in the [Input Form](/docs/tagoio/widgets/input-widgets/input-form/) widget. It lets the user select a single value from a list whose options change dynamically based on the values of other fields.

For this field to work, it must be linked to another field. In the example above, the "Food" field is linked to the "Food Types" field. Whenever the Food Types field changes, the options inside the Food field are refreshed.
## Configuration
In the Fields Configuration tab of your [Input Form](/docs/tagoio/widgets/input-widgets/input-form/) widget, select the field type **Filtered Variables** to configure this field.
- **Label**: The label above the field; it should indicate what the purpose of the field is.
- **Required**: Indicates whether this field must be filled before submitting the input form.
- **Show in a new line**: Determines if the field should start on a new line inside the widget.
- **Placeholder**: Extra explanation for the user; it appears as text inside the field when empty.
- **Filter origin**: The linked field that will filter the options based on its value (e.g., `food_types`).
- **Options**: The source of the list; the values of the selected variable populate the options in this field.
## Structure
To enable filtering, each option in your list must contain a `group` property. For example, if you have a variable named `food_options`, each item should look like:
```json
{
"variable": "food_options",
"value": "Apple",
"group": "fruit"
}
```
When the value of the **Filter origin** field changes, the list filters its items by comparing that value with the `group` property of each option. The comparison ignores whitespace and case (e.g., `"my value 1"` matches `"myvalue1"`).
## Example
Below is a concrete example using the structure described above.
```json
[
{
"variable": "food_options",
"value": "Apple",
"group": "fruit"
},
{
"variable": "food_options",
"value": "Banana",
"group": "fruit"
},
{
"variable": "food_options",
"value": "Broccoli",
"group": "vegetable"
},
{
"variable": "food_options",
"value": "Carrot",
"group": "vegetable"
}
]
```
In the input form, create two fields:
1. **Food Types**: a static dropdown with options `fruit` and `vegetable`.
2. **Food**: a Filtered Variables field that uses `food_options` as its source.
When a user selects `fruit` in the Food Types field, the Food field will display only `Apple` and `Banana`. Selecting `vegetable` shows `Broccoli` and `Carrot`.
---
## Form Fields Visibility
Fields Visibility lets you control the visibility of specific fields in your Input Form widget. By defining conditions, you can hide or show fields based on the values of other fields.


For example, a field following the "feedback type" can change depending on the selected feedback value. If the feedback type is suggestion, we show the "What is your suggestion?" field. If the feedback type is bug, we show the "What is the problem?" field.
## Benefits of Fields Visibility
1. Free up space in your form by hiding undesired fields at specific times.
2. Separate your fields. You no longer need to use a single field label for multiple purposes and then parse values differently based on other fields.
3. Organize the data posted. If a field is hidden, it will not be posted along with the rest of the visible fields.
## Creating Conditions
To use field visibility, you need to create conditions that define when a field will be visible. You can start creating conditions by editing your Input Form Widget and then navigating to the **Fields Visibility** tab.
- By default, each variable shows one box in this tab.
- If you don't set any visibility conditions on a field, it will always be visible.
- Once you define at least one visibility rule, the field will only appear if it matches one of the conditions. You can add up to **10** visibility conditions per field.
To create a condition, press the **Add Rule** button. A new row will appear with three key fields:
1. **Variable**: Choose a variable that was previously defined in the Main Configuration tab; this is the variable the condition will be based on.
2. **Condition**: Specify the comparison type (e.g., _equals_, _contains_, _greater than_, etc.) that will be used against the variable's value.
3. **Value**: Enter the value to compare against.
When you configure a row, the field `suggestion_feedback` will only appear when the chosen variable's field matches the condition specified. For example:
```
IF feedback_type
IS equal to "suggestion"
THEN suggestion_feedback (this field will be visible)
```
This textual representation helps clarify what each part of the rule does.
For more details on configuring each form field, see the [Input Form Widget](/docs/tagoio/widgets/input-widgets/input-form/) documentation.
---
## Input Form Widget
The Input form allows users to submit values to variables through a form.
## Editing the form
The Input form is a very dynamic widget. You can customize its colors, layout, text, and icons. To edit a specific section or element of this widget, go to the structure section and click on the desired element.
## Sections
Sections are the main structure of the form, and each section has its own fields.
You can show a divider between sections, duplicate sections, remove captions, and more.

### Fields
Fields are associated with a variable and device using the **Data Sources**. Once the form is submitted, the data can be sent through the device's storage.

Input form has many field types, such as Address, Barcode, Calendar, Checkbox, Device, Dropdown, Text, and more.
Learn more about [Input Form fields type](/docs/tagoio/widgets/input-widgets/input-form/field-types-for-input-form.md).
It is also possible to change a field's visibility based on other fields' values: see the article on [Form fields visibility](/docs/tagoio/widgets/input-widgets/input-form/form-fields-visibility.md).
#### Data Sources Field
This field allows you to set the device and variable that will be used in this widget. This field is dependent on the **type of dashboard** you are using; the difference is explained below.

##### Normal Dashboards
From the option **Data Sources** on the right menu, select the source of the data, which can be either a [Device](/docs/tagoio/devices/) or an [Entity](/docs/tagoio/entities.md). After that you can select the specific Device or Entity and its field or variable.
##### Blueprint Dashboards
From the option **Data Sources** on the right menu, select the source of the data, which can be either a Device or an Entity. Add the [Blueprint device](/docs/tagoio/devices/blueprint-devices-entities.md) and input the name of the variable or field that contains the information.
When using a [Blueprint dashboard](/docs/tagoio/dashboards/blueprint-dashboard.md), the inputs Variable or Field will suggest a list of variables to be picked using the currently selected blueprint device.
When using Entities, certain fields might be marked as required. If these essential fields are not included in your form, an error will occur. To resolve this issue, you can either add the missing fields to your input form or modify the field settings in your Entity so that it is no longer mandatory. See the article on [Entities](/docs/tagoio/entities.md).
## Buttons
It is also possible to create and customize buttons that can perform actions such as running analysis, clearing fields, and sending data to the device.

After the button has been clicked, you can show a confirmation dialog message that needs to be accepted so that the actions of the button can be performed.
When the button triggers an analysis, it sends the field data and the button identifier through the analysis's scope.
---
## Creating Keypad Widgets
## Overview
This article demonstrates how to create a Keypad Widget using the TagoIO Node.js SDK. See [Keypad Widget](/docs/tagoio/widgets/input-widgets/keypad-widget) and [TagoIO Node.js SDK](/docs/tagoio/analysis/sdk/nodejs-sdk.md) for more information.
To create keypad widgets, send the following structure to the `account.dashboards.widgets.create` method.
## Example widget structure
```json
{
"analysis_run": "",
"data": [
{
"bucket": "5d8d06027fe011b001b8d236b",
"origin": "5d8d06027fe011b001b8d236a",
"timezone": "Your/Timezone",
"variables": ["my_keypad_variable"]
}
],
"display": {
"buttons": [
{
"color": "",
"disabled": false,
"icon": "",
"text": "",
"type": "text",
"value": ""
}
],
"bypass_bucket": false,
"click_color": "#337ab7",
"help": "",
"main_color": "white",
"send_run_user": false,
"show_asterisk": true,
"show_digits_bar": true,
"show_last_column": true,
"show_last_row": false,
"show_number_sign": true
},
"label": "Keypad #1",
"type": "keypad"
}
```
Notes:
- **analysis_run**: This field should contain an ID indicating which analysis to run when a user submits the widget.
- **data**: This field should contain an array of variables used in the keypad widget. By default, the widget only uses the first variable, so this array should typically contain a single object.
- **display.buttons**: This field is required and defines all custom buttons inside the keypad widget. Each button's position corresponds to its index in the array (0-based). For example, the image below shows how each index maps to a physical key on the keypad:

- **display.bypass_bucket**: If set to `true`, the data sent by this widget will never be stored in the device. It will still be available inside the analysis scope.
- **display.click_color**: The color shown for each digit when a user clicks on it. Accepts any browser-recognizable color format (name, hex, rgb, hsl, etc.).
- **display.help**: Usual help text for the widget.
- **display.main_color**: Main color for the digits inside of the keypad.
- **display.send_run_user**: Indicates if a new variable called `run_user` should be sent along with the usual data from this widget. Read more about data manipulation at the bottom of the page.
- **display.show_asterisk**: Indicates if the asterisk digit should appear inside of the keypad. If set to `false`, a new slot for custom buttons will appear.
- **display.show_number_sign**: Indicates if the number sign digit should appear inside of the keypad. If set to `false`, a new slot for custom buttons will appear.
- **display.show_digits_bar**: Indicates if a digits bar should appear above the widget, showing asterisks representing the amount of digits typed by the user.
- **display.show_last_column**: Indicates if the last column of the buttons should appear or not.
- **display.show_last_row**: Indicates if the bottom row containing buttons should appear or not.
- **label**: The title of the widget inside the dashboard.
- **type**: Must be set to `"keypad"`.
Send this payload via the `account.dashboards.widgets.create` method using the TagoIO Node.js SDK.
---
## Keypad Widget
Keypad allows the user to submit a PIN code along with an optional action string. This widget is ideal to represent an alarm or to request a PIN code to unlock a resource.

## Creating your own
To add the Keypad to your dashboard, choose the Keypad widget from the widget list and customize it to your preference. You can edit its settings using the options located on the right side of the widget.
## 1. 'Data Sources' Field
This field allows you to set the device and variable that will be used in this widget.
### 1.1 'Data Sources' for Normal Dashboards
From the option **'Data Sources'** on the right menu, select one device from your list of devices and the variable that contains the data.
### 1.2 'Data Sources' for Blueprint Dashboards
From the option **'Data Sources'** on the right menu, add the [Blueprint device](/docs/tagoio/devices/blueprint-devices-entities.md) and input the name of the variable that contains the information.
:::info
When using a [Blueprint dashboard](/docs/tagoio/dashboards/blueprint-dashboard.md), the field **Variable** will list variables to be picked using the currently selected blueprint device.
:::
## 2. Visualization
By default, the numeric keypad contains 10 digits that range from 0‑9, and it also contains an asterisk (\*) and a number sign (#).
You have the option to customize the content of the keypad by creating buttons that will appear along with the digits.
Learn more about the [customization options of your keypad](/docs/tagoio/widgets/input-widgets/keypad-widget/keypad-visualization.md).
## 3. Data manipulation
The keypad will send the data immediately after pressing a custom button, or just by inputting a combination of numeric digits and waiting 3 seconds.
Whenever a PIN code is submitted, the structure sent to the variable will be:
```
DIGITS,ACTION
```
For instance, if the user has clicked the sequence **1234** and then **DISARM**, the output would be:
```
1234,disarm
```
Learn more about how to [manipulate the keypad's data](/docs/tagoio/widgets/input-widgets/keypad-widget/keypad-data-manipulation.md).
---
## Keypad Data Manipulation
The keypad sends data either when a custom button is pressed or when a combination is entered and left for 3 seconds. The values are delivered as a single string variable following a simple structure.
## Data format
Whenever a PIN code is submitted, the structure sent to the variable is:
```text
DIGITS,ACTION
```
- DIGITS: the numeric sequence entered (if any)
- ACTION: the custom button name (if any), lowercase
## Examples
- If the user enters the sequence 1234 and then presses a custom button named DISARM, the output will be:
```text
1234,disarm
```
- If the user enters a sequence and no action button, the action is excluded:
```text
1234
```
- If the user only presses a button and does not enter a sequence, the output will be the button name:
```text
disarm
```
## Using the data
The recommended way to extract and use this data is through an Analysis. The Keypad widget provides an option to run an analysis from the options section.

### Analysis
When you select an analysis, the scope that you will receive contains a JSON array with each item having the following structure:
```json
[
{
"device": "5da07e1269c7ae001b2faefe",
"origin": "5da07e1269c7ae001b2faeff",
"id": "5da07e1269c7ae001b2faefs",
"variable": "my_variable",
"created_at": "2019-10-16T00:00:00Z",
"time": "2019-10-16T00:00:00Z",
"value": "1234,disarm"
}
]
```
Since the data can be a digit sequence, an action string, or both, the easiest way to extract the parts inside the analysis is by using the `split` method on the string.
```python
# Python
scope[0].value.split(',')
# ['1234', 'disarm']
```
```javascript
// Node.js
scope[0].value.split(',');
# ['1234', 'disarm']
```
### Do not store data
If you select this option, the data sent by the widget will never reach the device's data storage. Keep in mind that the data sent will still be accessible inside of the analysis' scope.
### Capture User Information
> This option only works if you are using this widget inside of the [Run](/docs/tagoio/tagorun/) platform.
If you select this option, the data sent by the widget will have an additional object called `run_user`. The structure of that item is:
```json
{
"device": "5da07e1269c7ae001b2faefe",
"origin": "5da07e1269c7ae001b2faeff",
"id": "5da07e1269c7ae001b2faefs",
"variable": "run_user",
"created_at": "2019-10-16T00:00:00Z",
"time": "2019-10-16T00:00:00Z",
"value": "5da07e1269c7ae001b2faeff"
}
```
The value in this case will be the ID of the run user.
---
## Keypad Visualization

The Keypad widget displays a numeric keypad with digits 0 through 9, an asterisk (\*), and a number sign (#), just like a traditional telephone keypad.
> You can further tailor the keypad to your application by adding custom buttons in addition to the standard digits.
## Button Configuration

You can create and configure extra buttons to appear alongside the keypad digits. Button configuration options appear in the widget settings (accessible via the widget editor). In each button configuration, you have some options to customize the visuals and functionality of your button.
- **Content type**: The type of content that will be inside of the button. You can put text here or simply an icon.
- **Value**: This is the value that will be sent to the [device](/docs/tagoio/devices/) or [analysis](/docs/tagoio/analysis/creating-analysis.md) when a user submits the keypad. Learn more about [Keypad's Data Manipulation](/docs/tagoio/widgets/input-widgets/keypad-widget/keypad-data-manipulation.md).
- **Background color**: The color of the button's background. The font color will be automatically adjusted to either black or white for a better contrast with your color.
- **Enabled**: Indicates if the button is enabled or not. Disabled buttons cannot be clicked.
- **Remove Button**: If you click this option, this button will be removed from the keypad. You can still create another button in the same slot as this one.
---
## Push Button Widget
Push button allows users to select options for different states, similar to a mechanical switch. Developers can configure text or an icon for each state and associate colors with each option.
This widget works on both Normal and Blueprint dashboards.

You can also define an Analysis (script) to run when users change each state. See [Analysis](/docs/tagoio/analysis/).
This widget also accepts features like [metadata](/docs/tagoio/devices/payload-parser/metadata.md), which can be set in your variable data.
## Creating your own
To add the Push Button widget to your dashboard:
- Choose the Push Button widget from the widget list.
- Customize it to your preference.
- Edit its properties using the options located on the right side of the widget.
**Data Sources field**
This field allows you to set the device and variable that will be used in this widget.
For Normal dashboards, select a device and its variable from the list.
For Blueprint dashboards, add the Blueprint device and input the variable name; the variable selector will suggest a list of variables to be picked using the currently selected blueprint device.
**Push Button type**
The button can operate in two modes:
- **Mono-Stable**: The button stays in its default state unless clicked. A click temporarily changes it for a few seconds before returning to default. You can set colors, text, icons and optionally run an analysis during this temporary state.
- **Bi-Stable**: The button keeps the user's selected state indefinitely. Colors, texts or icons can be configured, and an optional analysis can run in each state.
### Color from Metadata
You can control the colors of the Button widget by using information stored in the metadata of the variable.
Set the option _Override colors with the conditions defined here_ to **False** on the Visualization tab.
The following metadata fields can be used:
- `text_color`: color for the text (value)
- `button_color`: color for the button
```json
{
"variable": "set_point",
"value": 5.3,
"unit": "°C",
"metadata": {
"text_color": "white",
"button_color": "blue"
}
}
```
:::tip
You can always enter colors using #HEX or RGB values instead of a color name.
:::
```json
{
"variable": "set_point",
"value": 5.3,
"unit": "°C",
"metadata": {
"text_color": "#FFFFFF",
"button_color": "rgb(44, 144, 223)"
}
}
```
---
## Step Button Widget
Step button allows users to increase and decrease values of a number or clock by using the (-) and (+) buttons.

You can also define the incremental step value and the allowed limits.
## Creating your own
To add the Step Button widget to your dashboard, choose the Step Button widget from the widget list and customize it to your preference. You can edit it using the options located on the right side of the widget.
### Data Sources Field
This field allows you to set the device and variable that will be used in this widget.
- **Normal Dashboards**: Select one device from your list of devices and choose the variable that contains the data.
- **Blueprint Dashboards**: Add the Blueprint device and input the name of the variable. When using a Blueprint dashboard, the Variable field will suggest a list of variables to be picked using the currently selected blueprint device.
### Step Button Types
The widget can control either a numeric value or a clock (time).
#### Number
When **Number** is selected:
- A unit can be added to the variable.
- The step, minimum, and maximum limits should be defined in the _Data Range & Format_ tab.
- In the _User Control_ tab you can define how many times the value will blink before posting the data to the variable.
The widget will POST using this format:
```json
{
"variable": "set_point",
"value": 2.5,
"unit": "°C"
}
```
### Color from Metadata
You can control the colors of the Step Button widget by using information stored in the metadata of the variable.
1. In the _Visualization_ tab, set **Override colors with the conditions defined here** to `False`.
2. Inside the metadata, include the following fields:
```json
{
"text_color": "white",
"button_color": "blue"
}
```
3. Example using HEX or RGB values instead of color names:
```json
{
"variable": "set_point",
"value": 5.3,
"unit": "°C",
"metadata": {
"text_color": "#FFFFFF",
"button_color": "rgb(44, 144, 223)"
}
}
```
:::tip
You can always enter colors using `#HEX` or `RGB` values instead of a color name.
:::
---
## Heat Map Widget
With the Heat Map widget you can create a colorful layer of bubbles over an image to represent the intensity of variables. Higher variable values produce redder (larger/intense) bubbles, and you can add a heat scale to the layer to show the value range.

This widget also accepts features like [metadata](/docs/tagoio/devices/payload-parser/metadata.md), which can be set in your variable data.
## Creating your own
To add the Heat Map widget to your dashboard, choose "Heat map" from the widget list and customize it to your preference. Edit the widget using the options located on the right side of the widget editor.
### Widget compatibility
This widget works for both **Normal** dashboards and **Blueprint** dashboards.
### Data Sources field
The _Data Sources_ field lets you select the device and variable that will feed data into this widget.
- In a Normal dashboard, choose one of your devices from the list and then pick the variable that contains the data.
- In a Blueprint dashboard, add the **Blueprint device** and input the name of the variable that holds the information.
When using a [Blueprint dashboard](/docs/tagoio/dashboards/blueprint-dashboard.md), the field **Variable** will list variables to be picked using the currently selected blueprint device.
### Setting a background image
The widget supports three types of background images:
1. **Static**: an image from a fixed URL.
2. **Conditional**: multiple conditions each with its own image URL; the image that matches the condition is displayed.

3. **Dynamic**: the image URL comes from variable data.
> You can store images in TagoIO's [file system](/docs/tagoio/files) and use a public URL.
### Bubble position
You must provide an X and Y coordinate for each variable. The coordinates are normalized between 0 and 1, where (0, 0) is the top‑left corner of the image and (1, 1) is the bottom‑right corner.
#### Using metadata
If your variable's metadata contains `x` and `y`, those values will be used as the bubble position:
```json
{
"variable": "heat",
"value": 71,
"metadata": { "x": 0.2, "y": 0.3 }
}
```
The widget will display a heat bubble at that location.
#### Using the widget editor
You can also set coordinates directly in the widget editor. If metadata is not present, the editor values are used.

### Range of scale
You can define a custom range for the heat bar that appears at the bottom of the image. By default, the scale is generated automatically from your variable values.
The scale determines which color corresponds to each value; even if you hide the heat bar, bubbles will still respect this mapping.
:::info
The range can be set with fixed or dynamic values using another variable's data, similar to [Gauge's range](/docs/tagoio/widgets/gaude-and-meters/gauge-data-range-format.md).
:::
---
## Image Marker Widget
The Image Marker widget places pins on custom images, such as floor plans, building maps, and facility diagrams, and connects each pin to live or historical device data. It's the standard choice for **indoor device tracking**, where GPS isn't available and positions are defined relative to an image.

To use this widget you need at least two things:
- **A pin data variable**: the device variable whose value is shown on the pin (e.g., temperature, status).
- **A layers variable**: a variable that stores the floor plan image and the `x`/`y` position of each pin for each layer.
Optionally, you can also configure a **geofence variable** to store the boundaries users draw on the image.
## How it works
1. Add the Image Marker widget to a [dashboard](/docs/tagoio/dashboards/).
2. Configure the **Data From** field with your pin data variables.
3. Set a **layer variable** to store the floor plan image and pin positions.
4. Use the **editor** to visually place pins on the image.
5. Optionally, draw geofences to define zones.
## Creating your own
To add the widget to your dashboard, choose Image Marker from the [widget list](/docs/tagoio/widgets/). Configure it using the options panel in the edit panel.
### 1. 'Data Sources' Field
This field sets which device variables supply pin data.
> **Note:** The Data From field supports up to 30 devices. To display more than 30 pins, use Advanced mode and configure a single device that stores each tracked device's position as a separate [group](/docs/tagoio/devices/grouping-variables). Each group becomes its own pin.
- **Normal Dashboards**: select a device and the variable that holds pin data.
- **[Blueprint Dashboards](/docs/tagoio/dashboards/blueprint-dashboard)**: add the [Blueprint device](/docs/tagoio/devices/blueprint-devices-entities) and type the variable name. The selector may appear empty because the dashboard does not yet know which devices are linked to the Blueprint.
#### Layer Variable
Below the main **Data From** selector there is a **"Variable used to store layers"** field. Set it to the variable that holds the floor plan image for each layer. When a user uploads or replaces a floor plan image via the editor, the new image URL is written back to this variable automatically.
#### Geofence Variable
The **"Geofence variable"** field sets the variable where geofence boundaries are saved. When users draw polygons or circles in the editor, the shapes are written to this variable automatically. See [Geofence](#geofence) for the full data format.
### 2. Basic vs. Advanced Mode
The widget has two modes. Both store pin positions identically in the layer variable. The difference is how the editor presents options when you assign a pin:
| Mode | Description |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Basic** | The editor shows the **devices** from the Data From field as pin assignment options. Each device gets one pin. Use this for simple setups where each physical device maps to one marker. |
| **Advanced** | The editor shows all occurrences of the selected **variable** that have different [groups](/docs/tagoio/devices/grouping-variables) as options. Each group becomes a separate pin. Use this when a single device reports multiple positions or when you need more than 30 pins. |
### 3. Layer
A layer is a background image on which pins and geofences are drawn. You can have multiple layers, for example, one per floor of a building, each with its own set of pins.
Each layer has its own dedicated variable. In the widget configuration, you assign one **"Variable used to store layers"** per layer. That variable stores both the floor plan image URL and the current pin positions in a `fixed_position` map.
#### fixed_position key format
The key format in `fixed_position` depends on the mode:
- **Basic mode**: the key is the **device ID** alone.
- **Advanced mode**: the key is the **device ID immediately followed by the group ID, with no separator**. For example, if the device ID is `abc123` and the group is `xyz789`, the key is `abc123xyz789`.
> The fields you configure are inside the [`metadata`](/docs/tagoio/devices/payload-parser/metadata) object: `fixed_position` (pin positions) and `file` (the floor plan image URL). System fields (`id`, `time`, `created_at`) are added automatically.
**Basic mode**: key is just the device ID:
```json
{
"value": "Layer #1",
"variable": "layer",
"metadata": {
"fixed_position": {
"": {
"device": "",
"label": "",
"link_type": "image",
"x": "0.49333079268292684",
"y": "0.4043542074363992"
}
},
"file": {
"url": "",
"path": ""
}
}
}
```
**Advanced mode**: key is device ID + group ID (no separator):
```json
{
"value": "Layer #1",
"variable": "layer",
"metadata": {
"fixed_position": {
"": {
"device": "",
"link_type": "image",
"value": "",
"x": "0.5965083073727934",
"y": "0.3530265748031496"
},
"": {
"device": "",
"link_type": "image",
"value": "",
"x": "0.2943276220145379",
"y": "0.26798720472440946"
}
},
"file": {
"url": "",
"path": ""
}
}
}
```
Layers are managed at the top of the editor screen. The pencil icon opens a panel to rename the layer or replace its image. Dragging an image file onto the editor replaces the current layer's image.
#### Positioning pins
Pin positions are stored in the `fixed_position` map of the layer variable, not in the device variable itself. Coordinates are relative to the image dimensions: `(0, 0)` is the top-left corner and `(1, 1)` is the bottom-right corner. Think of them as percentages: `x: 0.5` means the pin is at 50% of the image width.
You can set positions visually using the built-in editor without editing variable data directly. See [Editor](#4-editor) for details.
### 4. Editor
:::caution
Changes only take effect after clicking **Save**. Closing without saving discards all changes.
:::
The editor lets users place and adjust pins, manage layers, and draw geofences. It is enabled via the pencil icon in the top-left corner of the widget.
The toolbox contains four tools:
- **Default tool**: zoom and pan the image.
- **Add tool**: click on the image to place a new pin. The number of available pins is limited by the number of data entries.
- **Edit tool**: click an existing pin to change its variable, label, or icon.
- **Delete tool**: remove a pin.
## Additional features
### Infobox
Each pin has an infobox that opens when clicked. It shows the variable's value and can be customized with:
- **Image**: display a static or dynamic image alongside the data.
- **External link**: add a button that opens a URL.
- **[Formula](/docs/tagoio/widgets/general/formula)**: transform the displayed value using a math expression.
- **Embedded widget**: embed any dashboard widget inside the infobox.
### Filters
Filters let users narrow down which pins are visible. You can filter by **device**, **variable**, or **value**. A table below the filters lists all visible pins, and clicking a row centers the view on that pin.
### Geofence
Geofences are colored boundaries (polygons or circles) drawn on the image. Use them to define zones and detect when a tracked device enters or leaves one. You can also trigger [Actions](/docs/tagoio/actions/trigger-by-geofence) based on geofence events, similar to [geofences in the Map widget](/docs/tagoio/widgets/map-and-location/map-widget/geofences-in-map-widgets).
Before users can create geofences, you must configure at least one **event** in the **Geofence Options** tab of the widget settings. An event defines what trigger to associate with the geofence (for example, "enter" or "leave"). You can hardcode event options directly in the widget settings or supply them from a variable.
When using a variable, each entry represents one event option. The `value` field is the event identifier:
```json
[
{
"variable": "event",
"value": "enter"
},
{
"variable": "event",
"value": "leave"
}
]
```
When enabled, the geofence tools appear in the upper-right corner of the editor:
- **Draw polygon**: click points to define a shape; click the first point to close it.
- **Draw circle**: click the center, then click again to set the radius.
- **Edit tool**: modify an existing polygon or circle.
- **Delete tool**: remove a geofence.
Each geofence is saved as its own separate entry in the geofence variable. The shape type determines the format of the `coordinates` field.
**Circle geofence**: `coordinates` is a single center point `{x, y}`, and `radius` is the size (both in the 0 to 1 range):
```json
{
"value": "Geofence #2",
"variable": "geofence",
"metadata": {
"coordinates": {
"x": 0.45642229087452474,
"y": 0.3955462598425197
},
"radius": 0.09320854807801622,
"type": "circle",
"event": "enter",
"eventDescription": "enter",
"eventColor": "",
"layer": "5rzZEJza2e0f4LNs7OuEp"
}
}
```
**Polygon geofence:** `coordinates` is an array of `{x, y}` vertices:
```json
{
"value": "Geofence #1",
"variable": "geofence",
"metadata": {
"coordinates": [
{ "x": 0.4145971958174905, "y": 0.058538385826771656 },
{ "x": 0.6275249524714829, "y": 0.08373523622047244 },
{ "x": 0.5666884505703422, "y": 0.19712106299212598 },
{ "x": 0.430756891634981, "y": 0.181373031496063 },
{ "x": 0.32619415399239543, "y": 0.09318405511811023 },
{ "x": 0.43360860266159695, "y": 0.06956200787401574 }
],
"type": "polygon",
"event": "leave",
"eventDescription": "leave",
"eventColor": "",
"layer": "5rzZEJza2e0f4LNs7OuEp"
}
}
```
> **Note:** All coordinates are relative to the image dimensions (range 0-1), not geographic coordinates. The `layer` field references the layer this geofence belongs to using the variable group. Set `eventColor` to a hex value (e.g., `#ff0000`) to color-code the geofence boundary; leave it empty to use the default color.
---
## Geofences in Map Widgets
Geofences allow developers to color parts of the map and detect or trigger events inside specific areas.

## Creating geofences
To create a geofence, go to **Geofence options** in your widget's configuration. Then set the option **Allow users to edit geofence** to _yes_. You can choose to allow users to create polygons, circles or both, and even select the number of geofences allowed in the map.

> The maximum number of geofences allowed is **99**.
You can also set the title of the geofences in the field **Geofence tooltip title**. The title will be shown in all of the geofences' pop‑ups, like so:

After setting up the options, you are all set to create the geofences. Go to the map widget and click on the pencil icon; use the tools located on the top right of the screen.

The geofence toolbox is composed of:
- **Default tool**: allows you to zoom and drag the image;
- **Draw polygon**: click on the image to draw your geofence; when you click on the first drawn point, the geofence ends. You can rename it and add an event on the right of the editor;
- **Draw circle**: click at the center point of your circle, then click again to select the radius size. When you click the second time, the geofence ends; you can rename it and add an event to the right of the editor;
- **Delete tool**: delete the geofences by clicking on them.
Below the toolbox, you can find a list of all the geofences that are in the selected layer, and you can edit it by clicking on the geofence name.
## Events
You can add events to geofences. To do so, head over to the **Geofence Options** tab in the map's edit configuration. For each event, set an _event code_ (so it can be identified in analysis) and a _description_ (what users will see). You can also set a layer color; this color will change the color of the geofence itself.
## Geofence variable data
The geofences will be saved in a variable, so developers can manage them easily, display the geofences on the map, select the device and variable.
> This is similar to the _Data Sources_ field from the Map Widget.

When users create new geofence areas, they will be saved in the previously selected variable with the following format; notice that you will have the coordinates of the polygons in the variable's metadata.
```json
{
"id": "5b75c8ccdf9a8e00017f1488",
"variable": "fence",
"time": "2018-08-16T18:54:24.000Z",
"origin": "5b758f096319ee0001f9d570",
"value": "",
"metadata": {
"id": "l1w5r37clqwnk4ntf1rpjx16u1cff1wr5tgt",
"color": "#00FF22",
"geolocation": {
"type": "Polygon",
"coordinates": [
[
[42.300325960559654, -85.63790805637836],
[42.305971081721594, -85.63798734918238],
[42.30593698760671, -85.62895853072405],
[42.29844438901512, -85.62885962426662],
[42.298453564525126, -85.63375599682331],
[42.30022652075572, -85.63364267349243]
]
]
},
"event": "area_4"
}
}
```
---
## Images and Links in the Map's Pins
Developers can add an image and a URL link to each pin on the map.

To add images and a URL to your pins, open the Map widget options menu, go to Advanced Options, and then choose the Edit option.
### Setting images and Links using devices
Another way to insert an image and url link to a pin is to use the metadata parameter of the data the device sent. The widget will first look at these elements and, if not found, it will use the default settings of the widget.
```json
[
{
"variable": "location",
"value": "My Address",
"location": {
"lat": 42.2974279,
"lng": -85.628292
},
"metadata": {
"color": "green",
"icon": "car",
"url_pin": {
"url": "https://admin.tago.io/dashboards/info/5b16b4c86ba351000105badc",
"alias": "More Details"
},
"img_pin": "https://pbs.twimg.com/profile_images/882008081667772417/3_2eeoPj_400x400.jpg"
}
}
]
```
Click here to test the code using our [Device emulator!](https://admin.tago.io/devices/emulator)
The parameters that define the configuration through the metadata are `img_pin`, `url` and `alias`. They work exactly the same as setting the configuration through the widget and will have the same result.
---
## Map Widget
The Map Widget allows you to visualize the location variables of your device on a map and customize the visualization in many ways. By default, the map widget uses OpenStreetMap as provider; however, you can integrate your preferred map provider like Mapbox, Google Maps, or Here to fit your needs.
The widget also allows you to customize the visualization by adding icons, pins, images, links, geofences, filters, and much more!

The variable must include a `location` field with your device's coordinates. TagoIO accepts both GeoJSON and `{ lat, lng }` formats at ingestion. See [Sending Data](/docs/tagoio/devices/sending-data.md#location-field) for format details and the coordinate order caveat.
```json
{
"variable": "location",
"value": "My Address",
"location": {
"type": "Point",
"coordinates": [-85.628292, 42.2974279]
}
}
```
:::caution
In GeoJSON, the coordinate order is **[Longitude, Latitude]**, the opposite of the common lat/lng convention. Make sure your values are in the correct order.
:::
This widget also accepts features like [metadata](/docs/tagoio/devices/payload-parser/metadata.md) and [groups](/docs/tagoio/devices/grouping-variables.md), that can be set in your variable data.
## Creating your own
To add it to your dashboard, choose the Map widget from the list and customize it to your preference. You can edit it by using the options located on the right side of the widget.
## 1. 'Data Sources' Field
This field allows you to set the device and variable that will be used in this widget.
Click on the **settings icon** to edit specific options for this variable, such as: pin icon, formulas, and more. Click on the **close icon** to remove this variable from the widget's data.
This field is dependent on the **type of dashboard** you are using; the difference is explained below.
### 1.1 'Data Sources' for Normal Dashboards
From the option 'Data Sources' on the right menu, select one device from your list of devices and the variable that contains the **location** data.
### 1.2 'Data Sources' for Blueprint Dashboards
From the option 'Data Sources' on the right menu, add the [Blueprint device](/docs/tagoio/devices/blueprint-devices-entities.md) and input the name of the variable that contains the **location** information.
:::info
When using a [Blueprint dashboard](/docs/tagoio/dashboards/blueprint-dashboard.md), the field **Variable** will list variables to be picked using the first blueprint device it finds using the tags set for your Blueprint Device.
:::
## 2. Tile Provider
This option allows you to define which map provider will be used on your widget to display the map. This is great if you need to have customized maps to fit your specific use case. By default, the map tile provider is OpenStreetMap, which is free to use; however, you can go with other providers like [Mapbox](https://www.mapbox.com/), [Here Maps](https://www.here.com/), or [Google Maps](https://developers.google.com/maps).
:::tip
Each map provider has its own customization options for the tiles and pricing tiers, which often may be free up to a certain amount of tile requests. You should consult the providers to make an informed decision.
:::
### 2.1 How to configure a map tile provider on TagoIO
To use one of the alternative map tile providers, you will simply need to have an **API Key** from your provider and a username in some cases. The API key can be found by accessing the console of your map provider. We suggest consulting the documentation of your provider if you have trouble getting the key.
Once the key is at hand, you will have to store the API key value inside a [Secret](/docs/tagoio/security/secrets.md) on TagoIO. Go to your Admin, then the [Secrets](https://admin.tago.io/secrets) page, and create a new secret with the name of your map provider. In the **Secret Value**, paste the API Key and save.
Once you have a Secret with your API key created, you can go to your **Map widget**, then the **Tile Provider** option, select your map provider, and now in API Key, select the secret where the API key of your map provider is stored. Once you save the widget, the map will start displaying your provider's map and not the default.
### 2.2 How to configure Google Maps as map provider
For instance, using Google Maps as a provider is quite simple. All you need to do is have a Google account and set up an API Key. Read the full tutorial here: [How to Set Up Google Maps in Your Map Widget](https://community.tago.io/t/how-to-set-up-google-maps-in-your-map-widget/1742)
## 3. Infobox
Present more information with an infobox that'll automatically be associated with the pins, and customize it with icons, images, external links, street views, formulas, routes by GoogleMaps, and much more.
In the widget edit screen, you can customize the following options for the variables:
- **Image options**: Choose how the image will appear by selecting by top or side view and the perfect aspect ratio (portrait or landscape).
- **Allow users to get directions:** Create a button with another variable location data, so that once the button is pressed a new route is made by Google Maps and you are redirected to it.
Also, you can customize an image and a link that could be set through the edit screen or by metadata. **In this widget, metadata always has priority over options set by the edit screen.**
In addition, the map widget supports [groups](/docs/tagoio/devices/grouping-variables.md), so you can group your variables' data in the same infobox.
:::tip
If a group of variables has more than one **external link** or **location data**, only one will be considered based on the **Data Sources field's order**. This does not apply to **images,** as more than one image will produce a slide show inside the infobox.
:::
## 4. Filter
The filter makes your data easier to visualize by allowing users to filter for results.
You can filter pins by **device, variable, value,** and **time**. Filters also provide a table to list each pin that's being displayed, and you can click on a row to center it.
Learn more about [Map filters](/docs/tagoio/widgets/map-and-location/map-widget/map-filters.md).
## 5. Geofence
Draw colored geographic boundaries to make it easy to see your devices' trajectory through a zone.
You can draw polygon or circle figures and associate them with events, and build an [analysis](https://community.tago.io/t/implementing-an-analysis-to-notify-when-a-device-is-inside-of-a-geofence/525) to receive notifications when the device enters or leaves the zone.
Learn more about [Geofence in map widgets](/docs/tagoio/widgets/map-and-location/map-widget/geofences-in-map-widgets.md).
## 6. Layer GIS
Customize your Map with GeoJSON or Shapefiles layers. Display boundaries, areas, roads, pipelines, and more. Learn more about [Map Layer GIS](/docs/tagoio/widgets/map-and-location/map-widget/map-layer-gis.md).
## 7. Managing Multiple Stationary Devices
When displaying hundreds of fixed sensors simultaneously (e.g., soil monitors, utility meters, parking spots), sending individual location variables from each device can be inefficient. A common strategy is to centralize all location data in a single virtual accumulator Device.
### 7.1 How it works
Instead of reading location variables from each physical device directly, you configure the Map Widget to read from a single accumulator device that stores a location variable per sensor. An [Analysis](/docs/tagoio/analysis.md) or [Action](/docs/tagoio/actions.md) can be used to push each device's location into this central device whenever it changes.
This approach reduces the number of data sources the widget needs to query and keeps your dashboard performant as the number of devices grows.
### 7.2 Grouping pins correctly
To display each data point as an **independent pin** rather than a moving trajectory, you need to configure how the widget groups samples. In the widget's **Visual** settings, the **Group samples by** option controls this behavior:

- **Date and Time (default):** Each pin is treated as a separate marker based on its timestamp. For this to work correctly, each location variable sent to the accumulator device must have a distinct ISO 8601 timestamp in the `time` field.
```json
{
"variable": "location",
"value": "Sensor B",
"time": "2024-06-01T10:00:00.000Z",
"location": {
"type": "Point",
"coordinates": [-85.631, 42.299]
}
}
```
- **Groups:** Each pin is treated as a separate marker based on the `group` field. Set the `group` field to a unique identifier per sensor, such as the original Device ID.
```json
{
"variable": "location",
"value": "Sensor A",
"group": "device-id-abc123",
"location": {
"type": "Point",
"coordinates": [-85.628292, 42.2974279]
}
}
```
---
## Map Filters
You can filter the results displayed on a map to minimize the amount of information shown and help you find exactly what you're looking for.
## 1. Filter using pins' data
In the widget configuration, under Filter options, you can customize the filter and how pins' data will be listed in the table.
The Filter table lists the pins' data that are displayed on the map. You can choose either the device name or a variable value to represent a pin's data. When a row in the table is clicked, the corresponding pin will be centered on the map.
To add a filter, select the Add filter button. There are three ways to filter results:
- By the variable name itself
- By the device name
- By values from a variable
Filtering by values will show all values for the selected variables, but only for the variables you selected.

Once the filter is enabled, a filter button will be displayed on the top left of the map widget.

:::info
Keep in mind that list by value in the Filter table only shows the values from the variables, and not the latitude and longitude of positions, for example.
:::
---
## Map Layer GIS
Display geometries, boundaries, areas, roads, pipelines, and other spatial features using the [Map widget](/docs/tagoio/widgets/map-and-location/map-widget/) GIS layer. Each layer is defined by GeoJSON files (`.geojson`, `.json`, `.zip`), which you can upload and manage either statically or dynamically.
## Supported file types and shapefile conversion
- Accepted GeoJSON formats: `.geojson`, `.json`, and `.zip` (containing GeoJSON).
- Shapefiles are also accepted. To use shapefiles, upload a ZIP that contains the layer files (`.shx`, `.shp`, `.prj`, `.dbf`); the system will convert the uploaded shapefile to GeoJSON.
## Example

## Layer Type
### 1. Static layer
In the static layer, you can use the widget configuration to input and choose a file. The file can come from our [files module](/docs/tagoio/files) or an external URL. In addition, the label input defines how the layer will be listed in the map.
### 2. Dynamic layer
In the dynamic layer, the files are saved in a variable so developers can manage them easily and display the layers dynamically. This is similar to the **Data Sources** field of the Map widget.
Each variable value will display a GIS layer; you can control that using the [Dynamic Table widget](/docs/tagoio/widgets/tables/dynamic-table-widget.md) or the [Input Form widget](/docs/tagoio/widgets/input-widgets/input-form/).
A variable should have a payload like this so that the Map widget can successfully display a layer:
```json
{
"variable": "layer_variable",
"value": "file name",
"metadata": {
"url": "uploaded file URL",
"label": "layer label"
}
}
```
The **Metadata** field is used to indicate the file's URL or the label that defines how the layer will be listed.
## Layer color
You can use colors from your GeoJSON file by indicating which property represents this color. You can also override the file color with a fixed color or set up color conditions. Color conditions help indicate the layer status based on variable data; the variable used for color conditions is defined in the Layer color configuration of the Map widget.
---
## Router
You can enable the Router option to allow users to get directions to a destination on the map. This feature provides turn‑by‑turn routing to guide users to the locations of their devices.

### How to configure the Router
To use the router, enter the map widget in edit mode. You must first define an **origin** point.
This is done by selecting a device and specifying the variable that holds its coordinates (for example, GPS data from the TagoIO mobile app or any other device that reports latitude/longitude).
Once the origin is set, you can enable the router option in the map widget settings. The following screenshot shows how to activate it:

When users access the map from a mobile device, they will have the option to select one of the following navigation services:
- Google Maps
- Apple Maps
- Waze
:::info
Only Google Maps is available when using a browser from a computer.
:::
---
## Compose Widget
The compose widget allows you to add pins to images and videos. You can change the colors, values, and icons for each pin.

This widget also accepts features like [metadata](/docs/tagoio/devices/payload-parser/metadata.md) and [groups](/docs/tagoio/devices/grouping-variables.md), which can be set in your variable data.
## Creating your own
- **1. 'Data Sources' Field**
The _Data Sources_ field lets you choose the device and variable that will feed the widget. For normal dashboards, select a device from your list and then pick the variable containing the pin data. In Blueprint dashboards, you must first select the Blueprint device; the variable name is then entered manually because the system cannot automatically list variables linked to the Blueprint.
**Important:** The number of pins you can add is directly tied to the number of devices added in this section, and each device can provide one pin. At least two variables are required: one for the pin's data (label, color, icon, etc.) and another to store all pin positions on the media.
- **2. Media Backgrounds**
The widget supports four background types: an image, a YouTube video, a Vimeo video, or a generic video file. These can be set in the widget edit screen of the TagoIO admin console. You can also adjust the aspect ratio and resize mode (Fill, Contain, Cover) to fit your layout.
- **3. Positioning Pins**
Pin coordinates are expressed as normalized values between 0 and 1 for both X and Y axes, where (0, 0) is the top-left corner and (1, 1) is the bottom-right corner of the media.
_Manual method:_ Include `x` and `y` parameters in the variable's metadata:
```json
{
"fixed_position": {
"device_id": {
"device": "Temperature Sensor",
"label": "Pin One",
"color": "#313c46",
"icon": "presentation",
"x": "0.5",
"y": "0.5"
}
},
"file": {
"url": "https://api.tago.io/file/…/video.mp4",
"path": "/buckets/device_id/video.mp4"
}
}
```
_Editor method:_ Enable pin editing in the widget to let users place pins interactively and edit their info boxes.
- **4. Pin Editor**
The pin editor, accessed via a pencil icon on the left side of the widget (visible only if editing permissions are granted), allows users to add, modify, or delete pins. The toolbox offers four tools:
- _Default_: zoom and drag the image.
- _Add_: click on the image to place a new pin (limited by the number of devices).
- _Edit_: click an existing pin to change its data sources, label, embed content, icon, etc.
- _Delete_: remove a selected pin.
Changes are saved with the widget's save button; otherwise they are discarded.
- **5. Infobox**
Each pin can be linked to an infobox that displays additional information such as images, external links, formulas, or embedded widgets. The infobox content is automatically associated with the corresponding pin and can be customized directly within the editor.
---
## Image Widget
The Image widget provides the ability to present custom images in your dashboard. You can use it to display a company or customer logo, or any image that helps users better visualize your application.

## 1. Image Behavior
Image behavior defines where the image URL comes from. There are three types of image behavior: static, conditional, and dynamic.
You can use TagoIO's file system to store the image and cache the public URL (TagoIO Admin: File System).
### 1.1 Static
It displays the image from a static URL.
### 1.2 Conditional
Creates conditions, where each condition will have an image URL associated with it, and if the variable linked in the **Data Sources** field satisfies this condition, this image will be displayed.

### 1.3 Dynamic
For dynamic image behavior, the widget will use the value of the selected variable as the image URL. To display your image dynamically, simply store the file's URL in the value of the variable. When the variable updates, the widget will automatically display the image from that URL.
## 2. 'Data Sources' Field
The conditional and dynamic behavior has a **Data Sources** field to set the device and variable that will be used.
> This field is dependent on the type of dashboard you are using; the difference is explained below.
### 2.1 'Data Sources' for Normal Dashboards
From the option **Data Sources** on the right menu, select one device from your list of devices and the variable that contains the data.
### 2.2 'Data Sources' for Blueprint Dashboards
From the option **Data Sources** on the right menu, add the [Blueprint device](/docs/tagoio/devices/blueprint-devices-entities.md) and input the name of the variable that contains the information.
:::info
When using a [Blueprint dashboard](/docs/tagoio/dashboards/blueprint-dashboard.md), the field **Variable** will suggest a list of variables to be picked using the first blueprint device it finds using the tags set for your Blueprint Device.
:::
---
## Video Widget
The Video widget provides the ability to present custom videos in your dashboard. Use it to show a company or customer video, product demos, or any visual content that helps users better understand your application.

## 1. Video Behavior
Video behavior defines how the widget obtains the video URL. It can be one of three types: static, conditional, or dynamic.
### Static
It displays the video from a static URL.
You can store the video in TagoIO's file system and use the public URL that is cached for faster loading.
### Conditional
Create conditions where each condition has an associated video URL. If the variable linked in the **Data Sources** field satisfies this condition, the corresponding video will be displayed. You can show different videos based on device data or other criteria.
### Dynamic
The video URL comes from a variable's value. The widget reads the current value of the selected variable and uses it as the source for the video.
## 2. 'Data Sources' Field
The conditional and dynamic behavior has a Data Sources field to set the device and variable that will be used.
- **Normal Dashboards**: Select one device from your list of devices and choose the variable that contains the data.
- **Blueprint Dashboards**: Add the Blueprint device and input the name of the variable that holds the information. When using a Blueprint dashboard, the field will suggest a list of variables to be picked using the currently selected blueprint device.
---
## Device List Widget
The Device List widget is a table that dynamically populates with information about your devices, such as names, tags, configuration parameters, and IDs. It cannot access devices' telemetry or sensor data.
## 1. Editing the device list
The Device List works as a dynamic table. You can customize its colors and columns, and apply filters to control which devices appear in the list.

### 2. Device filtering
You must fill this option to show your devices. You can either filter the devices that will show up in your list by the devices' tag key and tag value, or by tag match for one of the blueprint devices on your dashboard.
You can also have multiple filters working at the same time. It works as an AND operator, and the device must match all the filters.
To set up tags on your devices, go to each Device's page and add the desired key/value pairs.
**a) Filter by Tag Key and Tag Value**
All devices for the list must have the same tag key and tag value specified in this filter.
**b) Match with Blueprint Devices tag**
Select a blueprint device from the dashboard and one tag key. The devices for the list must match the same value of that tag key in the selected blueprint device.
### 3. Setting parameters on Columns
Every column created will require you to select a parameter from the device to show up for the column.
- **Device Information**: Shows a piece of specific information from the Device, such as the device name, ID, Connector, or Network.
- **Tag Key**: Shows a value of a specified tag key. It will be an empty row if the tag key doesn't exist in the device settings.
- **Configuration parameter**: Shows a value of a specified configuration parameter. It will be an empty row if the configuration parameter key doesn't exist in the device settings.
> Configuration Parameter values in ISO‑8601 date format will be displayed as Date.
### 4. Editing device parameters
You can allow users to modify a tag or configuration parameter value inside of the list by enabling the edit option located inside the column's configuration.
> Device Information such as ID, Connector, and Network can't be edited.
To enable editing of device information, you must first activate the control column. After that, select the columns you wish to make editable. You can also choose the field type for each column.
The device list offers these field types: Text, Password, Number, Dropdown, Dropdown Multiple, Device, User and Address. These field types behave the same as the Input Form's fields.
### 5. Blueprint Device List for TagoRUN
When sharing a blueprint dashboard to Run Users, you must create an Access Rule so that the devices appear in the list. Without the Access Rule, users don't have access to the devices the Device List expects to show, even with your filtering from section 2.
Make sure you have an Access Rule granting Blueprint access of the devices to the RUN Users. The rules must match in some way the devices you are applying on the Device List filter.
---
## Dynamic Table Widget
Dynamic tables are tables that populate dynamically as your data arrives while keeping the history of previous values in each row.
## Editing the dynamic table
You can customize the widget color, apply formulas to columns, show a time column, enable a control column, and more.
:::info
This widget also accepts features like [metadata](/docs/tagoio/devices/payload-parser/metadata.md) and [groups](/docs/tagoio/devices/grouping-variables.md), which can be set in your variable data.
:::

To edit a specific section of this widget, hover over the desired element and click it. A new page will appear on the right side with all the editable options for the selected element.
### 1. 'Data Sources' Field
Each column has the field **Data Sources**; this field allows you to set the device and variable that will be used in this widget.
> This field is dependent on the type of dashboard you are using; the difference is explained below.
#### 1.1 'Data Sources' for Normal Dashboards
From the option **Data Sources** on the right menu, select one device from your list of devices and the variable that contains the data.
#### 1.2 'Data Sources' for Blueprint Dashboards
From the option **Data Sources** on the right menu, add the [Blueprint device](/docs/tagoio/devices/blueprint-devices-entities.md) and input the name of the variable that contains the information.
> When using a [Blueprint dashboard](/docs/tagoio/dashboards/blueprint-dashboard.md), the field _Variable_ will suggest a list of variables to be picked using the currently selected blueprint device.
### 2. Grouping data
The dynamic table lets you group related data from multiple columns in the same row by either **groups** or time.
The variable's data that has the same time will be grouped in the same row.
### 3. Data visualization
The dynamic table offers the possibility of customizing each column's data visualization. It is possible to apply **Formula**, make cell colors change based on conditions, use icons to represent your data, and more.
> The color cell can be sent by the variable metadata.
### 4. Filtering Data
Filtering data can be done by each column or by the whole table. It's customizable on the Widget's edit screen.
### 5. Editing data
You can allow users to modify a variable's value inside of a table by enabling the edit option located inside the column's configuration.
> It is possible to set some columns as required when editing the row value.
Dynamic Table offers a bunch of field types to indicate what kind of value can be submitted in the input once the user modifies it. These types are **Text, Password, Number, Dropdown, Dropdown Multiple, Address, Entities** and **Device** (configured by tags).
These field types have the same behavior as the [Form's fields](/docs/tagoio/widgets/input-widgets/input-form/field-types-for-input-form.md).
---
## Entity Table Widget
The Entity Table widget enables you to display data in a tabular format using fields from your [Entities](/docs/tagoio/entities.md). Unlike the [Static Table widget](/docs/tagoio/widgets/tables/static-table-widget.md) and the [Dynamic Table widget](/docs/tagoio/widgets/tables/dynamic-table-widget.md), it does not use data from Devices. The widget lets you incorporate data from one or more entities, apply formulas, set color conditions, and allow end-users to directly modify table values from their dashboard. You can also sort data based on the indexes you've created in your entity.
:::tip
Ensure that appropriate [Access Management](/docs/tagoio/tagorun/access-management/) permissions are granted to allow end-users to view the data displayed in the widget.
:::
## Editing the Table
To modify a specific part of the widget, hover over the element you want to edit and click it. Selecting the table header opens general settings where you can:
- Choose the data source entity
- Name the table
- Design the table layout
- Configure header buttons
- And more
Clicking on a column opens column‑specific settings where you can:
- Adjust column size
- Allow end-users to edit values
- Apply formulas
- Configure color conditions and other column behaviors
## Adding Data
To add data from your Entities into the table, first define which entities you want to use as a data source. The process differs depending on whether you are using a **Normal** or **Blueprint** dashboard.
### Normal Dashboards
- In edit mode, click the Table name to open the Entity Table settings.
- In the **Entity** section, select the single entity that will serve as the data source for all columns.
- Specify the **Index** that should be used as the default ordering field (enter the index name you created in your entity).
- For each column, navigate to the **Entity Data** section, enter the field name in the **Field** box, and click _Save_.
### Blueprint Dashboards
- In edit mode, click the Table name to open the Entity Table settings.
- In the **Entity** section, select the **Entity Association** that will act as the data source for all columns, so you can switch entities dynamically based on the blueprint context.
- Specify the **Index** that should be used as the default ordering field (enter the index name).
- For each column, navigate to the **Entity Data** section, enter the field name in the **Field** box, and click _Save_.
## Data Visualization
The Entity Table offers extensive customization options for each column's data visualization:
- Apply formulas to transform raw values.
- Change cell colors based on conditions (e.g., using a `metadata` variable).
- Use icons or other visual cues to represent data.
- Alter text properties such as bold, italic, and underline.
These settings allow you to tailor the appearance of each column to match your dashboard's design and usability goals.
## Filtering Data
Data filtering can be performed on individual columns or across the entire table. While in widget edit mode:
1. Click the table name.
2. Go to **Options** → **Search Options**.
3. Activate the feature for the desired columns or for the whole table.
This enables end‑users to quickly locate specific rows or values directly from their dashboard.
## Allowing End-Users to Edit Data
To let users modify a variable's value in your Entity through the widget:
1. Enable the **Control Column** in the **Options** section of the widget.
2. In each column's configuration, enable the _Edit_ option.
3. Choose an appropriate field type that matches the entity's field type (Text, Password, Number, Dropdown, Dropdown Multiple, Address, Device). Mismatched types may prevent edits from taking effect.
4. Optionally mark columns as **Required** when editing row values.
These settings give you fine‑grained control over which data can be edited and how it is presented to end‑users.
---
## Tables
Table widgets present your data in a tabular way. You can also allow users to change values in cells, delete rows, and perform other table-related actions.

The following table widgets are available:
- Static Table
- Dynamic Table
- Device List
- User List
- Entity Table
## Static Table
A Static Table is a special kind of widget that does not use the traditional variable selector. Instead, you specify a fixed number of rows and columns. This option is useful when you do not want to display historical values for variables and only need a fixed layout of data. Learn more about [Static Table](/docs/tagoio/widgets/tables/static-table-widget.md).
## Dynamic Table
Dynamic tables are tables that are populated dynamically as your data arrive while keeping the history of the previous data in each row. The configuration is very easy, all you have to do is pick your variables. Learn more about [Dynamic Table](/docs/tagoio/widgets/tables/dynamic-table-widget.md).
## Device List
Device List widget is a table that dynamically populates with information about your devices, such as names, tags, configuration parameters, and IDs. Learn more about [Device List](/docs/tagoio/widgets/tables/device-list-widget.md).
## User List
The User List widget is a table that dynamically populates with information about your users, such as names, tags, email, and IDs. Learn more about [User List](/docs/tagoio/widgets/tables/user-list-widget.md).
## Entity Table
The Entity Table widget enables you to display data in a tabular format using fields from your Entities. Unlike the Static and Dynamic Table widgets, it does not use data from Devices. Learn more about [Entity Table](/docs/tagoio/widgets/tables/entity-table-widget.md).
---
## Static Table Widget
Static tables are tables where each cell represents a different piece of information. A cell can contain fixed text or data from a variable.
## Editing the static table
It is possible to customize the widget color, apply a formula to a cell, edit options, and more.
> This widget also accepts features like [metadata](/docs/tagoio/devices/payload-parser/metadata.md), which can be set in your variable data. (See the metadata documentation in the TagoIO help center for details.)

When you want to change a specific cell, simply hover over it and click. A configuration panel will appear on the right side of the editor with all editable options for that element.
You can delete an entire row or column by clicking the top‑right corner of the table and selecting the appropriate option.
The widget works in both Normal dashboards and Blueprint dashboards. For Blueprint dashboards, the "Data Sources" field behaves slightly differently (see below).
### 1. 'Data Sources' Field
Each cell can display the last value of a variable by using the **Data Sources** field; it allows you to set the device and variable that will be used in this widget.
- **Normal dashboards:**
In the right‑hand menu, select _Data Sources_, choose one device from your list, and then pick the variable that contains the data.
- **Blueprint dashboards:**
Select _Data Sources_, add a Blueprint device, and input the name of the variable. The Variable dropdown will suggest a list of variables to be picked using the currently selected blueprint device.
### 2. Data visualization
The static table lets you customize each cell's data presentation:
- Apply **Formulas** to transform the value before it is shown.
- Change cell colors based on conditions or let the color be sent via the variable's metadata.
- Use icons, bold/italic/underline text styles, and other visual cues.
> These options are available in the cell configuration panel that appears when you click a cell.
### 3. Editing Data
You can allow users to modify a variable's value directly inside the table by enabling the **Edit** option in the cell's configuration.
- Some cells can be marked as _required_ when editing a row.
- The widget supports several field types for input: **Text, Password, Number, Dropdown, Dropdown Multiple, and Address**, behaving the same way as Input Form fields.
- After the user submits a change, you may apply **Dynamic Formulas** to the new value before it is stored in your device's data storage.
---
## User List Widget
The User List widget is a table that dynamically populates with information about your users, such as names, tags, email addresses, and IDs.
## 1. Editing the user list
The User List works as a dynamic table: you can customize its colors and columns, and filter which users appear in the list.

To edit a specific section of the widget, hover over the desired element and click it. A panel will appear on the right side with all editable options for the selected element.
You can also adjust the table's appearance by changing colors, font styles, and column widths directly from this panel. The widget automatically updates to reflect any changes you make.
## 2. User List filtering
You must fill this option to show your users. You can filter which users appear in the list by:
- **Tag Key and Tag Value**: All users for the list must have the same tag key and tag value specified by you in this filter.
- **Blueprint Device Tags**: Select a blueprint device from the dashboard and one tag key. The users for the list must match the same value of that tag key in the selected blueprint device.
You can also apply multiple filters simultaneously; note that this works as an AND operator, so the user must match _all_ specified filters.
To set up tags on your devices, navigate to the **User** page and add or edit tags there.
## 3. Setting parameters on Columns
Every column created will require you to select a parameter from the user to display in that column.
- **User Information**: Shows a specific piece of information from the User, such as the user name, ID, email, time zone, company, and more.
- **Tag Key**: Displays the value of a specified tag key; it will be an empty row if the tag key doesn't exist in the user settings.
> Tag values in ISO 8601 date format will be displayed as Date.
## 4. Editing user parameters
You can allow users to modify a tag or configuration parameter directly from the list by enabling the edit option inside the column's configuration.
> User Information such as ID, email, and password cannot be edited.
The user list offers multiple field types for input when a user modifies it. These include:
- Text
- Password
- Number
- Dropdown
- Dropdown Multiple
- Device List
- Address
These field types behave the same way as those in the [Input Form's fields](/docs/tagoio/widgets/input-widgets/input-form/field-types-for-input-form.md).
## 5. Blueprint User List for TagoRUN
When sharing a blueprint dashboard to **Run Users**, you must create an **Access Rule** so that the user can appear in the list. Without this rule, users won't have access to view other users that the User List expects to show, even if filtering is applied.
Make sure your Access Rule grants Blueprint access to the relevant users and matches the criteria used in the User List filter.
---
## TagoTiP - Transport IoT Protocol
Send IoT data to TagoIO in **130 bytes** instead of 487. No JSON, no HTTP headers - just a single human-readable line your microcontroller can build with `sprintf`.
```
PUSH|4deedd7bab8817ec|sensor-01|[temperature:=32.5#C;humidity:=65#%]
```
## Why TagoTiP?
| | HTTP/JSON | TagoTiP | TagoTiP(s) (encrypted) |
| -------------------- | ----------- | ---------------------------- | ----------------------------- |
| **Payload size** | ~487 bytes | ~130 bytes | ~119 bytes |
| **vs. HTTP/JSON** | - | 3.7x smaller | 4.1x smaller |
| **TLS required?** | Yes | Recommended | No - AEAD encryption built-in |
| **Parse complexity** | JSON parser | Linear scan, no backtracking | Envelope + linear scan |
### Built for constrained devices
- **Human-readable** - debug frames in a terminal, compose them by hand
- **Type-safe** - explicit operators for numbers (`:=`), strings (`=`), booleans (`?=`), and locations (`@=`)
- **C-friendly** - predictable buffer sizes, no dynamic allocation, linear parsing
- **Compact** - variable, value, unit, timestamp, group, location, and metadata in a single frame
- **Transport-agnostic** - works over UDP, TCP, HTTP(S), MQTT, or any byte-capable channel
## Encryption without TLS
Need security on raw UDP or constrained links where TLS is too expensive? **TagoTiP(s)** wraps frames in an AEAD authenticated encryption envelope - as little as **29 bytes** of overhead, with built-in replay protection and integrity verification.
| Cipher Suite | Key | Tag | Envelope Overhead |
| ----------------- | ------- | ---- | ----------------- |
| **AES-128-CCM** | 128-bit | 8 B | 29 bytes |
| AES-128-GCM | 128-bit | 16 B | 37 bytes |
| AES-256-CCM | 256-bit | 8 B | 29 bytes |
| AES-256-GCM | 256-bit | 16 B | 37 bytes |
| ChaCha20-Poly1305 | 256-bit | 16 B | 37 bytes |
Learn more in the [Encryption](/docs/tagotip/specification/encryption.md) guide.
## How it compares
### TagoTiP vs. other IoT data formats
| | TagoTiP | HTTP + JSON | MQTT + JSON | Protobuf |
| ------------------------------------ | ----------------------------------------- | ------------------- | ------------------- | -------------------- |
| **Typical payload** | ~130 bytes | ~487 bytes | ~210 bytes | ~80 bytes |
| **Human-readable** | Yes | Partially | Partially | No |
| **Schema required** | No | No | No | Yes |
| **Debug in a terminal** | Yes | Verbose | Binary framing | No |
| **Build with `sprintf`** | Yes | Complex | Needs MQTT library | Needs code generator |
| **IoT type system** | Built-in (number, string, bool, location) | Application-defined | Application-defined | Schema-defined |
| **Metadata, unit, group, timestamp** | Native syntax | Application-defined | Application-defined | Schema-defined |
### TagoTiP(s) vs. other IoT security
| | TagoTiP(s) | TLS 1.3 | DTLS 1.2 |
| --------------------------------- | -------------- | --------------- | --------------- |
| **Handshake** | None - 0 bytes | ~2-4 KB | ~2-5 KB |
| **Round trips before first data** | 0 | 1-2 | 2-3 |
| **Per-message overhead** | 29-37 bytes | ~29 bytes + TCP | ~29 bytes |
| **Session state** | Stateless | Per-connection | Per-connection |
| **Certificate management** | None | Required | Required or PSK |
| **Works over raw UDP** | Yes | No | UDP only |
| **Works without TCP** | Yes | No | UDP only |
## Quick example
Push a temperature reading with unit, timestamp, and metadata - all in one frame:
```
PUSH|4deedd7bab8817ec|sensor-01|@=39.74,-104.99@1694567890000^batch_42{firmware=2.1}[temperature:=32.5#C]
```
Pull the last value back:
```
PULL|4deedd7bab8817ec|sensor-01|[temperature]
<- ACK|OK|[temperature:=32.5#C@1694567890000]
```
## Tutorials
Follow our tutorials to start building your IoT project using UDP, TCP, HTTP, or MQTT:
:::tip[Get started with TagoTiP]
- [TagoTiP UDP: Quick Start](https://youtu.be/YhhHuCH7UZo)
- [TagoTiP TCP: Quick Start](https://youtu.be/cSMbQkPyrZo)
- [TagoTiP HTTP: Quick Start](https://youtu.be/oZfrq5xFS5k)
- [TagoTiP MQTT: Quick Start](https://youtu.be/EHnG_P28MPE)
:::
## SDKs
All language SDKs share a single Rust core (`tagotip-codec`, `no_std`), so parsing and frame building behave identically everywhere.
| Package | Language | Install |
| ---------------------------------------------------------------------------------- | -------------------- | -------------------------------------------------- |
| [`@tagoio/tagotip`](https://github.com/tago-io/tagotip-sdk/tree/main/tagotip-node) | TypeScript / Node.js | `npm install @tagoio/tagotip` |
| [`tagotip`](https://github.com/tago-io/tagotip-sdk/tree/main/tagotip-go) | Go | `go get github.com/tago-io/tagotip-sdk/tagotip-go` |
| [`tagotip`](https://github.com/tago-io/tagotip-sdk/tree/main/tagotip-python) | Python | `pip install tagotip` |
| [`TagoTiP`](https://github.com/tago-io/tagotip-sdk/tree/main/tagotip-arduino) | C / Arduino | Arduino Library Manager |
| [`tagotip-codec`](https://github.com/tago-io/tagotip-sdk/tree/main/tagotip-codec) | Rust | `cargo add tagotip-codec` |
Browse the full SDK source at [github.com/tago-io/tagotip-sdk](https://github.com/tago-io/tagotip-sdk).
## Open source
TagoTiP is open source under the [Apache License 2.0](https://github.com/tago-io/tagotip). Implement clients, servers, libraries, or gateways - for any purpose, including commercial use.
---
## Choosing a Transport
TagoTiP is transport-agnostic - the same protocol frames work over any transport. Pick the one that fits your hardware and use case.
UDP
Battery-powered sensors that wake up, send a reading, and sleep. Zero connection overhead - one datagram in, one datagram out. Every milliamp counts.
No connection
Server push via PING poll
TagoTiP(s) on port 5684
TCP
Always-on gateways that stream data and need commands instantly - reboots, config updates, OTA triggers. Guaranteed, ordered delivery over a persistent connection.
Persistent connection
Instant CMD push
TLS on port 5694
HTTP
Cloud integrations, serverless functions, or any device behind firewalls and proxies. Standard POST/GET/HEAD with a single Authorization header. No special libraries needed.
Per-request
Server push via HEAD poll
HTTPS on port 443
MQTT
Large device fleets on unreliable networks. Publish/subscribe with QoS levels 0, 1, and 2. Topic-based routing with native keepalive and reconnection handling.
Persistent connection
Instant via ack topic
TLS on port 8883
---
## Endpoints
TagoTiP servers are region-specific. Each transport protocol has its own dedicated hostname and static IP address. Connect to the region closest to your devices for the lowest latency.
## US-East-1
| Transport | Hostname | Port | Protocol | Security |
| --------- | ------------------------------------------------------------------------ | ---- | ---------- | ---------------- |
| UDP | `udp.tip.us-e1.tago.io`166.117.107.129 | 5683 | TagoTiP | None |
| UDP | `udp.tip.us-e1.tago.io`166.117.107.129 | 5684 | TagoTiP(s) | Encrypted (AEAD) |
| TCP | `tcp.tip.us-e1.tago.io`75.2.126.170 | 5693 | TagoTiP(s) | None |
| TCP | `tcp.tip.us-e1.tago.io`75.2.126.170 | 5694 | TagoTiP(s) | TLS |
| HTTP | `http.tip.us-e1.tago.io`52.223.14.189 | 80 | TagoTiP(s) | None (HTTP) |
| HTTP | `http.tip.us-e1.tago.io`52.223.14.189 | 443 | TagoTiP(s) | TLS (HTTPS) |
| MQTT | `mqtt.tip.us-e1.tago.io`15.197.247.146 | 1883 | TagoTiP | None (MQTT) |
| MQTT | `mqtt.tip.us-e1.tago.io`15.197.247.146 | 8883 | TagoTiP | TLS (MQTTS) |
## EU-West-1
| Transport | Hostname | Port | Protocol | Security |
| --------- | ------------------------------------------------------------------------ | ---- | ---------- | ---------------- |
| UDP | `udp.tip.eu-w1.tago.io`166.117.51.137 | 5683 | TagoTiP | None |
| UDP | `udp.tip.eu-w1.tago.io`166.117.51.137 | 5684 | TagoTiP(s) | Encrypted (AEAD) |
| TCP | `tcp.tip.eu-w1.tago.io`15.197.224.153 | 5693 | TagoTiP(s) | None |
| TCP | `tcp.tip.eu-w1.tago.io`15.197.224.153 | 5694 | TagoTiP(s) | TLS |
| HTTP | `http.tip.eu-w1.tago.io`166.117.2.140 | 80 | TagoTiP(s) | None (HTTP) |
| HTTP | `http.tip.eu-w1.tago.io`166.117.2.140 | 443 | TagoTiP(s) | TLS (HTTPS) |
| MQTT | `mqtt.tip.eu-w1.tago.io`166.117.88.178 | 1883 | TagoTiP | None (MQTT) |
| MQTT | `mqtt.tip.eu-w1.tago.io`166.117.88.178 | 8883 | TagoTiP | TLS (MQTTS) |
---
## Rate Limits
Limits are enforced at two levels: per [profile](/docs/tagoio/profiles/) and per device. Defaults vary by plan. Exceeding a limit returns `ACK|ERR|rate_limited` (or HTTP `429`).
RPM = requests per minute.
## Per profile
| Resource | Transports | Scale | Starter | Free |
| ------------------- | -------------------- | ----- | ------- | ---- |
| Uplink RPM (PUSH) | UDP, TCP, HTTP, MQTT | 1,000 | 500 | 60 |
| Downlink RPM (PULL) | UDP, TCP, HTTP, MQTT | 1,000 | 500 | 60 |
| Command RPM | MQTT | 1,000 | 500 | 60 |
| Connections per IP | TCP, HTTP, MQTT | 20 | 10 | 3 |
## Per device
| Resource | Transports | Scale | Starter | Free |
| ----------------------- | -------------------- | ------ | ------- | ------ |
| Max payload size | UDP, TCP, HTTP, MQTT | 100 KB | 100 KB | 100 KB |
| Connection TTL | TCP, MQTT | 15 s | 10 s | 10 s |
| Keep-alive idle timeout | TCP, MQTT | 5 s | 5 s | 5 s |
:::info[Connection TTL vs Keep-alive idle timeout]
These two TCP limits serve different purposes:
- **Connection TTL** is a hard cap on total connection duration. No matter how active the device is, the server closes the connection after this period and sends `ACK|ERR|ttl_expired`. The device should reconnect.
- **Keep-alive idle timeout** is the maximum silence between frames. If the device stops sending data for this long, the server closes the connection with `ACK|ERR|keep_alive_timeout`. Sending any frame (including `PING`) resets the timer.
Both timers start when the connection is accepted. TTL counts total elapsed time; keep-alive resets on every received frame.
:::
## Error and close responses
| Condition | Raw ACK | HTTP Status |
| --------------------------- | ------------------------------------------- | ---------------- |
| RPM exceeded | `ACK\|ERR\|rate_limited` | 429 |
| Payload too large | `ACK\|ERR\|payload_too_large` | 413 |
| Per-IP connections exceeded | `ACK\|ERR\|rate_limited` (then close) | 429 (then close) |
| Connection TTL exceeded | `ACK\|ERR\|ttl_expired` (then close) | N/A |
| Keep-alive idle timeout | `ACK\|ERR\|keep_alive_timeout` (then close) | N/A |
PING is exempt from rate limiting on TCP and UDP. On HTTP, `HEAD` counts toward the uplink RPM. On MQTT, keepalive is handled natively by PINGREQ/PINGRESP.
See each transport page for transport-specific limits.
---
## Quick Start
Set up your device and authorization before sending data over any transport.
## Step 1: Create a Device
1. Go to **Devices** in your TagoIO account and create a new device.
2. Select the network **TagoTiP** and the connector for your transport (UDP, TCP, HTTP, or MQTT).
3. Assign a **Serial Number** (e.g., `SN0042`) - this is how TagoTiP identifies your device.
## Step 2: Create an Authorization
1. Go to **Devices** > **Authorization** at the top of the page.
2. Click **Generate**.
3. Set a name and select the token format **TagoTiP(s)**.
4. Click **Generate**.
You receive two credentials:
| Credential | Example | Used for |
| ----------------- | --------------------------------- | ------------------------------------------------------------------- |
| **Token Hash** | `4deedd7bab8817ec` (16 hex chars) | TagoTiP frames (plaintext) |
| **Authorization** | `ate2bd...c0d0` (34 chars) | [TagoTiP(s)](/docs/tagotip/specification/encryption.md) encryption key derivation |
:::tip
The token hash is safe on the wire - it cannot be reversed to reveal the authorization. Keep the authorization (`at...`) secret; it is only needed for TagoTiP(s) key derivation.
:::
See the [Authorization guide](/docs/tagoio/integrations/general/authorization) for more details.
## Next steps
Now [choose a transport](/docs/tagotip/servers/choosing-a-transport.md) and send your first data point.
---
## TagoTiP(s) - Encrypted Communication
**AEAD authenticated encryption** for your IoT data - without TLS. TagoTiP(s) wraps frames in a compact binary envelope with built-in integrity verification and replay protection. As little as **29 bytes** of overhead.
## When to use TagoTiP(s)
- **No TLS available** - UDP has no TLS support; TagoTiP(s) is your only encryption option
- **Constrained links** - where TLS handshakes are too expensive
- **Defense in depth** - combine TagoTiP(s) with TLS on TCP port `5694` or HTTPS port `443` for layered security
## TagoTiP(s) supported ports
**US-East-1:**
| Transport | Host | Port | TLS | Notes |
| --------- | ------------------------ | ---- | --- | --------------- |
| **UDP** | `udp.tip.us-e1.tago.io` | 5684 | No | Dedicated port |
| **TCP** | `tcp.tip.us-e1.tago.io` | 5693 | No | Auto-detected |
| **TCP** | `tcp.tip.us-e1.tago.io` | 5694 | Yes | Auto-detected |
| **HTTP** | `http.tip.us-e1.tago.io` | 80 | No | `POST /v1/tips` |
| **HTTP** | `http.tip.us-e1.tago.io` | 443 | Yes | `POST /v1/tips` |
See [Endpoints](/docs/tagotip/servers/endpoints.md) for all regions.
## How it differs from TagoTiP
| | TagoTiP | TagoTiP(s) |
| --------------------- | ------------------------- | ------------------------------------- |
| **Format** | Human-readable text | Binary envelope |
| **Auth credential** | Token hash (16 hex chars) | Authorization token (`at...`) |
| **Encryption** | None (rely on TLS) | AEAD at the application layer |
| **Replay protection** | Optional sequence counter | Built-in (counter is nonce component) |
### Credentials
TagoTiP and TagoTiP(s) use **different credentials** from the same authorization:
| Protocol | Credential | Why |
| -------------- | ------------------------------------- | --------------------------------------------------- |
| **TagoTiP** | Token hash (`4deedd7bab8817ec`) | Identifies your account. Safe on the wire. |
| **TagoTiP(s)** | Authorization token (`ate2bd...c0d0`) | Derives the encryption key. Never sent on the wire. |
Both come from a single authorization with token format **TagoTiP(s)**. See the [Authorization guide](/docs/tagoio/integrations/general/authorization).
## How it works
TagoTiP(s) strips the method and auth from a TagoTiP frame, encrypts the rest, and wraps it in a fixed-structure binary envelope:
```
[Flags 1B] [Counter 4B] [Auth Hash 8B] [Device Hash 8B] [Ciphertext + Tag]
cipher nonce profile device encrypted payload
method component identifier identifier
version
```
**Total overhead:** 29 bytes (AES-128-CCM) to 37 bytes (GCM / ChaCha20-Poly1305).
## Cipher suites
| ID | Cipher | Key | Tag | Overhead |
| --- | ----------------- | ------- | ---- | -------- |
| 0 | **AES-128-CCM** | 128-bit | 8 B | 29 bytes |
| 1 | AES-128-GCM | 128-bit | 16 B | 37 bytes |
| 2 | AES-256-CCM | 256-bit | 8 B | 29 bytes |
| 3 | AES-256-GCM | 256-bit | 16 B | 37 bytes |
| 4 | ChaCha20-Poly1305 | 256-bit | 16 B | 37 bytes |
AES-128-CCM (ID 0) is required by all implementations.
## Key derivation
The encryption key is derived from your authorization token and device serial:
```
HMAC-SHA256(key = token_hex_without_at_prefix, msg = device_serial)
```
Truncated to the cipher's key size (16 bytes for AES-128, 32 bytes for AES-256 / ChaCha20).
## Size comparison
| Format | Size | vs. HTTP/JSON |
| -------------- | -------------- | ---------------- |
| HTTP + JSON | ~487 bytes | - |
| TagoTiP | ~112 bytes | 4.3x smaller |
| **TagoTiP(s)** | **~119 bytes** | **4.1x smaller** |
## Specification
For envelope parsing, nonce construction, ABNF grammar, and test vectors, see the [TagoTiP(s) Specification](/docs/tagotip/specification/tagotips-specification.md).
---
## TagoTiP Specification
# TagoTiP: Transport IoT Protocol
**Version:** 1.0
**Date:** April 2026
**Status:** Specification, Revision D
> For the encrypted envelope (TagoTiP/S), see [TagoTiPs.md](/docs/tagotip/specification/tagotips-specification).
---
## 1. Introduction
TagoTiP is a lightweight, human-readable protocol designed for sending and receiving IoT data to TagoIO. It provides a compact alternative to HTTP/JSON for resource-constrained embedded devices.
TagoTiP is **transport-agnostic**. It can be carried over UDP, TCP, HTTP(S), MQTT, or any other transport. This specification defines only the message format and parsing rules, not transport-specific behavior such as ports, connection management, or delivery guarantees.
> **Note:** For encryption without TLS, TagoTiP frames can be wrapped in a **TagoTiP/S** crypto envelope. See [TagoTiPs.md](/docs/tagotip/specification/tagotips-specification).
### 1.1 Protocol at a Glance
```mermaid
block-beta
columns 5
METHOD["METHOD\n4-6B"]:1
N["!N\n(opt.)"]:1
AUTH["AUTH\n16 hex"]:1
SERIAL["SERIAL\ndevice serial"]:1
BODY["BODY\ncontent"]:1
```
### 1.2 Design Goals
- **Human-readable**: frames can be read and composed manually in a terminal
- **Type-safe**: value types (number, string, boolean, location) are explicit in the syntax
- **C-friendly**: minimal string concatenation, predictable buffer sizes, linear parsing
- **Compact**: minimal overhead per frame compared to HTTP/JSON
- **Transport-agnostic**: works over UDP, TCP, HTTP(S), MQTT, or any byte-capable channel
- **Complete**: supports all TagoIO data model fields: variable, value, unit, time, group, location, and metadata
### 1.3 Conventions
- Keywords `MUST`, `SHOULD`, `MAY`, `MUST NOT` follow RFC 2119 definitions
- Method names (`PUSH`, `PULL`, `PING`), status codes (`OK`, `PONG`, `CMD`, `ERR`), and boolean values (`true`, `false`) are **case-sensitive** and MUST use the exact casing shown in this specification
- Timestamps are UNIX epoch in **milliseconds**
- Text frames are UTF-8 encoded. Implementations MAY restrict to printable ASCII; any non-ASCII bytes MUST be valid UTF-8.
- The NUL byte (`U+0000` / `0x00`) MUST NOT appear anywhere in a TagoTiP frame. This rule is maintained for protocol hygiene and C-string safety.
- All variable names, group names, and metadata keys are **lowercase** and MUST NOT contain `*`, `?`, `!`, `<`, `>`, `.`, `-`, `=`, `$`, or spaces (per TagoIO restrictions). Serial numbers MAY contain hyphens (`-`) in addition to alphanumeric characters and underscores.
- This specification defines version 1 of the protocol. Methods carry no version suffix. Future versions MAY use `METHOD/N` syntax (e.g., `PUSH/2`) to indicate a newer version while maintaining backward compatibility.
### 1.4 Terminology
| Term | Meaning |
|---|---|
| **Frame** | A TagoTiP text message (e.g., `PUSH\|AUTH\|SERIAL\|BODY`) |
| **Message** | The abstract unit of communication: either a frame or an envelope |
| **Uplink** | Client → Server direction |
| **Downlink** | Server → Client direction |
---
## 2. Credentials
Credentials are scoped to an **Account/Profile** (not to an individual device). A single profile may contain multiple devices, and the same credentials are used to authenticate traffic for any device that belongs to that profile.
| Credential | Format | Secrecy | Purpose |
|---|---|---|---|
| **Authorization Token** | `at` + 32 hex chars (34 chars total, e.g., `ate2bd319014b24e0a8aca9f00aea4c0d0`) | Secret | Identifies the Account/Profile. Used only to derive the Authorization Hash during device provisioning. Never transmitted on the wire. |
| **Authorization Hash** | 16 hex chars (8 bytes, e.g., `4deedd7bab8817ec`) | Public | Derived from the Authorization Token. Sent in TagoTiP frames to identify the Account/Profile. Safe to display in logs/UIs. |
**Authorization Hash derivation:**
```
Token: ate2bd319014b24e0a8aca9f00aea4c0d0
Input: e2bd319014b24e0a8aca9f00aea4c0d0 (strip "at" prefix)
Hash: SHA-256 of input (UTF-8 bytes)
Result: first 8 bytes as 16 hex chars
```
The server uses the Authorization Hash to resolve the Account/Profile, then routes the message to the device identified by the SERIAL field in the frame header.
> **Note:** TagoTiP/S uses additional credentials (Device Hash, Encryption Key) for the crypto envelope. See [TagoTiPs.md](/docs/tagotip/specification/tagotips-specification).
---
## 3. Transport Notes (Non-Normative)
The following guidance is non-normative and intended to help implementers.
| Transport | Frame Delimiter | Notes |
|---|---|---|
| TCP | `\n` (0x0A) terminates each frame | Server buffers bytes until `\n` is received. Clients SHOULD reuse connections. |
| UDP | End of datagram | Each datagram contains exactly one frame. `\n` terminator is OPTIONAL. |
| HTTP(S) | HTTP body | One frame per request body. Method and headers are transport-level. |
| MQTT | MQTT payload | One frame per MQTT message. Topic structure is transport-level. |
The `\n` byte (0x0A) MUST NOT appear inside frame field values. On stream transports (TCP), it terminates the frame. On message transports (UDP, MQTT, HTTP), it is unnecessary but harmless if present.
> **Normative clarification:** The ABNF grammar defines frames with a trailing `LF` for the canonical wire format. On message-boundary transports (UDP, MQTT, HTTP body), the trailing `LF` is OPTIONAL: receivers on these transports MUST accept frames both with and without a trailing `LF`. On stream transports (TCP), the trailing `LF` is REQUIRED as the frame delimiter.
**CMD Delivery (Non-Normative):** On connection-oriented transports (TCP), the server MAY send CMD frames at any time. On pub/sub transports (MQTT), the server MAY publish to device-specific topics. On request-response transports (HTTP, UDP), CMD frames are delivered as responses to client requests. Clients SHOULD use periodic PING to poll for pending commands.
---
## 4. Frame Structure
Each TagoTiP frame addresses exactly **one device**. To send data for multiple devices, the client sends multiple frames (one per device).
### 4.1 Uplink Frames (Client → Server)
> **Note:** The full frame structure described here applies to plaintext TagoTiP. When transmitted inside a TagoTiP/S envelope, a compact "headless" variant is used instead. See [TagoTiPs.md §4](/docs/tagotip/specification/tagotips-specification#4-headless-inner-frame).
Every uplink TagoTiP frame follows a pipe-delimited structure:
**Without sequence counter:**
```
METHOD|AUTH|SERIAL|BODY\n
METHOD|AUTH|SERIAL\n ← PING (no body)
```
**With sequence counter:**
```
METHOD|!N|AUTH|SERIAL|BODY\n
METHOD|!N|AUTH|SERIAL\n ← PING (no body)
```
| Field | Required | Description |
|---|---|---|
| `METHOD` | Yes | The action to perform (see §5) |
| `!N` | No | Sequence counter: `!` prefix + decimal integer (e.g., `!42`) |
| `AUTH` | Yes | Authorization Hash (16 hex chars, 8 bytes of SHA-256) |
| `SERIAL` | Yes | Device serial number (target device identifier) |
| `BODY` | Depends | Method-specific payload (see §6-§8). Omitted for PING. |
- Fields are separated by the pipe character `|` (byte `0x7C`)
- The `!` prefix distinguishes the optional counter field from the AUTH field (hex characters `0-9`, `a-f` never start with `!`)
**Examples:**
```
PUSH|4deedd7bab8817ec|sensor-01|[temperature:=32]
PUSH|!42|4deedd7bab8817ec|sensor-01|[temperature:=32]
PING|4deedd7bab8817ec|sensor-01
PING|!5|4deedd7bab8817ec|sensor-01
```
### 4.2 Downlink Frames (Server → Client)
All server-to-client communication uses the `ACK` frame format. This is a simplified frame with no AUTH field:
```
ACK|!N|STATUS|DETAIL\n ← correlated response (echoes uplink counter)
ACK|!N|STATUS\n ← correlated response (no detail)
ACK|STATUS|DETAIL\n ← unsolicited or no-counter client
ACK|STATUS\n
```
| Field | Required | Description |
|---|---|---|
| `!N` | No | Optional: echoes the sequence counter from the uplink request. Present only when the uplink frame included `!N`. |
| `STATUS` | Yes | Result code |
| `DETAIL` | No | Additional information |
The server does not need to authenticate itself to the client. When the uplink frame includes a sequence counter (`!N`), the server echoes the same value in the ACK response (see §9.5). ACK frames without `!N` are either responses to requests that had no counter, or unsolicited server-initiated messages (e.g., CMD). The echoed counter lets clients correlate responses to requests on pipelined connections.
See §9 for the full ACK specification including status codes for responses and commands.
### 4.3 Authentication
The **Authorization Hash** identifies the **Account/Profile**. The **SERIAL** field identifies the target device.
The server MUST:
1. Resolve the Account/Profile by Authorization Hash.
2. Verify that the `SERIAL` belongs to that Account/Profile.
3. Reject the request if the `SERIAL` does not belong to the profile (`ACK|ERR|device_not_found`).
For passthrough payloads (`>x`, `>b`), the SERIAL field still identifies the target device. The payload parser receives the raw data associated with that device (see §6.5).
### 4.4 Escaping
Escaping is supported inside **string values** (`VALCHAR`) and **metadata values** (`METAVALCHAR`). Unit strings (`UNITCHAR`) do **not** support escape sequences: they are plain text terminated by structural characters.
**Rule:** A backslash (`\`) escapes the next byte, producing the literal character. This applies to any reserved/structural character, including:
`|`, `[`, `]`, `;`, `,`, `{`, `}`, `#`, `@`, `^`, `\`, and `n` (newline escape).
| Sequence | Meaning |
|---|---|
| `\n` | Literal newline character (U+000A) in the decoded value (note: the raw byte `0x0A` MUST NOT appear on stream transports) |
| `\\` | Literal `\` |
| `\|` | Literal `|` |
| `\[` | Literal `[` |
| `\]` | Literal `]` |
| `\;` | Literal `;` |
| `\,` | Literal `,` |
| `\{` | Literal `{` |
| `\}` | Literal `}` |
| `\#` | Literal `#` |
| `\@` | Literal `@` |
| `\^` | Literal `^` |
On the wire, `\n` is the two-byte sequence `0x5C 0x6E` (backslash + lowercase n), which parsers decode to U+000A in the application-layer value.
A real newline byte (`0x0A`) always terminates a frame on stream transports and MUST NOT appear in values.
**Note:** When splitting frame fields by `|`, parsers MUST respect `\|` (backslash followed by pipe) as an escape sequence, not a field delimiter. The same applies to all structural characters within their respective contexts (e.g., `\;` inside variable lists, `\,` inside metadata blocks, `\}` inside metadata blocks).
### 4.5 Size Limits
For predictable memory usage on embedded clients and consistent server behavior:
- **Max plaintext frame size:** The server MUST reject any frame whose UTF-8 byte length exceeds **16,384 bytes** (excluding the optional `\n` terminator on stream transports) with `ACK|ERR|payload_too_large`.
Implementations MAY support larger limits, but clients SHOULD target this limit for maximum compatibility.
#### 4.5.1 Field-Level Limits
To enable safe fixed-buffer pre-allocation in C and other memory-constrained implementations, the following limits apply to **identifiers** (names and keys):
| Field | Max bytes | Reference |
|---|---|---|
| Variable name (`var-name`) | 100 | TagoIO platform: max 100 characters |
| Serial number (`serial`) | 100 | Aligned with variable name limit |
| Group name (`group`) | 100 | TagoIO platform: max 100 characters |
| Metadata key (`meta-key`) | 100 | Follows variable name rules |
| Unit string (`unit`) | 25 | TagoIO platform: max 25 characters |
The following limits apply to **element counts**:
| Field | Max count | Rationale |
|---|---|---|
| Variables per `[]` block (`var-list`, `pull-list`) | 100 | Generous for dataloggers; fits within frame budget |
| Metadata pairs per `{}` block (`meta-list`) | 32 | Sufficient for IoT; power-of-2 for C buffer sizing |
All limits above are normative: a frame that exceeds any of these limits MUST be rejected by the server with `ACK|ERR|invalid_payload`.
Number values, boolean values, location coordinates, and timestamp values are inherently bounded by their format definitions (§6.3.1) and do not require separate length limits.
String values and metadata values are bounded by the frame size limit (§4.5) and by application-level platform limits. The protocol does not define per-value byte limits.
All byte lengths are measured as UTF-8 encoded bytes. Since identifier fields (`VARNAMECHAR`, `SERIALCHAR`) are ASCII-only, the byte count equals the character count.
Implementations MAY enforce lower limits and SHOULD document their supported maximums.
---
## 5. Methods
### 5.1 Uplink Methods (Client → Server)
| Method | Purpose | Body Required |
|---|---|---|
| `PUSH` | Send data to a device | Yes |
| `PULL` | Retrieve last value of one or more variables | Yes |
| `PING` | Keepalive / connectivity test | No |
For real-time subscriptions to variable changes, use a transport that natively supports pub/sub (e.g., MQTT).
### 5.2 Downlink (Server → Client)
All downlink communication uses the `ACK` frame. The `STATUS` field determines the purpose:
| Status | Purpose | Example |
|---|---|---|
| `OK` | Successful response to PUSH, PULL | `ACK\|OK\|3`, `ACK\|!1\|OK\|3` |
| `PONG` | Response to PING | `ACK\|PONG`, `ACK\|!2\|PONG` |
| `CMD` | Server-initiated command | `ACK\|CMD\|reboot` (unsolicited, no counter) |
| `ERR` | Error response | `ACK\|ERR\|invalid_token`, `ACK\|!5\|ERR\|invalid_payload` |
See §9 for the full ACK specification.
---
## 6. PUSH: Sending Data
### 6.1 Basic Structure
```
PUSH|AUTH|SERIAL|BODY
PUSH|!N|AUTH|SERIAL|BODY
```
Where `BODY` is either a structured variable block or a passthrough payload:
```
PUSH|AUTH|SERIAL|@=LOC@TIMESTAMP^GROUP{META}[variables] ← structured
PUSH|AUTH|SERIAL|>xHEXDATA ← passthrough (hex)
PUSH|AUTH|SERIAL|>bBASE64DATA ← passthrough (base64)
```
### 6.2 Body-Level Modifiers
Optional body-level modifiers may appear before the variable block. They set defaults that cascade to all variables in the body:
```
PUSH|AUTH|SERIAL|@=LOCATION @TIMESTAMP ^GROUP {METADATA} [variables]
```
> *Spaces are shown for readability only and are not present in actual frames.*
| Component | Required | Prefix | Description |
|---|---|---|---|
| `@=LOCATION` | No | `@=` | Location applied to all variables |
| `@TIMESTAMP` | No | `@` | Timestamp (ms) applied to all variables |
| `^GROUP` | No | `^` | Group ID applied to all variables |
| `{METADATA}` | No | `{}` | Metadata applied to all variables |
| `[variables]` | Yes | `[]` | Variable block (always present for structured PUSH) |
Body-level modifiers MUST appear in the order shown (`@=LOCATION`, `@TIMESTAMP`, `^GROUP`, `{METADATA}`) when present. Each modifier MAY be omitted, but those present MUST follow this order. If the same modifier type appears more than once, the frame MUST be rejected with `invalid_payload`.
### 6.3 Variable Syntax
Variables are separated by semicolons (`;`) inside the brackets. Each variable follows this structure:
```
NAME OPERATOR VALUE #UNIT @=LOCATION @TIMESTAMP ^GROUP {METADATA}
```
All suffixes are optional and MUST appear in the order shown when present.
The variable list inside `[]` MUST contain at least one variable. Empty blocks (`[]`) MUST be rejected with `invalid_payload`.
Metadata blocks MUST contain at least one key-value pair. Empty metadata blocks (`{}`) MUST be rejected with `invalid_payload`.
The same variable name MAY appear multiple times within a single variable block. Each occurrence is treated as a separate data point, which supports batch uploads (see §11.7).
PUSH frames are **atomic**: if any variable in the block fails validation (malformed operator, invalid value, illegal suffix combination), the server MUST reject the entire frame with `ACK|ERR|invalid_payload`. No partial acceptance.
#### 6.3.1 Operators (Type Hints)
| Operator | Type | Value Format | Example |
|---|---|---|---|
| `:=` | Number | Integer or decimal | `temperature:=32.5` |
| `=` | String | Text | `status=running` |
| `?=` | Boolean | `true` or `false` | `active?=true` |
| `@=` | Location | `lat,lng` or `lat,lng,alt` | `position@=39.74,-104.99` |
Number values MUST match the pattern `-?(0|[1-9][0-9]*)(\.[0-9]+)?`: an optional minus sign, one or more digits, and an optional decimal fraction. Scientific notation, leading zeros (except in `0` and `0.x` forms), and special values (`NaN`, `Infinity`) are not valid.
String values MUST contain at least one character. Empty values (e.g., `status=`) are not valid.
Boolean values MUST be the exact lowercase strings `true` or `false`.
Location coordinates follow the same numeric format as the number type. Validation of coordinate ranges (e.g., latitude -90 to 90, longitude -180 to 180) is application-level and not enforced by the protocol parser.
#### 6.3.2 Suffixes
| Suffix | Prefix | Description | Example |
|---|---|---|---|
| Unit | `#` | Unit of measurement | `temperature:=32#F` |
| Location | `@=` | Geographic coordinates (`lat,lng[,alt]`) | `speed:=10@=39.74,-104.99` |
| Timestamp | `@` | UNIX timestamp in milliseconds | `temperature:=32@1694567890000` |
| Group | `^` | Group ID for linking data points | `temperature:=32^reading_001` |
| Metadata | `{}` | Key-value pairs separated by `,` | `temperature:=32{source=dht22,quality=high}` |
The `@=location` suffix attaches geographic coordinates to a variable with a non-location value type. The coordinate format is the same as the location operator value: `lat,lng` or `lat,lng,alt`. This enables sending a value and its location in a single variable.
The `#unit` and `@=location` suffixes MUST NOT be used with the location operator (`@=`). The server MUST reject the frame with `invalid_payload`. Altitude in a location triple is always in meters.
Parsers disambiguate the `@=` location suffix from the `@` timestamp suffix by checking the character after `@`: if `=`, parse as location; if digit, parse as timestamp; otherwise, reject with `invalid_payload`.
Metadata keys follow the same character rules as variable names (lowercase alphanumeric and underscore). Metadata keys do not support escape sequences: they are restricted to the identifier charset (`[a-z0-9_]`), which contains no structural characters. Metadata values follow the same encoding rules as string values (printable UTF-8, with escaping for structural characters).
#### 6.3.3 Full Variable Form
With all optional suffixes:
```
temperature:=32.5#C@=39.74,-104.99@1694567890000^reading_001{source=dht22,quality=high}
```
### 6.4 Inheritance Rules
Body-level modifiers cascade to all variables in the body:
```
PUSH|4deedd7bab8817ec|sensor-01|@=39.74,-104.99@1694567890000^batch_42{firmware=2.1}[temp:=32#C;humidity:=65#%]
```
Both `temp` and `humidity` inherit:
- Location: `{lat: 39.74, lng: -104.99}`
- Timestamp: `1694567890000`
- Group: `batch_42`
- Metadata: `firmware=2.1`
Variable-level modifiers **override** body-level for location, timestamp, and group:
```
PUSH|4deedd7bab8817ec|sensor-01|@=39.74,-104.99@1694567890000[temp:=32@=39.75,-105.00@1694567891000;humidity:=65]
```
Here `temp` uses its own location and timestamp, while `humidity` uses the body-level values. Variables using the `@=` operator carry their own location as the value itself; body-level `@=LOCATION` has no effect on them.
```
PUSH|4deedd7bab8817ec|sensor-01|@=39.74,-104.99[speed:=10;position@=40.00,-105.50]
```
Here `speed` inherits the body-level location `{lat: 39.74, lng: -104.99}`, but `position` uses its own value `{lat: 40.00, lng: -105.50}`. Body-level `@=` does not override `@=` operator variables.
For metadata, variable-level **merges** with body-level (variable wins on key conflicts):
```
PUSH|4deedd7bab8817ec|sensor-01|{firmware=2.1}[temp:=32{source=dht22};humidity:=65]
```
- `temp` has metadata: `{firmware: "2.1", source: "dht22"}`
- `humidity` has metadata: `{firmware: "2.1"}`
### 6.5 Passthrough
When a device needs to send raw data instead of structured variables, the BODY begins with `>` followed by an encoding flag:
| Prefix | Encoding | Delivered As | Example |
|---|---|---|---|
| `>x` | Hexadecimal | Raw buffer (bytes): hex is decoded | `PUSH\|AUTH\|SERIAL\|>xDEADBEEF01020304` |
| `>b` | Base64 | Text string (base64): delivered as-is, parser decodes if needed | `PUSH\|AUTH\|SERIAL\|>b3q2+7wECAwQ=` |
The `>` prefix signals **passthrough mode**: the server authenticates the frame (validates AUTH), identifies the target device (by SERIAL), but does NOT parse the BODY as variables. The data is delivered to the device's payload parser:
- `>x` → the hex string is decoded and delivered as a **raw byte buffer**. The hex string MUST have an even number of characters (each byte is two hex digits); odd-length hex MUST be rejected with `invalid_payload`.
- `>b` → the base64 string is delivered as a **text string** (the payload parser is responsible for decoding if needed)
Passthrough mode (`>x`, `>b`) is **uplink-only**.
The payload parser receives the raw data and can process it however needed. If the decoded data happens to be a TagoTiP frame (e.g., a device that encodes TagoTiP text as hex), a TagoTiP parser helper function is available in the payload parser environment to convert it to structured JSON objects.
```
PUSH|4deedd7bab8817ec|sensor-01|>xDEADBEEF01020304
PUSH|4deedd7bab8817ec|sensor-01|>b3q2+7wECAwQ=
```
The effective maximum passthrough data size depends on the frame budget remaining after method, auth, serial, and `>x`/`>b` prefix fields.
---
## 7. PULL: Retrieving Data
### 7.1 Request
```
PULL|AUTH|SERIAL|[VAR_NAME;VAR_NAME;...]
```
Retrieves the last stored value of one or more variables from the specified device. Variable names are enclosed in brackets (`[]`) and separated by semicolons (`;`), matching the PUSH body syntax. Even a single variable MUST be bracket-wrapped.
### 7.2 Response
The server MUST return the found variables in bracket-wrapped standard syntax:
```
ACK|OK|[VARIABLE OPERATOR VALUE #UNIT @=LOCATION @TIMESTAMP ^GROUP {METADATA};...]
```
The response always uses bracket-wrapped variable syntax, matching the PUSH body format. Only found variables are included: variables that do not exist or have no stored values are silently omitted. If **none** of the requested variables are found, the server MUST respond with `ACK|ERR|variable_not_found`.
The server does not echo the serial in ACK responses (see §9).
Examples:
```
→ PULL|4deedd7bab8817ec|weather-denver|[temperature]
← ACK|OK|[temperature:=32#F@1694567890000]
→ PULL|4deedd7bab8817ec|weather-denver|[temperature;humidity;pressure]
← ACK|OK|[temperature:=32#F@1694567890000;humidity:=65#%@1694567890000]
→ PULL|4deedd7bab8817ec|drone-07|[speed]
← ACK|OK|[speed:=10#km/h@=39.74,-104.99@1694567890000]
```
In the second example, `pressure` was requested but not found, so it is silently omitted from the response. The third example shows a response with the `@=` location suffix attached to a numeric value.
---
## 8. PING: Keepalive
### 8.1 Request
```
PING|AUTH|SERIAL
```
No body field. The SERIAL field identifies the device performing the keepalive.
### 8.2 Response
```
ACK|PONG
```
---
## 9. ACK: Server Response
All downlink communication uses the `ACK` frame:
```
ACK|!N|STATUS|DETAIL
ACK|!N|STATUS
ACK|STATUS|DETAIL
ACK|STATUS
```
| Field | Required | Description |
|---|---|---|
| `!N` | No | Echoed sequence counter from uplink request (`!` prefix + decimal integer) |
| `STATUS` | Yes | Result code |
| `DETAIL` | No | Additional information |
ACK frames never include a device serial number. A device may have multiple associated serials (e.g., after hardware replacement), so the server does not echo a serial in responses. The client already knows which device it addressed in the uplink request.
### 9.1 Status Codes
When `!N` is present, it appears between `ACK` and `STATUS` (e.g., `ACK|!1|OK|3`). The status codes themselves are unchanged:
| Status | Meaning | Detail |
|---|---|---|
| `OK` | Operation succeeded | For PUSH: decimal count of data points added to the device bucket. This applies to both structured payloads and passthrough payloads (`>x`, `>b`). For passthrough, the count reflects data points produced by the payload parser. For PULL: bracket-wrapped variable list in standard syntax (see §7.2). `ACK|OK|0` is valid and means the frame was accepted but produced no data points (e.g., all variables were filtered by the payload parser). |
| `PONG` | Response to PING | (none) |
| `CMD` | Server-initiated command | Command string (application-defined) |
| `ERR` | Operation failed | Error code |
### 9.2 Error Codes
| Detail | Meaning |
|---|---|
| `invalid_token` | Authorization Hash is missing, expired, or invalid |
| `invalid_method` | Unknown method |
| `invalid_payload` | Malformed body / parse error |
| `invalid_seq` | Sequence counter is not greater than last accepted value |
| `device_not_found` | Device serial is not found under the authenticated Account/Profile |
| `variable_not_found` | No requested variables exist or have stored values (for PULL) |
| `rate_limited` | Rate limit exceeded |
| `auth_failed` | TagoTiP/S envelope authentication or decryption failed |
| `unsupported_version` | TagoTiP/S envelope version is not supported by the server |
| `payload_too_large` | Frame exceeds maximum size |
| `server_error` | Internal server error |
### 9.3 Examples
Without sequence counter (unsolicited or no-counter client):
```
ACK|OK|2
ACK|OK|[temperature:=32#F@1694567890000]
ACK|PONG
ACK|CMD|reboot
ACK|CMD|ota=https://example.com/v2.1.bin
ACK|ERR|invalid_token
ACK|ERR|invalid_payload
ACK|ERR|auth_failed
```
With sequence counter (correlated responses):
```
ACK|!1|OK|2
ACK|!2|OK|[temperature:=32#F@1694567890000]
ACK|!3|PONG
ACK|CMD|reboot ← unsolicited, no counter
ACK|!5|ERR|invalid_token
ACK|!6|ERR|invalid_seq
ACK|!7|ERR|invalid_payload
```
### 9.4 Client Guidance (Non-Normative)
- Clients receiving `rate_limited` SHOULD implement exponential backoff.
- `invalid_token` SHOULD NOT be retried without re-provisioning.
- `server_error` MAY be retried after a delay.
### 9.5 Response Correlation
When the uplink frame includes a sequence counter (`!N`), the server MUST echo the same `!N` value in the ACK response. Clients match the echoed value to correlate responses to their originating requests on pipelined connections.
**Rules:**
- The server MUST echo `!N` when the uplink included it
- The server MUST NOT include `!N` in unsolicited messages (CMD pushed without a request)
- The client uses presence/absence of `!N` to distinguish solicited responses from unsolicited CMDs
- The `!` prefix disambiguates the counter from STATUS: status codes are alphabetic (`OK`, `PONG`, `CMD`, `ERR`) and never start with `!`
---
## 10. Sequence Counter (Optional)
TagoTiP supports an optional, monotonically increasing sequence counter. When used, the counter provides:
- **Replay protection**: the server rejects messages with a counter value it has already seen
- **Message ordering**: the server can detect out-of-order delivery
- **Deduplication**: the server can discard duplicate messages
> The counter is also a nonce component in TagoTiP/S. See [TagoTiPs.md](/docs/tagotip/specification/tagotips-specification).
### 10.1 Counter Rules
| Rule | Description |
|---|---|
| Size | 32-bit unsigned integer (0 to 4,294,967,295) |
| Initial value | Device chooses; `1` is RECOMMENDED |
| Increment | MUST be strictly increasing for each client→server message when enabled. Increment by exactly 1 is RECOMMENDED |
| Persistence | Device SHOULD persist the counter across reboots (e.g., in flash/EEPROM) |
| Wraparound | When the counter reaches `0xFFFFFFFF`, device MUST re-provision or reset with the server |
### 10.2 Server-Side Validation
When the server is configured to enforce sequence counters, it MUST maintain the last-seen counter value per device (identified by the SERIAL field).
When the server has no previously recorded counter for a device (first message ever, or after a server-side reset), the server MUST accept any valid counter value and store it as the new last-seen value.
The server SHOULD accept a message only if its counter is **strictly greater** than the last-seen value. The server MAY allow a configurable acceptance window to tolerate minor reordering. The counter MAY be reset by server-side policy (e.g., after an idle timeout or manual reset by the device owner).
Sequence counter validation (when enabled) applies to every uplink frame, including PING. The server MUST update the last-seen counter value regardless of method.
When the uplink frame includes a sequence counter, the server echoes it in the ACK response for correlation purposes (see §9.5). Sequence counter enforcement (monotonic validation) applies only to client→server messages.
### 10.3 Representation
In TagoTiP, the sequence counter is included in the frame header with a `!` prefix followed by the decimal integer:
```
PUSH|!42|4deedd7bab8817ec|sensor-01|[temperature:=32]
PING|!5|4deedd7bab8817ec|sensor-01
```
---
## 11. Examples
### 11.1 Simple Push
```
PUSH|4deedd7bab8817ec|weather-denver|[temperature:=32;humidity:=65]
```
### 11.2 Push with Sequence Counter
```
PUSH|!1|4deedd7bab8817ec|weather-denver|[temperature:=32;humidity:=65]
```
### 11.3 Typed Values
```
PUSH|4deedd7bab8817ec|sensor-0A1F|[temperature:=32.5#C;status=online;active?=true]
```
Negative number example:
```
PUSH|4deedd7bab8817ec|sensor-0A1F|[temperature:=-15.3#C]
```
### 11.4 With Location
Location as value (using `@=` operator):
```
PUSH|4deedd7bab8817ec|drone-07|[position@=39.74,-104.99,305]
```
Location attached to a non-location value (using `@=` suffix):
```
PUSH|4deedd7bab8817ec|drone-07|[speed:=10#km/h@=39.74,-104.99,305]
```
### 11.5 With Metadata
```
PUSH|4deedd7bab8817ec|sensor-01|[temperature:=32{source=dht22,quality=high}]
```
### 11.6 Body-Level Defaults
```
PUSH|4deedd7bab8817ec|sensor-01|@=39.74,-104.99@1694567890000^batch_42{firmware=2.1}[temperature:=32#C;humidity:=65#%]
```
### 11.7 Variable-Level Timestamps (Datalogger)
```
PUSH|4deedd7bab8817ec|datalogger-7|[temp:=32@1694567890000;temp:=33@1694567900000;temp:=31@1694567910000]
```
### 11.8 Passthrough (Hex)
```
PUSH|4deedd7bab8817ec|sensor-01|>xDEADBEEF01020304
```
### 11.9 Passthrough (Base64)
```
PUSH|4deedd7bab8817ec|sensor-01|>b3q2+7wECAwQ=
```
### 11.10 Retrieve Last Value
```
PULL|4deedd7bab8817ec|weather-denver|[temperature]
```
### 11.11 Retrieve Last Value with Sequence Counter
```
PULL|!7|4deedd7bab8817ec|weather-denver|[temperature]
```
### 11.12 Keepalive
```
PING|4deedd7bab8817ec|sensor-01
```
### 11.13 Full Conversation Flow
```
→ PING|4deedd7bab8817ec|weather-denver
← ACK|PONG
→ PUSH|4deedd7bab8817ec|weather-denver|[temperature:=32#F;humidity:=65#%;active?=true]
← ACK|OK|3
→ PULL|4deedd7bab8817ec|weather-denver|[temperature]
← ACK|OK|[temperature:=32#F@1694567890000]
← ACK|CMD|reboot
→ PUSH|4deedd7bab8817ec|weather-denver|[invalid=broken
← ACK|ERR|invalid_payload
```
### 11.14 Conversation with Sequence Counter
```
→ PING|!1|4deedd7bab8817ec|weather-denver
← ACK|!1|PONG
→ PUSH|!2|4deedd7bab8817ec|weather-denver|[temperature:=32#F]
← ACK|!2|OK|1
→ PUSH|!3|4deedd7bab8817ec|weather-denver|[humidity:=65#%]
← ACK|!3|OK|1
→ PUSH|!2|4deedd7bab8817ec|weather-denver|[pressure:=1013#hPa]
← ACK|!2|ERR|invalid_seq
```
---
## 12. Parsing Rules
### 12.1 Frame Parsing
1. Read the message (delimited by transport: `\n` for TCP, end of datagram for UDP, end of HTTP body, etc.)
2. Split by `|` into fields (respecting `\|` escape sequences)
3. If field 1 is `ACK`, check if field 2 starts with `!`: if yes, parse as `[ACK, SEQ, STATUS[, DETAIL]]`; otherwise parse as `[ACK, STATUS[, DETAIL]]`
4. Otherwise, check if field 2 starts with `!`: if yes, parse as `[METHOD, SEQ, AUTH, SERIAL[, BODY]]`; otherwise parse as `[METHOD, AUTH, SERIAL[, BODY]]`
5. Validate METHOD against known methods
6. If SEQ is present in an uplink frame, parse the decimal integer after `!` and validate against the last-seen counter (when counter enforcement is enabled; see §10.2)
7. Route to method-specific parser
**Field-count matrix (after `|`-splitting, respecting escapes):**
| Method | With `!N` | Without `!N` | Notes |
|---|---|---|---|
| PUSH | `METHOD \| SEQ \| AUTH \| SERIAL \| BODY` (5 fields) | `METHOD \| AUTH \| SERIAL \| BODY` (4 fields) | BODY is required |
| PULL | `METHOD \| SEQ \| AUTH \| SERIAL \| BODY` (5 fields) | `METHOD \| AUTH \| SERIAL \| BODY` (4 fields) | BODY = `[VARNAME;...]` |
| PING | `METHOD \| SEQ \| AUTH \| SERIAL` (4 fields) | `METHOD \| AUTH \| SERIAL` (3 fields) | No BODY |
| ACK | `ACK \| SEQ \| STATUS \| DETAIL` (4 fields) | `ACK \| STATUS \| DETAIL` (3 fields) | DETAIL is optional → min 2 or 3 fields |
For ACK without `!N`: minimum 2 fields (`ACK|STATUS`), maximum 3 (`ACK|STATUS|DETAIL`).
For ACK with `!N`: minimum 3 fields (`ACK|!N|STATUS`), maximum 4 (`ACK|!N|STATUS|DETAIL`).
### 12.2 PUSH Body Parsing
1. If BODY starts with `>`, this is a **passthrough**: read encoding flag (`x` or `b`), deliver the data to the payload parser without further parsing
2. Otherwise, scan for `[`: everything before `[` is body-level modifiers, everything inside `[]` is variables
3. Parse body-level modifiers for optional `@=LOCATION`, `@TIMESTAMP`, `^GROUP`, `{METADATA}` (MUST appear in this order when present; reject duplicates with `invalid_payload`). After `@`, check: if `=` follows → location; if digit follows → timestamp; otherwise reject with `invalid_payload`.
4. Split variable content by `;` into individual variables (respecting `\;` escape)
5. For each variable, parse left-to-right (single pass, no backtracking):
- **Name**: Read until operator is found (`:=`, `?=`, `@=`, or `=`)
- **Operator**: Determines value type
- **Value**: Read until `#`, `@`, `^`, `{`, `;`, or `]` (respecting escapes)
- **#unit**: If `#` found, read until `@`, `^`, `{`, `;`, or `]`. MUST NOT appear with `@=` operator.
- **@=location**: If `@=` found, read coordinates (two or three comma-separated numbers) until `@`, `^`, `{`, `;`, or `]`. MUST NOT appear with `@=` operator.
- **@timestamp**: If `@` found (not followed by `=`), read digits until `^`, `{`, `;`, or `]`
- **^group**: If `^` found, read until `{`, `;`, or `]`
- **\{metadata\}**: If `{` found, read until `}` and parse key-value pairs by `,` (respecting `\,` and `\}` escapes). Each metadata pair is split on the first `=`; subsequent `=` characters are part of the value.
### 12.3 PULL Body Parsing
The BODY is a bracket-wrapped list of variable names: `[var1;var2;...]`. Strip the enclosing `[` and `]`, then split by `;` to obtain individual variable names, each matching `1*VARNAMECHAR`. A single variable is valid (e.g., `[temperature]`).
### 12.4 Operator Disambiguation
The parser MUST check for multi-character operators first:
1. Check for `:=` → Number
2. Check for `?=` → Boolean
3. Check for `@=` → Location
4. Fallback to `=` → String
---
## 13. Size Comparison
The same data point expressed across formats:
**HTTP/JSON (~487 bytes with headers):**
```json
{
"variable": "temperature",
"value": 32,
"unit": "F",
"group": "batch_42",
"time": "1694567890000",
"location": {"lat": 39.74, "lng": -104.99},
"metadata": {"source": "dht22"}
}
```
**TagoTiP (~103 bytes):**
```
PUSH|4deedd7bab8817ec|sensor-01|@1694567890000^batch_42[temperature:=32#F@=39.74,-104.99{source=dht22}]
```
**TagoTiP/S (~110 bytes):**
```
Headless inner frame (81 bytes):
sensor-01|@1694567890000^batch_42[temperature:=32#F@=39.74,-104.99{source=dht22}]
(removed "PUSH|4deedd7bab8817ec|" = 22 bytes)
Envelope: 1 (flags) + 4 (counter) + 8 (auth hash) + 8 (device hash) + 81 (ciphertext) + 8 (auth tag) = 110 bytes
```
| Format | Approximate Size | vs. HTTP/JSON |
|---|---|---|
| HTTP/JSON | ~487 bytes | baseline |
| TagoTiP | ~103 bytes | ~4.7× smaller |
| TagoTiP/S | ~110 bytes | ~4.4× smaller |
TagoTiP sizes exclude transport-layer overhead (TCP/IP headers). The HTTP/JSON body alone is ~180 bytes; the ~487 figure includes typical HTTP request headers. TagoTiP/S adds encryption overhead (29-37 bytes depending on cipher suite) but removes the method and auth hash fields from the inner frame.
---
## 14. Grammar (ABNF)
```abnf
; Core rules (ALPHA, DIGIT, HEXDIG, LF, etc.) per RFC 5234, Appendix B.
; === Uplink Frames (Client → Server) ===
; LF is REQUIRED on stream transports (TCP); OPTIONAL on message transports (UDP, MQTT, HTTP)
frame = push-frame / pull-frame / ping-frame
push-frame = "PUSH" "|" [seq "|"] auth "|" serial "|" push-body LF
pull-frame = "PULL" "|" [seq "|"] auth "|" serial "|" pull-body LF
ping-frame = "PING" "|" [seq "|"] auth "|" serial LF
; Future: METHOD "/" 1*DIGIT
seq = "!" counter-value ; Optional sequence counter
counter-value = "0" / (%x31-39 *DIGIT) ; No leading zeros
auth = 16HEXDIG ; Authorization Hash (8 bytes as hex)
serial = 1*100SERIALCHAR ; Device serial number (max 100 bytes)
; PUSH
push-body = passthrough-body / structured-body
passthrough-body = ">x" 1*(2HEXDIG) ; Hex-encoded passthrough (byte pairs)
/ ">b" 1*BASE64CHAR ; Base64-encoded passthrough
structured-body = [body-mods] "[" var-list "]"
body-mods = ["@=" loc-value] ["@" timestamp] ["^" group] ["{" meta-list "}"]
var-list = variable *99(";" variable) ; max 100 variables
variable = var-name ":=" num-value [common-suffixes]
/ var-name "=" str-value [common-suffixes]
/ var-name "?=" bool-value [common-suffixes]
/ var-name "@=" loc-value [loc-suffixes]
var-name = 1*100VARNAMECHAR ; max 100 bytes
num-value = ["-"] int-part ["." 1*DIGIT]
int-part = "0" / (%x31-39 *DIGIT) ; 0, or non-zero digit followed by any digits
str-value = 1*VALCHAR
bool-value = "true" / "false"
loc-value = coordinate "," coordinate ["," coordinate]
coordinate = ["-"] int-part ["." 1*DIGIT]
common-suffixes = ["#" unit] ["@=" loc-value] ["@" timestamp] ["^" group] ["{" meta-list "}"]
loc-suffixes = ["@" timestamp] ["^" group] ["{" meta-list "}"]
; no #unit or @=location for @= operator (§6.3.2)
unit = 1*25UNITCHAR ; max 25 bytes
timestamp = 1*DIGIT ; UNIX ms
group = 1*100VARNAMECHAR ; max 100 bytes
meta-list = meta-pair *31("," meta-pair) ; max 32 metadata pairs
meta-pair = meta-key "=" meta-value
meta-key = 1*100VARNAMECHAR ; max 100 bytes
meta-value = 1*METAVALCHAR
; PULL
pull-body = "[" pull-list "]"
pull-list = var-name *99(";" var-name) ; max 100 variables
pull-response = "[" var-list "]" ; Bracket-wrapped, same as PUSH body
; === Downlink Frames (Server → Client) ===
ack-frame = "ACK" "|" [seq "|"] ack-status ["|" ack-detail] LF
; seq starts with "!", unambiguous vs. ack-status (alphabetic)
ack-status = "OK" / "PONG" / "CMD" / "ERR"
ack-detail = 1*DIGIT ; PUSH OK: count of accepted data points
/ pull-response ; PULL OK: bracket-wrapped variable list
/ 1*DETAILCHAR ; CMD detail or ERR error code
; Character classes below cover the ASCII subset only.
; Non-ASCII UTF-8 sequences (RFC 3629) are also valid in VALCHAR,
; METAVALCHAR, and UNITCHAR positions when the implementation supports UTF-8.
; === Character classes ===
VARNAMECHAR = %x61-7A / DIGIT / "_" ; lowercase a-z, digits, underscore
SERIALCHAR = ALPHA / DIGIT / "-" / "_" ; serial numbers (hyphens allowed)
VALCHAR = %x20-22 / %x24-3A / %x3C-3F / %x41-5A / %x5F-60 / %x61-7A / %x7E
/ "\" ("|" / "[" / "]" / ";" / "," / "{" / "}" / "#" / "@" / "^" / "n" / "\")
; printable ASCII excluding # ; @ [ \ ] ^ { | }
; with escape sequences for structural characters
METAVALCHAR = %x20-22 / %x24-2B / %x2D-3A / %x3C-3F / %x41-5A / %x5F-60 / %x61-7A / %x7E
/ "\" ("|" / "[" / "]" / ";" / "," / "{" / "}" / "#" / "@" / "^" / "n" / "\")
; like VALCHAR but also excludes unescaped ","
UNITCHAR = %x20-22 / %x24-3A / %x3C-3F / %x41-5A / %x5F-60 / %x61-7A / %x7E
; printable ASCII excluding # ; @ [ \ ] ^ { | }
; No escape sequences: units are plain text
DETAILCHAR = %x21-7B / %x7D-7E ; VCHAR excluding "|"
BASE64CHAR = ALPHA / DIGIT / "+" / "/" / "="
; Padding position enforced by decoder
```
**Note:** The character classes above precisely exclude structural delimiters from their base ranges. `VALCHAR` allows unescaped `,` (used literally in string values), while `METAVALCHAR` excludes it (since `,` separates metadata pairs). The `variable` production is split by operator type to enforce type-specific value formats (§6.3) and the rules that `#unit` and `@=location` MUST NOT be used with the `@=` operator (§6.3.2). See §12.2 for detailed parsing rules.
---
## 15. Symbol Reference
| Symbol | Meaning | Context |
|---|---|---|
| `\|` | Field separator | Frame level |
| `!` | Sequence counter prefix | Before counter value (e.g., `!42`) |
| `[]` | Variable block delimiters | Encloses variables for a device |
| `;` | Variable separator | Inside `[]` |
| `,` | Metadata pair separator | Inside `{}` |
| `:=` | Number assignment | Variable operator |
| `=` | String assignment | Variable operator |
| `?=` | Boolean assignment | Variable operator |
| `@=` | Location assignment / Location suffix | Variable operator or suffix |
| `#` | Unit suffix | After variable value |
| `@` | Timestamp suffix | After value/unit/location |
| `^` | Group suffix/prefix | Body-level modifier or variable level |
| `{}` | Metadata block | Body-level modifier or variable level |
| `>x` | Hex passthrough payload | PUSH body prefix |
| `>b` | Base64 passthrough payload | PUSH body prefix |
| `\n` | Frame terminator | Stream transports (TCP) |
| `\` | Escape prefix | Before `\|`, `;`, `]`, `}`, `n`, `\`, etc. |
---
## 16. Security Considerations
- The Authorization Hash is a truncated SHA-256 of the token; it does not expose the original token. However, TLS or equivalent transport-level encryption is RECOMMENDED in production environments
- Authorization Tokens SHOULD NOT be hardcoded in source code shared publicly
- The server MUST validate the Authorization Hash and match it against the Device Serial Number before processing any message
- The optional sequence counter provides replay protection but does NOT provide confidentiality
> For encryption-based security without TLS, see [TagoTiPs.md](/docs/tagotip/specification/tagotips-specification) (TagoTiP/S).
---
## 17. License
This specification is **open source**, published under the [Apache License 2.0](https://github.com/tago-io/tagotip/blob/main/LICENSE).
Anyone is free to implement TagoTiP (clients, servers, libraries, gateways, or any other component) for any purpose, including commercial use, without requiring permission from TagoIO Inc. The Apache 2.0 license includes an express patent grant to all implementers.
The names "TagoTiP", "TagoTiP/S", and "TagoIO" are trademarks of TagoIO Inc. See [NOTICE](https://github.com/tago-io/tagotip/blob/main/NOTICE) for trademark details.
Copyright 2026 TagoIO Inc.
---
## TagoTiP/S Specification
# TagoTiP/S: Secure Crypto Envelope
**Version:** 1.0
**Date:** April 2026
**Status:** Specification, Revision D
> TagoTiP/S wraps TagoTiP data in a binary envelope. For the plaintext frame format (methods, variable syntax, parsing), see [TagoTiP.md](/docs/tagotip/specification/tagotip-specification).
---
## 1. Introduction
TagoTiP/S (Secure) is a binary crypto envelope that provides **AEAD authenticated encryption** for TagoTiP data without requiring TLS. It is designed for links where TLS is unavailable or too expensive, such as LoRa, Sigfox, NB-IoT, or raw UDP.
TagoTiP/S uses a compact **headless inner frame** that omits fields carried by the envelope header, saving ~40-50 bytes per message (see §4).
### 1.1 How It Works
```mermaid
flowchart LR
Frame["PUSH #124; AUTH #124; serial #124; #91;temp:=32#93;"]
Frame -->|TagoTiP| Send1(["send as-is"])
Frame -->|TagoTiP/S| Strip["build headless"]
Strip --> Encrypt["encrypt"]
Encrypt --> Envelope["envelope"]
Envelope --> Send2(["send"])
```
### 1.2 Envelope at a Glance
```mermaid
block-beta
columns 6
FL["Flags\n1 byte\n(cipher+version+method)"]:1
CTR["Counter\n4 bytes\n(nonce component)"]:1
AH["Auth Hash\n8 bytes\nSHA-256(token w/o at)"]:1
DH["Device Hash\n8 bytes\nSHA-256(serial)"]:1
CT["Ciphertext + Auth Tag\nN bytes (inner frame + tag)\nAEAD"]:2
```
> 21 bytes header + ciphertext + auth tag
### 1.3 Design Goals
- **Encrypted**: AEAD authenticated encryption (configurable cipher suite) for links where TLS is unavailable
- **Integrity-verified**: built-in authentication tag detects tampering or corruption
- **Compact**: 29-37 bytes of envelope overhead (depending on cipher suite)
- **Transport-agnostic**: works over UDP, TCP, HTTP(S), MQTT, or any byte-capable channel
- **C-friendly**: predictable buffer sizes, no dynamic allocation required
---
## 2. Credentials
TagoTiP/S uses the following credentials for envelope construction and processing.
| Credential | Format | Secrecy | Purpose |
|---|---|---|---|
| **Authorization Token** | `at` + 32 hex chars (34 chars total) | Secret | Identifies the Account/Profile. Used to derive the Authorization Hash. Never transmitted on the wire. |
| **Authorization Hash** | 8 bytes (derived) | Public | First 8 bytes of SHA-256 of the Authorization Token **without the `at` prefix** (UTF-8 encoded). Used as the **profile identifier** in the envelope header and in TagoTiP frames. Safe to display in UIs/logs. |
| **Device Hash** | 8 bytes (derived) | Public | First 8 bytes of SHA-256 of the device serial number (UTF-8 encoded). Used as the **device identifier** in the envelope header. Allows the server to identify the device before decryption. |
| **Encryption Key** | 16 or 32 bytes (depends on cipher suite) | Secret | AEAD key used to encrypt/decrypt frames. Scoped **per-device**: each device has its own key. Looked up by the combination of Authorization Hash and Device Hash. |
Encryption Keys MUST be unique per device within a profile. Key reuse across devices is a configuration error and may compromise nonce uniqueness. Key length (16 or 32 bytes) is determined by the cipher suite (see §3.1). Changing to a cipher suite with a different key length requires reprovisioning the Encryption Key.
### 2.1 Deriving the Authorization Hash
Given an Authorization Token `ate2bd319014b24e0a8aca9f00aea4c0d0`:
1. Strip the `at` prefix: `e2bd319014b24e0a8aca9f00aea4c0d0`
2. Encode the hex string as UTF-8 bytes
3. Compute SHA-256 of those bytes
4. Take the first 8 bytes of the digest: `0x4d 0xee 0xdd 0x7b 0xab 0x88 0x17 0xec`
> **Note:** The Authorization Hash derivation is shared between TagoTiP and TagoTiP/S. See [TagoTiP.md §2](/docs/tagotip/specification/tagotip-specification#2-credentials) for the same computation.
### 2.2 Deriving the Device Hash
Given a device serial `sensor-01`:
1. Compute SHA-256 of the UTF-8 bytes of `sensor-01`
2. Take the first 8 bytes of the digest (e.g., `0xab 0x77 0x88 0xd2 0x2e 0xb7 0x37 0x2f`)
> **Note:** With an 8-byte hash (2^64 space), collisions within a single profile are virtually impossible in practice. If multiple devices nonetheless share the same Device Hash within a profile, the server SHOULD attempt decryption with each matching device's key until one succeeds or all fail. If all decryption attempts fail, the server MUST respond with `ACK|ERR|auth_failed`.
### 2.3 Provisioning (Non-Normative)
The Encryption Key and Authorization Token SHOULD be provisioned during manufacturing or via a secure out-of-band channel. The protocol does not define a key exchange mechanism. The cipher suite SHOULD also be agreed upon during provisioning. The protocol does not include an in-band cipher negotiation mechanism; both sides must be configured to use the same cipher suite for a given device.
### 2.4 Optional Key Derivation (HMAC-SHA256)
Implementations MAY derive the Encryption Key from the Authorization Token and device serial number using HMAC-SHA256 instead of provisioning a separate key. This is a convenience feature: pre-provisioned keys remain fully supported and are the default.
**Construction:**
```
derive_key(token, serial, key_len):
1. hex_part = strip "at" prefix from token (if present)
2. hmac_key = UTF-8 bytes of hex_part
3. message = UTF-8 bytes of serial
4. output = HMAC-SHA256(key=hmac_key, msg=message) // 32 bytes
5. return output[0..key_len]
```
- `key_len` = 16 for AES-128 cipher suites, 32 for AES-256 / ChaCha20-Poly1305
- HMAC-SHA256 requires only SHA-256 (already required by §2.1 and §2.2) plus the HMAC wrapper (RFC 2104)
- The token hex part is the HMAC key (secret); the serial is the message (semi-public). This is safe because HMAC security depends on the key being secret, not the message
- SHA-256 output is 32 bytes, covering all cipher suite key sizes
**Test vector** (using the spec credentials from §11.1):
```
Token: ate2bd319014b24e0a8aca9f00aea4c0d0
Serial: sensor-01
HMAC key (UTF-8): "e2bd319014b24e0a8aca9f00aea4c0d0"
HMAC message (UTF-8): "sensor-01"
Derived key (32 bytes):
e5 05 f0 3c c9 e9 3f db cc 38 28 44 cc a3 e1 7f
df 0b b3 13 18 58 53 95 ce aa a3 9a 5d 14 19 64
First 16 bytes (AES-128): e5 05 f0 3c c9 e9 3f db cc 38 28 44 cc a3 e1 7f
```
> **Note:** When using derived keys, the Authorization Token becomes the sole secret for a given device. Implementations using key derivation MUST treat the token with the same care as an Encryption Key.
### 2.5 Hex Utilities (Non-Normative)
SDK implementations SHOULD provide `hex_to_bytes` and `bytes_to_hex` utility functions to simplify working with pre-provisioned keys supplied as hex strings. These are not protocol-level operations but are commonly needed by integrators.
---
## 3. Security Primitives
### 3.1 Encryption
TagoTiP/S supports multiple AEAD cipher suites. The cipher suite is encoded in bits 7-5 of the Flags byte (see §5.1).
| ID | Name | Key | Tag | Nonce | L |
|----|------|-----|-----|-------|---|
| 0 | AES-128-CCM | 16 B | 8 B | 13 B | 2 |
| 1 | AES-128-GCM | 16 B | 16 B | 12 B | n/a |
| 2 | AES-256-CCM | 32 B | 8 B | 13 B | 2 |
| 3 | AES-256-GCM | 32 B | 16 B | 12 B | n/a |
| 4 | ChaCha20-Poly1305 | 32 B | 16 B | 12 B | n/a |
| 5-7 | Reserved | n/a | n/a | n/a | n/a |
> **L** is the CCM length-field size parameter (in bytes). For CCM suites the nonce length is 15 - L; with L = 2 the nonce is 13 bytes and the maximum plaintext size is 2^16 - 1 bytes (64 KiB). The L column does not apply to non-CCM cipher suites (marked "n/a").
All implementations MUST support cipher suite 0 (AES-128-CCM). Support for additional cipher suites is OPTIONAL but RECOMMENDED for deployments with higher security requirements.
The device's **Encryption Key** (pre-provisioned, never transmitted) must match the key size required by the selected cipher suite.
### 3.2 Authentication Tag
The authentication tag is produced automatically by the AEAD cipher during encryption. Its length depends on the cipher suite: **8 bytes** for CCM suites, **16 bytes** for GCM and ChaCha20-Poly1305. The tag covers both the plaintext (the headless inner frame) and the Associated Authenticated Data (AAD = envelope header, bytes 0-20). There is no separate hash computation.
### 3.3 Nonce
The protocol uses a bandwidth-efficient nonce derived from the counter and device hash fields in the envelope header. The nonce length depends on the cipher suite:
- **13 bytes** for CCM suites (L=2, nonce = 15 - L)
- **12 bytes** for GCM and ChaCha20-Poly1305 suites
The full nonce is never transmitted.
**Components:**
- **Counter:** The 4-byte value from the envelope header (Big-Endian).
- **Device Hash:** The first 4 bytes of the 8-byte Device Hash from the envelope header.
**Construction (13-byte nonce, CCM suites):**
```
[Flags (1 byte)] [00 00 00 00] [Device Hash (4 bytes)] [Counter (4 bytes)]
```
**Construction (12-byte nonce, GCM / ChaCha20-Poly1305 suites):**
```
[Flags (1 byte)] [00 00 00] [Device Hash (4 bytes)] [Counter (4 bytes)]
```
For cipher suites with a 12-byte nonce, the zero-padding is 3 bytes instead of 4.
**Transmission:** The 4-byte counter and 8-byte Device Hash are sent in the envelope header. The receiver reconstructs the full nonce using the Flags byte (byte 0 of the envelope), the first 4 bytes of the Device Hash, Counter, and the nonce length from the cipher suite table.
Including the Flags byte in the nonce guarantees uniqueness across message directions and cipher suites: different cipher/method combinations in the Flags byte occupy distinct nonce spaces, preventing nonce reuse even when the counter values coincide. The Device Hash component extends that uniqueness across devices that share the same encryption key.
---
## 4. Headless Inner Frame
TagoTiP/S does not encrypt a complete TagoTiP frame. Instead, it uses a compact **headless inner frame** that omits fields already carried by the envelope header:
| Field | In plaintext TagoTiP frame | In TagoTiP/S headless inner frame |
|---|---|---|
| **METHOD** | Inside frame (`PUSH\|...`) | **Flags byte** in envelope header |
| **AUTH** | Inside frame (34 bytes) | **Omitted**: Auth Hash in header identifies the profile |
| **!N** (counter) | Inside frame (optional) | **Omitted**: Counter field in header is authoritative |
| **SERIAL** | Inside frame | Inside frame (encrypted for device privacy) |
| **BODY** | Inside frame | Inside frame (encrypted) |
The Device Hash in the envelope header is an 8-byte routing hint derived from the serial. The full SERIAL stays inside the encrypted payload so that the actual device identity remains confidential.
### 4.1 Uplink Inner Frame Formats
| Method (in Flags byte) | Inner Frame | Example |
|---|---|---|
| PUSH (`0x0`) | `SERIAL\|BODY` | `sensor-01\|[temp:=32]` |
| PULL (`0x1`) | `SERIAL\|[VARNAME;...]` | `sensor-01\|[temperature]` |
| PING (`0x2`) | `SERIAL` | `sensor-01` |
The BODY follows the same syntax as in plaintext TagoTiP (structured variables, passthrough, etc.). Only the frame-level fields (method, auth, counter) are removed.
### 4.2 Downlink Inner Frame Formats
| Method (in Flags byte) | Inner Frame | Example |
|---|---|---|
| ACK (`0x3`) | `STATUS[\|DETAIL]` | `OK\|3`, `OK\|[temperature:=32#F@1694567890000]` (PULL), `CMD\|ota=https://example.com/v2.1.bin`, or `PONG` |
The `ACK|` prefix from plaintext TagoTiP is not present: the method is encoded in the Flags byte. The inner frame starts directly with the STATUS field.
---
## 5. Envelope Structure
The envelope is a single binary message constructed by concatenating the following components with no delimiters:
```
[Flags] + [Counter] + [Authorization Hash] + [Device Hash] + [Ciphertext + Auth Tag]
```
| Component | Size | Description |
|---|---|---|
| **Flags** | 1 byte | Cipher (bits 7-5), Version (bits 4-3), and Method (bits 2-0). See §5.1. |
| **Counter** | 4 bytes | Sequence counter or random nonce (Big-Endian). Used to derive the AEAD nonce. |
| **Authorization Hash** | 8 bytes | First 8 bytes of SHA-256 of the Authorization Token without the `at` prefix (UTF-8 encoded). Used by the server to identify the profile and look up the Encryption Key. |
| **Device Hash** | 8 bytes | First 8 bytes of SHA-256 of the device serial number (UTF-8 encoded). Used by the server to identify the device before decryption. The first 4 bytes are also incorporated into the nonce to prevent nonce collisions across devices. |
| **Ciphertext + Auth Tag** | Variable | AEAD-encrypted headless inner frame (see §4) followed by the authentication tag. Cipher suite selected by Flags byte. |
**Total overhead:** 29-37 bytes, depending on cipher suite. Formula: `21 (header) + tag_length` where tag_length is 8 bytes for CCM suites or 16 bytes for GCM / ChaCha20-Poly1305. No padding is required.
**Associated Authenticated Data (AAD):** The first 21 bytes of the envelope (Flags + Counter + Authorization Hash + Device Hash) are passed as AAD to the AEAD cipher. The header is integrity-protected but not encrypted: an attacker cannot modify any header field without causing authentication failure during decryption.
### 5.1 Flags Byte
The first byte of the envelope encodes the cipher suite, protocol version, and method:
```
Bit 7 6 5 4 3 2 1 0
└─Cipher──┘ └Ver─┘ └Method─┘
```
| Field | Bits | Values |
|---|---|---|
| **Cipher** | 7-5 | Cipher suite ID (see the cipher suite table in §3.1). |
| **Version** | 4-3 | `0` = current version. `1`-`3` reserved for future versions. |
| **Method** | 2-0 | `0` = PUSH, `1` = PULL, `2` = PING, `3` = ACK. `4`-`7` reserved. |
**Examples:**
| Flags byte | Hex | Meaning |
|---|---|---|
| `000 00 000` | `0x00` | AES-128-CCM, Version 0, PUSH |
| `000 00 001` | `0x01` | AES-128-CCM, Version 0, PULL |
| `000 00 010` | `0x02` | AES-128-CCM, Version 0, PING |
| `000 00 011` | `0x03` | AES-128-CCM, Version 0, ACK |
| `100 00 000` | `0x80` | ChaCha20-Poly1305, Version 0, PUSH |
Receivers MUST reject envelopes with an unsupported version number or unsupported cipher suite.
> **Note:** The Flags byte value `0x41` (`010 00 001`) corresponds to cipher 2 (AES-256-CCM), version 0, PULL. This value is reserved for framing disambiguation (see §5.4) and MUST NOT appear as a valid Flags byte. Implementations using AES-256-CCM with PULL MUST use version ≥ 1 (i.e., `010 01 001` = `0x49`) or use a different cipher suite.
### 5.2 Byte Map Example
For a PUSH with serial `sensor-01` and body `[temp:=32]`, the headless inner frame is `sensor-01|[temp:=32]` (20 bytes):
```mermaid
block-beta
columns 6
F["00\nFlags"]:1
C["00 00 00 2A\nCounter (42)"]:1
A["4d ee dd 7b\nab 88 17 ec\nAuth Hash"]:1
D["ab 77 88 d2\n2e b7 37 2f\nDevice Hash"]:1
CT["20 bytes\nCiphertext"]:1
T["8 bytes\nAuth Tag"]:1
```
> Header (AAD) = Flags + Counter + Auth Hash + Device Hash (first 21 bytes)
| Field | Bytes | Description |
|---|---|---|
| Flags | `00` (= cipher 0 AES-128-CCM, v0, PUSH) | Cipher, version, and method |
| Counter | `00 00 00 2A` (= 42) | Nonce component |
| Auth Hash | `4d ee dd 7b ab 88 17 ec` | SHA-256(token without "at"), first 8 bytes |
| Device Hash | `ab 77 88 d2 2e b7 37 2f` | SHA-256(serial), first 8 bytes |
| Ciphertext | 20 bytes | Headless inner frame, encrypted |
| Auth Tag | 8 bytes | CCM authentication tag |
Total envelope: 1 + 4 + 8 + 8 + 20 + 8 = **49 bytes**.
### 5.3 Transport Notes
| Transport | Framing | Notes |
|---|---|---|
| UDP | End of datagram | One envelope per datagram. |
| HTTP(S) | HTTP body | One envelope per request body. |
| MQTT | MQTT payload | One envelope per MQTT message. |
| TCP | Length-prefixed (see below) | Binary envelopes cannot use `\n` as a delimiter. |
On all transports, the receiver distinguishes plaintext fallback frames from TagoTiP/S envelopes by inspecting the first byte of the message: `0x41` (ASCII `A`) indicates a plaintext ACK frame (e.g., `ACK|ERR|auth_failed`); any other value indicates a TagoTiP/S envelope. The only plaintext frames that can appear on a TagoTiP/S connection are server-side ACK error fallbacks (see §9), so reserving `0x41` alone is sufficient for disambiguation.
### 5.4 Framing on Stream Transports (TCP)
The TagoTiP/S envelope is **binary** and may contain any byte value (including `0x0A`). Therefore, newline (`\n`) MUST NOT be used as a delimiter for TagoTiP/S on stream transports.
When carrying TagoTiP/S over TCP (or any stream transport), **all messages** MUST be framed using a length prefix:
```
[Length (uint16, Big-Endian)] + [Message bytes...]
```
Where `Length` is the number of bytes in the following message (not including the length field). This applies to both TagoTiP/S envelopes and plaintext fallback frames (e.g., `ACK|ERR|auth_failed`).
The receiver distinguishes between envelope and plaintext by inspecting the first byte of the message content: if the first byte of the message content is `0x41` (ASCII `A`), it is a plaintext ACK fallback frame; otherwise it is a TagoTiP/S envelope. The byte value `0x41` is reserved and MUST NOT be used as a Flags byte in any future protocol version.
### 5.5 Size Limits
- **Max raw inner frame size inside TagoTiP/S:** The unencrypted headless inner frame MUST NOT exceed **16,384 bytes**.
- **Max TagoTiP/S envelope size:** With the envelope overhead of `21 (header) + tag_length` bytes, the server MUST reject any envelope larger than **16,384 + 21 + tag_length** bytes (16,413 for CCM suites with 8-byte tag; 16,421 for GCM/ChaCha20 suites with 16-byte tag).
Field-level limits (variable name length, metadata pair count, etc.) are defined in [TagoTiP.md §4.5.1](/docs/tagotip/specification/tagotip-specification#451-field-level-limits) and apply equally to headless inner frames.
---
## 6. Payload Construction Steps
1. **Build the headless inner frame.** Omit method, auth, and counter: only serial and body (for uplink). For example, a PUSH:
```
sensor-01|[temperature:=32;humidity:=65]
```
2. **Compute the Device Hash.** SHA-256 the device serial number (UTF-8 bytes); keep the first 8 bytes.
3. **Build the Flags byte.** Cipher (bits 7-5) + Version (bits 4-3) + Method (bits 2-0). For example, AES-128-CCM PUSH version 0 = `0x00`.
4. **Build the envelope header (AAD).** Concatenate: `[Flags (1B)] + [Counter (4B)] + [Authorization Hash (8B)] + [Device Hash (8B)]` (21 bytes total).
5. **Construct the nonce.** Length depends on cipher suite (see §3.3): 13 bytes for CCM, 12 bytes for GCM/ChaCha20. `[Flags (1B)] [zero-padding] [Device Hash[:4] (4B)] [Counter (4B)]`. Only the first 4 bytes of the Device Hash are used in the nonce.
6. **Encrypt.** Use the AEAD cipher indicated by the Flags byte with the Encryption Key, nonce, plaintext = headless inner frame bytes, and AAD = the 21-byte header. The cipher produces the ciphertext (same length as plaintext) and the authentication tag.
7. **Assemble the envelope.** Concatenate: `[Header (21B)] + [Ciphertext (NB)] + [Auth Tag]`.
---
## 7. Decryption Steps
When the receiver processes a TagoTiP/S envelope:
1. **Read Flags byte** (byte 0). Extract cipher (bits 7-5), version (bits 4-3), and method (bits 2-0). Reject if version is unsupported or cipher suite is unsupported.
2. **Parse header.** Read Counter (bytes 1-4), Authorization Hash (bytes 5-12), Device Hash (bytes 13-20). Use the Authorization Hash to identify the profile and look up the Encryption Key.
3. **Extract ciphertext.** Bytes 21 through end = ciphertext + auth tag.
4. **Reconstruct the nonce.** Use the cipher suite to determine nonce length (see §3.3): `[Flags (1B)] [zero-padding] [Device Hash[:4] (4B)] [Counter (4B)]`. Only the first 4 bytes of the Device Hash are used in the nonce.
5. **Decrypt.** Use the AEAD cipher from the Flags byte with the Encryption Key, nonce, ciphertext + auth tag, and AAD = bytes 0-20 (the 21-byte header). The cipher verifies integrity automatically: if the authentication tag does not match, decryption fails. Discard the envelope on authentication failure.
6. **Parse headless inner frame.** Use the method from the Flags byte to determine the format. Inner-frame field splitting (e.g., `SERIAL|BODY`, `STATUS|DETAIL`) MUST respect the same escape sequences defined in TagoTiP §4.4.
- **PUSH (0):** `SERIAL|BODY`: parse BODY as a push body (structured variables or passthrough)
- **PULL (1):** `SERIAL|[VARNAME;...]`: parse bracket-wrapped variable name list
- **PING (2):** `SERIAL`: no body
- **ACK (3):** `STATUS[|DETAIL]`: parse as a downlink response
---
## 8. Sequence Counter in TagoTiP/S
The sequence counter has two roles in TagoTiP/S: **replay protection** and **nonce derivation** for AEAD encryption.
> For general counter rules (initial value, increment, wraparound, persistence), see [TagoTiP.md §10](/docs/tagotip/specification/tagotip-specification#10-sequence-counter-optional).
### 8.1 Binary Representation
In the TagoTiP/S envelope, the counter is stored as a **4-byte Big-Endian unsigned integer** in bytes 1-4 of the envelope header.
| Decimal | Bytes (Big-Endian) |
|---|---|
| 42 | `0x0000002A` |
| 1000 | `0x000003E8` |
### 8.2 Counter as Nonce Component
The Counter field in the envelope header is the sole source of the sequence number for TagoTiP/S messages. The `!N` field from standard TagoTiP frames is not present in the headless inner frame.
### 8.3 Server-Side Validation
The server uses the Authorization Hash and Device Hash from the envelope header as routing hints to locate candidate devices and their Encryption Keys. After successful decryption, the server tracks the last-seen counter value per **resolved device identity** (not per the pre-decryption hash pair).
### 8.4 When the Counter Is Not Used
When the sequence counter is **not used**, the device MUST still provide a 4-byte value in the Counter field of the envelope. A random 4-byte nonce MAY be used as a last resort on devices without persistent storage, but this sacrifices replay protection and ordering guarantees. Devices with persistent storage MUST NOT use random nonces: they MUST use a monotonic counter. With a 32-bit random nonce, the birthday paradox yields approximately 50% collision probability after ~77,000 messages; deployments using random nonces SHOULD implement application-level safeguards to limit total message count per key.
---
## 9. Downlink (Server → Client)
Downlink frames use method ACK (`0x3`) in the Flags byte. If the uplink message was received as a TagoTiP/S envelope, the server MUST respond using TagoTiP/S on the same connection or session.
If the server cannot identify the profile or decrypt the envelope, it SHOULD respond with a plaintext `ACK|ERR|auth_failed` frame, since it cannot construct a valid TagoTiP/S response without the correct Encryption Key. If the envelope has an unsupported version, the server SHOULD respond with a plaintext `ACK|ERR|unsupported_version` frame. If the cipher suite is unsupported, the server SHOULD respond with a plaintext `ACK|ERR|unsupported_cipher` frame. If the envelope exceeds the size limits defined in §5.5, the server SHOULD respond with a plaintext `ACK|ERR|envelope_too_large` frame. On stream transports (TCP), these plaintext fallback frames MUST be length-prefixed per §5.4.
Note that replay counter rejection (when counter enforcement is enabled) occurs **after** successful decryption and device resolution (see §8.3), so the server responds with an encrypted `ACK|ERR|invalid_seq` envelope, not a plaintext fallback.
The headless inner frame for downlink is `STATUS|DETAIL` or just `STATUS`. The `ACK|` prefix from plaintext TagoTiP is not present (the method is in the Flags byte). See [TagoTiP.md §9](/docs/tagotip/specification/tagotip-specification#9-ack-server-response) for status codes and semantics.
- The 4-byte Counter field is used only as a **nonce seed** for the AEAD cipher. It does **not** represent a server sequence counter in this version.
- The nonce seed MUST be unique within the lifetime of the current Encryption Key for a given (Authorization Hash, Device Hash) pair (e.g., by using a per-connection monotonic seed or a uniqueness-checked random value).
- The server SHOULD use a per-device monotonic counter for downlink nonce seeds, stored persistently or derived from a server-wide counter. Random nonce seeds are permitted but carry the same birthday-bound collision risk as uplink random nonces: ~50% collision probability after ~77,000 messages per device per key (see §8.4 and §14). High-frequency downlink scenarios (e.g., command bursts) SHOULD use monotonic counters to avoid nonce reuse.
- Clients MUST NOT enforce monotonic ordering on downlink nonce seeds.
On a given TagoTiP/S connection or session, the client MUST NOT send a new uplink request until it has received the ACK response for the previous request (i.e., at most one outstanding request at a time). The server MUST preserve request-response ordering.
> **Note:** TagoTiP/S provides cryptographic integrity and confidentiality for downlink messages but does not provide replay protection at the envelope level. Applications that deliver state-changing commands (e.g., `CMD|unlock_door`) SHOULD implement application-level idempotency or command nonces.
> **Note:** The `!N` response correlation mechanism from plaintext TagoTiP (§9.5) does not apply to TagoTiP/S. The envelope's Counter field is a nonce seed for the AEAD cipher. The server MUST manage its own counter or nonce space for downlink envelopes independently of uplink counter values. Correlation is unnecessary on the constrained transports TagoTiP/S targets.
---
## 10. Passthrough over TagoTiP/S
When using TagoTiP/S with passthrough payloads (`>x` or `>b`), the server first unwraps the envelope (AEAD decryption automatically verifies integrity), then processes the resulting headless inner frame. If the body contains a passthrough payload, it is delivered to the payload parser the same way as in plaintext TagoTiP.
See [TagoTiP.md §6.5](/docs/tagotip/specification/tagotip-specification#65-passthrough) for passthrough syntax details.
---
## 11. Examples
### 11.1 Encrypted Push: AES-128-CCM (Test Vector)
The following is a fully computed test vector. Implementations SHOULD verify their output matches these values byte-for-byte.
```
Inputs:
Token: ate2bd319014b24e0a8aca9f00aea4c0d0
Serial: sensor-01
Encryption Key: fe 09 da 81 bc 44 00 ee 12 ab 56 cd 78 ef 90 12
Counter: 42
Method: PUSH
Cipher Suite: 0 (AES-128-CCM)
Derived values:
Auth Hash: 4d ee dd 7b ab 88 17 ec (SHA-256(token without "at")[:8])
Device Hash: ab 77 88 d2 2e b7 37 2f (SHA-256("sensor-01")[:8])
Flags: 0x00 (cipher 0, version 0, PUSH)
Headless inner frame (20 bytes):
ASCII: sensor-01|[temp:=32]
Hex: 73 65 6e 73 6f 72 2d 30 31 7c 5b 74 65 6d 70 3a 3d 33 32 5d
AEAD parameters:
Nonce (13B): 00 00 00 00 00 ab 77 88 d2 00 00 00 2a
(uses first 4 bytes of Device Hash)
AAD (21B): 00 00 00 00 2a 4d ee dd 7b ab 88 17 ec ab 77 88 d2 2e b7 37 2f
Output:
Ciphertext (20B): c8 c5 aa 56 d7 55 58 2b ac ea 13 bb 57 24 93 bb 8c b1 08 03
Auth Tag (8B): cf 82 6f db 83 3b 79 c6
Complete envelope (49 bytes):
00 00 00 00 2a 4d ee dd 7b ab 88 17 ec ab 77 88
d2 2e b7 37 2f c8 c5 aa 56 d7 55 58 2b ac ea 13
bb 57 24 93 bb 8c b1 08 03 cf 82 6f db 83 3b 79
c6
Breakdown: 1 (flags) + 4 (counter) + 8 (auth hash) + 8 (device hash) + 20 (ciphertext) + 8 (auth tag) = 49 bytes
```
> **Note:** This test vector uses a pre-provisioned Encryption Key. The derived key for these credentials (§2.4) would be `e5 05 f0 3c ...`, a different value, which confirms that key derivation is optional.
### 11.2 Downlink with Command
```
Server has queued command for sensor-01.
Server wraps response in envelope using sensor-01's Device Hash, Flags = 0x03 (ACK),
and a unique nonce seed.
Headless inner frame: CMD|ota=https://example.com/v2.1.bin
Device decrypts using its Encryption Key and the reconstructed nonce.
AEAD decryption verifies authenticity; device reads method=ACK from Flags,
parses STATUS=CMD, DETAIL=ota=https://example.com/v2.1.bin.
```
### 11.3 Encrypted Push: ChaCha20-Poly1305 (Conceptual)
```
Headless inner frame: sensor-01|[temp:=32] (20 bytes)
Counter: 1
Flags: 0x80 (cipher 4 = ChaCha20-Poly1305, version 0, PUSH)
Auth Hash: SHA-256("e2bd319014b24e0a8aca9f00aea4c0d0")[:8]
→ 4deedd7bab8817ec (8 bytes)
Device Hash: SHA-256("sensor-01")[:8] → 8 bytes (e.g., 0xab7788d22eb7372f)
Encryption Key: 32 bytes (pre-provisioned, ChaCha20 requires 256-bit key)
Step 1: Build Flags byte = 0x80 (cipher 4, version 0, PUSH)
Step 2: Build header (AAD) = [0x80] + [0x00000001] + [Auth Hash (8B)] + [Device Hash (8B)] → 21 bytes
Step 3: Nonce (12 bytes for ChaCha20) = 0x80 000000 ab7788d2 00000001
↑flags ↑ 3 zeros ↑ dev hash[:4] ↑ counter
Step 4: ChaCha20-Poly1305 encrypt (key, nonce, plaintext=20B inner frame, AAD=21B header)
→ 20 bytes ciphertext + 16 bytes auth tag
Step 5: Envelope = [Header (21B)] + [Ciphertext (20B)] + [Auth Tag (16B)]
Total: 1 + 4 + 8 + 8 + 20 + 16 = 57 bytes
```
---
## 12. Envelope Parsing
1. Read byte 0 as Flags. Extract cipher (bits 7-5), version (bits 4-3), and method (bits 2-0). Reject if version is unsupported, cipher suite is unsupported, or method is unknown.
2. Read bytes 1-4 as Counter (Big-Endian uint32)
3. Read bytes 5-12 as Authorization Hash
4. Read bytes 13-20 as Device Hash
5. Everything from byte 21 to end is the Ciphertext + Auth Tag
6. Look up the profile by Authorization Hash; retrieve the Encryption Key. Look up cipher suite parameters (key size, tag length, nonce length) from §3.1.
7. Identify candidate device(s) by (Authorization Hash, Device Hash) pair. Multiple devices may share the same Device Hash (see §2.2).
8. Reconstruct the nonce using the cipher suite's nonce length (see §3.3): `[Flags] [zero-padding] [Device Hash[:4]] [Counter]`. Only the first 4 bytes of the Device Hash are used in the nonce.
9. Decrypt with the cipher suite's AEAD algorithm using the Encryption Key, nonce, ciphertext + auth tag, and AAD = bytes 0-20 (the 21-byte header). The cipher verifies integrity automatically. If authentication fails and multiple candidate devices were identified in step 7, retry decryption with the next candidate's Encryption Key. If all candidates fail, discard the envelope and respond with `ACK|ERR|auth_failed`.
10. Verify that the SERIAL in the decrypted inner frame matches the device associated with the Encryption Key used for decryption. Reject the envelope if the SERIAL does not match.
11. Parse the decrypted data as a headless inner frame using the method from the Flags byte (see §4)
---
## 13. Size Comparison
| Format | Approximate Size | vs. HTTP/JSON |
|---|---|---|
| HTTP/JSON | ~487 bytes | baseline |
| TagoTiP | ~103 bytes | ~4.7× smaller |
| **TagoTiP/S** | **~110 bytes** | **~4.4× smaller** |
Example breakdown for a 103-byte TagoTiP frame:
```
Full TagoTiP frame (103 bytes):
PUSH|4deedd7bab8817ec|sensor-01|@1694567890000^batch_42[temperature:=32#F@=39.74,-104.99{source=dht22}]
Headless inner frame (81 bytes):
sensor-01|@1694567890000^batch_42[temperature:=32#F@=39.74,-104.99{source=dht22}]
(removed "PUSH|4deedd7bab8817ec|" = 22 bytes)
Envelope (AES-128-CCM): 1 (flags) + 4 (counter) + 8 (auth hash) + 8 (device hash) + 81 (ciphertext) + 8 (auth tag) = 110 bytes
Envelope (GCM/ChaCha20): 1 + 4 + 8 + 8 + 81 + 16 (auth tag) = 118 bytes
```
> **Note:** 29 bytes overhead with AES-128-CCM (8B tag), 37 bytes with GCM or ChaCha20-Poly1305 (16B tag).
---
## 14. Security Considerations
### 14.0 Threat Model
TagoTiP/S is designed to protect constrained IoT links against **passive eavesdropping**, **active message tampering**, and **replay attacks**. The following are explicitly **out of scope**: key exchange and PKI (keys are provisioned out-of-band), denial-of-service at the transport level, and side-channel attacks on endpoint implementations. The protocol assumes that the Encryption Key has been securely provisioned via an out-of-band mechanism (see §2.3) and that endpoint devices can perform AEAD operations correctly.
- The Encryption Key MUST be provisioned securely (e.g., during manufacturing or via a secure provisioning channel)
- The Encryption Key MUST NOT be transmitted over the wire
- The Authorization Hash is a truncated SHA-256 of the Authorization Token (without the `at` prefix). Because SHA-256 is preimage-resistant, the hash does not leak any bits of the original token. The Device Hash (first 8 bytes of SHA-256 of serial) is similarly protected. Neither hash compromises the Encryption Key
- All supported cipher suites provide **authenticated encryption** (AEAD): confidentiality and integrity in a single primitive. The authentication tag protects both the plaintext and the envelope header (via AAD). Tag length is 8 bytes for CCM suites and 16 bytes for GCM/ChaCha20-Poly1305 suites
- The AAD mechanism protects envelope header integrity. An attacker cannot modify the Flags, Counter, Authorization Hash, or Device Hash fields without causing authentication failure
- **Cipher suite downgrade protection:** The cipher suite is encoded in the Flags byte, which is integrity-protected via AAD. An attacker cannot downgrade the cipher suite without causing authentication failure
- The nonce is constructed as `[Flags (1B)] [zero-padding] [Device Hash[:4] (4B)] [Counter (4B)]` (see §3.3 for length), using the first 4 bytes of the 8-byte Device Hash from the header. Including the Flags byte guarantees that different cipher/method combinations produce distinct nonces even when the counter values coincide, preventing nonce reuse across directions and cipher suites. The Device Hash component keeps nonces unique per device even when multiple devices share the same encryption key and profile. When using a monotonic counter, the 4-byte counter field provides 2^32 unique nonces per device per direction; devices MUST re-provision before counter exhaustion. When using a random nonce (§8.4), nonce uniqueness is not guaranteed: the birthday paradox yields approximately 50% collision probability after ~77,000 messages. Devices with persistent storage MUST use a monotonic counter (see §8.4). Monotonic counters are RECOMMENDED for TagoTiP/S.
- The server SHOULD reject messages with a counter value ≤ the last accepted value to prevent replay attacks (when sequence counter enforcement is enabled)
- The Flags byte exposes the cipher suite, version, and method (PUSH, PULL, PING, or ACK) in the unencrypted header. This reveals the cipher in use and whether a device is sending data, requesting data, or performing a keepalive. However, the method is already largely inferrable from message direction (uplink vs. downlink) and envelope size in practice. The Flags byte is integrity-protected via AAD, preventing an attacker from altering the cipher suite, version, or method without detection.
### 14.1 Key Rotation (Non-Normative)
Key rotation is supported without protocol changes. When a device's Encryption Key is rotated, the server trial-decrypts with both old and new keys using the Device Hash collision mechanism (see §2.2 and §12 step 9).
A dedicated Key Epoch header field was considered and intentionally omitted:
- Trial decryption with 2 candidate keys is computationally cheap (microseconds on modern hardware)
- Adding 1 byte per message for a rare operation (key rotation) is a poor tradeoff for constrained links
- The collision-resolution mechanism in §12 step 9 already handles this case naturally
Implementations SHOULD support a configurable key rotation window (e.g., accept both old and new keys for a grace period after rotation). After the grace period, the server SHOULD remove the old key to reduce trial-decryption candidates.
### 14.2 Key Derivation Trade-offs (Non-Normative)
When using optional key derivation (§2.4), the Authorization Token becomes the sole secret for all devices in a profile. Compromising the token allows deriving Encryption Keys for every device that uses derived keys under that profile. Pre-provisioned keys provide per-device isolation: compromising one device's key does not affect others.
Deployments requiring per-device key isolation SHOULD use pre-provisioned keys.
---
## 15. License
This specification is **open source**, published under the [Apache License 2.0](https://github.com/tago-io/tagotip/blob/main/LICENSE).
Anyone is free to implement TagoTiP/S (clients, servers, libraries, gateways, or any other component) for any purpose, including commercial use, without requiring permission from TagoIO Inc. The Apache 2.0 license includes an express patent grant to all implementers.
The names "TagoTiP", "TagoTiP/S", and "TagoIO" are trademarks of TagoIO Inc. See [NOTICE](https://github.com/tago-io/tagotip/blob/main/NOTICE) for trademark details.
Copyright 2026 TagoIO Inc.
---
## TagoTiP over HTTP
:::tip[Tutorial]
Follow our step-by-step tutorial to connect your first device over HTTP: [TagoTiP HTTP: Quick Start](https://youtu.be/oZfrq5xFS5k)
:::
Standard HTTP you already know - `POST` to send, `GET` to retrieve, `HEAD` to ping. A single `Authorization` header and a clean REST-style API. Works through every firewall and proxy.
## Endpoint
| Region | Host | IP | Ports |
| --------- | ------------------------ | --------------- | --------------------------- |
| US-East-1 | `http.tip.us-e1.tago.io` | `52.223.14.189` | `80` (HTTP) / `443` (HTTPS) |
| EU-West-1 | `http.tip.eu-w1.tago.io` | `166.117.2.140` | `80` (HTTP) / `443` (HTTPS) |
Both ports accept TagoTiP and TagoTiP(s) via URL path.
:::info[Use HTTPS in production]
Port `443` (HTTPS) for production. Port `80` (HTTP) for development or when TLS is handled externally.
:::
## Why HTTP?
- **Universal** - every language and platform has an HTTP client
- **Firewall-friendly** - passes through proxies and load balancers
- **Familiar** - standard methods, headers, and status codes
- **Quick integration** - one `curl` command gets you started
## HTTP method mapping
| HTTP Method | TagoTiP Action | Purpose |
| ----------- | -------------- | ------------------------- |
| `POST` | PUSH | Send data |
| `GET` | PULL | Retrieve last values |
| `HEAD` | PING | Keepalive / poll commands |
## Arduino example (ESP32)
```cpp
#include
#include
const char* SSID = "your-wifi";
const char* PASSWORD = "your-password";
const char* TIP_URL = "http://http.tip.us-e1.tago.io/v1/tip/sensor-01"; // replace serial
const char* TOKEN_HASH = "4deedd7bab8817ec"; // replace with yours
void setup() {
Serial.begin(115200);
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED) delay(500);
Serial.println("WiFi connected");
}
void loop() {
if (WiFi.status() != WL_CONNECTED) return;
float temperature = analogRead(34) * 0.1; // example reading
char body[128];
snprintf(body, sizeof(body), "[temperature:=%.1f#C]", temperature);
char auth[64];
snprintf(auth, sizeof(auth), "TagoTiP %s", TOKEN_HASH);
HTTPClient http;
http.begin(TIP_URL);
http.addHeader("Authorization", auth);
http.addHeader("Content-Type", "text/plain");
int code = http.POST(body);
if (code == 200) {
Serial.print("Stored: ");
Serial.println(http.getString()); // "1"
} else {
Serial.print("Error: ");
Serial.println(code);
}
http.end();
delay(10000); // send every 10 seconds
}
```
## Quick test with curl
Replace `4deedd7bab8817ec` with your token hash and `sensor-01` with your serial.
### Push a temperature reading
```bash
curl -X POST https://http.tip.us-e1.tago.io/v1/tip/sensor-01 \
-H "Authorization: TagoTiP 4deedd7bab8817ec" \
-H "Content-Type: text/plain" \
-d '[temperature:=25.5#C]'
```
```
1
```
### Push multiple variables
```bash
curl -X POST https://http.tip.us-e1.tago.io/v1/tip/sensor-01 \
-H "Authorization: TagoTiP 4deedd7bab8817ec" \
-H "Content-Type: text/plain" \
-d '[temperature:=25.5#C;humidity:=60#%;active?=true]'
```
```
3
```
### Push with body-level defaults
```bash
curl -X POST https://http.tip.us-e1.tago.io/v1/tip/sensor-01 \
-H "Authorization: TagoTiP 4deedd7bab8817ec" \
-H "Content-Type: text/plain" \
-d '@=39.74,-104.99@1694567890000^batch_01{firmware=2.1}[temperature:=25.5#C;humidity:=60#%]'
```
### Push location data
```bash
curl -X POST https://http.tip.us-e1.tago.io/v1/tip/tracker-01 \
-H "Authorization: TagoTiP 4deedd7bab8817ec" \
-H "Content-Type: text/plain" \
-d '[position@=39.74,-104.99,1609;speed:=45.2#km/h@=39.74,-104.99]'
```
### Push raw payload (passthrough)
```bash
curl -X POST https://http.tip.us-e1.tago.io/v1/tip/sensor-01 \
-H "Authorization: TagoTiP 4deedd7bab8817ec" \
-H "Content-Type: text/plain" \
-d '>xDEADBEEF01020304'
```
Raw bytes are delivered to your device's [Payload Parser](/docs/tagoio/devices/payload-parser).
### Pull the last stored values
```bash
curl https://http.tip.us-e1.tago.io/v1/tip/sensor-01?variables=temperature,humidity \
-H "Authorization: TagoTiP 4deedd7bab8817ec"
```
```
[temperature:=25.5#C@1694567890000;humidity:=60#%@1694567890000]
```
### Ping (connectivity check + command polling)
```bash
curl -I https://http.tip.us-e1.tago.io/v1/tip/sensor-01 \
-H "Authorization: TagoTiP 4deedd7bab8817ec"
```
No command: `204 No Content`
When a command is pending:
```http
HTTP/1.1 200 OK
X-TagoTiP-CMD: reboot
```
## Endpoints
### `POST /v1/tip/{serial}` - Send Data
| | |
| ---------------- | ------------------------------------- |
| **Header** | `Authorization: TagoTiP ` |
| **Content-Type** | `text/plain` |
| **Body** | TagoTiP PUSH body |
| **Success** | `200 OK` - body is data point count |
### `GET /v1/tip/{serial}?variables=...` - Retrieve Data
| | |
| ----------- | ------------------------------------- |
| **Header** | `Authorization: TagoTiP ` |
| **Query** | `variables=var1,var2,...` |
| **Success** | `200 OK` - body is variable list |
### `HEAD /v1/tip/{serial}` - Keepalive / Commands
| | |
| ------------------- | ------------------------------------- |
| **Header** | `Authorization: TagoTiP ` |
| **No command** | `204 No Content` |
| **Pending command** | `200 OK` + `X-TagoTiP-CMD` header |
### `POST /v1/tips` - TagoTiP(s) (Encrypted)
| | |
| ---------------- | -------------------------- |
| **Content-Type** | `application/octet-stream` |
| **Body** | Binary TagoTiP(s) envelope |
No `Authorization` header. See [Encryption](/docs/tagotip/specification/encryption.md).
## Operators
| Operator | Type | Example |
| -------- | --------------------------------- | ------------------------- |
| `:=` | Number | `temperature:=25.5` |
| `=` | String | `status=online` |
| `?=` | Boolean | `active?=true` |
| `@=` | Location (lat,lng or lat,lng,alt) | `position@=39.74,-104.99` |
## Response codes
| HTTP Status | Meaning | Body |
| --------------------------- | ----------------------------- | ----------------------------- |
| `200 OK` | Success | PUSH: count. PULL: variables. |
| `204 No Content` | PING ok | - |
| `400 Bad Request` | Malformed body | `invalid_payload` |
| `401 Unauthorized` | Invalid auth | `invalid_token` |
| `404 Not Found` | Unknown device/variable | `device_not_found` |
| `413 Payload Too Large` | Body exceeds max payload size | `payload_too_large` |
| `429 Too Many Requests` | Rate limited | `rate_limited` |
| `500 Internal Server Error` | Server error | `server_error` |
## Limits
### Protocol limits
| Limit | Value |
| ------------------------- | ------------ |
| Max request body (wire) | 16,384 bytes |
| Max variables per request | 100 |
| Max metadata pairs | 32 |
| Variable name length | 100 chars |
| Unit length | 25 chars |
| Serial length | 100 chars |
RPM = requests per minute.
### Per-profile rate limits
| Resource | Scale | Starter | Free |
| ----------------------- | ----- | ------- | ---- |
| Uplink RPM (POST, HEAD) | 1,000 | 500 | 60 |
| Downlink RPM (GET) | 1,000 | 500 | 60 |
| Connections per IP | 20 | 10 | 3 |
### Per-device limits
| Resource | Default |
| ---------------- | ------- |
| Max payload size | 100 KB |
Unlike TCP/UDP, `HEAD` (PING) counts toward the uplink RPM on HTTP.
## Specification
For the complete protocol grammar, parsing rules, and ABNF, see the [TagoTiP Specification](/docs/tagotip/specification/tagotip-specification.md).
---
## TagoTiP over MQTT
:::tip[Tutorial]
Follow our step-by-step tutorial to connect your first device over MQTT: [TagoTiP MQTT: Quick Start](https://youtu.be/EHnG_P28MPE)
:::
**Publish/subscribe messaging** with QoS delivery guarantees. TagoTiP over MQTT maps the protocol to standard MQTT topics - the server pushes commands to your device in real time, and QoS levels handle delivery reliability at the transport layer.
## Endpoint
| Region | Host | IP | Ports |
| --------- | ------------------------ | ---------------- | ------------------------------ |
| US-East-1 | `mqtt.tip.us-e1.tago.io` | `15.197.247.146` | `1883` (MQTT) / `8883` (MQTTS) |
| EU-West-1 | `mqtt.tip.eu-w1.tago.io` | `166.117.88.178` | `1883` (MQTT) / `8883` (MQTTS) |
:::info[Use TLS in production]
Port `8883` (MQTTS) for production. Port `1883` (MQTT) for development or when TLS is handled externally. When using port `1883`, it is recommended to use the `$tips/` topic prefix (TagoTiP/S) for application-layer security: see [TagoTiP/S](#tagotips) below.
:::
## Why MQTT?
- **Pub/sub patterns** - topic-based routing with flexible subscriptions
- **QoS delivery** - at-most-once (0), at-least-once (1), or exactly-once (2)
- **Real-time commands** - server pushes `CMD` to the device's `ack` topic instantly
- **Intermittent connectivity** - designed for unreliable networks
## Authentication
The Authorization Hash (16 hex chars) is split across the MQTT CONNECT credentials:
| Field | Value | Example |
| -------- | --------------------------------------- | ---------- |
| Username | First 8 hex chars of Authorization Hash | `4deedd7b` |
| Password | Last 8 hex chars of Authorization Hash | `ab8817ec` |
The server reconstructs the full hash by concatenating username + password, then resolves the Account/Profile.
### Context isolation
All MQTT connections sharing the same credentials (derived from the same Authorization Token) belong to the same context. Any device in that context can publish or subscribe to any `$tip/{serial}/...` topic within it, enabling inter-device communication. If devices require isolation, use separate Authorization Tokens so each device operates in its own context.
## Topic structure
TagoTiP uses the `$tip/` prefix for all protocol traffic:
| Topic | Direction | Purpose |
| -------------------- | ---------------- | ---------------------- |
| `$tip/{serial}/push` | Device -> Server | Publish data |
| `$tip/{serial}/pull` | Device -> Server | Request last values |
| `$tip/{serial}/ack` | Server -> Device | Responses and commands |
The device serial is embedded in the topic path, so it does not appear in the payload.
The device **must subscribe** to `$tip/{serial}/ack` at connect time to receive responses and commands.
## TagoTiP/S {#tagotips}
TagoTiP/S provides application-layer encryption and is available on both ports. It is a good security option when not using TLS, for example, when connecting on port 1883.
To enable it, replace the `$tip/` topic prefix with `$tips/`:
| Topic | Direction | Purpose |
| --------------------- | ---------------- | ---------------------- |
| `$tips/{serial}/push` | Device -> Server | Publish data |
| `$tips/{serial}/pull` | Device -> Server | Request last values |
| `$tips/{serial}/ack` | Server -> Device | Responses and commands |
The payload format and all protocol semantics are identical to the standard `$tip/` topics. Only the prefix changes. The one exception is [downlink commands](#sending-commands-downlink), which are delivered on plain `$tip/` only.
:::tip[Enforce encrypted-only communication]
When creating the device in TagoIO, set **Protocol** to **"TagoTips only"**. The server will then reject any unencrypted traffic on `$tip/` topics for that device, so all communication goes through TagoTiP/S.
:::
## Payload format
The MQTT payload carries only the TagoTiP body, with an optional sequence counter prefix (`!N|`).
### PUSH (publish to `$tip/{serial}/push`)
Structured variables or passthrough:
```
[temp:=32#C;humidity:=65#%]
@=39.74,-104.99@1694567890000^batch_42[temp:=32#C]
>xDEADBEEF01020304
>b3q2+7wECAwQ=
!42|[temp:=32#C;humidity:=65#%]
```
### PULL (publish to `$tip/{serial}/pull`)
Comma-separated variable names:
```
temperature,humidity
!7|temperature,humidity
```
### ACK (received on `$tip/{serial}/ack`)
Status with optional detail and counter:
```
ACK|OK|3
ACK|!42|OK|2
ACK|OK|[temperature:=32#F@1694567890000;humidity:=65#%@1694567890000]
ACK|!7|OK|[temperature:=32#F@1694567890000]
ACK|ERR|invalid_payload
ACK|CMD|reboot
ACK|CMD|ota=https://example.com/v2.1.bin
```
When a sequence counter (`!N`) is present in the uplink, the server echoes it in the corresponding downlink so the device can correlate responses. Downlink commands are unsolicited and carry no counter.
## Arduino example (ESP32)
```cpp
#include
#include
const char* SSID = "your-wifi";
const char* PASSWORD = "your-password";
const char* MQTT_HOST = "mqtt.tip.us-e1.tago.io";
const int MQTT_PORT = 1883;
const char* MQTT_USER = "4deedd7b"; // first 8 hex chars of auth hash
const char* MQTT_PASS = "ab8817ec"; // last 8 hex chars of auth hash
const char* SERIAL_N = "sensor-01";
WiFiClient wifi;
PubSubClient mqtt(wifi);
char pushTopic[64];
char ackTopic[64];
void onMessage(char* topic, byte* payload, unsigned int length) {
char msg[256];
memcpy(msg, payload, min(length, sizeof(msg) - 1));
msg[min(length, sizeof(msg) - 1)] = '\0';
Serial.print("ACK: ");
Serial.println(msg);
// check for commands
if (strncmp(msg, "ACK|CMD|", 8) == 0) {
Serial.print("Command: ");
Serial.println(msg + 8);
}
}
void setup() {
Serial.begin(115200);
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED) delay(500);
Serial.println("WiFi connected");
snprintf(pushTopic, sizeof(pushTopic), "$tip/%s/push", SERIAL_N);
snprintf(ackTopic, sizeof(ackTopic), "$tip/%s/ack", SERIAL_N);
mqtt.setServer(MQTT_HOST, MQTT_PORT);
mqtt.setCallback(onMessage);
}
void reconnect() {
while (!mqtt.connected()) {
if (mqtt.connect("esp32-client", MQTT_USER, MQTT_PASS)) {
mqtt.subscribe(ackTopic, 1);
Serial.println("MQTT connected");
} else {
delay(5000);
}
}
}
void loop() {
if (!mqtt.connected()) reconnect();
mqtt.loop();
static unsigned long lastSend = 0;
if (millis() - lastSend > 10000) {
lastSend = millis();
float temperature = analogRead(34) * 0.1;
char payload[128];
snprintf(payload, sizeof(payload), "[temperature:=%.1f#C]", temperature);
mqtt.publish(pushTopic, payload, false);
}
}
```
## Quick test with mosquitto
Replace the username/password with your Authorization Hash halves and `sensor-01` with your serial. `mosquitto_rr` sends the message and waits for the response on the `ack` topic in a single connection.
### Push a temperature reading
```bash
mosquitto_rr -h mqtt.tip.us-e1.tago.io -p 1883 \
-u 4deedd7b -P ab8817ec \
-t '$tip/sensor-01/push' -e '$tip/sensor-01/ack' \
-m '[temperature:=25.5#C]'
```
### Push multiple variables
```bash
mosquitto_rr -h mqtt.tip.us-e1.tago.io -p 1883 \
-u 4deedd7b -P ab8817ec \
-t '$tip/sensor-01/push' -e '$tip/sensor-01/ack' \
-m '[temperature:=25.5#C;humidity:=60#%;active?=true]'
```
### Push raw payload (passthrough)
```bash
mosquitto_rr -h mqtt.tip.us-e1.tago.io -p 1883 \
-u 4deedd7b -P ab8817ec \
-t '$tip/sensor-01/push' -e '$tip/sensor-01/ack' \
-m '>xDEADBEEF01020304'
```
Raw bytes are delivered to your device's [Payload Parser](/docs/tagoio/devices/payload-parser).
### Pull the last stored values
```bash
mosquitto_rr -h mqtt.tip.us-e1.tago.io -p 1883 \
-u 4deedd7b -P ab8817ec \
-t '$tip/sensor-01/pull' -e '$tip/sensor-01/ack' \
-m 'temperature,humidity'
```
### Using TLS (port 8883)
```bash
mosquitto_rr -h mqtt.tip.us-e1.tago.io -p 8883 --capath /etc/ssl/certs \
-u 4deedd7b -P ab8817ec \
-t '$tip/sensor-01/push' -e '$tip/sensor-01/ack' \
-m '[temperature:=25.5#C]'
```
## Operators
| Operator | Type | Example |
| -------- | --------------------------------- | ------------------------- |
| `:=` | Number | `temperature:=25.5` |
| `=` | String | `status=online` |
| `?=` | Boolean | `active?=true` |
| `@=` | Location (lat,lng or lat,lng,alt) | `position@=39.74,-104.99` |
## Suffixes
Append after the value, in this order:
| Suffix | Prefix | Example |
| --------- | ------ | --------------------------------- |
| Unit | `#` | `temperature:=25.5#C` |
| Location | `@=` | `speed:=10@=39.74,-104.99` |
| Timestamp | `@` | `temperature:=25.5@1694567890000` |
| Group | `^` | `temperature:=25.5^batch_01` |
| Metadata | `{}` | `temperature:=25.5{source=dht22}` |
All combined: `temperature:=25.5#C@=39.74,-104.99@1694567890000^batch_01{source=dht22,quality=high}`
## Response codes
Received on the `$tip/{serial}/ack` topic:
| Response | Meaning |
| ----------------------------- | ----------------------------------------- |
| `ACK\|OK\|N` | `N` data points stored |
| `ACK\|OK\|[...]` | PULL response with variable data |
| `ACK\|CMD\|` | Server command (delivered asynchronously) |
| `ACK\|ERR\|invalid_token` | Invalid or expired credentials |
| `ACK\|ERR\|device_not_found` | Serial not found under your account |
| `ACK\|ERR\|invalid_payload` | Malformed payload |
| `ACK\|ERR\|invalid_seq` | Counter not greater than last accepted |
| `ACK\|ERR\|rate_limited` | Back off and retry |
| `ACK\|ERR\|payload_too_large` | Payload exceeds max size |
| `ACK\|ERR\|server_error` | Retry after a delay |
## Sending commands (downlink)
Send a command with the [TagoIO SDK](/docs/tagoio/analysis/sdk/nodejs-sdk). Construct the `TagoTiP` client with a [Service Authorization token](/docs/tagoio/integrations/general/authorization) and call `cmd()` with the device serial, protocol, and command body.
| Field | Type | Rules |
| ---------- | ------ | ------------------------------------------------------------- |
| `serial` | string | Device serial, 1-100 chars. May not contain `/`, `+`, or `#`. |
| `protocol` | string | Only `"mqtt"` is accepted. |
| `body` | string | The command. Non-empty UTF-8, max 15 KB (15,360 bytes). |
```js
const { TagoTiP } = require("@tago-io/sdk");
const tagoTiP = new TagoTiP({ token: "your-service-authorization-token" });
await tagoTiP.cmd({
serial: "sensor-01",
protocol: "mqtt",
body: "reboot",
});
```
The device (subscribed to `$tip/sensor-01/ack`) receives:
```
ACK|CMD|reboot
```
### Responses
| Status | Meaning |
| ------ | ------------------------------------------------------------------------------------------------------------------- |
| `200` | Command accepted for delivery. See best-effort note below. |
| `400` | `Authorization denied` (missing/invalid token), `Device can't be found`, or invalid `serial` / `protocol` / `body`. |
| `429` | Per-profile command rate limit exceeded. |
| `500` | The command could not be accepted. Try again. |
A `200` returns:
```json
{ "status": true, "result": "Command sent" }
```
:::warning[Best-effort delivery]
A `200` means the command was accepted, **not** that the device received it. If no matching device is connected at that moment, the command is not delivered - there is no delivery receipt, retry, or persistence.
:::
### Delivery scope and limits
- **Token-scoped**: the command reaches only clients authenticated with the **same authorization token** that owns the device. Two tokens under the same profile stay isolated from each other.
- **Serial case**: the ack topic `$tip/{serial}/ack` is matched verbatim and is case-sensitive. Firmware must subscribe with the **lowercase** serial for the command to arrive.
- **Plain `$tip` only**: downlink commands are not supported on the encrypted `$tips/` topics. A device set to **TagoTips only** rejects `$tip/` traffic, so it cannot receive downlink commands.
## Supported features
| Feature | Support |
| ------------------- | ------------------------------------------- |
| MQTT versions | 3.1, 5 |
| QoS levels | 0, 1, 2 |
| TLS | Yes (port 8883, recommended for production) |
| TagoTiP/S | Yes |
| Publish | Isolated per context |
| Subscribe | Isolated per context |
| Retain | No |
| Last Will | No |
| Persistent Sessions | No |
| Offline Messages | No |
## Limits
### Protocol limits
| Limit | Value |
| ---------------------------- | ------------ |
| Max topic length | 128 chars |
| Max payload size (wire) | 16,384 bytes |
| Max variables per payload | 100 |
| Max metadata pairs | 32 |
| Variable name length | 100 chars |
| Unit length | 25 chars |
| Serial length | 100 chars |
| Subscriptions per connection | 5 |
RPM = requests per minute.
### Per-profile rate limits
| Resource | Scale | Starter | Free |
| ------------------------------ | ----- | ------- | ---- |
| Uplink RPM (PUSH) | 1,000 | 500 | 60 |
| Downlink RPM (PULL) | 1,000 | 500 | 60 |
| Connections per IP | 20 | 10 | 3 |
| Active connections per profile | 500 | 50 | 10 |
| Subscription request RPM | 100 | 50 | 5 |
| Command RPM (`POST /tip/cmd`) | 1,000 | 500 | 60 |
### Per-device limits
| Resource | Scale | Starter | Free |
| ----------------------- | ------ | ------- | ------ |
| Max payload size | 100 KB | 100 KB | 100 KB |
| Connection TTL | 15 s | 10 s | 10 s |
| Keep-alive idle timeout | 5 s | 5 s | 5 s |
Keepalive is handled natively by MQTT's PINGREQ/PINGRESP mechanism and does not require a TagoTiP-level PING.
## Specification
For the complete protocol grammar, parsing rules, and ABNF, see the [TagoTiP Specification](/docs/tagotip/specification/tagotip-specification.md).
---
## TagoTiP over TCP
:::tip[Tutorial]
Follow our step-by-step tutorial to connect your first device over TCP: [TagoTiP TCP: Quick Start](https://youtu.be/cSMbQkPyrZo)
:::
**Guaranteed delivery** and **real-time commands**. TagoTiP over TCP ensures every data point arrives in order - and the server can push commands to your device the moment they are ready.
## Endpoint
| Region | Host | IP | Ports |
| --------- | ----------------------- | ---------------- | --------------------------------- |
| US-East-1 | `tcp.tip.us-e1.tago.io` | `75.2.126.170` | `5693` (plaintext) / `5694` (TLS) |
| EU-West-1 | `tcp.tip.eu-w1.tago.io` | `15.197.224.153` | `5693` (plaintext) / `5694` (TLS) |
Both ports accept TagoTiP and TagoTiP(s). The server detects the mode **once per connection** by inspecting the first byte.
## Why TCP?
- **Reliable delivery** - every frame is acknowledged
- **Persistent connections** - one connect, many frames
- **Real-time commands** - server pushes `CMD` frames instantly
- **Ordered data** - frames arrive in sequence
## How it works
```
Device TagoIO
| |
|── PUSH|hash|serial|[temp:=25]\n ──────────> |
|<──────── ACK|OK|1\n ─────────────────────── |
| |
|<──────── ACK|CMD|reboot\n ───────────────── | (server push)
```
The connection stays open. The server can push `CMD` frames at any time - no polling needed. Each frame **must** end with `\n` (byte `0x0A`).
## Arduino example (ESP32)
```cpp
#include
#include
const char* SSID = "your-wifi";
const char* PASSWORD = "your-password";
const char* TIP_HOST = "tcp.tip.us-e1.tago.io";
const int TIP_PORT = 5693;
const char* TOKEN_HASH = "4deedd7bab8817ec"; // replace with yours
const char* SERIAL_N = "sensor-01"; // replace with yours
WiFiClient client;
void setup() {
Serial.begin(115200);
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED) delay(500);
Serial.println("WiFi connected");
if (!client.connect(TIP_HOST, TIP_PORT)) {
Serial.println("Connection failed");
return;
}
Serial.println("Connected to TagoTiP TCP");
}
void loop() {
// reconnect if needed
if (!client.connected()) {
client.connect(TIP_HOST, TIP_PORT);
return;
}
float temperature = analogRead(34) * 0.1; // example reading
char frame[256];
snprintf(frame, sizeof(frame),
"PUSH|%s|%s|[temperature:=%.1f#C]\n",
TOKEN_HASH, SERIAL_N, temperature);
client.print(frame);
// read response
unsigned long timeout = millis() + 5000;
while (!client.available() && millis() < timeout) delay(10);
if (client.available()) {
String response = client.readStringUntil('\n');
Serial.println(response); // ACK|OK|1
// check for server-pushed commands
if (response.startsWith("ACK|CMD|")) {
String cmd = response.substring(8);
Serial.print("Command received: ");
Serial.println(cmd);
}
}
delay(10000); // send every 10 seconds
}
```
## Quick test with netcat
Replace `4deedd7bab8817ec` with your token hash and `sensor-01` with your serial.
### Push a temperature reading
```bash
echo 'PUSH|4deedd7bab8817ec|sensor-01|[temperature:=25.5#C]' \
| nc tcp.tip.us-e1.tago.io 5693
```
```
ACK|OK|1
```
### Interactive session
Open a persistent connection:
```bash
nc tcp.tip.us-e1.tago.io 5693
```
Type frames and see responses in real time:
```
PING|4deedd7bab8817ec|sensor-01
ACK|PONG
PUSH|4deedd7bab8817ec|sensor-01|[temperature:=25.5#C;humidity:=60#%]
ACK|OK|2
PULL|4deedd7bab8817ec|sensor-01|[temperature]
ACK|OK|[temperature:=25.5#C@1694567890000]
```
### Push a batch with timestamps (datalogger)
```bash
echo 'PUSH|4deedd7bab8817ec|logger-01|[temp:=25.5@1694567890000;temp:=26.1@1694567900000;temp:=25.8@1694567910000]' \
| nc tcp.tip.us-e1.tago.io 5693
```
```
ACK|OK|3
```
### Push raw payload (passthrough)
```bash
echo 'PUSH|4deedd7bab8817ec|sensor-01|>xDEADBEEF01020304' \
| nc tcp.tip.us-e1.tago.io 5693
```
Raw bytes are delivered to your device's [Payload Parser](/docs/tagoio/devices/payload-parser).
### Pull the last stored values
```bash
echo 'PULL|4deedd7bab8817ec|sensor-01|[temperature;humidity]' \
| nc tcp.tip.us-e1.tago.io 5693
```
```
ACK|OK|[temperature:=25.5#C@1694567890000;humidity:=60#%@1694567890000]
```
## Receiving commands
On a persistent connection, the server pushes commands instantly:
```
ACK|CMD|reboot
ACK|CMD|ota=https://example.com/v2.1.bin
```
No polling needed - just keep the connection open.
## Using TLS (port 5694)
```bash
echo 'PUSH|4deedd7bab8817ec|sensor-01|[temperature:=25.5#C]' \
| openssl s_client -connect tcp.tip.us-e1.tago.io:5694 -quiet
```
Same protocol, same frames - TLS is handled by the load balancer.
## Operators
| Operator | Type | Example |
| -------- | --------------------------------- | ------------------------- |
| `:=` | Number | `temperature:=25.5` |
| `=` | String | `status=online` |
| `?=` | Boolean | `active?=true` |
| `@=` | Location (lat,lng or lat,lng,alt) | `position@=39.74,-104.99` |
## Suffixes
Append after the value, in this order:
| Suffix | Prefix | Example |
| --------- | ------ | --------------------------------- |
| Unit | `#` | `temperature:=25.5#C` |
| Location | `@=` | `speed:=10@=39.74,-104.99` |
| Timestamp | `@` | `temperature:=25.5@1694567890000` |
| Group | `^` | `temperature:=25.5^batch_01` |
| Metadata | `{}` | `temperature:=25.5{source=dht22}` |
All combined: `temperature:=25.5#C@=39.74,-104.99@1694567890000^batch_01{source=dht22,quality=high}`
## Response codes
| Response | Meaning |
| ----------------------------- | -------------------------------------- |
| `ACK\|OK\|N` | `N` data points stored |
| `ACK\|OK\|[...]` | PULL response with variable data |
| `ACK\|PONG` | Keepalive acknowledged |
| `ACK\|CMD\|` | Server command (pushed at any time) |
| `ACK\|ERR\|invalid_token` | Invalid or expired token hash |
| `ACK\|ERR\|device_not_found` | Serial not found under your account |
| `ACK\|ERR\|invalid_payload` | Malformed frame or body |
| `ACK\|ERR\|invalid_seq` | Counter not greater than last accepted |
| `ACK\|ERR\|rate_limited` | Back off and retry |
| `ACK\|ERR\|payload_too_large` | Frame exceeds max payload size |
| `ACK\|ERR\|server_error` | Retry after a delay |
## Limits
### Protocol limits
| Limit | Value |
| ----------------------- | ------------ |
| Max frame size (wire) | 16,384 bytes |
| Max variables per frame | 100 |
| Max metadata pairs | 32 |
| Variable name length | 100 chars |
| Unit length | 25 chars |
| Serial length | 100 chars |
RPM = requests per minute.
### Per-profile rate limits
| Resource | Scale | Starter | Free |
| ------------------- | ----- | ------- | ---- |
| Uplink RPM (PUSH) | 1,000 | 500 | 60 |
| Downlink RPM (PULL) | 1,000 | 500 | 60 |
| Connections per IP | 20 | 10 | 3 |
### Per-device limits
| Resource | Scale | Starter | Free |
| ----------------------- | ------ | ------- | ------ |
| Max payload size | 100 KB | 100 KB | 100 KB |
| Connection TTL | 15 s | 10 s | 10 s |
| Keep-alive idle timeout | 5 s | 5 s | 5 s |
PING is exempt from rate limiting on TCP.
:::warning
Send a `PING` before the keep-alive idle timeout to keep the connection alive. The connection is closed after the TTL regardless of activity.
:::
## Specification
For the complete protocol grammar, parsing rules, and ABNF, see the [TagoTiP Specification](/docs/tagotip/specification/tagotip-specification.md).
---
## TagoTiP over UDP
:::tip[Tutorial]
Follow our step-by-step tutorial to connect your first device over UDP: [TagoTiP UDP: Quick Start](https://youtu.be/YhhHuCH7UZo)
:::
The **fastest path** from sensor to cloud. No connection setup, no handshake - a single datagram carries your data to TagoIO in as few as 60 bytes.
## Endpoint
| Region | Host | IP | Ports |
| --------- | ----------------------- | ----------------- | ---------------------------------------- |
| US-East-1 | `udp.tip.us-e1.tago.io` | `166.117.107.129` | `5683` (plaintext) / `5684` (TagoTiP(s)) |
| EU-West-1 | `udp.tip.eu-w1.tago.io` | `166.117.51.137` | `5683` (plaintext) / `5684` (TagoTiP(s)) |
## Why UDP?
- **Zero connection overhead** - no handshake, no teardown
- **Minimal power consumption** - ideal for battery-powered devices
- **Fire-and-forget** - send and move on
- **Tiny footprint** - a single `sprintf` builds your frame
## How it works
```
Device TagoIO
| |
|── PUSH|hash|serial|[temp:=25] ────────> |
|<──────── ACK|OK|1 ───────────────────── ──|
```
One datagram in, one datagram out. Each datagram carries exactly **one** frame. Trailing `\n` is optional.
## Arduino example (ESP32)
```cpp
#include
#include
const char* SSID = "your-wifi";
const char* PASSWORD = "your-password";
const char* TIP_HOST = "udp.tip.us-e1.tago.io";
const int TIP_PORT = 5683;
const char* TOKEN_HASH = "4deedd7bab8817ec"; // replace with yours
const char* SERIAL_N = "sensor-01"; // replace with yours
WiFiUDP udp;
void setup() {
Serial.begin(115200);
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED) delay(500);
Serial.println("WiFi connected");
}
void loop() {
float temperature = analogRead(34) * 0.1; // example reading
char frame[256];
snprintf(frame, sizeof(frame),
"PUSH|%s|%s|[temperature:=%.1f#C]",
TOKEN_HASH, SERIAL_N, temperature);
udp.beginPacket(TIP_HOST, TIP_PORT);
udp.print(frame);
udp.endPacket();
// read response (optional for fire-and-forget)
delay(100);
int len = udp.parsePacket();
if (len > 0) {
char buf[128];
udp.read(buf, sizeof(buf) - 1);
buf[len] = '\0';
Serial.println(buf); // ACK|OK|1
}
delay(10000); // send every 10 seconds
}
```
## Quick test with netcat
Replace `4deedd7bab8817ec` with your token hash and `sensor-01` with your serial.
### Push a temperature reading
```bash
echo -n 'PUSH|4deedd7bab8817ec|sensor-01|[temperature:=25.5#C]' \
| nc -u -w1 udp.tip.us-e1.tago.io 5683
```
```
ACK|OK|1
```
### Push multiple variables
```bash
echo -n 'PUSH|4deedd7bab8817ec|sensor-01|[temperature:=25.5#C;humidity:=60#%;active?=true]' \
| nc -u -w1 udp.tip.us-e1.tago.io 5683
```
```
ACK|OK|3
```
### Push location data
```bash
echo -n 'PUSH|4deedd7bab8817ec|tracker-01|[position@=39.74,-104.99,1609;speed:=45.2#km/h@=39.74,-104.99]' \
| nc -u -w1 udp.tip.us-e1.tago.io 5683
```
### Push raw payload (passthrough)
```bash
echo -n 'PUSH|4deedd7bab8817ec|sensor-01|>xDEADBEEF01020304' \
| nc -u -w1 udp.tip.us-e1.tago.io 5683
```
Raw bytes are delivered to your device's [Payload Parser](/docs/tagoio/devices/payload-parser).
### Pull the last stored value
```bash
echo -n 'PULL|4deedd7bab8817ec|sensor-01|[temperature]' \
| nc -u -w1 udp.tip.us-e1.tago.io 5683
```
```
ACK|OK|[temperature:=25.5#C@1694567890000]
```
### Ping (connectivity check + command polling)
```bash
echo -n 'PING|4deedd7bab8817ec|sensor-01' \
| nc -u -w1 udp.tip.us-e1.tago.io 5683
```
```
ACK|PONG
```
When a command is pending:
```
ACK|CMD|reboot
```
## Operators
| Operator | Type | Example |
| -------- | --------------------------------- | ------------------------- |
| `:=` | Number | `temperature:=25.5` |
| `=` | String | `status=online` |
| `?=` | Boolean | `active?=true` |
| `@=` | Location (lat,lng or lat,lng,alt) | `position@=39.74,-104.99` |
## Suffixes
Append after the value, in this order:
| Suffix | Prefix | Example |
| --------- | ------ | --------------------------------- |
| Unit | `#` | `temperature:=25.5#C` |
| Location | `@=` | `speed:=10@=39.74,-104.99` |
| Timestamp | `@` | `temperature:=25.5@1694567890000` |
| Group | `^` | `temperature:=25.5^batch_01` |
| Metadata | `{}` | `temperature:=25.5{source=dht22}` |
All combined: `temperature:=25.5#C@=39.74,-104.99@1694567890000^batch_01{source=dht22,quality=high}`
## Response codes
| Response | Meaning |
| ----------------------------- | -------------------------------------- |
| `ACK\|OK\|N` | `N` data points stored |
| `ACK\|OK\|[...]` | PULL response with variable data |
| `ACK\|PONG` | Keepalive acknowledged |
| `ACK\|CMD\|` | Server command (via PING response) |
| `ACK\|ERR\|invalid_token` | Invalid or expired token hash |
| `ACK\|ERR\|device_not_found` | Serial not found under your account |
| `ACK\|ERR\|invalid_payload` | Malformed frame or body |
| `ACK\|ERR\|invalid_seq` | Counter not greater than last accepted |
| `ACK\|ERR\|rate_limited` | Back off and retry |
| `ACK\|ERR\|payload_too_large` | Frame exceeds max payload size |
| `ACK\|ERR\|server_error` | Retry after a delay |
## Limits
### Protocol limits
| Limit | Value |
| ----------------------- | ------------ |
| Max frame size (wire) | 16,384 bytes |
| Max variables per frame | 100 |
| Max metadata pairs | 32 |
| Variable name length | 100 chars |
| Unit length | 25 chars |
| Serial length | 100 chars |
:::tip
Keep datagrams under ~1,400 bytes to avoid IP fragmentation.
:::
RPM = requests per minute.
### Per-profile rate limits
| Resource | Scale | Starter | Free |
| ------------------- | ----- | ------- | ---- |
| Uplink RPM (PUSH) | 1,000 | 500 | 60 |
| Downlink RPM (PULL) | 1,000 | 500 | 60 |
### Per-device limits
| Resource | Default |
| ---------------- | ------- |
| Max payload size | 100 KB |
PING is exempt from rate limiting on UDP.
## Specification
For the complete protocol grammar, parsing rules, and ABNF, see the [TagoTiP Specification](/docs/tagotip/specification/tagotip-specification.md).