The Internet of Things on AWS – Official Blog
Introducing AWS IoT Core’s Direct Messaging: Lower-cost server-to-device communication with better observability
AWS IoT Core now supports direct messaging for point-to-point communication. Previously, sending a message to a single IoT device required publishing to an MQTT topic the device subscribed to, with no built-in delivery confirmation from the device. With the new direct messaging capability, you can send a message to any device connected to AWS IoT Core, reducing messaging cost compared to publish-subscribe (Pub-Sub) messaging for one-to-one communication patterns, and providing a delivery acknowledgment from the IoT device. AWS IoT Core also uses the delivery acknowledgment to provide detailed API response codes and emit Amazon CloudWatch Logs, so you have visibility into message delivery status and failure reasons.
Point-to-point communication between backend servers and IoT devices is a common pattern in connected device architectures. It shows up in use cases like sending firmware updates to a smart home appliance, pushing transaction updates to a payment device, or controlling a smart vehicle. For these one-to-one interactions, routing through a Pub-Sub message broker isn’t efficient, because you’re not using the fan-out benefit that Pub-Sub was designed to provide. In addition, you receive the message delivery acknowledgment from the Pub-Sub broker rather than from the IoT device, requiring you to build your own custom solution for delivery confirmation.
What is Direct Messaging?
AWS IoT Core introduces the SendDirectMessage HTTP API. With this API, you can send a message to a specific IoT device or client identified by its MQTT client ID. Rather than publishing to an MQTT topic and relying on subscription matching, AWS IoT Core routes the message point-to-point to the target device. You skip the subscription-matching layer, there’s no fan-out, and you have the option to receive confirmation from the client.
The message is delivered to the device on its existing MQTT connection (over TCP) to AWS IoT Core, with no device-side changes required. The API supports two modes:
- Without delivery confirmation – The message is delivered at MQTT Quality of Service (QoS) 0. The HTTP API returns
200 OKafter AWS IoT Core accepts and dispatches the message. - With delivery confirmation – The message is delivered at MQTT QoS 1. The HTTP API returns
200 OKonly after the device acknowledges receipt with an MQTT PUBACK (publish acknowledgment), providing true end-to-end delivery confirmation. If the device doesn’t acknowledge within the timeout, the API returns504 Gateway Timeout.
Key benefits
| Characteristic | Direct Messaging | Traditional Pub-Sub Messaging |
| Routing | Point-to-point by MQTT client ID | Through MQTT topic with fan-out |
| Delivery confirmation | Direct from device (QoS 1) | Indirect from message broker |
| Offline device feedback | Immediate (HTTP error code) | None (publish succeeds regardless) |
| Device-side changes | None required if the device already has an active MQTT connection to AWS IoT Core | Not applicable |
Built-in delivery confirmation
With delivery confirmation enabled (confirmation=true), you receive acknowledgment directly from the device, not only from the broker. AWS IoT Core delivers the message at QoS 1 and waits for the device’s PUBACK before returning 200 OK. This removes the need to build custom acknowledgment logic.
Immediate offline device feedback
With traditional Pub-Sub, a server publishing to a topic receives a successful response regardless of whether the device received the message. With Direct Messaging, if the target device isn’t connected, the API returns 404 Not Found immediately. The HTTP response message and Amazon CloudWatch Logs describe the specific reason. For example, when the response message states that the target client ID isn’t connected but has an active persistent session, the device has an unexpired persistent session but is currently offline.
Cost optimization
If you use direct messaging without delivery confirmation, you pay for a single Direct Message instead of separate publish-in and publish-out operations. If you use direct messaging with delivery confirmation, you pay for a single Direct Message with Confirmation instead of separate publish-in, publish-out, and publish-ack operations. For details, see the AWS IoT Core pricing page.
No device-side changes
Direct messages are delivered on existing MQTT connections already established with AWS IoT Core. No firmware updates, SDK changes, or new topic subscriptions are required. If your device already subscribes to the target topic, Direct Messaging works immediately. If your device doesn’t subscribe to the topic, verify that your MQTT client library doesn’t filter messages on unsubscribed topics. Most production clients (including the AWS IoT Device SDKs) handle this correctly.
Use cases
Direct Messaging is the right choice when:
- You need to send a message to a specific device (not a multicast).
- You need to know whether the device actually received the message.
- You want to simplify retry and error-handling logic.
Examples: server-to-device commands (locking a vehicle, toggling a smart appliance, pushing a configuration update), device acknowledgments, cost-sensitive high-volume messaging, and real-time notifications.
Traditional Pub-Sub remains the right choice when you need fan-out (one message to many subscribers) or message queuing for offline devices.
Getting started
In this post, you will configure the AWS Identity and Access Management (IAM) policies, send a Direct Message in three different ways, and receive it on a connected device.
Prerequisites
You must have the following prerequisites to follow along with this post.
- An AWS account with AWS IoT Core configured.
- IoT devices registered and connected through MQTT 3.1.1 or MQTT 5.0. See the AWS IoT Core Developer Guide to learn how to connect devices.
- Backend server code that calls the Direct Messaging API (server-side changes only).
Step 1: Configure authorization
Both the sender and the receiver require specific policy actions. The sender needs iot:SendDirectMessage permission with the target client’s Amazon Resource Name (ARN) as the resource. You can optionally restrict which topics can be used with the iot:Topic condition key.
The following sender policy allows direct messages to client myDevice on the topics commands/reboot and commands/update. For SigV4-authenticated backend servers, add this to an IAM policy. For X.509-authenticated devices, add it to an AWS IoT Core policy.
The receiver must have iot:Receive permission on the target topic. The receiver doesn’t need iot:Subscribe. Direct Messaging delivers to the connected client without requiring a topic subscription.
The following receiver policy allows iot:Receive on two specific topics:
You can also use a wildcard to receive direct messages on any topic under a prefix:
For more policy examples, including sending to any client on specific topics or on any topic, see Direct messaging policy examples.
Step 2: Send a Direct Message
Senders make an HTTP POST request to a client-specific URL:
{IoT_data_endpoint} is your account’s AWS IoT device data endpoint. The following three examples send the same commands/reboot message to client myDevice with delivery confirmation.
Using curl (X.509 client certificate authentication, port 8443):
Using the AWS Command Line Interface (AWS CLI):
The --cli-binary-format raw-in-base64-out option is required with AWS CLI v2 so the --payload value is sent as-is. To make it the default, run aws configure set cli-binary-format raw-in-base64-out. This command requires AWS CLI v2 version 2.34.57 or newer.
Using the AWS SDK for Python (Boto3):
With confirmation=true, the API waits for the device to acknowledge. A 200 OK confirms end-to-end delivery. A 504 Gateway Timeout indicates the receiver didn’t acknowledge within the timeout period. The delivery state is ambiguous, so implement idempotent handling if you retry.
Request parameters:
| Parameter | Type | Required | Description |
clientId |
String | Yes | The MQTT client ID of the receiving device. Max 128 characters; must not start with $; URL-encode characters that are invalid in HTTP requests (spaces, /, UTF-8). |
topic |
String | Yes | The topic on which the receiver receives the message. URL-encoded; must not start with $ or be a reserved topic. |
confirmation |
Boolean | No | true delivers at QoS 1 and waits for PUBACK; false delivers at QoS 0. Default: false. |
timeout |
Integer | No | Seconds to wait for acknowledgment. Used only when confirmation=true. Valid range: 1–15. Default: 5. |
contentType |
String | No | MQTT 5.0 content type (for example, application/json), forwarded to the receiver. |
responseTopic |
String | No | MQTT 5.0 response topic for request-response patterns. Must not contain wildcards. |
Step 3: Receive Direct Messages on the device
Your existing MQTT client receives Direct Messages on the specified topic. Unlike standard MQTT Pub-Sub, the device doesn’t need an active subscription. Direct Messages are delivered based on the client ID. The device can still optionally subscribe to the topic, but it’s not required for delivery.
The QoS level of the delivered message is set by the sender’s confirmation parameter, not by the receiver’s subscription. When confirmation=true, the message arrives at QoS 1 and the client must send a PUBACK to acknowledge delivery. Most MQTT client libraries do this automatically. When confirmation=false, the message arrives at QoS 0 with no acknowledgment required. Make sure your client handles both QoS 0 and QoS 1 incoming messages correctly.
Monitoring with CloudWatch
AWS IoT Core returns detailed HTTP response codes for every SendDirectMessage call. When you enable AWS IoT Core CloudWatch logging, the service also emits SendDirectMessage event logs that include a machine-readable reason field, so you can build automated retry logic, monitor device connectivity, and troubleshoot delivery issues programmatically. Review the HTTP response message or CloudWatch logs to identify the specific reason for any failure.
HTTP response status codes:
| Code | Meaning and recommended action |
200 OK |
With confirmation=true, the receiver has acknowledged receipt. Otherwise, the message was dispatched successfully. |
400 Bad Request |
One of the parameters is invalid. Verify that the topic name and client ID are valid and URL-encoded correctly. |
403 Forbidden |
The sender’s policy doesn’t include iot:SendDirectMessage on the target client and topic, or the receiver’s policy doesn’t include iot:Receive on the topic. Update the corresponding policy. |
404 Not Found |
The target client ID isn’t connected to AWS IoT Core. Verify the receiver is connected and retry. A message noting an active persistent session means the client is offline but has an unexpired session. |
413 Payload Too Large |
The payload exceeds the maximum allowed size. Reduce the payload and retry. See AWS IoT Core service quotas. |
429 Too Many Requests |
The account exceeded the SendDirectMessage requests-per-second limit, or the receiver connection exceeded its outbound publish limit. Reduce the request rate and use exponential backoff. |
500 Internal Server Error |
An unexpected server-side error. Retry with exponential backoff. If it persists, contact AWS Support with the traceId from the response. |
504 Gateway Timeout |
With confirmation=true, the receiver didn’t send PUBACK within the timeout. Increase the timeout, verify the client sends PUBACK for QoS 1 messages, or check whether the receiver is processing messages slowly. |
Limitations and considerations
- No message queuing – Direct Messages aren’t queued for offline devices. Use Pub-Sub with QoS 1 and persistent sessions for message persistence.
- No message retention – The MQTT
retainedflag isn’t supported. Use Amazon DynamoDB or AWS IoT Device Shadow for state synchronization. - No Rules Engine processing – The AWS IoT rules engine doesn’t process Direct Messages.
- Reserved topics – Direct Messaging supports custom topics. Topics must not start with
$and must not be AWS IoT reserved topics. - Client ID restrictions – Client IDs must not exceed 128 characters and must not start with
$. Client IDs containing characters that are invalid in HTTP requests (such as/) must be percent-encoded in the URL path. - Limits and quotas – See the AWS IoT Core developer guide and service quotas for payload size, topic depth, and API limits.
- Protocol – Works with existing MQTT 3.1.1 and MQTT 5.0 clients. (MQTT 5.0 properties such as content type and response topic are not delivered to MQTT 3.1.1 clients.)
Availability
AWS IoT Core Direct Messaging is available today in all AWS Regions where AWS IoT Core is available.
Conclusion
In this post, you saw how AWS IoT Core Direct Messaging routes server-to-device messages point-to-point by MQTT client ID, returns a true end-to-end delivery acknowledgment when you opt in with confirmation=true, and reports actionable HTTP response codes for offline or unauthorized targets. The capability works on existing MQTT connections, so you can adopt it without modifying device firmware.
To get started, see the Direct Messaging topic in the AWS IoT Core Developer Guide. For pricing details, see the AWS IoT Core pricing page. Sign in to the AWS IoT console to start sending direct messages to your connected devices.