CoAP Integration
CoAP Integration lets devices that use the Constrained Application Protocol (CoAP) send data to ThingsBoard through a dedicated CoAP endpoint. CoAP is a lightweight UDP-based request/response protocol for low-power, low-bandwidth hardware. An uplink data converter transforms each incoming request into device or asset data, including telemetry and attributes. The integration is uplink-only: it does not send downlink messages to devices.
Use CoAP Integration when:
- Devices use CoAP (RFC 7252): constrained MCUs, NB-IoT modules, or similar hardware.
- Firmware lacks the memory or power budget for HTTP or MQTT.
- The network is lossy or UDP-only.
- The device firmware sends a payload format you cannot change.
Architecture
Section titled “Architecture”- Receive data. A device sends a CoAP
POSTorPUTrequest to the integration endpoint — port5683for plain CoAP,5684for DTLS. Confirmable (CON) and non-confirmable (NON) requests are both accepted. - Decode the payload. The uplink converter identifies the device or asset and extracts attributes and telemetry.
- Process the message. ThingsBoard processes the converted data through the Rule Engine. If Allow create devices or assets is enabled, ThingsBoard can create an entity that does not exist yet.
- Respond. ThingsBoard answers
2.01 Createdon success or4.00 Bad Requestif processing fails. The response carries no data: CoAP Integration does not support downlink.
Prerequisites
Section titled “Prerequisites”- Access to ThingsBoard or ThingsBoard Cloud with integrations enabled for your tenant.
- Permission to create integrations and data converters.
- Devices that can reach the ThingsBoard CoAP port over UDP.
- A sample of the payload your device sends.
- A CoAP client for testing, for example
coap-clientfrom libcoap. On Debian and Ubuntu, thelibcoap3-binpackage installs it ascoap-client-notls(plain CoAP) andcoap-client-gnutlsorcoap-client-openssl(with DTLS support).
Set Up CoAP Integration
Section titled “Set Up CoAP Integration”Create the uplink converter, assign it to a new CoAP integration, and send a test request.
Create the Uplink Data Converter
Section titled “Create the Uplink Data Converter”The uplink converter receives the CoAP request body as a byte array, resolves the target device, and returns its telemetry and attributes. CoAP Integration uses the Generic converter: you write the decoder in TBEL or JavaScript. For converter types and the full decoder reference, see Uplink data converter.
-
Go to Integrations ⇾ Data converters and click + Add data converter ⇾ Create new converter.
-
Converter type — keep Uplink.
-
Integration type — select CoAP.
-
Name — enter a name, for example
CoAP Uplink Converter. -
Main decoding configuration — the editor is pre-filled with the default decoder template for JSON payloads. For JSON, you can keep it. For text or binary payloads, replace it with the template for your payload type below: the default template rejects them with
Payload is not a JSON object. Use it as a starting point and, if needed, adapt the field names to your device. The editor opens in TBEL; use the TBEL / JS toggle to switch to JavaScript./** Decoder **/// decode payload to stringvar strArray = decodeToString(payload);var payloadArray = strArray.replaceAll("\"", "").split(',');// Result object with device attributes/telemetry datavar result = {deviceName: payloadArray[0],deviceType: payloadArray[1],attributes: {serialNumber: payloadArray[2]},telemetry: {temperature: parseFloat(payloadArray[3])}};/** Helper functions 'decodeToString' and 'decodeToJson' are already built-in **/return result;/** Decoder **/// decode payload to stringvar strArray = decodeToString(payload);var payloadArray = strArray.replace(/\"/g, "").replace(/\s/g, "").replace(/\\n/g, "").split(',');// Result object with device attributes/telemetry datavar result = {deviceName: payloadArray[0],deviceType: payloadArray[1],attributes: {serialNumber: payloadArray[2]},telemetry: {temperature: parseFloat(payloadArray[3])}};/** Helper functions **/function decodeToString(payload) {return String.fromCharCode.apply(String, payload);}return result;The payload is a comma-separated string with a fixed field order:
deviceName,deviceType,serialNumber,temperature.To adapt:
- Another delimiter — replace
split(','), for example withsplit(';'). - Another field order — change the
payloadArrayindexes. - More values — add them to
telemetry, for examplebatteryLevel: parseFloat(payloadArray[4]). - Attributes or telemetry — move a field between the
attributesandtelemetrymaps.
/** Decoder **/// decode payload to JSONvar data = decodeToJson(payload);// Result object with device attributes/telemetry datavar result = {deviceName: data.deviceName,deviceType: data.deviceType,attributes: {serialNumber: data.serialNumber},telemetry: {temperature: data.temperature}};/** Helper functions 'decodeToString' and 'decodeToJson' are already built-in **/return result;/** Decoder **/// decode payload to JSONvar data = decodeToJson(payload);// Result object with device attributes/telemetry datavar result = {deviceName: data.deviceName,deviceType: data.deviceType,attributes: {serialNumber: data.serialNumber},telemetry: {temperature: data.temperature}};/** Helper functions **/function decodeToString(payload) {return String.fromCharCode.apply(String, payload);}function decodeToJson(payload) {var str = decodeToString(payload);var data = JSON.parse(str);return data;}return result;The default template already decodes JSON: it reads
deviceNameanddeviceType, storesserialNumberas an attribute, takestsortimestampas the time, and stores all other fields as telemetry. It rejects a message withoutdeviceName, and a payload that is not a JSON object, with a clear error in the debug events. Keep it as is, or use the shorter example above if you want to map only specific fields.The example parses the payload as JSON and maps the fields directly.
To adapt:
- Other identity fields — replace
data.deviceNameanddata.deviceType, for example withdata.idanddata.model. - Other telemetry — add or remove fields, for example
pressure: data.pressure. - Device properties — move them to
attributes, for examplefirmware: data.fw.
/** Decoder **/// decode payload to stringvar payloadStr = decodeToString(payload);var deviceName = payloadStr.substring(0, 13);var deviceType = payloadStr.substring(13, 23);// Result object with device attributes/telemetry datavar result = {deviceName: deviceName,deviceType: deviceType,attributes: {serialNumber: payloadStr.substring(23, 31)},telemetry: {temperature: parseFloat(payloadStr.substring(31, 35))}};/** Helper functions 'decodeToString' and 'decodeToJson' are already built-in **/return result;/** Decoder **/// decode payload to stringvar payloadStr = decodeToString(payload);var deviceName = payloadStr.substring(0, 13);var deviceType = payloadStr.substring(13, 23);// Result object with device attributes/telemetry datavar result = {deviceName: deviceName,deviceType: deviceType,attributes: {serialNumber: payloadStr.substring(23, 31)},telemetry: {temperature: parseFloat(payloadStr.substring(31, 35))}};/** Helper functions **/function decodeToString(payload) {return String.fromCharCode.apply(String, payload);}function decodeToJson(payload) {var str = decodeToString(payload);var data = JSON.parse(str);return data;}return result;The decoder converts the bytes to a string and reads each value from a fixed offset — typical for compact binary protocols.
To adapt:
- Another byte layout — change the
substring(start, end)offsets. - Raw integer fields — use
parseBytesToInt(payload, offset, length, bigEndian)instead ofparseFloat(payloadStr.substring(...)), for exampletemperature: parseBytesToInt(payload, 31, 2, true) / 100.0. - Fixed name or type — replace the
substringcalls with string literals.
- Another delimiter — replace
-
Click Test decoder function and check the result (see Test the Decoder).
-
Click Add.
Sample Payloads
This guide uses device Thermostat-01, which reports its serial number and temperature in one of three formats:
Thermostat-01,thermostat,SN-12345,25.5{ "deviceName": "Thermostat-01", "deviceType": "thermostat", "serialNumber": "SN-12345", "temperature": 25.5}Hex string:
\x54\x68\x65\x72\x6d\x6f\x73\x74\x61\x74\x2d\x30\x31\x74\x68\x65\x72\x6d\x6f\x73\x74\x61\x74\x53\x4e\x2d\x31\x32\x33\x34\x35\x32\x35\x2e\x35Byte layout:
| Bytes | Hex | Value |
|---|---|---|
| 0–12 | \x54\x68\x65\x72\x6d\x6f\x73\x74\x61\x74\x2d\x30\x31 |
Device name: Thermostat-01 |
| 13–22 | \x74\x68\x65\x72\x6d\x6f\x73\x74\x61\x74 |
Device type: thermostat |
| 23–30 | \x53\x4e\x2d\x31\x32\x33\x34\x35 |
Serial number: SN-12345 |
| 31–34 | \x32\x35\x2e\x35 |
Temperature: 25.5 |
Decoder Inputs
The decoder signature is function decoder(payload, metadata): object | object[].
| Variable | Type | Content |
|---|---|---|
payload |
byte array | The CoAP request body. |
metadata |
{[key: string]: string} |
integrationName, the key–value pairs from the integration’s Metadata field, and the CoAP options below. |
CoAP options added to metadata when present in the request:
| Key | Value |
|---|---|
Option:content-format |
Numeric Content-Format code, for example 50 for application/json, 0 for text/plain, 42 for application/octet-stream. |
Option:uri |
Request URI path and query, for example /i/{integrationKey}/extra?x=1. |
Option:location-path |
Location-Path option values, comma-separated. |
Option:location-query |
Location-Query option values, comma-separated. |
The Content-Format also sets how debug events display the payload: JSON (codes 50, 110, 11543), text (0, 41, 46, 11542), or binary (any other code, or none).
Built-in helpers:
| Function | Result |
|---|---|
decodeToJson(payload) |
Parses the bytes as JSON and returns an object. |
decodeToString(payload) |
Converts the bytes to a string. |
parseBytesToInt(payload, offset, length, bigEndian) |
TBEL only. Reads an integer from raw bytes. |
For field renaming, nested values, and other patterns, see Common scripting patterns.
Test the Decoder
Click Test decoder function below the editor. The dialog opens with pre-filled Metadata rows, Payload content type set to JSON, and a sample payload. To test the JSON template, replace the sample payload with:
{ "deviceName": "Thermostat-01", "deviceType": "thermostat", "serialNumber": "SN-12345", "temperature": 25.5}Click Test. The Output panel shows:
{ "deviceName": "Thermostat-01", "deviceType": "thermostat", "attributes": { "serialNumber": "SN-12345" }, "telemetry": { "temperature": 25.5 }}Check that deviceName is set, attributes holds serialNumber, and telemetry holds temperature. The output above comes from the JSON example. The default template also adds the time: "telemetry": {"ts": <current time>, "values": {"temperature": 25.5}}. If the output is empty or shows an error, fix the decoder and test again.
Create the Integration
Section titled “Create the Integration”- Go to Integrations ⇾ Integrations and click + Add integration.
- Basic settings:
- Integration type — select CoAP.
- Name — keep the default
CoAP integrationor enter your own. - Enable integration and Allow create devices or assets — keep both on, so the device is created on its first uplink.
- Click Next.
- Uplink data converter:
- Click Select existing and choose
CoAP Uplink Converter. - Alternatively, click Create new to write a decoder here.
- Click Next.
- Click Select existing and choose
- Connection:
- Keep Security mode set to NO SECURE. To encrypt traffic, see Enable DTLS.
- Copy the CoAP endpoint URL. You need it to send uplinks.
- Leave the other parameters at their defaults. See Connection Settings for details.
- Click Add.
Connection Settings
Section titled “Connection Settings”| Parameter | Description |
|---|---|
| Security mode | NO SECURE (default) — plain CoAP only; DTLS requests are rejected. DTLS — CoAP over DTLS 1.2 only; plain CoAP requests are rejected. MIXED — both. DTLS and MIXED need DTLS enabled on the server; see Enable DTLS. |
| Base URL | Plain CoAP address, used in NO SECURE and MIXED modes. Pre-filled with your host, for example coap://my.thingsboard. You can append a path, for example /sensors; it becomes part of the endpoint URL. |
| DTLS Base URL | DTLS address, used in DTLS and MIXED modes. Pre-filled with your host, for example coaps://my.thingsboard. |
| CoAP endpoint URL | Read-only. Format: {Base URL}/i/{integrationKey}, where integrationKey is a UUID assigned at creation. The DTLS endpoint uses the same format with the DTLS Base URL. Accepts POST and PUT. Extra path segments after the key are accepted and reach the converter in Option:uri. |
| Execute remotely | Runs the integration as a separate process outside the ThingsBoard cluster, for example in a private network. ThingsBoard generates an Integration key and Integration secret for it. See Remote Integration. |
| Description | Optional free-text description. |
| Metadata | Key–value pairs added to the metadata of every message from this integration. |
Test Uplink
Section titled “Test Uplink”Send a request to the endpoint, then confirm that ThingsBoard decoded it and created the device.
Send a Test Uplink
Section titled “Send a Test Uplink”Send the test message with coap-client (part of libcoap) or any CoAP client that supports POST requests.
Replace $YOUR_COAP_ENDPOINT_URL with the CoAP endpoint URL and run the command for your payload type:
echo -n 'Thermostat-01,thermostat,SN-12345,25.5' | coap-client -m post $YOUR_COAP_ENDPOINT_URL -t text/plain -f-echo -e -n '{"deviceName": "Thermostat-01", "deviceType": "thermostat", "serialNumber": "SN-12345", "temperature": 25.5}' | coap-client -m post $YOUR_COAP_ENDPOINT_URL -t application/json -f-echo -e -n '\x54\x68\x65\x72\x6d\x6f\x73\x74\x61\x74\x2d\x30\x31\x74\x68\x65\x72\x6d\x6f\x73\x74\x61\x74\x53\x4e\x2d\x31\x32\x33\x34\x35\x32\x35\x2e\x35' | coap-client -m post $YOUR_COAP_ENDPOINT_URL -t application/octet-stream -f-No output means success: the endpoint answered 2.01 Created with an empty body.
Verify the Result
Section titled “Verify the Result”- Integration events — go to Integrations ⇾ Integrations, open the integration, and click the Events tab. The uplink appears with status OK.
- Converter events — open
CoAP Uplink Converterin Integrations ⇾ Data converters and click the Events tab. In shows the received payload, Out shows the decoded result, and Metadata shows the CoAP options (Option:content-format,Option:uri) and the integration name. - Device — go to Devices & assets ⇾ Devices. The integration has created Thermostat-01. Open it and confirm
temperature = 25.5on the Latest telemetry tab andserialNumber = SN-12345on the Attributes tab (Client attributes).
Enable DTLS
Section titled “Enable DTLS”DTLS encrypts CoAP traffic. To use the DTLS or MIXED security mode, the ThingsBoard CoAP server must have DTLS enabled. Otherwise, the integration fails to save with CoAP server doesn't have DTLS Endpoint enabled!.
Enable DTLS in thingsboard.yml or set these environment variables, then restart ThingsBoard:
# Enable/disable DTLS 1.2 supportexport COAP_DTLS_ENABLED=true# Default CoAP DTLS bind portexport COAP_DTLS_BIND_PORT=5684# Server credentials type: KEYSTORE (Java key store) or PEM (the default)export COAP_DTLS_CREDENTIALS_TYPE=KEYSTORE# Path to the key store that holds the SSL certificateexport COAP_DTLS_KEY_STORE=coapserver.jks# Password used to access the key storeexport COAP_DTLS_KEY_STORE_PASSWORD=server_ks_password# Password used to access the keyexport COAP_DTLS_KEY_PASSWORD=server_key_password# Key aliasexport COAP_DTLS_KEY_ALIAS=serveralias# Skip certificate validity check for client certificatesexport TB_COAP_X509_DTLS_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT=falseThen open the integration, click Toggle edit mode (pencil icon), set Security mode to DTLS or MIXED, check the DTLS Base URL, and click Apply changes. Devices send to the coaps:// endpoint on port 5684. In DTLS mode, plain CoAP requests to port 5683 are rejected with 4.00 Bad Request. In MIXED mode, the plain endpoint stays active on port 5683.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Fix |
|---|---|---|
| No response or timeout | Wrong endpoint URL, or the UDP port is blocked. | Copy the CoAP endpoint URL again. Open UDP port 5683 (or 5684 for DTLS). |
4.04 Not Found |
Wrong integration key in the URI, or the integration is disabled. | Copy the endpoint URL from the integration again. |
4.05 Method Not Allowed |
The device sent GET or DELETE. |
Use POST or PUT. |
4.00 Bad Request |
The decoder threw an error or the payload is malformed. | Check the converter’s Events tab for the error. |
4.00 Bad Request for every request; the converter receives nothing |
The request protocol does not match Security mode: a DTLS request to a NO SECURE integration, or a plain CoAP request to a DTLS integration. | Send to the endpoint that matches the mode: coap:// (port 5683) for NO SECURE, coaps:// (port 5684) for DTLS. To accept both, set Security mode to MIXED. |
Integration fails to save: CoAP server doesn't have DTLS Endpoint enabled! |
DTLS or MIXED mode is selected, but the server has DTLS off. | Enable DTLS on the server, or use NO SECURE. |
| DTLS handshake fails | Client and server certificates or cipher suites do not match. | Check the key store and client credentials against the DTLS server configuration. |
4.00 Bad Request; the events show Device name is missing. Provide deviceName in the payload. |
The converter uses the default template, and the payload has no deviceName, or it is empty. |
Add deviceName to the payload, or adapt the decoder to read the name from another field. |
4.00 Bad Request; the events show Payload is not a JSON object |
The device sends text or binary data, and the converter uses the default template, which decodes JSON only. The error also shows the request’s Content-Format. | Replace the decoder with the Text payload or Binary payload example from Create the Uplink Data Converter. |
4.00 Bad Request; the events show an error about deviceName or assetName, for example Either 'deviceName' or 'assetName' should be present in the converter output! |
A custom decoder returned no deviceName (for example {}) or an empty one. |
Check In and Error in the converter events. Make sure the payload has the field the decoder reads. |
| Telemetry not saved, no error | The decoder returns [] or empty telemetry. |
Make sure telemetry holds at least one key. |
| Wrong timestamp | The device sends ts in seconds. |
Convert it: ts: data.ts * 1000. |
To read debug events, open the integration or converter, click Events, and select an event: In is the received data, Out is the converter result, and Error holds the error text.
See Also
Section titled “See Also”- Integrations overview — how integrations, converters, uplinks, and downlinks fit together.
- Uplink data converter — decoder inputs, output format, and scripting patterns.
- Remote Integration — run the integration outside the ThingsBoard cluster.
- CoAP API — connect devices directly to ThingsBoard over CoAP, without an integration.
- TBEL — built-in functions and syntax for converter scripts.
- Rule Engine — how messages are processed after decoding.
Was this helpful?