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": [] }
StatusMeaning
201Processed (check created/skipped/errors)
400Invalid JSON, invalid payload, or no valid items
401Missing, invalid, or revoked token
429Rate 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.