CoAP
This page describes the CoAP connectivity in more detail
The Constrained Application Protocol (CoAP) (RFC 7252) is a specialized web transfer protocol designed for resource-constrained devices and networks. Operating over UDP, CoAP provides a low-overhead alternative to TCP/HTTP, making it ideal for IP-based IoT devices that require minimal power and bandwidth consumption.
To connect a CoAP-enabled device to akenza, you must first create a CoAP Device Connector and assign it to a Data Flow (Create Data Flow > Start with empty Data Flow > + Device Connector > CoAP > New CoAP Device Connector).
For Pre-shared Key Authentication: a unique secret is generated upon creating the connector. Your device must pass this secret and the Device ID as query parameters in every CoAP request.
For Secure X.509 Authentication: devices authenticate cryptographically via DTLS. You must submit your Custom Certificate Authority (CA) to akenza and provision devices matching the certificates, as detailed in the setup guide below.
By default CoAP communication without DTLS is insecure. Even if data transmission over a NB-IoT network is inherently encrypted within the cellular network, once the data leaves the carrier network for the public internet, additional measures are required to maintain security.
Therefore, we recommend using DTLS to secure the connection (see below). If your device does not support DTLS, the following alternative architecture can be implemented.
Fallback Architecture: Secure Tunnel
Private APN: Use a private APN configured by your network operator.
VPN Tunnel: Establish a secure VPN tunnel between the operator's APN and akenza.
Result: Data remains in its lightweight UDP format for the device, while the "hop" over the public internet is protected from eavesdropping by the tunnel.
Contact akenza for more information regarding transport security.
Secure Transport (DTLS)
To secure communication over unencrypted UDP networks, akenza supports Datagram Transport Layer Security (DTLS). This ensures confidentiality, data integrity, and secure device authentication.
X.509 Certificate Authentication
Devices can authenticate securely with akenza using X.509 Certificates. During the DTLS handshake, the device presents its cryptographic certificate to verify its identity, establishing an encrypted channel before any CoAP requests are transmitted. akenza will automatically extract the deviceId from the common name (CN) in the certificate.
To utilize X.509 certificate authentication, you must generate a Device Certificate Authority (CA) and individual Device Client Certificates for your devices. For more information, refer to DTLS Device Client Certificates.
Verifying the server identity
akenza uses server certificates issued by ZeroSSL. Make sure your devices trust the Sectigo Root Certificates specifically ZeroSSLECCDVSSLCA2.crt.
DTLS with Connection ID (CID)
To avoid costly DTLS handshakes after periods of inactivity, the protocol uses DTLS Connection IDs (as defined in RFC 9146). The CID allows the DTLS session to be resumed without a full handshake even when the device's IP address or UDP port changes (e.g., after a network sleep cycle).
The device aims to perform as few handshakes as possible. The DTLS session is kept alive across transmission intervals by using the CID, so a new handshake is only required when the session cannot be resumed.
Uplinks
Content Format
The akenza CoAP connector supports multiple payload formats. Ensure your device includes the correct Content-Format option header in its requests to match the transmitted data type:
50
application/json
Standard JSON payload, parsed directly into a structured JSON object. If JSON parsing fails:
Valid UTF-8 payload: Converted to plain text (
value) plus Hex (payloadHex) and Base64 representations (payloadBase64).Invalid UTF-8 payload: Converted to Hex (
payloadHex) and Base64 representations (payloadBase64).
60
application/cbor
Concise Binary Object Representation (CBOR). Parsed and converted into standard JSON.
41
application/xml
XML payload, automatically converted into a JSON structure.
42
application/octet-stream
Raw binary data. Automatically converted into both Hex (payloadHex) and Base64 (payloadBase64) string representations for the decoder.
0
text/plain
Plain text. The raw string is placed into a JSON object under the key value. Note that the string is expected to have utf-8 format.
Sending a CoAP Uplink
POST coap://coap.akenza.io:5683/v3/capture?secret={uplinkSecret}&deviceId={deviceId}
The body can be any JSON object.
Query Parameters
timestamp
string
The timestamp of the event (ISO-8601 formatted - the current time will be used if not provided)
topic
string
The data topic ("default" will be used if not provided)
uplinkSecret
string
The uplink secret used to authenticate the request
deviceId
string
The device ID
Headers
content_format
number
application/json
Sample NodeJS Script
The below sample nodeJS script allows sending a CoAP uplink. It requires the node module coap to be installed.
Downlinks
CoAP downlinks enable cloud-to-device command delivery CoAP devices. All downlink interactions enforce a secure connection, support persistent queuing, and adhere to RFC 7252 delivery mechanics.
Security & Encryption
Enforced Encryption: Plain CoAP downlinks over UDP is prohibited. All interactions must be wrapped in DTLS (CoAPs) to prevent command spoofing and replay attacks.
Replay Protection: Devices must track transaction sequence IDs to identify and suppress duplicate frames.
Queue Handling Strategies
Commands are stored in a persistent queue prior to transmission. When new commands are dispatched from the backend, the queue applies one of two policies:
Queue Policy
Description
ADD
Appends the incoming command to the target device's existing command queue.
REPLACE
Purges all pending, unacknowledged commands before inserting the new command.
Delivery Patterns
Because CoAP runs over UDP and devices are typically sleeping, akenza never pushes commands; instead devices retrieve pending commands when they wake up, via either a dedicated command pull or piggy-backed on a regular uplink. Commands are delivered strictly FIFO, one command per request.
Command Pull: a dedicated GET /v3/commands request:
Commands pending
2.05 Content with the command payload, in the content format chosen at enqueue time (JSON by default)
Queue empty
2.05 Content with an empty response based on Accept header:
JSON (default):
{}CBOR:
0xA0XML:
""
Piggy-Backed Uplink: a pending command replaces the response of a standard POST /v3/capture uplink (requires the downlinkOnUplink feature on the device connector):
Commands pending
2.05 Content with the command payload in the content format chosen at enqueue time (JSON by default) instead of the usual uplink acknowledgment
Queue empty
2.01 Created with the normal uplink acknowledgment
Devices using piggy-back must therefore branch on the response code: 2.05 carries a command, 2.01 is a plain uplink acknowledgment.
Each command contains a transaction id (tx_id) that is used to confirm receipt on the application-layer (see Piggy-Backed Execution Flow):
Command Pull Execution Flow (confirmed command)
Confirmed commands (confirmed=true, the default) are delivered as a separate response (RFC 7252 §5.2.2) so that the device's transport-level ACK can serve as the delivery confirmation:
Piggy-Backed Execution Flow
A piggy-backed response travels inside the CoAP ACK itself, so no transport-level delivery confirmation exists. The command stays IN_FLIGHT until the device confirms it with a completed_tx_id in a later uplink (or the command expires):
Unconfirmed command pulls (confirmed=false) are delivered as a piggy-backed response and also require a separate acknowledgement in a later uplink (not recommended):
Transport Reliability & Response Mechanics
The server uses the two response patterns:
Piggy-backed response: response code and payload travel inside the ACK, sharing the request's Message ID (1 RTT). Used for empty-queue replies,
confirmed=falsecommands, and all piggy-backed uplink deliveries. Fast, but provides no delivery confirmation. Use this strategy for fleets that require the 1-RTT/RAI pattern and acknowledge at the application layer.Separate response: used for confirmed commands (
confirmed=true) on the pull endpoint. This is a deliberate choice to obtain a delivery confirmation.Empty ACK (
0.00): sent immediately with the request's Message ID; the device stops its request retransmission timer. The ACK is a promise that a response follows.Separate CON delivery: the command follows in a new CON message with a new Message ID and the original Token, retransmitted with standard Stop-and-Wait ARQ and exponential backoff until the device acknowledges or rejects it.
Delivery confirmation: the device answers the CON with an empty ACK of its own - this is what purges the command from the queue.
Critical requirement: devices must stay awake after receiving an Empty ACK. The follow-up CON leaves the server within milliseconds; a device that sleeps immediately misses it (and its retransmissions), and the command is requeued after the backoff.
Failure Handling & Crash Recovery
Re-queuing: any failed delivery attempt returns the command to
QUEUEDfor the next pull or uplink window. All three transport failure signals are handled: retransmission timeout, device rejection (RSTthe server stops retransmitting immediately, per RFC 7252), and send errors (e.g. the DTLS session is gone).In-flight lock: fetching a command moves it from
QUEUEDtoIN_FLIGHTwith an attempt counter and a 60-second lock. AnIN_FLIGHThead is intentionally re-delivered on the device's next request: if the server restarts mid-delivery, the device's retransmitted request receives the same command again. Devices must therefore track receivedtx_ids and suppress duplicates.Expiry: commands not delivered within the item TTL (default 7 days) are dropped.
Application-level ACK (
completed_tx_id): independent of all transport events (retransmissions, DTLS re-handshakes, server restarts), a device can purge a command by reporting its transaction ID at the root of a subsequent uplink payload. This is the only way to clear a piggy-backed / unconfirmed command:
Queuing a CoAP Downlink
Body Parameters
coapDownlink.payload
object
A json object containing the command payload. If a device type encoder is present this will be first encoded.
coapDownlink.clearQueue
boolean
Whether to replace any pending downlinks.
coapDownlink.contentType
string
The command encoding type. Can be one of JSON (default), CBOR or XML.
coapDownlink.confirmed
boolean
Whether the downlink is confirmable (only relevant for command pull).
raw
boolean
Whether to skip the device type encoder, this value has no effect for passthrough data flows.
Additional devices
akenza provides dedicated support for Efento devices.
Last updated
Was this helpful?