Message Formats
Overview
Know the shape of every JSON object that passes through a V2 integration, so your system and your data converter agree on each field. This article describes the uplink objects, the downlink objects and the V1 differences. It applies to the V2 types HTTP_V2, MQTT_V2 and MQTT_BROKER.
To choose an integration type, see Integrations Overview.
How it works
Where each object appears
A V2 integration converts between the raw bytes of a LoRaWAN® device and JSON, with a Custom v2 data converter:
| Direction | Object | Who creates it | Who reads it |
|---|---|---|---|
| Uplink | Decoder input | KONA Core | Your decodeUplink function |
| Uplink | Decoder output | Your decodeUplink function | KONA Core, which delivers the result to your system |
| Downlink | Downlink message | Your system | KONA Core |
| Downlink | Encoder input | KONA Core, from the downlink message | Your encodeDownlink function |
| Downlink | Encoder output | Your encodeDownlink function | KONA Core, which puts the bytes in the device's downlink queue |
The structures below come from the default code of a new Custom v2 converter, and from the KONA Core REST API. The example values are fake.
Uplink: decoder input
KONA Core calls decodeUplink(input) for each uplink that passes the integration's filters. The default decoder describes input like this:
// input object structure:
// - bytes - int[]
// - fPort - int
// - recvTime - Date (needs type check)
// - tektelicMetadata - Object
| Field | Type | What it holds |
|---|---|---|
bytes | Array of integers | The decrypted payload of the uplink, one integer per byte. |
fPort | Integer | The port of the uplink (FPort). |
recvTime | Date | The time KONA Core received the uplink. Check its type with instanceof Date before you use it. |
tektelicMetadata | Object | Device, application, frame and radio metadata. The fields are listed in the message your system receives. |
Example, for an uplink of 3 bytes on port 10:
{
"bytes": [1, 103, 0],
"fPort": 10,
"recvTime": "2026-01-01T00:00:00.000Z",
"tektelicMetadata": {}
}
confirm how recvTime is shown when the input is serialized as JSON; inside the decoder it's a Date object.
Uplink: decoder output
The decoder returns an object with four fields. This is the return statement of the default decoder:
// output object structure:
// - data - Object
// - errors - string[]
// - warnings - string[]
// - tektelicMetadata - Object
return {
"data": {},
"errors": [],
"warnings": [],
"tektelicMetadata": input.tektelicMetadata
};
| Field | Type | What it holds |
|---|---|---|
data | Object | The decoded values, in a structure you choose. |
errors | Array of strings | Errors your decoder found, for example an unknown message type. Empty when decoding succeeded. |
warnings | Array of strings | Problems that didn't stop decoding, for example a value out of range. |
tektelicMetadata | Object | The metadata from the input. The default decoder passes it through unchanged. We recommend keeping it, so your system receives the metadata with the data. |
Example output, for a decoder that reads a temperature:
{
"data": {
"temperature": 10.3
},
"errors": [],
"warnings": [],
"tektelicMetadata": {}
}
Return an object with these four fields. confirm whether KONA Core checks the code when you save a converter, and which message it shows for invalid code. If the decoder throws an exception, KONA Core raises the event INTEGRATION_UL_DECODING_FAILURE. confirm whether KONA Core raises INTEGRATION_UL_DECODING_FAILURE when errors isn't empty, and whether it still delivers the message.
Uplink: the message your system receives
The integration delivers the decoded uplink to your system:
- HTTP_V2: as an HTTP request to the Application address (IP or name without http or https prefix), Port and Base path of the integration.
- MQTT_V2: published to the Push Topic on your broker.
- MQTT_BROKER: published to the Uplink Topic (subscribe) on the KONA Core broker. The topic contains the DevEUI of the device.
The body is a JSON object with the device, the LoRaWAN frame and the radio data of the uplink. The field names below come from a failed HTTP_V2 delivery that KONA Core recorded in an INTEGRATION_DISCONNECTED alarm.
| Field | Type | What it holds |
|---|---|---|
deviceEui | String | The DevEUI of the device, 16 hexadecimal digits. |
devAddr | String | The DevAddr of the device, 8 hexadecimal digits. |
deviceName | String | The name of the device in KONA Core. |
deviceClass | String | The device class, for example A. |
deviceModelId, deviceModelName | String | The ID and the name of the device model. |
applicationId, applicationName | String | The ID and the name of the device's application. |
fPort | Integer | The port of the uplink. |
fCntUp | Integer | The uplink frame counter. |
fCntDown | Integer | The downlink frame counter. |
confirmed | Boolean | true when the uplink is a confirmed message. |
adrBit | Boolean | The ADR bit of the uplink: true when the device asks for ADR. |
ackBit | Boolean | The ACK bit of the uplink: true when the device acknowledges a confirmed downlink. |
recvTime | Integer | The reception time in GPS seconds, counted from 1980-01-06. This differs from the decoder input's JavaScript Date. |
rfRegion | String | The frequency plan of the receiving gateway with the strongest signal, for example EU868. |
uLFreq | Integer | The uplink frequency. the recorded KONA Core message uses an integer consistent with Hz, while the legacy field description says MHz. |
dataRate | Integer | The data rate index of the uplink. |
gatewayCount | Integer | The number of gateways that received the uplink. |
margin | Integer | The radio margin reported in DevStatusAns, in dB. It describes the device's reception of the status request, not gateway RSSI. |
battery | Integer | The raw battery value from DevStatusAns; see Device status values. |
devStatusTimeMillis | Integer | The time of the last DevStatusAns, in Unix milliseconds. |
gatewayRxInfo | Array of objects | One entry per gateway that received the uplink. See the next table. |
Device status values
LoRaWAN defines the raw DevStatusAns battery value as follows: 0 means external power, 1–254 is the battery range, and 255 means unavailable. It isn't a percentage. The radio margin is a signed SNR value from -32 to 31 dB.
These definitions describe the protocol values. A value of 0 alone doesn't prove that KONA Core has received a status response. Check the status timestamp too.
For the protocol definitions, see section 5.5 of the LoRaWAN L2 specification.
Gateway reception values
Each entry in gatewayRxInfo:
| Field | Type | What it holds |
|---|---|---|
gwEui | String | The GW-ID of the gateway, 16 hexadecimal digits. |
gatewayName | String | The name of the gateway in KONA Core. |
rssi | Integer | The RSSI of the uplink at this gateway, in dBm. |
snr | Number | The SNR of the uplink at this gateway, in dB. |
channel, antennaId | Integer | The channel and the antenna that received the uplink. |
codeRate | String | The LoRa coding rate, for example 4/5. |
crcStatus | Integer | The CRC check result. confirm the values of crcStatus. |
dataRate | Object | modulation (for example LORA), spreadFactor and bandwidth (in kHz). |
lat, lon, alt | Number or null | The location of the gateway, when known. |
gwTimeUtcMillis, gwTimeGpsMillis | Integer or null | The time the gateway received the uplink, in UTC and GPS time, when the gateway reports it. |
rsig | Array or null | Optional per-antenna reception information: antenna, channel, snr and rssi. |
Example, with fake values, for an uplink received by one gateway:
{
"deviceEui": "0011223344556677",
"devAddr": "01ABCDEF",
"deviceName": "Example sensor",
"deviceClass": "A",
"deviceModelId": "00000000-0000-0000-0000-000000000001",
"deviceModelName": "Example model",
"applicationId": "00000000-0000-0000-0000-000000000002",
"applicationName": "Example application",
"fPort": 10,
"fCntUp": 42,
"fCntDown": 3,
"confirmed": false,
"adrBit": true,
"ackBit": false,
"recvTime": 1451260818,
"rfRegion": "EU868",
"uLFreq": 868100000,
"dataRate": 5,
"gatewayCount": 1,
"margin": 0,
"battery": 0,
"devStatusTimeMillis": 0,
"gatewayRxInfo": [
{
"gwEui": "AABBCCDDEEFF0011",
"gatewayName": "Example gateway",
"antennaId": 0,
"channel": 4,
"rssi": -80,
"snr": 9.5,
"codeRate": "4/5",
"crcStatus": 1,
"dataRate": {
"modulation": "LORA",
"spreadFactor": 7,
"bandwidth": 125
},
"lat": null,
"lon": null,
"alt": null,
"gwTimeUtcMillis": null,
"gwTimeGpsMillis": null,
"rsig": null
}
]
}
KONA Core merges output.data and output.tektelicMetadata into the delivered JSON object. If both contain the same key, the value from data takes precedence. The errors and warnings arrays aren't included in that body.
confirm the HTTP method and content type of the HTTP_V2 request, and that MQTT_V2 and MQTT_BROKER publish the same body.
When the application has Send app payload encrypted selected, the payload stays encrypted with the AppSKey. confirm what a V2 decoder receives in bytes when Send app payload encrypted is selected.
Downlink: the message your system sends
To send a downlink through a V2 integration, your system sends a downlink message. For HTTP_V2, it's a request to the integration's HTTP URL path on KONA Core: POST /api/v2/integration/http/<routing key>. The routing key in the path is specific to the integration. Treat the path like a credential. The request body:
| Field | Type | What it holds |
|---|---|---|
deviceEui | String | The DevEUI of the device that gets the downlink. |
data | Object | Your data, in a structure you choose. KONA Core passes it to the encoder. |
confirmed | Boolean | true for a confirmed downlink, false for an unconfirmed one. |
msgId | String | Your identifier for the downlink, shown in the device's Downlink queue. confirm whether msgId is required. |
Example:
{
"deviceEui": "0011223344556677",
"data": {
"reportingIntervalMinutes": 15
},
"confirmed": true,
"msgId": "example-0001"
}
The REST API describes the request in Process downlink v2.
For MQTT_V2 and MQTT_BROKER, publish the same V2 envelope: deviceEui, confirmed, msgId and data. KONA Core passes the data object to encodeDownlink(input) as input.data.
Downlink: encoder input and output
KONA Core calls encodeDownlink(input) with the data from the downlink message. The default encoder:
// input object structure:
// - data - Object (customer-defined)
// output object structure:
// - fPort - int
// - bytes - int[]
// - errors - string[]
// - warnings - string[]
return {
"fPort": 0,
"bytes": [],
"errors": [],
"warnings": []
};
| Object | Field | Type | What it holds |
|---|---|---|---|
| Input | data | Object | The data of the downlink message, as your system sent it. |
| Output | fPort | Integer | The port to send the downlink on. |
| Output | bytes | Array of integers | The payload, one integer per byte. |
| Output | errors | Array of strings | Errors your encoder found. Empty when encoding succeeded. |
| Output | warnings | Array of strings | Problems that didn't stop encoding. |
Example, for the downlink message above:
{
"fPort": 100,
"bytes": [16, 0, 15],
"errors": [],
"warnings": []
}
If the encoder fails, KONA Core raises INTEGRATION_DL_ENCODING_FAILURE. An invalid downlink raises INVALID_DL_PAYLOAD. When encoding succeeds, KONA Core puts the port and bytes in the device's queue. For the queue, the receive windows and the confirmation, see Uplink & Downlink Data Flow.
Other messages
confirm whether a V2 integration delivers messages other than uplinks to your system, for example events, join notifications or downlink acknowledgments, and their JSON format. No allowed source shows one.
KONA Core records events for integrations, such as INTEGRATION_DISCONNECTED, DECODER_BANNED and INTEGRATION_FIRST_UL. You see them in KONA Core, and alarm rules can send notifications for them. See Events & Alarms.
V1 differences
The legacy types HTTP and MQTT use V1 converters, with other function signatures and other objects:
| V1 | V2 | |
|---|---|---|
| Decoder | Decoder(bytes, port): returns any object | decodeUplink(input): returns data, errors, warnings, tektelicMetadata |
| Encoder | Encoder(data): returns port and bytes | encodeDownlink(input): returns fPort, bytes, errors, warnings |
| HTTP downlink path | /api/v1/integration/http/<routing key> | /api/v2/integration/http/<routing key> |
Downlink data | HTTP: a JSON-encoded string. MQTT: an object. Its structure must match the V1 encoder. | An object that your encoder turns into bytes |
| Device field in the downlink | HTTP: devEUI. MQTT: device. | deviceEui |
The default V1 encoder reads the Base64 payload from data.params.data and the port from data.params.port after the wrapper is parsed.
For HTTP V1, encode the converter input as a JSON string in data:
{
"devEUI": "0011223344556677",
"confirmed": false,
"msgId": "example-0001",
"data": "{\"params\":{\"data\":\"AQID\",\"port\":10}}"
}
For MQTT V1, publish an object:
{
"device": "0011223344556677",
"data": {"params": {"data": "AQID", "port": 10}}
}
V1 uplinks also differ by transport: HTTP carries payloadMetaData and a converter-defined payload. MQTT uses the device EUI as a key, with an array of timestamped ts and values objects.
For the V1 formats, see HTTP V1 Integration and MQTT V1 Integration. To move to V2, see Migrate from V1 to V2.