Designing CoAP APIs follows REST principles: structure resources as nouns (e.g., /sensors/temp/value), use CoAP methods as verbs (GET, POST, PUT, DELETE), and select compact content formats like CBOR over JSON to minimize payload size on constrained networks. Proper response codes (2.xx/4.xx/5.xx), DTLS security, and rate limiting are essential for robust production IoT APIs.
Phoebe’s Field Notes: Does the 63% Number Survive Real Battery Physics?
Phoebe’s Why
This chapter’s own worked example is charge accounting done correctly: milliamp-seconds for a TX pulse plus an RX ACK wait, summed over 525,600 messages a year, giving CON 190 mAh/year against NON’s 117 mAh/year – a genuine 63% longer life for NON on a 220 mAh CR2032. That comparison is more robust than it looks, because a milliamp-hour is defined as current integrated over time, \(Q=\int I\,dt\), and that integral does not care what the voltage was doing while the current flowed. Voltage sag from the cell’s own internal resistance changes how many real joules were delivered and whether the radio browns out mid-message – but it does not change the coulomb count this chapter’s 63% figure is built from.
The Derivation
Charge is a pure current-time integral, independent of voltage:
\[Q=\int I\,dt\]
Terminal voltage under load is what sag actually changes:
\[V_{load}=V_{oc}-I\,R_{int}\]
Real delivered energy differs from the naive \(V_{oc}Q\) figure by the internal \(I^2R\) loss:
Worked Numbers: This Chapter’s Own CON/NON Comparison
The 63% figure, reproduced from this chapter’s own numbers: CON \(=525{,}600\times0.278\,\mu\text{Ah}+43.8=189.8\) mAh/yr \(\to220/189.8=1.159\) yr; NON \(=525{,}600\times0.139\,\mu\text{Ah}+43.8=116.8\) mAh/yr \(\to220/116.8=1.884\) yr – a 62.5% longer life, matching this chapter’s stated 63% (the small gap comes from this chapter’s own intermediate rounding to 146/73/190/117).
Does voltage sag change that number? No – \(Q=\int I\,dt\) is the same integral whether or not the cell sags, so the mAh comparison above is exact regardless of \(R_{int}\).
What sag does threaten (catalog-typical CR2032, \(R_{int}\approx15\,\Omega\) fresh): the 10 mA TX pulse sags \(10\times15=150\) mV (5.00% of 3.0 V, terminal 2.85 V); the 5 mA RX-ACK wait sags 75 mV (2.50%, terminal 2.925 V) – both comfortable on a fresh cell. Catalog-typical aged \(R_{int}\approx100\,\Omega\) turns the TX sag into \(10\times100=1.00\) V, terminal 2.00 V – close enough to many 3.3 V-class radios’ brownout floor that the “63% longer life” promise is only as good as the cell’s ability to survive that specific 10 mA pulse near end of life, a question the mAh comparison cannot answer by itself.
Self-discharge over this chapter’s own timescale: at a catalog-typical CR2032 shelf rate of \(\approx1\%\)/year, CON’s 1.16-year life loses \(\approx1.16\%\) to self-discharge and NON’s 1.88-year life loses \(\approx1.88\%\) – a 0.72 percentage-point drag against NON’s advantage, negligible next to the 63% headline. That is the opposite emphasis from a multi-decade CR2032 claim, where self-discharge dominates; over this chapter’s own 1-2 year window, the CON-versus-NON message design is still the number that matters.
9.1 Start With the Sensor Menu
Before code, imagine what another device needs to ask: “what is the current value?”, “what format can I get?”, “may I change the sampling rate?”, and “how do I know the change worked?” A good CoAP API turns those questions into short resource paths, method rules, formats, response codes, and limits.
The story of this page is the move from a friendly idea to a resource contract that a sleepy client, gateway, cache, and security review can all understand.
9.2 Learning Objectives
By the end of this chapter, you will be able to:
Design RESTful CoAP APIs: Construct resource hierarchies using nouns with proper URI conventions and versioning strategies
Distinguish Content Formats: Compare JSON, CBOR, and text/plain payloads and select the appropriate format for constrained versus development environments
Implement Error Handling: Apply proper CoAP response codes (2.xx/4.xx/5.xx) and construct helpful error payloads for production systems
Configure Security Controls: Configure DTLS, rate limiting, and per-device access control for production CoAP deployments
Calculate Energy Tradeoffs: Calculate battery life impact of CON versus NON message types and justify the selection for different IoT use cases
Diagnose API Pitfalls: Identify and resolve common CoAP API problems such as Observe notification floods and proxy caching issues
Quick Check: CoAP API Boundary
For Beginners: CoAP API Design
Designing a CoAP API means deciding how IoT devices expose their data and accept commands. Think of it as creating a menu for your sensor – you define endpoints like /temperature or /status that other devices can read. Good API design makes your IoT system easy to use and understand, even for developers who have never seen your device before.
Sensor Squad: Building Sammy’s Data Menu
“I want other devices to read my temperature, but I also want them to set my sampling rate,” said Sammy the Sensor. “How do I organize all that?”
Max the Microcontroller sketched out a plan. “You create a resource tree, Sammy! Your root is /sensor, and under it you have /sensor/temperature for reading data and /sensor/config for settings. When someone sends a GET to /sensor/temperature, they get your latest reading. When they send a PUT to /sensor/config, they can change your sampling rate.”
“Keep your URIs short!” advised Bella the Battery. “Every extra character in the path costs bytes, and CoAP packets should stay small enough to fit in a single UDP datagram. Use /temp instead of /temperature-reading-in-celsius. Every byte saved is energy saved!”
Lila the LED added: “And don’t forget content negotiation! Some devices want your data in JSON, others in CBOR – which is like compressed JSON. Your API should let the requester choose the format using the Accept option. One sensor, multiple formats, everyone is happy!”
9.3 Prerequisites
Before diving into this chapter, you should be familiar with:
9.4 Continue: CoAP Resource Contract and Negotiation Design
The main chapter below stays focused on the API design flow: resource naming, payload choices, security, rate limits, worked examples, and implementation checks. For the deeper contract behind URI/method/content-format/response-code tuples, Accept versus Content-Format negotiation, Max-Age and ETag cache behavior, Observe throttling, and HTTP-CoAP proxy mapping, continue to CoAP Resource Contract and Negotiation Design.
9.5 RESTful Resource Design
Minimum Viable Understanding: CoAP REST Design
Core Concept: CoAP follows REST principles - design resources as nouns, use HTTP-like methods as verbs. The URI identifies WHAT you’re accessing, the method specifies HOW.
Why It Matters: Consistent RESTful design makes APIs intuitive for developers, enables caching, supports proxying, and allows standard tooling to work seamlessly.
Key Takeaway: Good resource design: coap://sensor.local/v1/temperature (noun). Bad design: coap://sensor.local/getTemperature (verb in URL).
9.5.1 Good vs. Bad Resource Design
Good resource design (nouns):
coap://sensor.local/v1/temperature - read a temperature resource
coap://sensor.local/v1/humidity - read a humidity resource
coap://actuator.local/v1/led/state - manage LED state
IoT devices often run for years - plan for API evolution:
URI versioning (recommended for CoAP):
coap://sensor.local/v1/temperature - original API
coap://sensor.local/v2/temperature - new version with metadata
Why URI versioning for IoT:
Simple for embedded clients
No custom header parsing needed
Clear in logs and debugging
Works with all CoAP libraries
Version migration example:
v1: GET coap://sensor.local/v1/temperature returns "23.5".
v2: GET coap://sensor.local/v2/temperature returns a richer CBOR payload with value, unit, and timestamp.
Rollout rule: legacy devices stay on v1 while new devices adopt v2.
9.6 Error Handling
Use proper CoAP response codes and provide helpful error payloads:
9.6.1 Standard Response Codes
2.01 Created - POST created a new resource
2.04 Changed - PUT updated an existing resource
2.05 Content - GET returned a payload
4.00 Bad Request - request syntax or payload was invalid
4.01 Unauthorized - authentication is required
4.04 Not Found - resource does not exist
4.05 Method Not Allowed - method does not apply to that resource
5.00 Internal Server Error - server failed while handling the request
9.6.2 Error Payload Format
JSON for human readability:
{"error":{"code":"SENSOR_OFFLINE","message":"Device has not reported in 5 minutes","timestamp":"2025-01-15T10:30:00Z","device_id":"temp42","retry_after":300}}
CBOR for production (more efficient):
1 -> "SENSOR_OFFLINE"
2 -> "Device offline"
3 -> 1642259400
4 -> "temp42"
5 -> 300
9.7 Message Type Selection
Choose between CON and NON based on criticality:
Use CON (Confirmable) for:
Actuator commands (LED on/off, valve open/close)
Configuration changes
Alerts and alarms
Any operation where failure must be detected
Use NON (Non-confirmable) for:
Frequent sensor readings (temperature every minute)
Telemetry streams
Status updates
Any data where next update supersedes previous
Decision tree:
Is the data critical?
If yes, use CON.
If no, ask whether the data will be sent again soon.
If yes, use NON.
If no, use CON so you can detect loss.
9.8 Security Best Practices
9.8.1 Always Use DTLS in Production
coaps://sensor.local/v1/temperature # Secure CoAP
Authentication options:
Pre-Shared Key (PSK) - Simplest for constrained devices
Raw Public Key (RPK) - No certificate infrastructure needed
CoAP API design applies REST principles to constrained devices. Resources should be nouns, methods should carry the action, payloads should stay compact, and discovery metadata should help clients understand available endpoints without hard-coded assumptions.