1221  CoAP Message Format and Structure

1221.1 Learning Objectives

By the end of this chapter, you will be able to:

  • Decode CoAP Headers: Interpret the 4-byte fixed header byte-by-byte
  • Understand Options Encoding: Parse CoAP options using delta compression
  • Apply Response Codes: Use proper 2.xx/4.xx/5.xx codes in implementations
  • Match Tokens to Requests: Implement proper request-response correlation

1221.2 Prerequisites

Before diving into this chapter, you should be familiar with:

  • CoAP Introduction - Understanding why CoAP exists and its design goals
  • Binary number representation (bits, bytes, hexadecimal notation)

1221.3 The CoAP Message Format

TipMinimum Viable Understanding: CoAP Message Structure

Core Concept: A CoAP message consists of a 4-byte fixed header, optional token (0-8 bytes), zero or more options, and an optional payload. The fixed header contains version, message type, token length, method/response code, and message ID.

Why It Matters: Understanding the binary format is essential for debugging network captures, implementing CoAP libraries, and optimizing message sizes for constrained networks where every byte counts.

Key Takeaway: The 4-byte header efficiency is CoAP’s main advantage over HTTP - it encodes everything needed for request-response matching, reliability selection, and method identification in just 32 bits.

CoAP messages follow a compact binary format designed for efficiency:

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#16A085', 'secondaryColor': '#E67E22', 'tertiaryColor': '#ecf0f1', 'noteTextColor': '#2C3E50', 'noteBkgColor': '#fff9e6', 'textColor': '#2C3E50', 'fontSize': '13px'}}}%%
flowchart TB
    subgraph Message["CoAP Message Format"]
        direction TB
        Header["Fixed Header (4 bytes)<br/>Ver | Type | TKL | Code | Message ID"]
        Token["Token (0-8 bytes)<br/>For request/response matching"]
        Options["Options (variable length)<br/>URI, Content-Format, etc."]
        Marker["Payload Marker (0xFF)<br/>Separates options from payload"]
        Payload["Payload (application data)<br/>Sensor readings, commands, etc."]
    end

    Header --> Token --> Options --> Marker --> Payload

    style Header fill:#2C3E50,stroke:#16A085,stroke-width:2px,color:#fff
    style Token fill:#16A085,stroke:#2C3E50,stroke-width:2px,color:#fff
    style Options fill:#E67E22,stroke:#2C3E50,stroke-width:2px,color:#fff
    style Marker fill:#7F8C8D,stroke:#2C3E50,stroke-width:2px,color:#fff
    style Payload fill:#16A085,stroke:#2C3E50,stroke-width:2px,color:#fff

Figure 1221.1: CoAP message structure showing the 4-byte fixed header followed by optional token, options with delta compression, payload marker (0xFF), and application payload

1221.4 The 4-Byte Fixed Header

The fixed header contains all essential message metadata in just 32 bits:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Ver| T |  TKL  |      Code     |          Message ID           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Field Bits Description
Ver 2 Version (always 01 for CoAP 1.0)
T 2 Type: CON (00), NON (01), ACK (10), RST (11)
TKL 4 Token Length (0-8 bytes)
Code 8 Method (0.xx) or Response (2.xx-5.xx)
Message ID 16 For deduplication and ACK matching

1221.4.1 Header Fields Explained

Version (Ver): Always 01 binary (value 1) for CoAP RFC 7252.

Type (T): Determines reliability and message flow:

Type Binary Name Purpose
CON 00 Confirmable Reliable, requires ACK
NON 01 Non-confirmable Fire-and-forget
ACK 10 Acknowledgment Confirms CON receipt
RST 11 Reset Rejects message

Token Length (TKL): Number of token bytes (0-8). Tokens correlate responses to requests.

Code: Split into class (3 bits) and detail (5 bits), formatted as class.detail:

Class Range Meaning
0 0.01-0.04 Methods (GET, POST, PUT, DELETE)
2 2.01-2.05 Success responses
4 4.00-4.15 Client error
5 5.00-5.05 Server error

Message ID: 16-bit identifier for: - Matching ACK/RST to CON messages - Detecting duplicate messages (server caches for ~247 seconds)

Sammy the Sensor says: “Imagine sending a message in a bottle! The CoAP header is like the label you put on the bottle.”

The label tells you: - Type: “Please write back!” (CON) or “No reply needed” (NON) - Code: What you want - “Can I have the temperature?” (GET) - Message ID: A number so you know which bottle they’re answering

Just like how a bottle label is tiny compared to the letter inside, CoAP’s header is tiny (4 bytes!) compared to HTTP’s headers (hundreds of bytes!).

1221.5 CoAP Method Codes

CoAP methods mirror HTTP methods but use numeric codes:

Code Method Description Idempotent
0.01 GET Retrieve resource Yes
0.02 POST Create resource or submit data No
0.03 PUT Update/replace resource Yes
0.04 DELETE Remove resource Yes
NoteIdempotent Methods

An idempotent method produces the same result regardless of how many times it’s called: - GET /temperature returns current temperature (safe to retry) - PUT /led {"on": true} sets LED on (calling twice = same result) - DELETE /alarm/5 removes alarm 5 (calling twice = same result, even if already deleted)

POST is NOT idempotent - POST /log {"event": "click"} creates new entry each time.

Why this matters: When a CON message times out, CoAP can safely retry idempotent methods. For POST, the application must handle potential duplicates.

1221.6 Response Codes

CoAP uses a structured response code system similar to HTTP:

1221.6.1 Success (2.xx)

Code Name HTTP Equivalent Use Case
2.01 Created 201 Created POST created new resource
2.02 Deleted 200 OK (for DELETE) Resource removed
2.03 Valid 304 Not Modified Cached response still valid
2.04 Changed 200 OK (for PUT) Resource updated
2.05 Content 200 OK GET successful with payload

1221.6.2 Client Error (4.xx)

Code Name HTTP Equivalent Cause
4.00 Bad Request 400 Malformed request
4.01 Unauthorized 401 Missing authentication
4.03 Forbidden 403 Access denied
4.04 Not Found 404 Resource doesn’t exist
4.05 Method Not Allowed 405 Wrong method for resource
4.12 Precondition Failed 412 ETag mismatch
4.15 Unsupported Content-Format 415 Unknown payload format

1221.6.3 Server Error (5.xx)

Code Name HTTP Equivalent Cause
5.00 Internal Server Error 500 Server crashed
5.01 Not Implemented 501 Method not supported
5.02 Bad Gateway 502 Proxy error
5.03 Service Unavailable 503 Server overloaded
5.04 Gateway Timeout 504 Proxy timeout

1221.7 Token: Request-Response Matching

The Token correlates responses to requests, especially important when multiple requests are in flight:

Client sends: GET /temperature, Token=0xAB12, MsgID=0x5678
Client sends: GET /humidity, Token=0xCD34, MsgID=0x5679

Server sends: 2.05 Content, Token=0xCD34, "65%"  (humidity response)
Server sends: 2.05 Content, Token=0xAB12, "22.5" (temperature response)

Token vs. Message ID:

Aspect Token Message ID
Purpose Match response to request Deduplication, ACK matching
Scope Application-level Transport-level
Length 0-8 bytes (TKL field) Fixed 16 bits
Persistence Across retransmissions Changes on each transmission
Generated by Client Client
WarningToken Security Consideration

In secure deployments (DTLS), tokens should be unpredictable (randomly generated) to prevent response spoofing attacks. A malicious actor who can predict tokens could inject fake responses.

# BAD: Sequential tokens (predictable)
token = counter
counter += 1

# GOOD: Random tokens (unpredictable)
import os
token = os.urandom(4)  # 4 random bytes

1221.8 CoAP Options

Options carry metadata similar to HTTP headers but use efficient binary encoding:

1221.8.1 Common Options

Option Number Name Length Purpose
3 Uri-Host String Target host
7 Uri-Port 0-2 bytes Target port
11 Uri-Path String Path segments (/sensors/temp)
12 Content-Format 0-2 bytes Payload MIME type
14 Max-Age 0-4 bytes Cacheability (seconds)
17 Accept 0-2 bytes Acceptable response format
35 Proxy-Uri String For proxied requests

1221.8.2 Option Delta Encoding

Options are sorted by number and use delta encoding to minimize bytes:

Option format:
+----+----+----------------+----------------+
| OD | OL | [Extended OD]  | Value          |
+----+----+----------------+----------------+
  4    4    0, 1, or 2       Variable
bits  bits   bytes

OD = Option Delta (difference from previous option number)
OL = Option Length

Example: Encoding Uri-Path /sensors/temp

Option 1: Uri-Path = "sensors"
  Delta = 11 (first option, no previous)
  Length = 7 ("sensors")
  Encoded: 0xB7 | "sensors"

Option 2: Uri-Path = "temp"
  Delta = 0 (11 - 11 = 0, same option type)
  Length = 4 ("temp")
  Encoded: 0x04 | "temp"

Total: 13 bytes vs HTTP "GET /sensors/temp HTTP/1.1\r\n" = 27 bytes

1221.8.3 Content-Format Codes

The Content-Format option specifies the payload MIME type:

Code MIME Type Description
0 text/plain Simple text
40 application/link-format Resource discovery
41 application/xml XML data
42 application/octet-stream Binary data
47 application/exi Efficient XML
50 application/json JSON data
60 application/cbor Compact Binary (CBOR)
TipCBOR vs JSON for IoT

CBOR (Concise Binary Object Representation) is to JSON what CoAP is to HTTP - a compact binary alternative:

# JSON (42 bytes)
{"device":"temp42","value":23.5,"unit":"C"}

# CBOR (~20 bytes) - same data, half the size
A3 66 64 65 76 69 63 65 66 74 65 6D 70 34 32...

When to use: - CBOR: Production deployments, battery-powered devices, constrained networks - JSON: Development, debugging, cloud integration with JSON-native APIs

1221.9 Worked Example: Parsing a CoAP Message

NoteWorked Example: Decoding a GET Request

Raw bytes (hexadecimal):

44 01 5A F2 AB CD 00 00 B5 68 65 6C 6C 6F

Step 1: Parse fixed header (4 bytes)

Byte 1: 0x44 = 0100 0100 binary - Ver = 01 = Version 1 (valid CoAP) - T = 00 = CON (Confirmable) - TKL = 0100 = 4 (4-byte token)

Byte 2: 0x01 = Code - Class = 0, Detail = 01 = 0.01 = GET

Bytes 3-4: 0x5AF2 = Message ID = 23282

Step 2: Parse token (TKL = 4 bytes)

Bytes 5-8: AB CD 00 00 = Token = 0xABCD0000

Step 3: Parse options

Byte 9: 0xB5 = 1011 0101 - Option Delta = 11 (decimal) = Uri-Path - Option Length = 5

Bytes 10-14: 68 65 6C 6C 6F = ASCII “hello”

Decoded message:

Field Value
Version 1
Type CON (Confirmable)
Method GET
Message ID 23282
Token 0xABCD0000
Uri-Path /hello

Full URI: coap://server/hello

1221.10 Common Pitfalls

CautionPitfall: Forgetting Resource Discovery Content-Format

The Mistake: Implementing /.well-known/core resource discovery but returning plain JSON instead of the required CoRE Link Format (application/link-format, Content-Format 40), causing standard CoAP clients and gateways to reject the discovery response with 4.06 Not Acceptable.

Why It Happens: Developers familiar with REST APIs assume JSON is universal. However, RFC 6690 mandates that /.well-known/core responses use the CoRE Link Format media type (application/link-format), which has a specific syntax with angle brackets, semicolons, and comma-separated attributes.

The Fix: Return properly formatted CoRE Link Format with Content-Format option set to 40:

Incorrect (JSON - causes 4.06 Not Acceptable):

{"resources": [{"uri": "/temperature", "type": "sensor"}]}

Correct (CoRE Link Format - Content-Format: 40):

</temperature>;rt="oic.r.temperature";if="oic.if.s";ct=50,
</humidity>;rt="oic.r.humidity";if="oic.if.s";ct=50,
</led>;rt="oic.r.switch.binary";if="oic.if.a";ct=50

Link Format attributes: - rt (resource type): Semantic type from IANA registry - if (interface): Interaction model (oic.if.s = sensor, oic.if.a = actuator) - ct (content-format): Payload format for resource (50 = application/json) - obs (observable): Present if resource supports Observe

Always test discovery with standard tools: coap-client -m get coap://device/.well-known/core should return parseable link format, not a 4.06 error.

CautionPitfall: Confusing Message ID with Token

The Mistake: Using Message ID for request-response correlation instead of Token, causing responses to be mismatched when multiple requests are in flight.

Why It Happens: Both fields look like “identifiers,” but they serve completely different purposes:

  • Message ID: Transport-level, for ACK matching and deduplication. Changes on retransmission.
  • Token: Application-level, for matching responses to requests. Same across retransmissions.

The Fix: Always use Token for correlating responses to requests:

# BAD: Using Message ID for correlation
pending_requests[message_id] = callback

# GOOD: Using Token for correlation
pending_requests[token] = callback

When you send a CON message and it times out, you retransmit with the SAME Message ID (so server can deduplicate) but the Token stays the same throughout, allowing proper response matching.

1221.11 Summary

The CoAP message format achieves remarkable efficiency:

  • 4-byte fixed header encodes version, type, code, and message ID
  • Tokens (0-8 bytes) correlate responses to requests
  • Delta-encoded options minimize metadata overhead
  • Response codes mirror HTTP semantics (2.xx success, 4.xx client error, 5.xx server error)

Understanding this format is essential for debugging network captures, implementing CoAP libraries, and optimizing message sizes for constrained networks.

1221.12 What’s Next

Now that you understand the message format: