Skip to main content

TagoTiP over MQTT

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

HostIPPorts
mqtt.tip.us-e1.tago.io15.197.247.1461883 (MQTT) / 8883 (MQTTS)

See Endpoints for all regions.

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

FieldValueExample
UsernameFirst 8 hex chars of Authorization Hash4deedd7b
PasswordLast 8 hex chars of Authorization Hashab8817ec

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:

TopicDirectionPurpose
$tip/{serial}/pushDevice -> ServerPublish data
$tip/{serial}/pullDevice -> ServerRequest last values
$tip/{serial}/ackServer -> DeviceResponses 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

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/:

TopicDirectionPurpose
$tips/{serial}/pushDevice -> ServerPublish data
$tips/{serial}/pullDevice -> ServerRequest last values
$tips/{serial}/ackServer -> DeviceResponses and commands

The payload format and all protocol semantics are identical to the standard $tip/ topics — only the prefix changes.

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, ensuring 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#%]
@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:

OK|3
!42|OK|2
OK|[temperature:=32#F@1694567890000;humidity:=65#%@1694567890000]
!7|OK|[temperature:=32#F@1694567890000]
ERR|invalid_payload
CMD|reboot
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.

Arduino example (ESP32)

#include <WiFi.h>
#include <PubSubClient.h>

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, "CMD|", 4) == 0) {
Serial.print("Command: ");
Serial.println(msg + 4);
}
}

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

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

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)

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.

Pull the last stored values

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)

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

OperatorTypeExample
:=Numbertemperature:=25.5
=Stringstatus=online
?=Booleanactive?=true
@=Location (lat,lng or lat,lng,alt)position@=39.74,-104.99

Suffixes

Append after the value, in this order:

SuffixPrefixExample
Unit#temperature:=25.5#C
Timestamp@temperature:=25.5@1694567890000
Group^temperature:=25.5^batch_01
Metadata{}temperature:=25.5{source=dht22}

All combined: temperature:=25.5#C@1694567890000^batch_01{source=dht22,quality=high}

Response codes

Received on the $tip/{serial}/ack topic:

ResponseMeaning
OK|NN data points stored
OK|[...]PULL response with variable data
CMD|<command>Server command (delivered asynchronously)
ERR|invalid_tokenInvalid or expired credentials
ERR|device_not_foundSerial not found under your account
ERR|invalid_payloadMalformed payload
ERR|invalid_seqCounter not greater than last accepted
ERR|rate_limitedBack off and retry
ERR|payload_too_largePayload exceeds max size
ERR|server_errorRetry after a delay

Supported features

FeatureSupport
MQTT versions3.1, 5
QoS levels0, 1, 2
TLSYes (port 8883, recommended for production)
TagoTiP/SYes
PublishIsolated per context
SubscribeIsolated per context
RetainNo
Last WillNo
Persistent SessionsNo
Offline MessagesNo

Limits

Protocol limits

LimitValue
Max topic length128 chars
Max payload size (wire)16,384 bytes
Max variables per payload100
Max metadata pairs32
Variable name length100 chars
Unit length25 chars
Serial length100 chars
Subscriptions per connection5

RPM = requests per minute.

Per-profile rate limits

ResourceScaleStarterFree
Uplink RPM (PUSH)1,00050060
Downlink RPM (PULL)1,00050060
Connections per IP100205
Active connections per profile5005010
Subscription request RPM100505

Per-device limits

ResourceDefault
Max payload size100 KB
Connection TTL60 s
Keep-alive idle timeout20 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.