How to Integrate TagoIO with a Generic HTTPS Endpoint

This guide shows how to send data from any service that supports HTTPS forwarding or webhooks to TagoIO using the Generic HTTPS Endpoint. You can use it with gateways, LNS, or any system that can post JSON or query parameters over HTTPS.

Use this when:

  • Your network server or gateway isn’t natively supported by TagoIO.
  • You need a simple HTTPS endpoint to receive uplinks.
  • You want to map your service’s fields to TagoIO device variables with minimal setup.

This integration handles uplinks only. It does not support downlinks natively. If you need downlinks, use an Analysis to call your network server’s API following their documentation.


Overview of the flow

  1. Create an Authorization Key in TagoIO with optional “Additional Parameters” that tell TagoIO which fields in your payload correspond to serial, payload, and port.
  2. Configure your external service to POST data to the TagoIO Generic HTTPS endpoint with your Authorization (header or query).
  3. (Optional) Adjust your outgoing JSON so it matches the simplest format.
  4. Create the device in TagoIO and pick the right connector (Generic Endpoint + Generic Sensor or a decoder-enabled connector).

1) Create an Authorization Key

Create a key at: Devices → Authorization

You’ll see a field called Additional Parameter. For this integration, you can map the fields your service sends to TagoIO’s expected names:

  • serial: device EUI or serial number
  • payload: payload, ideally in hex
  • port: sensor or LoRaWAN FPort

Example: If your service sends JSON like:

{ "devaddr": "12BAB34B54", "payload_raw": "0011AA", "fport": 10 }

Set Additional Parameter to:

serial=devaddr;payload=payload_raw;port=fport

How it works:

  • TagoIO searches both the JSON body and query string for the mapped keys.
  • If you don’t set Additional Parameter, TagoIO tries to auto-detect common keys. This may not always match your payload—explicit mapping is safer.
  • Any extra fields in your JSON become variables on your TagoIO device automatically.

Nested JSON paths are supported for “serial.”
Example: If your ID lives under {"deviceInfo": {"serial": "AA-BB"}}, set:
serial=deviceInfo.serial


2) Point your service to the TagoIO Generic HTTPS Endpoint

Your service must send HTTPS POSTs to the TagoIO Generic Middleware endpoint for your region.

  • Base URL format:
    https://generic.middleware.REGION.tago.io/uplink
    Replace REGION with your deployment region (e.g., us-e1, eu-w1).
    See available regions: TagoIO Network Integration regions

  • Example:
    https://generic.middleware.us-e1.tago.io/uplink

You can provide your Authorization in one of two ways:

Option A: Authorization header

  • Header key: Authorization
  • Header value: Your Authorization Key from step 1

Option B: Authorization in the query string

  • URL:
    https://generic.middleware.REGION.tago.io/uplink?authorization=YOUR-AUTHORIZATION-HERE

Do not expose Authorization keys in client apps or public code. Keep them on the server or in your network server’s secure configuration.


3) (Optional) Standardize your outgoing JSON

If your service lets you format the outgoing JSON, use this clean schema to minimize mapping:

{
  "serial": "{device_eui}",
  "payload": "{payload}",        // hex preferred
  "port": {port},
  "latitude": {latitude},
  "longitude": {longitude}
}

Details:

  • Only serial is required, plus the Authorization configured in step 2.
  • Include any other fields your device reports (e.g., RSSI, SNR, battery). TagoIO stores them as variables.
  • If you’re using Additional Parameter mappings from step 1, you can keep your original field names and map them there instead.

4) Create the device in TagoIO

In TagoIO, go to Devices → Generic Endpoint and create a device using your EUI/serial.

Pick the right connector:

  • If your external service already decodes the payload into final variables, use the Generic Sensor connector.
  • If you want TagoIO to decode using built-in decoders, make sure your Additional Parameter mapping is correct so the decoder receives serial, payload, and port.

Alternatively, implement your own decoding in a Payload Parser:


Troubleshooting

Use the Device’s Live Inspector to confirm uplinks are reaching TagoIO. If not, check:

  • HTTP status codes from your service:
    • 401/403: Invalid or missing Authorization. Check header/query value and region.
    • 415: Content-Type not set to application/json for JSON bodies.
    • 400: Missing required serial after mapping. Verify Additional Parameter keys match your payload.
  • Incorrect region in the endpoint URL. Match your account’s deployment region.
  • Wrong Additional Parameter mapping. Typos or wrong nesting cause TagoIO to miss the fields.
  • Payload encoding. If your decoder expects hex, send hex. If you send base64, adjust your parser.
  • Service-side errors. Confirm the service logs show successful POSTs to TagoIO.

@Benoît Moyaux

Hello,

How we can debug it? I try to use Objenious a french Lorawan LNS. I create an authorisation on TagoIO with “serial=device_id;payload=payload_cleartext;port=port;” parameters. The json from Objnious should be formatted like this:

{
“id”:“string”,
“device_id”:“number”,
“group_id”:“number”,
“group”:“string”,
“profile_id”:“number”,
“profile”:“string”,
“type”:“string”,
“timestamp”:“string” //datetime format ISO 8601 (2020-10-14T13:45:55Z),
“count”:“number”,
“payload”:“array”,
“payload_encrypted”:“string”,
“payload_cleartext”:“string”,
“device_properties”:{
“appeui”:“string”,
“deveui”:“string”,
“external_id”:“string”,
“mobility”:“string”
},
“protocol_data”:{
“AppNonce”:“string”,
“DevAddr”:“string”,
“DevNonce”:“string”,
“NetID”:“string”,
“best_gateway_id”:“string”,
“gateways”:“number”,
“lora_version”:“number”,
“noise”:“number”,
“port”:“number”,
“requested_nbrep”:“number”,
“rssi”:“number”,
“sf”:“number”,
“signal”:“number”,
“snr”:“number”
},
“lat”:“number”,
“lng”:“number”,
“geolocation_type”:“string”,
“geolocation_precision”:“number”,
“city_code”:“string”,
“city_name”:“string”,
“delivered_at”:“string” //datetime format ISO 8601 (2020-10-14T13:45:55Z),
}
I set the https://generic.middleware.tago.io/uplink?authorization=xxxxxxxx routing on Objenious
I create the generic endpoint with the “device_id”.
But when I try to see the Live inspector, I see nothing…

So how I can see if TagoIO receive well the routing and where is the problem?

best regards,

Mathieu

@Benoît Moyaux

the true json seems to be :

{
  "id": "uplink-284654-7e7a",
  "device_id": 50384020831207944,
  "group_id": 56,
  "group": "watteco-hennebont",
  "profile_id": 113,
  "profile": "temperature-deportee-50-70-043-nc-interne-datavision-1-1",
  "type": "uplink",
  "timestamp": "2021-09-20T15:43:19.916Z",
  "count": 284654,
  "payload": [
    {
      "timestamp": "2021-09-20T15:43:19Z",
      "data": {
        "TMC_Temperature": 2682,
        "TMC_Temperature_2": 26.82,
        "attribute_id": 0,
        "attribute_type": 41,
        "cluster_id": 1026,
        "command_id": 10,
        "flag": 49,
        "zCODEC_VERSION": "1.3",
        "zCODEC_VERSION_DATE": "20190604"
      }
    }
  ],
  "payload_encrypted": "68fde19e99b635db7a",
  "payload_cleartext": "310a04020000290a7a",
  "device_properties": {
    "appeui": "70b3d5e75f600000",
    "deveui": "70b3d5e75e008df9",
    "external_id": ""
  },
  "protocol_data": {
    "AppNonce": "287fd9",
    "DevAddr": "0f92d703",
    "DevNonce": "7e7a",
    "NetID": "000007",
    "best_gateway_id": "M_Micro15",
    "gateways": 2,
    "lora_version": 0,
    "noise": -84.95746206410165,
    "port": 125,
    "requested_nbrep": 1,
    "rssi": -77,
    "sf": 12,
    "signal": -77.75746206410165,
    "snr": 7.2
  },
  "lat": 47.8025211562767,
  "lng": -3.34714481307406,
  "geolocation_type": "network",
  "geolocation_precision": 5000,
  "city_code": "56036",
  "city_name": "Caudan",
  "delivered_at": null
}

IN bold the interesting field.

I try to set parameters authorisation to : serial=device_properties.deveui;payload=payload_cleartext;port=protocol_data.port;

but always nothing in the live inspector…

@Vitor Lima

it seems your devic eui parameter is inside another object. You would need to setup the authorization as follow:

payload=device_properties.deveui;payload=payload_cleartext

There is no much way to debug this, unless you do have access to the responses from the middleware request. You can do that by getting your JSON and sending a POST request to https://generic.middleware.tago.io/uplink?authorization=xxxxxxxx

@Benoît Moyaux

Is it a mistake on first argument payload=device_properties.deveui;payload=payload_cleartext ?

@Benoît Moyaux

with postman, for this request: https://generic.middleware.tago.io/uplink?authorization=at6c3caa934e1axxxxxxxxxxxxxxx

and the body

{
  "id": "uplink-6-5a09",
  "device_id": 50384020831207879,
  "group_id": 56,
  "group": "watteco-hennebont",
  "profile_id": 111,
  "profile": "triphaso",
  "type": "uplink",
  "timestamp": "2021-09-21T07:19:19.256Z",
  "count": 6,
  "payload": [
    {
      "timestamp": "2021-09-21T07:19:19Z",
      "data": {
        "EPM_NegativeActiveEnergy": 0,
        "EPM_NegativeActivePower": 0,
        "EPM_NegativeReactiveEnergy": 0,
        "EPM_NegativeReactivePower": 0,
        "EPM_PositiveActiveEnergy": 9,
        "EPM_PositiveActivePower": 0,
        "EPM_PositiveReactiveEnergy": 0,
        "EPM_PositiveReactivePower": 0,
        "PHASEA_Negative_Active_Energy": 0,
        "PHASEA_Negative_Active_Power": 0,
        "PHASEA_Negative_Reactive_Energy": 0,
        "PHASEA_Negative_Reactive_Power": 0,
        "PHASEA_Positive_Active_Energy": 9,
        "PHASEA_Positive_Active_Power": 0,
        "PHASEA_Positive_Reactive_Energy": 0,
        "PHASEA_Positive_Reactive_Power": 0,
        "attribute_id": 0,
        "attribute_type": 65,
        "cluster_id": 32778,
        "command_id": 10,
        "flag": 17,
        "size_8": 32,
        "zCODEC_VERSION": "1.4",
        "zCODEC_VERSION_DATE": "20191213"
      }
    }
  ],
  "payload_encrypted": "340b6007ebe335c0d4c2f850e1c00446dd68644915dc94264e6058b1eccf56347f22fdacefb64cb8",
  "payload_cleartext": "110a800a000041200000000900000000000000000000000000000000000000000000000000000000",
  "device_properties": {
    "appeui": "70b3d5e75f600000",
    "deveui": "70b3d5e75e008df9",
    "external_id": ""
  },
  "protocol_data": {
    "AppNonce": "214081",
    "DevAddr": "0f1495d2",
    "DevNonce": "5a09",
    "NetID": "000007",
    "best_gateway_id": "M_Micro15",
    "gateways": 1,
    "lora_version": 0,
    "noise": -80.81209675612978,
    "port": 125,
    "requested_nbrep": 1,
    "rssi": -72,
    "sf": 12,
    "signal": -72.61209675612977,
    "snr": 8.2
  },
  "ack": true,
  "delivered_at": null
}

i receive the answer:

<html>
<head><title>400 Bad Request</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
</body>
</html>

@Vitor Lima

Hi @bmoyaux,
Can you check if it’s working for you now? I updated the middleware yesterday.

@Benoît Moyaux

Hello Vitor,

I receive now the payload from my device on tago:

[
  {
    "variable": "generic_payload",
    "value": "{\"id\":\"uplink-286900-7e7a\",\"device_id\":50384020831207944,\"group_id\":56,\"group\":\"watteco-hennebont\",\"profile_id\":113,\"profile\":\"temperature-deportee-50-70-043-nc-interne-datavision-1-1\",\"type\":\"uplink\",\"timestamp\":\"2021-09-24T09:33:17.128Z\",\"count\":286900,\"payload\":[{\"timestamp\":\"2021-09-24T09:33:17Z\",\"data\":{\"TMC_Temperature\":2343,\"TMC_Temperature_2\":23.43,\"attribute_id\":0,\"attribute_type\":41,\"cluster_id\":1026,\"command_id\":10,\"flag\":49,\"zCODEC_VERSION\":\"1.3\",\"zCODEC_VERSION_DATE\":\"20190604\"}}],\"payload_encrypted\":\"25dcf45f9dfd126892\",\"payload_cleartext\":\"310a04020000290927\",\"device_properties\":{\"appeui\":\"70b3d5e75f600000\",\"deveui\":\"70b3d5e75e008df9\",\"external_id\":\"\"},\"protocol_data\":{\"AppNonce\":\"287fd9\",\"DevAddr\":\"0f92d703\",\"DevNonce\":\"7e7a\",\"NetID\":\"000007\",\"best_gateway_id\":\"M_Micro15\",\"gateways\":1,\"lora_version\":0,\"noise\":-79.46683163887967,\"port\":125,\"requested_nbrep\":1,\"rssi\":-71,\"sf\":12,\"signal\":-71.66683163887967,\"snr\":7.8},\"delivered_at\":null}"
  }
]

But with serial=device_properties.deveui;payload=payload_cleartext additional parameter, I don’t have in the out a variable which is name payload based on payload_cleartext

the generated json is:

[
  { "variable": "id", "value": "uplink-286900-7e7a", "serie": 1632476008693 },
  { "variable": "device_id", "value": 50384020831207944, "serie": 1632476008693 },
  { "variable": "group_id", "value": 56, "serie": 1632476008693 },
  { "variable": "group", "value": "watteco-hennebont", "serie": 1632476008693 },
  { "variable": "profile_id", "value": 113, "serie": 1632476008693 },
  {
    "variable": "profile",
    "value": "temperature-deportee-50-70-043-nc-interne-datavision-1-1",
    "serie": 1632476008693
  },
  { "variable": "type", "value": "uplink", "serie": 1632476008693 },
  { "variable": "timestamp", "value": "2021-09-24T09:33:17.128Z", "serie": 1632476008693 },
  { "variable": "count", "value": 286900, "serie": 1632476008693 },
  { "variable": "0_timestamp", "value": "2021-09-24T09:33:17Z", "serie": 1632476008693 },
  { "variable": "data_tmc_temperature", "value": 2343, "serie": 1632476008693 },
  { "variable": "data_tmc_temperature_2", "value": 23.43, "serie": 1632476008693 },
  { "variable": "data_attribute_id", "value": 0, "serie": 1632476008693 },
  { "variable": "data_attribute_type", "value": 41, "serie": 1632476008693 },
  { "variable": "data_cluster_id", "value": 1026, "serie": 1632476008693 },
  { "variable": "data_command_id", "value": 10, "serie": 1632476008693 },
  { "variable": "data_flag", "value": 49, "serie": 1632476008693 },
  { "variable": "data_zcodec_version", "value": "1.3", "serie": 1632476008693 },
  { "variable": "data_zcodec_version_date", "value": "20190604", "serie": 1632476008693 },
  { "variable": "payload_encrypted", "value": "25dcf45f9dfd126892", "serie": 1632476008693 },
  { "variable": "payload_cleartext", "value": "310a04020000290927", "serie": 1632476008693 },
  { "variable": "device_properties_appeui", "value": "70b3d5e75f600000", "serie": 1632476008693 },
  { "variable": "device_properties_deveui", "value": "70b3d5e75e008df9", "serie": 1632476008693 },
  { "variable": "device_properties_external_id", "value": "", "serie": 1632476008693 },
  { "variable": "protocol_data_appnonce", "value": "287fd9", "serie": 1632476008693 },
  { "variable": "protocol_data_devaddr", "value": "0f92d703", "serie": 1632476008693 },
  { "variable": "protocol_data_devnonce", "value": "7e7a", "serie": 1632476008693 },
  { "variable": "protocol_data_netid", "value": "000007", "serie": 1632476008693 },
  { "variable": "protocol_data_best_gateway_id", "value": "M_Micro15", "serie": 1632476008693 },
  { "variable": "protocol_data_gateways", "value": 1, "serie": 1632476008693 },
  { "variable": "protocol_data_lora_version", "value": 0, "serie": 1632476008693 },
  { "variable": "protocol_data_noise", "value": -79.46683163887967, "serie": 1632476008693 },
  { "variable": "protocol_data_port", "value": 125, "serie": 1632476008693 },
  { "variable": "protocol_data_requested_nbrep", "value": 1, "serie": 1632476008693 },
  { "variable": "protocol_data_rssi", "value": -71, "serie": 1632476008693 },
  { "variable": "protocol_data_sf", "value": 12, "serie": 1632476008693 },
  { "variable": "protocol_data_signal", "value": -71.66683163887967, "serie": 1632476008693 },
  { "variable": "protocol_data_snr", "value": 7.8, "serie": 1632476008693 }
]

@Vitor Lima

I can see it was generated as variable payload_cleartext

{ "variable": "payload_cleartext", "value": "310a04020000290927", "serie": 1632476008693 }

@mathieu pouillot

Yes, but if we use a generic parser which work with TTN,… and based on “payload” field, it is necessary to modify all parsers. If other provider have other field, at each time we will modify the generic parser.

In your example, I should think than paramaters are done to modify the json structure to modify it to corresponds to a generic one. It is true for payload, but could true for other fields port, rssi, snr,…

@Vitor Lima

Ok, I got it.

It is planned to payload_cleartext be converted to payload in the next update of this integration. The same with port, in order to get it normalized to work with the payload parser we already have.

@Ahmad Farhan Bin Saifulddin

Hello,

I have managed to receive data in the Live Inspector but stumbled upon a “Connection refused, invalid payload” error due to “Invalid location:lng field (type)”. This is the result from Generic Endpoint Payload Parser:

[
  {
    "variable": "applicationid",
    "value": "8",
    "serie": 1633412551986
  },
  {
    "variable": "applicationname",
    "value": "TagoIO_Milesight_VS121_1533",
    "serie": 1633412551986
  },
  {
    "variable": "devicename",
    "value": "Milesight VS121_1533",
    "serie": 1633412551986
  },
  {
    "variable": "deveui",
    "value": "24e124600b251533",
    "serie": 1633412551986
  },
  {
    "variable": "0_mac",
    "value": "24e124fffef24138",
    "serie": 1633412551986
  },
  {
    "variable": "0_time",
    "value": "2021-10-04T07:08:26.979442Z",
    "serie": 1633412551986
  },
  {
    "variable": "0_rssi",
    "value": -95,
    "serie": 1633412551986
  },
  {
    "variable": "0_lorasnr",
    "value": 9.5,
    "serie": 1633412551986
  },
  {
    "variable": "0_name",
    "value": "Local Gateway",
    "serie": 1633412551986
  },
  {
    "variable": "0_location",
    "value": "0, undefined",
    "location": {
      "lat": 0,
      "lng": null
    },
    "serie": 1633412551986
  },
  {
    "variable": "0_altitude",
    "value": 0,
    "serie": 1633412551986
  },
  {
    "variable": "txinfo_frequency",
    "value": 923200000,
    "serie": 1633412551986
  },
  {
    "variable": "datarate_modulation",
    "value": "LORA",
    "serie": 1633412551986
  },
  {
    "variable": "datarate_bandwidth",
    "value": 125,
    "serie": 1633412551986
  },
  {
    "variable": "datarate_spreadfactor",
    "value": 7,
    "serie": 1633412551986
  },
  {
    "variable": "txinfo_adr",
    "value": true,
    "serie": 1633412551986
  },
  {
    "variable": "txinfo_coderate",
    "value": "4/5",
    "serie": 1633412551986
  },
  {
    "variable": "fcnt",
    "value": 18,
    "serie": 1633412551986
  },
  {
    "variable": "fport",
    "value": 85,
    "serie": 1633412551986
  },
  {
    "variable": "data",
    "value": "BMkAAQAA",
    "serie": 1633412551986
  },
  {
    "variable": "time",
    "value": "2021-10-04T07:08:26.979442Z",
    "serie": 1633412551986
  }
]

Is it possible to receive the data while ignoring this variable? How do I go about rectifying this?

@Vitor Lima

I just updated the Generic decoder to prevent this kind of issue.

I also included, when you create the device, an option for you to select if the payload is in base64 or hexadecimal. If it’s base64, it will convert to hexadecimal, so you can use all our tutorials and current decoder without need to change all code to work with base64.

@Ahmad Farhan Bin Saifulddin

Hello Vitor,

Thank you for resolving the previous issue. I have tried the option to select if the payload is in base64 or hexadecimal. Despite selecting base64, I face an issue whereby the payload doesn’t convert to hexadecimal and is still in base 64. This is the result from Generic Endpoint Payload Parser:

Result of [Generic Endpoint] payload parser:
[
  { "variable": "applicationid", "value": "8", "serie": 1633925975422 },
  { "variable": "applicationname", "value": "TagoIO_Milesight_VS121_1533", "serie": 1633925975422 },
  { "variable": "devicename", "value": "Milesight VS121_1533", "serie": 1633925975422 },
  { "variable": "deveui", "value": "24e124600b251533", "serie": 1633925975422 },
  { "variable": "0_mac", "value": "24e124fffef24138", "serie": 1633925975422 },
  { "variable": "0_time", "value": "2021-10-10T05:45:30.135926Z", "serie": 1633925975422 },
  { "variable": "0_rssi", "value": -85, "serie": 1633925975422 },
  { "variable": "0_lorasnr", "value": 14, "serie": 1633925975422 },
  { "variable": "0_name", "value": "Local Gateway", "serie": 1633925975422 },
  { "variable": "0_altitude", "value": 0, "serie": 1633925975422 },
  { "variable": "txinfo_frequency", "value": 923400000, "serie": 1633925975422 },
  { "variable": "datarate_modulation", "value": "LORA", "serie": 1633925975422 },
  { "variable": "datarate_bandwidth", "value": 125, "serie": 1633925975422 },
  { "variable": "datarate_spreadfactor", "value": 7, "serie": 1633925975422 },
  { "variable": "txinfo_adr", "value": true, "serie": 1633925975422 },
  { "variable": "txinfo_coderate", "value": "4/5", "serie": 1633925975422 },
  { "variable": "fcnt", "value": 14, "serie": 1633925975422 },
  { "variable": "time", "value": "2021-10-10T05:45:30.135926Z", "serie": 1633925975422 },
  { "variable": "payload", "value": "BMkABAAA", "serie": 1633925975422 },
  { "variable": "port", "value": 85, "serie": 1633925975422 }
]

@Vitor Lima

Hi @ahmad.farhan.saifuld,
I fixed the issue. Thanks for reporting.

Hello,

I have an issue to link serial with the field DEVICE_ID.

The JSON received is :

{

“TYPE”:“DEVICE_PRC”,

“MODE”:“BASIC”,

“VERSION”:“1.1”,

“DATA”:[

  {

     "DEVICE\_ID":216955,

     "MSG\_ID":"961014953249251328",

     "MSG\_DATE":"2022-04-01T18:20:02.449Z",

     "CHECKED":"Y",

     "SENSORS":{

        "RAW\_DATA":"00000000000000000000000000000000000000000000000000",

        "CRC\_OK":true

     }

  }

]

}

In the additonal parameters inside the authorization I tried :

serial=DATA.DEVICE_ID;payload=DATA.SENSORS.RAW_DATA;

However I get the following answer :

{

"message": "Serial Number / Device EUI parameter couldn't be found. Please set an Authorization additional parameter to help us find the correct parameter."

}

How can I link DEVICE_ID which is recognized outside the field DATA if I modify the JSON but not inside ? Same for RAW_DATA.

Thank you for your help,

Regards

A few items of note for this integration:

  1. The URL details above are incorrect. https://generic.middleware.us-e1.tago.io/uplink does not work. Instead you must use https://generic.middleware.tago.io/uplink
  2. Make sure your HTTP POST body payload is a single JSON object, using an array won’t be accepted

Hello AutoDog,

Thank you for bringing this to our attention. You are correct; the URL was indeed incorrect. We’ve just updated it to match the documentation.

@mateus.silva can you double-check this? Behavior hasn’t seemed to change: https://generic.middleware.us-e1.tago.io/uplink does not work (timeout) but https://generic.middleware.tago.io/uplink is OK

Thanks for taking a closer look

@AutoDog I tested the endpoint (https://generic.middleware.REGION.tago.io/uplink?authorization=YOUR-AUTHORIZATION-HERE) following all steps from the documentation, and it worked fine, returning status 200 without any issues.

Could you please confirm if you’ve thoroughly followed each step described in the guide? The documentation has been recently updated, especially regarding the REGION. Also, could you provide more details about your setup or any error messages received? This information will help us better understand what might be causing the timeout issue on your end.