Skip to content

HTTP Payload Parsing and Telemetry Writing ​

Applies to ThinkLink 2.00.099. Both API families perform real ingestion with side effects: activity, latest values, history, forwarding, triggers, and related-device notifications may change. Simulate Send also processes real data; it is not a dry run. Confirm the tenant, devices, and saved model bindings before submission.

Choose an API ​

ItemRaw payload parsingParsed telemetry writing
Single / batchparse-payload / batch-parse-payloadwrite-telemetry / batch-write-telemetry
Inputdata.msg is the raw JSON object passed to the scriptdata.msg contains a model identifier and a telemetry data object
Target model scriptRuns selected bound modelsSkips the target device parser
Model locationTop-level thing_model_ids / thing_model_id_namesPer-message msg.thing_model_id / msg.thing_model_id_name
return_data: trueThis invocation's raw script outputThis write's merged telemetry snapshot

All requests use POST, JSON, and the existing token header. Tenant routes begin with /thinklink/device/; system routes begin with /thinklink/system/device/, followed by an operation above. Tenant routes derive the tenant from the token; an optional tenant must match it. System routes require SYSADMIN and an explicit top-level tenant containing the target tenant_code. Supplying a tenant does not grant access.

Parameters and model selection ​

  • Single data is an object; batch data is a non-empty array. Each entry has a non-empty string eui and an object msg. Each batch belongs to one tenant; duplicate EUIs are allowed and processed in input order.
  • Parsing passes msg directly to the model script without another eui/data/tenant wrapper, extra JSON.parse, or injected MQTT topic. It must not be a string, array, or null; business fields remain intact.
  • Parsing selectors are top-level arrays: thing_model_ids contains database ID strings; thing_model_id_names contains id_name values, not display names. Both together form a deduplicated union executed in device binding order. Omit both to run all bound models. Empty arrays or blank identifiers are invalid. Models must be bound and usable by the tenant; each name must uniquely match an available bound model. Selection neither changes bindings nor propagates to related virtual-device parsing.
  • Putting selectors inside a data entry returns 400. A batch shares the same selectors and every device must satisfy them. Simulate Send lists only saved bindings and submits top-level thing_model_ids.
  • Writing requires at least one non-empty msg.thing_model_id_name or msg.thing_model_id; when both are present they must identify the same model. Name lookup prefers the tenant then PUBLIC, with permission and binding checks. Top-level parsing selectors are rejected with 400.
  • Writing requires an existing device and bound model, and never creates or binds them. msg.data must be an object, including an empty object. Field values preserve 0, false, null, and nested objects. The whole msg.data must not be null, an array, or a string.
  • Writing merges latest telemetry by model, retains omitted fields, and updates telemetry update_time and device activity using current server time. History follows real_time_storage; disabling history still updates latest values and notifications. Existing forwarding, triggers, related-device and protocol notifications apply; related devices can still run their own parsers.
  • This is not a historical backfill, attribute-update, or arbitrary RPC endpoint. Sending a timestamp does not turn it into backfill. Ordinary telemetry writes do not change configuration time.
  • option is optional; option.return_data defaults to false and accepts only a boolean, not the string "true".

Eight request examples ​

Set TKL_BASE in your own terminal (for example https://your-thinklink-host, without /thinklink) and TKL_TOKEN. These commands submit real data. Replace EUIs, saved model identifiers, tenant and the appropriately authorized token first. The example temperature model must already exist and be bound; parsing output depends on its script. Parsing may instead use top-level thing_model_ids: ["actual-model-id"]; writing uses thing_model_id inside msg.

Tenant side · parse-payload ​

sh
curl --request POST "$TKL_BASE/thinklink/device/parse-payload" \
  --header "token: $TKL_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
  "data": {
    "eui": "00112233",
    "msg": {
      "temperature": 23.5
    }
  },
  "thing_model_id_names": [
    "temperature"
  ],
  "option": {
    "return_data": true
  }
}'

System side (SYSADMIN) · parse-payload ​

sh
curl --request POST "$TKL_BASE/thinklink/system/device/parse-payload" \
  --header "token: $TKL_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
  "tenant": "tenantA",
  "data": {
    "eui": "00112233",
    "msg": {
      "temperature": 23.5
    }
  },
  "thing_model_id_names": [
    "temperature"
  ],
  "option": {
    "return_data": true
  }
}'

Tenant side · batch-parse-payload ​

sh
curl --request POST "$TKL_BASE/thinklink/device/batch-parse-payload" \
  --header "token: $TKL_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
  "data": [
    {
      "eui": "00112233",
      "msg": {
        "temperature": 23.5
      }
    },
    {
      "eui": "00445566",
      "msg": {
        "temperature": 24.5
      }
    }
  ],
  "thing_model_id_names": [
    "temperature"
  ],
  "option": {
    "return_data": true
  }
}'

System side (SYSADMIN) · batch-parse-payload ​

sh
curl --request POST "$TKL_BASE/thinklink/system/device/batch-parse-payload" \
  --header "token: $TKL_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
  "tenant": "tenantA",
  "data": [
    {
      "eui": "00112233",
      "msg": {
        "temperature": 23.5
      }
    },
    {
      "eui": "00445566",
      "msg": {
        "temperature": 24.5
      }
    }
  ],
  "thing_model_id_names": [
    "temperature"
  ],
  "option": {
    "return_data": true
  }
}'

Tenant side · write-telemetry ​

sh
curl --request POST "$TKL_BASE/thinklink/device/write-telemetry" \
  --header "token: $TKL_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
  "data": {
    "eui": "00112233",
    "msg": {
      "thing_model_id_name": "temperature",
      "data": {
        "temperature": 23.5,
        "humidity": 60
      }
    }
  },
  "option": {
    "return_data": true
  }
}'

System side (SYSADMIN) · write-telemetry ​

sh
curl --request POST "$TKL_BASE/thinklink/system/device/write-telemetry" \
  --header "token: $TKL_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
  "tenant": "tenantA",
  "data": {
    "eui": "00112233",
    "msg": {
      "thing_model_id_name": "temperature",
      "data": {
        "temperature": 23.5,
        "humidity": 60
      }
    }
  },
  "option": {
    "return_data": true
  }
}'

Tenant side · batch-write-telemetry ​

sh
curl --request POST "$TKL_BASE/thinklink/device/batch-write-telemetry" \
  --header "token: $TKL_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
  "data": [
    {
      "eui": "00112233",
      "msg": {
        "thing_model_id_name": "temperature",
        "data": {
          "temperature": 23.5,
          "humidity": 60
        }
      }
    },
    {
      "eui": "00445566",
      "msg": {
        "thing_model_id_name": "temperature",
        "data": {
          "temperature": 24.5,
          "humidity": 0
        }
      }
    }
  ],
  "option": {
    "return_data": true
  }
}'

System side (SYSADMIN) · batch-write-telemetry ​

sh
curl --request POST "$TKL_BASE/thinklink/system/device/batch-write-telemetry" \
  --header "token: $TKL_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
  "tenant": "tenantA",
  "data": [
    {
      "eui": "00112233",
      "msg": {
        "thing_model_id_name": "temperature",
        "data": {
          "temperature": 23.5,
          "humidity": 60
        }
      }
    },
    {
      "eui": "00445566",
      "msg": {
        "thing_model_id_name": "temperature",
        "data": {
          "temperature": 24.5,
          "humidity": 0
        }
      }
    }
  ],
  "option": {
    "return_data": true
  }
}'

Responses and failure boundaries ​

Without returned data, both families respond with {"result":true} after processing. A single returned-data envelope is shown below; the ID and time are illustrative:

json
{
  "result": true,
  "data": {
    "eui": "00112233",
    "results": [
      {
        "thing_model_id": "1234567890",
        "data": {
          "telemetry_data": {
            "temperature": 23.5,
            "humidity": 60,
            "update_time": "2026-10-26T00:00:00.000Z"
          }
        }
      }
    ]
  }
}

This writing response contains only the written model and its merged snapshot, including retained fields and server time. It is a deep JSON snapshot captured before the first asynchronous wait. Parsing uses the same envelope but each data is the raw script output (object, array, or null), in binding order, not the complete device state. A normal empty output is not a failure. Batch responses use an outer data array in input order, without deduplicating EUIs.

The whole batch is prechecked for format, tenant, devices, model permissions and bindings, then processed in order with a fresh check before each entry. Precheck failure prevents this batch's writes. Execution is not a transaction: it stops at the first error, earlier writes and notifications remain, and the failing entry may already have partial side effects.

Preserve HTTP 400 (parameters, models, processing), 403 (tenant/permission boundary), 404 (missing target device), and AppError message, format, values. An older server with no route can also return 404; inspect its error and version instead of automatically switching to a different write endpoint.

After timeout or an unknown outcome, mark the result as pending confirmation. Inspect latest values, history, notifications, and device feedback before a bounded retry of unfinished work. Do not blindly replay the whole batch: history and notifications can be duplicated. HTTP success does not confirm buffer flush, external notification delivery, or physical device execution.

RPC shares these writing semantics; see writeTelemetry. Local validation, deployment, and device acceptance are separate states; see the 2.00.099 upgrade guide.