Automatic Data Ingestion (Sensor Webhook)
Sensors can submit measurements directly to AquaTracker over HTTPS, without manual entry. Each aquarium has its own ingest tokens, created in Aquarium → Settings → Automatic data ingestion.
Endpoint
POST /api/ingest
Authorization: Bearer awt_<your-token>
Content-Type: application/json
The token identifies the aquarium — no aquarium ID is needed in the payload. The plaintext token is shown once at creation; only its SHA-256 hash is stored. Tokens can be revoked at any time in aquarium settings.
Payload formats
Flat map (simplest)
{
"ph": 7.2,
"temperature": 25.4,
"measuredAt": "2026-07-17T08:00:00Z",
"notes": "optional note applied to all values"
}
Every key except measuredAt and notes is treated as a parameter name.
measuredAt is optional (defaults to server time) and applies to all values
in the payload.
Batch
{
"measurements": [
{ "parameter": "ph", "value": 7.2, "measuredAt": "2026-07-17T08:00:00Z" },
{ "parameter": "temperature", "value": 25.4 },
{ "parameter": "nitrate", "value": 10, "notes": "after water change" }
]
}
Max 100 items per request.
Parameter names
Use the parameterName values, e.g.: temperature, ph, ammonia,
nitrite, nitrate, gh, kh, co2, phosphate_freshwater,
calcium, alkalinity, salinity, … (see the full list in aquarium
settings / parameter definitions). Parameters not applicable to the
aquarium's type (freshwater/saltwater) are rejected per item.
Response
{ "created": 2, "skipped": 0, "errors": [] }
created— measurements stored. Threshold alerts (and critical alert emails) fire automatically, exactly as with manual entry.skipped— duplicates: an item with the same parameter andmeasuredAtalready exists (safe to resend the same payload).errors— per-item problems (unknown parameter, not applicable to this aquarium type,measuredAtmore than 5 minutes in the future, …). One bad item never fails the whole batch.
| Status | Meaning |
|---|---|
| 201 | Processed (check created/skipped/errors) |
| 400 | Invalid JSON, invalid payload, or no valid items |
| 401 | Missing, invalid, or revoked token |
| 429 | Rate limited (shared API limit: 100 req / 10 s per IP) |
Examples
curl
curl -X POST "https://your-domain.com/api/ingest" \
-H "Authorization: Bearer awt_your_token_here" \
-H "Content-Type: application/json" \
-d '{"ph": 7.2, "temperature": 25.4}'
ESPHome (ESP32)
Sends pH and temperature on every sensor update:
# Requires ESPHome 2023.12+ for the http_request component.
http_request:
id: aquatracker_ingest
# verify_ssl: false # only if you must skip certificate verification
sensor:
- platform: ezo
id: ph_ezo
address: 99
name: "pH"
unit_of_measurement: "pH"
update_interval: 60s
on_value:
then:
- http_request.post:
url: https://your-domain.com/api/ingest
headers:
Authorization: !secret aquatracker_token # "Bearer awt_..."
Content-Type: application/json
json:
ph: !lambda 'return x;'
- platform: dallas_temp # DS18B20
name: "Temperature"
update_interval: 60s
on_value:
then:
- http_request.post:
url: https://your-domain.com/api/ingest
headers:
Authorization: !secret aquatracker_token
Content-Type: application/json
json:
temperature: !lambda 'return x;'
For fewer requests, buffer values and send one batch on a script/interval
trigger instead.
Home Assistant
# configuration.yaml
rest_command:
aquatracker_ingest:
url: "https://your-domain.com/api/ingest"
method: POST
headers:
Authorization: "Bearer awt_your_token_here"
Content-Type: "application/json"
payload: '{"temperature": {{ states("sensor.aquarium_temperature") }} }'
content_type: "application/json"
automation:
- alias: "Push aquarium temperature to AquaTracker"
triggers:
- trigger: state
entity_id: sensor.aquarium_temperature
actions:
- action: rest_command.aquatracker_ingest
For multiple parameters in one call, use the batch payload format.