Private beta · free during the beta
An MQTT broker with the ingestion pipeline and storage already behind it.
Give each device its own credential, publish JSON over MQTT with TLS, and read back what arrived from the dashboard or the REST API. We run the broker, the durable pipeline, and the time-series database.
Any MQTT 5 or 3.1.1 client works: ESP32, Raspberry Pi, Linux gateways, Python, Node.js, Go, or mosquitto_pub.
$ mosquitto_pub -h mqtt.iotaps.com -p 8883 \
--capath /etc/ssl/certs -V mqttv5 -q 1 \
-i "$DEVICE_ID" -u "$CREDENTIAL_ID" -P "$SECRET" \
-t "v1/$ORG/$PROJECT/$DEVICE_ID/telemetry" \
-m '{"schema_version":1,"message_id":"m-0001",
"metrics":{"temperature_c":21.5}}'
# a moment later, from the API behind the dashboard
GET /api/v1/organizations/…/devices/…/latest
{
"device_id": "01a10e7f-efd6-76fb-b2a7-3c41d0a95e12",
"last_message_id": "m-0001",
"message_count": 1,
"metrics": {
"temperature_c": {
"value": 21.5,
"received_at": "2026-10-05T10:15:30.412Z"
}
}
}
What happens to a message
Each step is a checkpoint with its own metric. A PUBACK tells your device the broker has the message; it does not yet mean the data is stored.
-
Broker-acknowledged
The broker checks the credential, the client ID, and the topic, then sends PUBACK for QoS 1.
-
Platform-accepted
Ingestion validates the envelope and fsyncs it to a durable stream before it acknowledges the broker delivery.
-
Persisted
One database transaction writes the event, latest state, and usage counters. A repeated
message_idstops here. -
Live
The committed event is pushed to open dashboards over Server-Sent Events. History stays the source of truth.
What is in it
Everything listed here works today. Things that do not exist yet are on the roadmap.
- Per-device credentials
- The client ID is the device ID and the username is the credential ID. Secrets are shown once and stored as hashes. Up to five active credentials per device, so rotation needs no downtime.
- Topic-level access control
- A device can publish only to its own topic and cannot subscribe. Revoking a credential disconnects its live sessions; in our tests that took under a second.
- Durable ingestion
- Messages are written to a durable stream before the broker delivery is acknowledged. Retries with the same
message_idare stored once within seven days. - Explicit rejections
- A payload that breaks the contract is rejected with one of twelve reason codes, never truncated or coerced, and shows up on the device page.
- History and aggregates
- Raw points for ranges up to 24 hours; min, average, and max buckets for up to 30 days. History is kept for 30 days during the beta.
- Live device view
- Latest values, charts, recent messages, and rejections on one page, updated as data is committed.
- Organizations and roles
- Owner, Admin, Developer, and Viewer roles; sign-in through OpenID Connect; row-level security scopes every query to one organization.
- Usage and audit log
- Daily counts of persisted, duplicate, and rejected messages per project, and a log of who changed devices, credentials, and members.
Connect a device
One topic per device and a small, versioned JSON envelope. The dashboard fills in the IDs for you when you create a credential.
mosquitto_pub -h mqtt.iotaps.com -p 8883 --capath /etc/ssl/certs \
-V mqttv5 -q 1 -i "$DEVICE_ID" -u "$CREDENTIAL_ID" -P "$SECRET" \
-t "v1/$ORG_ID/$PROJECT_ID/$DEVICE_ID/telemetry" \
-m '{"schema_version":1,"message_id":"m-0001",
"metrics":{"temperature_c":21.5}}'
import json, uuid
import paho.mqtt.client as mqtt # paho-mqtt 2.x
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id=DEVICE_ID, protocol=mqtt.MQTTv5)
client.username_pw_set(CREDENTIAL_ID, SECRET)
client.tls_set() # system CA store
client.connect("mqtt.iotaps.com", 8883)
client.loop_start()
message = {"schema_version": 1, "message_id": str(uuid.uuid4()),
"metrics": {"temperature_c": 21.5, "fan_on": True}}
client.publish(TOPIC, json.dumps(message), qos=1).wait_for_publish()
import mqtt from "mqtt"; // mqtt.js 5.x
import { randomUUID } from "node:crypto";
const client = mqtt.connect("mqtts://mqtt.iotaps.com:8883", {
clientId: DEVICE_ID, username: CREDENTIAL_ID, password: SECRET,
protocolVersion: 5,
});
client.on("connect", () => {
const message = { schema_version: 1, message_id: randomUUID(),
metrics: { temperature_c: 21.5 } };
client.publish(TOPIC, JSON.stringify(message), { qos: 1 },
() => client.end());
});
Limits and test results
Limits are part of the contract. The last two rows come from our own failure tests while hardening the beta, not from a load test of your workload.
- Payload size
- 64 KiB per message
- Metrics per message
- 64, numbers or booleans
- Deduplication window
- 7 days, on device ID +
message_id - History retention
- 30 days during the beta
- Revocation
- Live session closed in under 1 s after a credential is revoked
- Restarts
- No lost or duplicated messages across processor, database, and Redis restarts
Where it stands
IoTAPS is in private beta. This is what you can use, what is being built, and what comes later.
Works today
- Device and credential lifecycle
- MQTT over TLS, durable ingestion
- History, aggregates, live view
- Organizations, roles, invitations
- Usage counters and audit log
Being built
- Threshold and no-data alerts
- Email and signed webhooks
- Configurable dashboards
- CSV export
Later
- Commands to devices
- Fleet provisioning with claim codes
- API keys for server-to-server access
- Client spaces for integrators
Questions
Something else? Write to hello@iotaps.com.
Is this a broker, a database, or a dashboard?
All three, run as one service: a TLS MQTT broker with per-device access control, an ingestion pipeline, a time-series database, and a web dashboard and REST API on top of it.
What happens to messages when something fails?
Accepted messages wait in a durable stream until they are committed. Processing resumes after an outage, redeliveries are deduplicated by message_id, and messages that can never be stored go to a dead-letter queue for review instead of being dropped.
How is our data kept apart from other customers?
The broker limits each credential to its own topic, the API checks your membership on every request, and row-level security in the database scopes every query to one organization.
Can we get our data out?
Yes. History, latest state, and recent messages are available through the REST API today. CSV export is being built.
What will it cost?
Nothing during the beta. Before paid plans start we will publish what each plan includes and the price of usage beyond it, and give beta users notice. See pricing.
Try it with your own devices
We onboard beta teams one at a time. Tell us what you are connecting.