LoRaWAN Device
Network Server
TTN · ChirpStack · Helium
HTTPS Webhook
HydroNode

One unified webhook endpoint

HydroNode exposes a single webhook URL that auto-detects whether the incoming uplink is from TTN (v3) or ChirpStack (v4/v3). There are no separate endpoints and no per-network configuration.

POST https://hydronode.texhfexlabs.de/api/webhook/lorawan/uplink/{webhookToken}
Token auth
A per-user secret token (wht_…) in the URL proves the request belongs to your account. Never expires unless you rotate it.
DevEUI ownership
After decoding, HydroNode verifies the DevEUI is registered to the token's owner. Unknown or foreign devices are rejected.
fPort decoding
The binary payload is decoded with the sensor's configured fPort mapping, then stored. The same pipeline for every network.
Helium: Helium Console emits ChirpStack-compatible payloads. Add an HTTP integration pointing at your webhook URL and follow the ChirpStack column wherever the steps below differ.

Before you start

  1. 1
    A sensor exists in HydroNode
    Create the sensor via the dashboard or API first.
  2. 2
    The sensor is saved as Outdoor
    Open General settings, select Outdoor, and save. A LoRaWAN binding creates a Station Interface automatically, and only outdoor sensors can be stations.
  3. 3
    The physical DevEUI is at hand
    16 hex characters printed on the device, e.g. a840411b2182cd44.
  4. 4
    You know your payload layout
    Which fPort carries which channels and at which byte offsets (configured in Step 4).

Register your device on the network server

This is the one step that differs between networks.

The Things Network
  1. Open console.thethings.network and select your application.
  2. Go to End devices → Add end device.
  3. Enter JoinEUI / AppEUI, DevEUI, and AppKey from the device firmware.
  4. Complete registration, then note the DevEUI and Application ID.
ChirpStack
  1. Log in to your ChirpStack instance and open (or create) an Application.
  2. Ensure a Device profile matches your device's LoRaWAN version.
  3. Go to Devices → Add device and enter the DevEUI + profile.
  4. Under the device's Keys tab set the AppKey (OTAA) or session keys (ABP). Note the DevEUI and Application ID.

Bind the device to your sensor

Done entirely in the HydroNode web app. Open your sensor and go to its Settings page, then use the LoRaWAN Binding card.

  1. 1
    Confirm the sensor is Outdoor
    If it is still Indoor, change it under General settings and save before continuing.
  2. 2
    Enter the DevEUI
    The 16 lowercase hex characters from your device (and noted in Step 02).
  3. 3
    Enter the LoRaWAN Application ID (optional)
    The Application ID from TTN or ChirpStack. Only needed if several applications share a DevEUI range; otherwise leave it blank.
  4. 4
    Click Save LoRaWAN Binding
    HydroNode creates the Station Interface automatically, generates your personal Webhook URL, and shows it in the same connection card.
  5. 5
    Reveal and copy the Webhook URL
    Click the eye icon to reveal it, then Copy. You paste this into the network server in Step 04.
The Webhook URL embeds a secret token; anyone who has it can submit data as you. Keep it private and never commit it to source control.
The Station Interface can later be removed in Station Settings without deleting the sensor or its measurements. Remove the LoRaWAN binding separately if the device should stop sending uplinks.

Point the network server at your URL

The Things Network
  1. Application → Integrations → Webhooks → Add webhook → Custom webhook.
  2. Webhook format: JSON.
  3. Base URL: paste the Webhook URL you copied in Step 03.
  4. Enable Uplink message. Leave everything else default. No headers needed.
  5. Save changes.

Only devices whose DevEUI is registered in HydroNode are processed; the rest get a silent 401.

ChirpStack
  1. Application → Integrations → HTTP.
  2. Event endpoint URL(s): paste the Webhook URL you copied in Step 03.
  3. Headers: leave empty.
  4. Enabled events: select up (uplink) at minimum.
  5. Submit.

Non-uplink events (join, ack…) return 400 and are safely ignored by ChirpStack.

Configure fPort decoding

Each fPort carries a fixed set of channels packed into the payload bytes. You tell HydroNode how to decode them in the web app: on the same sensor Settings page, scroll to fPort Configurations. See Sensor Types for the supported types and encodings.

  1. 1
    Click Add fPort
    Set the fPort number (1–223) your firmware sends on, and an optional description.
  2. 2
    Add one channel per value
    Pick a Sensor Type (presets fill in bytes, encoding, scale and unit), then adjust Bytes (1, 2, 3 or 4), Encoding (signed or unsigned), Byte Order, Scale Factor, Offset, Channel Name and Unit if needed. Choose Custom for a type not in the list.
  3. 3
    Order the channels
    Channels decode top to bottom with no gaps. Use the up and down arrows so the order matches the byte layout your device transmits.
  4. 4
    Click Create
    Repeat for every fPort your device uses. Existing configs can be edited or deleted from the same list.

How a channel is decoded

Every channel reads a fixed number of bytes and turns them into one physical value:

value = raw × scaleFactor + offset
FieldAcceptsWhen you need it
Bytes1, 2, 3 or 4 1 byte suits battery percent, wind direction as an octant or a status flag. 4 bytes suit counters and high resolution values.
EncodingSigned or unsigned Signed reads the value as two's complement over the configured width, so a temperature can go below zero.
Byte OrderBig endian or little endian Big endian is the default and is what most devices send. Milesight and several other vendors pack the least significant byte first; pick little endian for those instead of changing your firmware.
Scale FactorAny number above zero Converts the raw integer into the physical unit. A temperature sent in hundredths of a degree uses 0.01.
OffsetAny number, 0 by default Added after scaling. Devices that bias their reading to avoid negative numbers need it: a device sending raw × 0.1 − 40 takes scale 0.1 and offset −40.
Uplinks arriving with no matching fPort config are rejected with 400 no-fport-config and routed to the dead-letter queue. Configure the fPort before sending live data.

Native payload formats (auto-detected)

No transformation needed. HydroNode reads each network's native JSON and picks the format from its shape: end_device_ids means The Things Stack v3, deviceInfo or a top level devEui means ChirpStack v4. Helium is not a third format; its console runs on ChirpStack and sends the ChirpStack event. Anything else is rejected with 400 rather than guessed at.

Repeat deliveries are recognised by deduplicationId when the network server sends one, and by the frame counter otherwise. If a payload carries neither, deduplication is skipped for that uplink: a duplicate reading can be removed later, a dropped one cannot be recovered.

TTN v3 uplink
{
  "end_device_ids": {
    "dev_eui": "a840411b2182cd44",
    "application_ids": {
      "application_id": "my-app"
    }
  },
  "uplink_message": {
    "f_port": 1,
    "f_cnt": 9846,
    "frm_payload": "AQH0AgBk",
    "rx_metadata": [
      { "rssi": -85, "snr": 7.2 }
    ],
    "received_at": "2026-07-21T11:24:34.027Z"
  },
  "received_at": "2026-07-21T11:24:39.500Z"
}
ChirpStack v4 uplink
{
  "deduplicationId": "f2b0b8ec-1d5a-4c1f-9d0e-2f2f7d3a1b22",
  "time": "2026-07-21T11:24:34.027+00:00",
  "deviceInfo": {
    "devEui": "a840411b2182cd44",
    "applicationId": "abc123"
  },
  "fPort": 1,
  "fCnt": 9846,
  "data": "AQH0AgBk",
  "rxInfo": [
    { "rssi": -85, "snr": 7.2 }
  ]
}

Confirm it works

Trigger a test uplink (TTN's Simulate uplink or a real transmission). On success, the server logs LoRaWAN uplink processed: network=… devEui=… fPort=… and the value appears under the sensor.

A 202 means the values are stored, not merely received. HydroNode waits for its own storage to confirm them before answering, so a 202 is a real delivery receipt. If storage is unavailable you get 503 instead and your network server should redeliver. Repeated deliveries of the same frame are recognised and stored once, so retrying is always safe.

CodeMeaningWhat to do
202Uplink received and queuedNothing, success
400no-fport-configConfigure the fPort (Step 05)
400Missing payload / Invalid fPortJoin or MAC frame, safe to ignore
400Payload errorCheck the channel order in the fPort config
401Unknown device / UnauthorizedBind the DevEUI (Step 03) or check the token in the URL
403Device not owned by token userDevEUI belongs to another account
429Too many requests You are sending faster than the limit allows. Honour Retry-After and slow down. A normal device reports every few minutes and never reaches this.
503Upstream unavailable, retry HydroNode could not store the values and is asking for the uplink again. Configure your network server to retry; the same uplink is accepted on the next delivery and duplicates are filtered out.
500Internal errorCheck HydroNode logs
Once the first 202 arrives and a value shows up on the sensor, the integration is live. Every other device in the same application that you register in HydroNode starts flowing automatically.