Aquairi Public API

Connect your IoT devices to the Aquairi platform. Send sensor data, water parameters, and photos from any internet-connected aquarium hardware.

Base URL: https://iot.aquairi.appVersion: 1.0Protocol: REST / HTTPS

Getting Started

1

Create an Aquairi Account

Sign up at aquairi.app and create your aquarium profile.

2

Register a Device

In the Aquairi mobile app, go to your aquarium settings and add a new device. Choose the device type that matches your hardware (thermometer, pH meter, camera, etc.).

3

Get Your Device Token

After registering the device, you will receive a device API token. This token is tied to a specific device and aquarium. Store it securely.

Device Token
aqk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0...
4

Start Sending Data

Use the token in the Authorization header of every request:

cURL
curl -X POST https://iot.aquairi.app/sensor-data \
  -H "Authorization: Bearer aqk_your_device_token" \
  -H "Content-Type: application/json" \
  -d '{"temperature": 25.5, "ph": 7.2}'
Base URL: https://iot.aquairi.app|Protocol: HTTPS (REST)|Content-Type: application/json

Authentication

All API endpoints (except /health) require a valid device token in the Authorization header.

Token Format

Header
Authorization: Bearer <device_token>

How It Works

  1. Your device sends the token in the Authorization header.
  2. The API validates the token against the Aquairi backend.
  3. Valid tokens are cached for 5 minutes to reduce latency.
  4. The API checks that the device has the required capability for the endpoint.
  5. If the token is invalid or the device lacks the capability, the request is rejected.

Device Payload

When a token is validated, the following information is extracted:

typescript
interface DevicePayload {
  deviceId: string;      // Unique device identifier
  aquariumId: string;    // Aquarium this device belongs to
  userId: string;        // Owner of the device
  deviceType: string;    // e.g., "thermometer", "ph_meter"
  capabilities: string[]; // e.g., ["send_parameters"]
}

Capabilities

CapabilityRequired ForDescription
send_parametersPOST /parameters, GET /parameters/latestSend and read water parameters
send_sensor_dataPOST /sensor-dataSubmit sensor readings to event history
upload_photoPOST /photosUpload aquarium photos

Endpoints Reference

GET/health

Check that the API is running. No authentication required.

Response

json
{
  "status": "ok",
  "service": "public-bff",
  "timestamp": "2026-03-01T12:00:00.000Z"
}

Code Examples

bash
curl https://iot.aquairi.app/health
POST/parameters

Log water parameters for the aquarium linked to the device.

Required capability: send_parameters

Request Body

json
{
  "temperature": 25.5,
  "humidity": 60
}

Response

json
{ "success": true }

Code Examples

bash
curl -X POST https://iot.aquairi.app/parameters \
  -H "Authorization: Bearer aqk_your_token" \
  -H "Content-Type: application/json" \
  -d '{"temperature": 25.5, "humidity": 60}'
GET/parameters/latest

Retrieve the most recent water parameters for the device's aquarium.

Required capability: send_parameters

Response

json
{
  "temperature": 25.5,
  "ph": 7.2,
  "tds": 300,
  "timestamp": "2026-03-01T12:00:00.000Z"
}

Code Examples

bash
curl https://iot.aquairi.app/parameters/latest \
  -H "Authorization: Bearer aqk_your_token"
POST/sensor-data

Submit a sensor reading to the aquarium's event history. The data is automatically enriched with the device's ID and type.

Required capability: send_sensor_data

Request Body

json
{
  "temperature": 25.5
}

Response

json
{ "success": true }

Code Examples

bash
curl -X POST https://iot.aquairi.app/sensor-data \
  -H "Authorization: Bearer aqk_your_token" \
  -H "Content-Type: application/json" \
  -d '{"temperature": 25.5}'
POST/photos

Upload an aquarium photo from a camera device. Files are sent as multipart form data. Maximum file size: 10 MB.

Required capability: upload_photo

Parameters

FieldTypeRequiredDescription
fileFileYesImage file (JPEG, PNG). Max 10 MB.

Response

json
{
  "url": "https://cdn.aquairi.app/photos/aqua-abc123/photo.jpg"
}

Code Examples

bash
curl -X POST https://iot.aquairi.app/photos \
  -H "Authorization: Bearer aqk_your_token" \
  -F "file=@/path/to/photo.jpg"

Device Types & Data Schemas

Each device type has a specific data schema that defines which fields are required and which are optional. Unknown fields are silently stripped.

Thermometer

thermometer

Temperature sensor with optional humidity

Required:temperature
Optional:humidity

pH Meter

ph_meter

pH sensor with optional temperature

Required:ph
Optional:temperature

TDS Meter

tds_meter

Total Dissolved Solids sensor

Required:tds
Optional:conductivitysalinity

Light Sensor

light_sensor

Light intensity sensor

Required:light
Optional:uvIndex

Camera

camera

Aquarium camera for photo uploads

No sensor data fields. Use POST /photos to upload images.

Multi-Sensor

multi_sensor

Combined sensor board with multiple readings

Optional:temperaturephtdslightammonianitritenitrate

Generic

generic

Custom / DIY device (up to 20 fields)

Optional:temperaturephtdslighthumidityammonianitritenitratesalinityconductivityuvIndexdissolvedOxygenco2pressureflowRatewaterLevelturbidityorpalkalinitycalcium

Error Handling

All error responses follow a consistent format:

TypeScript Interface
interface ErrorResponse {
  success: false;
  statusCode: number;
  code?: string;        // Machine-readable error code
  message: string;      // Human-readable description
  errors?: string[];    // Validation error details
  timestamp: string;    // ISO 8601 timestamp
  path: string;         // Request path
}

Example Error Response

json
{
  "success": false,
  "statusCode": 400,
  "code": "DEVICE-VAL-001",
  "message": "Invalid device data",
  "errors": [
    "Missing required field: temperature"
  ],
  "timestamp": "2026-03-01T12:00:00.000Z",
  "path": "/parameters"
}

Error Code Reference

CodeStatusDescription
DEVICE-AUTH-001401Authorization token is missing
DEVICE-AUTH-002401Token is invalid or expired
DEVICE-AUTH-003401Token has been revoked
DEVICE-AUTH-004403Device lacks required capability
DEVICE-VAL-001400Sensor data validation failed
HTTP-VAL-400400Generic validation error
HTTP-RATE-429429Rate limit exceeded
HTTP-THROTTLE-409409Write interval too frequent (min 5 minutes)
HTTP-SRV-500500Internal server error

Rate Limits & Write Interval

5-Minute Write Interval

All data write operations are throttled to a minimum interval of 5 minutes per device per channel. This applies to both REST API and MQTT.

OperationMin IntervalRejection
POST /parameters5 minutes409 Conflict
POST /sensor-data5 minutes409 Conflict
MQTT parameters5 minutesSilently dropped
MQTT telemetry5 minutesSilently dropped
GET /parameters/latestNo limit
POST /photosNo limit

Why 5 Minutes?

Aquarium parameters change slowly. A 5-minute interval provides optimal data granularity while keeping storage costs low and dashboards responsive. For most setups this means ~288 readings per day per device.

Handling 409 Responses

If you send data more frequently than every 5 minutes via REST, the API returns 409 Conflict. Your device should handle this gracefully:

REST APICheck response status — skip on 409
MQTTFire-and-forget — broker drops excess
Best practiceSet device loop to delay(300000) (5 min)

Read Endpoints

Read endpoints (GET /parameters/latest, GET /health) have no throttle. Poll as needed, though we recommend aligning reads with the 5-minute write cadence.

MQTT Protocol

The Aquairi IoT platform supports MQTT for low-overhead, persistent communication. Ideal for microcontrollers (ESP32, Arduino) and constrained devices.

Connection Details

SettingValue
Brokeriot.aquairi.app
TCP Port1883
WebSocket Port9001
ProtocolMQTT 3.1.1
QoS1 (at least once)
Client IDYour device token

Topic Structure

All topics follow the format:

text
aquairi/devices/{device_token}/{channel}
ChannelDirectionDescriptionDestination
parametersDevice → CloudWater quality data (pH, temp, ammonia, etc.)Aquariums Service → parameters collection
telemetryDevice → CloudRaw sensor readings (light, CO₂, flow, etc.)History Service → sensor_reading event

Authentication

The device token (aqk_...) is embedded in the topic path. The broker validates it against the device registry with a 5-minute validation cache. No username/password needed — the token IS the auth.

Write Interval

Minimum interval: 5 minutes. Messages published more frequently than every 5 minutes per device per channel are silently dropped. This protects the platform and ensures data quality.

Examples

Publish Parameters (mosquitto_pub)

bash
mosquitto_pub -h iot.aquairi.app -p 1883 \
  -t "aquairi/devices/aqk_your_token/parameters" \
  -m '{"temperature": 25.5, "ph": 7.2}' \
  -q 1

Publish Telemetry (mosquitto_pub)

bash
mosquitto_pub -h iot.aquairi.app -p 1883 \
  -t "aquairi/devices/aqk_your_token/telemetry" \
  -m '{"temperature": 25.5, "light": 1250}' \
  -q 1

Arduino / ESP32

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

const char* BROKER = "iot.aquairi.app";
const char* TOKEN  = "aqk_your_token";

WiFiClient wifi;
PubSubClient mqtt(wifi);

void setup() {
  mqtt.setServer(BROKER, 1883);
}

void loop() {
  if (!mqtt.connected()) mqtt.connect(TOKEN);
  mqtt.loop();

  String topic = String("aquairi/devices/")
    + TOKEN + "/parameters";
  String payload = "{\"temperature\":" +
    String(readTemp()) + "}";

  mqtt.publish(topic.c_str(), payload.c_str());
  delay(300000); // 5 minutes
}

Python (paho-mqtt)

python
import paho.mqtt.client as mqtt
import json, time

TOKEN  = "aqk_your_token"
BROKER = "iot.aquairi.app"
TOPIC  = f"aquairi/devices/{TOKEN}/parameters"

client = mqtt.Client(client_id=TOKEN)
client.connect(BROKER, 1883)

while True:
    payload = json.dumps({
        "temperature": read_temp(),
        "ph": read_ph(),
    })
    client.publish(TOPIC, payload, qos=1)
    time.sleep(300)  # 5 minutes

Payload Format

JSON object with sensor fields matching the device type schema (see Device Types). Unknown fields are ignored. Values are validated server-side before storage.

json
// parameters channel
{ "temperature": 25.5, "ph": 7.2, "ammonia": 0.02 }

// telemetry channel
{ "temperature": 25.5, "light": 1250, "uvIndex": 3 }

Best Practices

Send Data at Reasonable Intervals

For most aquarium monitoring, sending data every 1-5 minutes is sufficient. Do not flood the API with sub-second requests.

Implement Retry with Exponential Backoff

Retry transient errors (5xx, 429) with increasing delays. Never retry auth errors.

typescript
async function sendWithRetry(
  url: string,
  data: Record<string, number>,
  token: string,
  maxRetries = 3,
): Promise<void> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const res = await fetch(url, {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${token}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(data),
    });

    if (res.ok) return;

    // Don't retry auth errors
    if (res.status === 401 || res.status === 403) {
      throw new Error("Auth failed");
    }

    // Retry server errors with backoff
    if (res.status >= 500 || res.status === 429) {
      const delay = Math.pow(2, attempt) * 1000;
      await new Promise(r => setTimeout(r, delay));
      continue;
    }

    throw new Error(`Request failed: ${res.status}`);
  }

  throw new Error("Max retries exceeded");
}

Store Tokens Securely

On ESP32/Arduino, use the Preferences library or encrypted NVS. On Raspberry Pi, use environment variables. Never hardcode tokens in public repositories.

Validate Data Before Sending

Check that sensor readings are within reasonable ranges. This prevents corrupted data from entering the system.

Handle Network Failures

Buffer sensor readings locally when the network is unavailable. Send buffered data when connectivity is restored.

Use the Correct Device Type

Register your device with the most specific type that matches your hardware. This enables proper validation and better analytics in the app.