Skip to main content

TagoTiP over TCP

:::tip Tutorial Follow our step-by-step tutorial to connect your first device over TCP: TagoTiP TCP — Quick Start :::

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

RegionHostIPPortsUS-East-1tcp.tip.us-e1.tago.io75.2.126.1705693 (plaintext) / 5694 (TLS)EU-West-1tcp.tip.eu-w1.tago.io15.197.224.1535693 (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?

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)

#include <WiFi.h>
#include <WiFiClient.h>

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

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:

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)

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)

echo 'PUSH|4deedd7bab8817ec|sensor-01|>xDEADBEEF01020304' \
| nc tcp.tip.us-e1.tago.io 5693

Raw bytes are delivered to your device's Payload Parser.

Pull the last stored values

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)

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

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:

SuffixPrefixExampleUnit#temperature:=25.5#CLocation@=speed:=10@=39.74,-104.99Timestamp@temperature:=25.5@1694567890000Group^temperature:=25.5^batch_01Metadata{}temperature:=25.5{source=dht22}

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

Response codes

ResponseMeaningACK|OK|NN data points storedACK|OK|[...]PULL response with variable dataACK|PONGKeepalive acknowledgedACK|CMD|<command>Server command (pushed at any time)ACK|ERR|invalid_tokenInvalid or expired token hashACK|ERR|device_not_foundSerial not found under your accountACK|ERR|invalid_payloadMalformed frame or bodyACK|ERR|invalid_seqCounter not greater than last acceptedACK|ERR|rate_limitedBack off and retryACK|ERR|payload_too_largeFrame exceeds max payload sizeACK|ERR|server_errorRetry after a delay

Limits

Protocol limits

LimitValueMax frame size (wire)16,384 bytesMax variables per frame100Max metadata pairs32Variable name length100 charsUnit length25 charsSerial length100 chars

RPM = requests per minute.

Per-profile rate limits

ResourceScaleStarterFreeUplink RPM (PUSH)1,00050060Downlink RPM (PULL)1,00050060Connections per IP20103

Per-device limits

ResourceScaleStarterFreeMax payload size100 KB100 KB100 KBConnection TTL15 s10 s10 sKeep-alive idle timeout5 s5 s5 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.