5 Service API Contracts: Durable Device Interfaces
5.1 Start With the Decision
A field device may outlive the service behind it. Design its API so upgrades do not break the wire contract.
5.2 Route Overview
This is part 1 of 2. Continue with Service API Contracts: Versioning and Compatibility.
5.3 Part Objectives
- Define the api is the long-lived wire contract with explicit inputs, errors, and change rules.
- Define iot api surface with explicit inputs, errors, and change rules.
5.4 Start With the Client That Cannot Change Quickly
Keep One Field Device Working Through a Service Change
Picture a pump controller that calls the same service for ten years while the platform behind it changes twice. A clean internal redesign is a failure if the old device can no longer report or receive a safe command. The first contract starts with what that field client must keep seeing.
Firmware means the software stored on a device. A gateway means a device or service that joins two system paths. A protocol means the shared rules for a message exchange. HTTP means Hypertext Transfer Protocol, a request-and-response message system used by web services.
Record the request shape, response shape, error body, version, retry key, rate limit, and final physical result. Add an optional field, remove a required field in a test copy, repeat a command, fail one service instance, and prove that the stable entry point behaves as promised.
This runway does not prove that one service layout suits every fleet. The deeper sections cover resources, commands, schemas, compatible change, discovery, rate limits, repeated requests, and version retirement.
An IoT API is tested by the client that stays in the field after the backend changes. Firmware, gateways, dashboards, and partner integrations may keep calling the same contract long after the service team has reorganized internals.
The start-simple move is to design the command, retry, error, and version story before implementation. A stable contract lets the platform move behind the scenes without teaching every deployed client a new backend shape.
- Start With the Client That Cannot Change Quickly
- In 60 Seconds
- Minimum Viable Understanding
- The API Is The Long-Lived Wire Contract
- Design The Command API Before The Button
- Compatibility Is Operational Machinery
- Checkpoint: Contract Boundary
- Check Your API Contract Boundary
- Most Valuable Understanding
- Prerequisites
- API Contract Map
- Resource-Oriented API Design
- IoT API Surface
- Checkpoint: Resource Surface
- First define the API as the long-lived wire contract.
- Then model resources, commands, errors, and schemas.
- Next separate compatible changes from breaking changes.
- After that put discovery behind stable gateways, DNS, Kubernetes Services, or registries.
- Finally add rate limits, retries, idempotency, and a review checklist.
Checkpoint callouts recap design tests; collapsed quizzes and “Try It” sections are optional.
5.5 The API Is The Long-Lived Wire Contract
An IoT API should hide internal service shape while making client behavior predictable. A thermostat, gateway, installer app, operations dashboard, partner integration, and support console may all use the same platform capability at different speeds and with different update cycles.
That is why the contract needs stable resource names, explicit status codes, clear schemas, version rules, and retry behavior. The backend can move from a monolith to services, from one region to many, or from one database table to another without forcing every deployed client to change. The visible contract is the part that field devices, SDKs, dashboards, and partner systems can safely depend on after they leave the development bench.
Consider a building gateway that manages CO2 sensors, thermostats, occupancy counters, and door controllers. The API should expose durable resources such as /v1/devices/{id}, /v1/zones/{id}/telemetry, /v1/devices/{id}/commands, and /v1/alerts/{id}. It should not expose table names, Pod addresses, queue names, or internal class names. If the platform later replaces PostgreSQL tables with TimescaleDB hypertables, moves command dispatch behind Kafka, or adds an Envoy gateway in front of services, the client should still see the same resource shape and error contract.
- Resource paths name durable concepts such as devices, fleets, commands, telemetry, rules, alerts, and credentials.
- Error bodies tell firmware, SDKs, dashboards, and support tools whether to retry, stop, ask the user, or escalate.
- Discovery choices keep constrained clients on a stable DNS name or API gateway while internal services can move behind Kubernetes Services, EndpointSlices, or a registry.
A good API review therefore asks what each deployed client must know. Firmware usually needs a stable host, authentication method, resource path, payload schema, timeout, retry rule, and clock expectation. Operations tools need trace ids, audit events, rate-limit reasons, and command state. Analytics systems need pagination, time windows, units, quality flags, and schema evolution rules. Those responsibilities belong in the contract before scale turns an informal endpoint into a production dependency.
5.7 Compatibility Is Operational Machinery
Compatibility is enforced by tooling and runtime behavior, not by hope. An OpenAPI document, JSON Schema examples, consumer contract tests, gateway validation, and synthetic clients can catch breaking changes before old firmware or partner SDKs see them.
- Schema evolution: additive optional fields are usually safe when clients ignore unknown values; renamed fields, new units, reordered coordinates, and changed enum meanings need a new version.
- Concurrency:
ETagandIf-Matchprotect updates when a dashboard, automation rule, and support agent can edit the same device record. - Gateway controls: Envoy, Kong, NGINX Ingress, AWS API Gateway, or Azure API Management can enforce authentication, quotas, routing, and request size before traffic reaches services.
- Service location: Kubernetes Services, CoreDNS, EndpointSlices, Consul, or Eureka belong behind stable entry points so firmware does not depend on Pod IPs or cluster topology.
The machinery has several layers. A CI gate can compare OpenAPI diffs and fail a pull request that removes a required field or changes a response type. Gateway policy can reject requests that exceed body size, omit authentication, use an unsupported media type, or violate a schema. Runtime metrics can separate 4xx client errors, 429 overload, 5xx service failure, idempotency-key reuse, old-version traffic, and command-state transitions. These signals tell operators whether clients are misusing the contract or the platform is failing to honor it. Before following a retry, use Figure to see where the durable command identity prevents a network timeout from becoming a second physical action.
flowchart TD
A[Client chooses one logical command] --> B[POST command with Idempotency-Key]
B --> C{Key already stored?}
C -->|No| D[Create command resource]
D --> E[Dispatch to device or gateway]
E --> F[Persist command state and trace id]
C -->|Yes| G[Return stored command result]
F --> H[Client polls or subscribes to command state]
G --> H
H --> I{Final state?}
I -->|Applied| J[Record success for operator and audit views]
I -->|Rejected or expired| K[Return problem detail and retry boundary]
In Figure, the path begins at Client chooses one logical command and carries the same Idempotency-Key into the stored-key decision. A No result creates and dispatches the command, whereas Yes returns the stored result; both paths converge on Client polls or subscribes to command state. The final Applied and Rejected or expired branches show why compatibility includes stable status semantics as well as request schemas: a retry must preserve one command identity and one auditable outcome.
Compatibility also depends on storage and messaging choices. An idempotency table or Redis entry needs a retention window that matches client retry behavior. A command queue such as Kafka, NATS JetStream, RabbitMQ, or Amazon SQS needs a deduplication or command-id rule so a retry does not become a second actuator request downstream. A telemetry query endpoint needs explicit timestamp, unit, quality-flag, and pagination semantics so data warehouses and dashboards do not infer meaning from table layout.
The design is mature when a client can survive retries, stale reads, deprecations, regional failover, and service movement without learning how the backend is deployed. At that point, the API contract is not just documentation; it is a set of tests, gateway policies, storage rules, and operational dashboards that keep deployed IoT clients useful while the service platform changes behind them.
Checkpoint: Contract Boundary
You now know:
- The public API should expose durable resources such as
/v1/devices/{id},/v1/zones/{id}/telemetry,/v1/devices/{id}/commands, and/v1/alerts/{id}rather than tables, Pod addresses, or queue names. - A command request should return
202 Accepted, command id, state, timestamp, trace id, and a status link. - Compatibility comes from OpenAPI diffs, JSON Schema examples, consumer tests, gateway validation, and runtime metrics.
5.8 Learning Objectives
By the end of this chapter, you will be able to:
- Model IoT device-management and telemetry APIs as stable resources instead of ad hoc remote procedure calls.
- Select a versioning approach and classify API changes as compatible or breaking.
- Use deprecation and sunset signals to communicate API lifecycle changes.
- Compare client-side discovery, server-side discovery, Kubernetes Services, and API gateways.
- Design rate-limit, retry, and idempotency behavior for constrained IoT clients.
- Review an API contract for maintainability before teams depend on it.
API design is the part of microservices that other teams actually experience. A clean service boundary still fails if the API is chatty, ambiguous, impossible to version, or unable to survive retries. Treat every endpoint, schema field, status code, and deprecation rule as a long-lived contract.
5.9 Prerequisites
- SOA and Microservices Fundamentals: Understand modular monoliths, SOA, microservices, and capability decomposition.
- MQTT Fundamentals: Understand why device telemetry often uses publish-subscribe while management APIs use request-response.
- Cloud Computing for IoT: Know where gateways, services, and managed infrastructure run.
5.10 API Contract Map
An API contract is more than a URL list. It defines the resource model, allowed operations, schemas, errors, lifecycle policy, and discovery path.
Inspect Figure 5.1 before separating those contract elements. It shows why a constrained client needs one stable public promise even when authentication, routing, service logic, storage, and analytics are owned by different platform components. The purpose is to locate the boundary a firmware team can safely depend on.
Read Figure 5.1 from left to right. Devices and gateways first meet the API gateway, where shared edge controls are applied; authorization and REST services then enforce domain behavior; storage and analytics sit behind those services rather than becoming client-visible dependencies. That order keeps the running contract discussion explicit: clients receive stable endpoints and response behavior while internal responsibilities can evolve.
5.10.1 Resource Model
Names the stable things clients can address: devices, fleets, commands, telemetry streams, rules, and alerts.
5.10.2 Operation Semantics
Defines what GET, POST, PUT, PATCH, and DELETE mean for each resource.
5.10.3 Schema Contract
Defines fields, types, required values, defaults, enum behavior, and compatibility rules.
5.10.4 Error Contract
Defines status codes, retry hints, problem details, trace IDs, and user-actionable messages.
5.10.5 Lifecycle Policy
Defines versions, deprecation signals, sunset dates, migration guides, and monitoring.
5.10.6 Discovery Path
Defines how clients locate the API: public DNS, gateway, Kubernetes Service, service registry, or mesh.
5.11 Resource-Oriented API Design
Resource-oriented design starts from nouns and relationships. Methods then act on those resources. This keeps the API stable even when the backend implementation changes.
# Prefer resources
GET /v1/devices
POST /v1/devices
GET /v1/devices/{deviceId}
PATCH /v1/devices/{deviceId}
GET /v1/devices/{deviceId}/telemetry
POST /v1/devices/{deviceId}/commands
GET /v1/fleets/{fleetId}/devices
# Avoid RPC-style endpoint sprawl
POST /getDevices
POST /createDevice
POST /updateDeviceMetadata
POST /sendDeviceCommand
POST /findDevicesInFleet
5.11.1 Good Resource Signals
- Nouns name business concepts, not tables.
- Collection and item paths are consistent.
- A response schema is stable across related operations.
- The client can retrieve a resource after changing it.
- Backend migrations do not leak into the public contract.
5.11.2 Design Smells
- Verbs dominate the URL.
- Every new feature adds a one-off endpoint.
- Clients must know database keys or table structure.
- A dashboard needs many sequential calls to draw one page.
- The same field means different things in different endpoints.
5.12 IoT API Surface
IoT platforms usually need more than one API style. Device telemetry, command, management, analytics, and operator workflows have different latency, payload, and reliability needs.
5.12.1 Device Management API
Creates devices, rotates credentials, updates ownership, reads lifecycle state, and exposes inventory metadata.
5.12.2 Telemetry Query API
Retrieves historical readings, aggregates, windows, and quality flags. Use pagination, time filters, and field selection.
5.12.3 Command API
Creates command requests, returns command state, and supports idempotency keys so retries do not perform the command twice.
5.12.4 Rules and Alerts API
Manages thresholds, alert routes, suppression windows, and notification preferences.
5.12.5 Partner API
Exposes a stable, documented subset of platform capabilities with stronger compatibility guarantees.
5.12.6 Internal Service API
An internal service API can be more specialized, but it still needs contracts, versioning, observability, and compatibility discipline. Start by naming the owning capability and its data, then define which callers may depend on the operation, schema, error, and latency behavior. Treat deployment proximity as an implementation detail: a call inside one cluster can still be retried, delayed, or consumed by independently released code. This connects the internal surface to the same long-lived contract review used for device, partner, and dashboard clients.
Checkpoint: Resource Surface
You now know:
- Resource-oriented APIs start with nouns such as devices, fleets, commands, telemetry, rules, and alerts.
- Good resource signals include consistent paths, stable schemas, and no leaked database structure.
- Most IoT platforms need several surfaces: device management, telemetry query, command, rules and alerts, partner, and internal service APIs.
Once the resource surface is clear, ask which changes fit the same contract and which need a parallel version.
5.13 Continue to the Next Part
Carry this evidence into Service API Contracts: Versioning and Compatibility, which begins with Versioning Strategies.
