MQTT 5 Request Response Pattern Explained: RPC Over MQTT

By | July 1, 2026

MQTT was born as a publish/subscribe protocol — messages flow from many publishers to many subscribers with no expectation of a response. This model fits telemetry perfectly: sensors publish, dashboards subscribe, nobody waits for anyone. But when you need one client to ask another client a specific question and get one specific answer back, pub/sub alone falls short. You need request/response.

MQTT 3.1.1 forced engineers to invent their own request/response schemes on top of pub/sub — with topic-name conventions, payload correlation, and vendor-specific hacks. MQTT 5 solves this natively by defining four properties that together enable clean RPC-style communication over MQTT: Response Topic (0x08), Correlation Data (0x09), Request Response Information (0x19), and Response Information (0x1A).

This article explains the complete MQTT 5 request/response pattern: how the four properties work together, the two deployment models (peer-to-peer and service-discovery-based), the architectural patterns this unlocks (RPC, saga workflows, health checks), and how MQTT-based request/response compares to alternatives like HTTP and gRPC.

What the MQTT 5 request/response pattern is in one paragraph

The MQTT 5 request/response pattern is the ability for one MQTT client to send a specific query to another and receive a specific answer back, using four PUBLISH-level properties. Response Topic (0x08) tells the responder where to publish its response. Correlation Data (0x09) identifies which request a response belongs to. Request Response Information (0x19) in CONNECT tells the Server the client wants response topic information. Response Information (0x1A) in CONNACK returns a Server-suggested topic prefix for the client to use for its response topics. Together, these four properties transform MQTT from pure pub/sub into a protocol capable of RPC, distributed workflows, health probes, and other request/response patterns while retaining pub/sub’s ability to handle thousands of concurrent asynchronous operations on a single connection.

The four building-block properties

Request/response in MQTT 5 uses four properties across CONNECT, CONNACK, and PUBLISH:

Response Topic (Property 0x08)

Appears in PUBLISH packets. UTF-8 string telling the responder where to publish its response.

AspectDetail
Where usedPUBLISH packets (in the request)
TypeUTF-8 String
Max length65,535 bytes
WildcardsNot allowed
Required for request/responseYes

Correlation Data (Property 0x09)

Appears in PUBLISH packets. Opaque binary bytes matching a response back to its request.

AspectDetail
Where usedPUBLISH packets (in both request AND response)
TypeBinary Data
Max length65,535 bytes
FormatApplication-chosen (UUID, sequence number, structured)
Required for multi-requestYes (single in-flight can skip)

Request Response Information (Property 0x19)

Appears in CONNECT packets. Boolean indicating the client wants Server-provided response topic information.

AspectDetail
Where usedCONNECT packet
TypeByte (boolean: 0 or 1)
Default0 (client doesn’t request response info)
PurposeClient opt-in for service discovery pattern

Response Information (Property 0x1A)

Appears in CONNACK packets. UTF-8 string providing a topic prefix or identifier the client should use for its response topics.

AspectDetail
Where usedCONNACK packet
TypeUTF-8 String
Only sent ifClient sent Request Response Information = 1 in CONNECT
ContentServer-defined (topic prefix, unique identifier)
PurposeServer tells client where to route response traffic

The first two properties (Response Topic + Correlation Data) are the runtime mechanism — used on every request. The second two (Request Response Information + Response Information) are the discovery mechanism — used once at connection setup for centrally-managed deployments.

The two deployment models

MQTT 5 supports two distinct approaches to implementing request/response:

Model 1: Peer-to-peer

The client and responder agree on response topic conventions independently. The client picks its own response topic (typically embedding its Client Identifier) and includes it in every request. Simple and self-contained.

Model 2: Service Discovery

The Server centrally manages response topic conventions. Clients opt in via Request Response Information; the Server responds with a suggested topic prefix via Response Information. Suitable for multi-tenant systems, centrally-authorized deployments, or when clients shouldn’t hardcode topic conventions.

Both models use the same underlying Response Topic and Correlation Data mechanism. They differ only in how the client chooses its response topic.

Peer-to-peer pattern (direct response topics)

The simpler pattern. Client picks its own response topic.

The full flow

Setup (once at client startup):
  Client → Server: SUBSCRIBE "device-007/responses" (QoS 1)
  Server → Client: SUBACK

Per request:
  Client → Server: PUBLISH
    Topic: "services/calculator/add"
    Properties:
      Response Topic: "device-007/responses"
      Correlation Data: <UUID_1>
    Payload: {"a": 5, "b": 7}

  Server → Service: PUBLISH (forwarded with same properties)
  
  Service processes request, computes 5 + 7 = 12
  
  Service → Server: PUBLISH
    Topic: "device-007/responses"     ← from Response Topic
    Properties:
      Correlation Data: <UUID_1>       ← echoed from request
    Payload: {"result": 12}
  
  Server → Client: PUBLISH (forwarded)
  Client matches UUID_1 to outstanding request, resolves the future

Advantages

  • Simple — no discovery step, no Server-side coordination
  • Portable — works with any MQTT 5 broker
  • Predictable — response topics are stable and known

Disadvantages

  • Client controls topic names — makes centralized topic-based authorization harder
  • No enforcement — clients could use any response topic they want
  • No tenant isolation — one tenant’s responses could accidentally cross into another tenant’s topic space (with poor topic design)

For simple deployments with a controlled set of clients, peer-to-peer is the right choice. Most public MQTT documentation demonstrates this pattern.

Service discovery pattern

The Server-managed pattern. The client asks the Server “where should my responses come to?” and the Server tells it.

The full flow

Client → Server: CONNECT
  Client Identifier: "sensor-042"
  Properties:
    Request Response Information: 1  ← "I want response info"

Server → Client: CONNACK
  Reason Code: 0x00 Success
  Properties:
    Response Information: "tenant-acme/responses/sensor-042"  
                                    ← Server-assigned prefix

Client subscribes to the Server-assigned prefix (possibly with wildcards):
  Client → Server: SUBSCRIBE "tenant-acme/responses/sensor-042/#"

Per request, client uses topics under the Server-assigned prefix:
  Client → Server: PUBLISH
    Topic: "services/calculator/add"
    Properties:
      Response Topic: "tenant-acme/responses/sensor-042/req-12345"
      Correlation Data: <UUID_1>
    Payload: {"a": 5, "b": 7}

What the Server provides in Response Information

The MQTT 5 spec deliberately leaves Response Information format flexible. Common Server-supplied patterns:

PatternExample
Topic prefix"tenant-acme/responses/sensor-042"
Unique identifier"7d3b2a1e-5f6c-4c3d-a1b2-9e8f7d6c5b4a"
Structured path"/tenants/acme/clients/sensor-042/rpc"
Simple identifier"C7A2B1" (short ID mapped internally)

The client is expected to use this value as-is (as a topic prefix) or resolve it somehow (looking up the mapping internally). The specific interpretation depends on the Server implementation.

Advantages

  • Multi-tenant enforcement — Server assigns topic prefixes per tenant
  • Authorization simplicity — topic-based auth follows Server-assigned patterns
  • Central topic naming — Server controls the topic conventions
  • Dynamic configuration — Server can rotate topic prefixes over time

Disadvantages

  • Server-specific behavior — different Servers implement Response Information differently
  • Extra complexity — discovery step adds moving parts
  • Portability challenges — code depending on Server-provided Response Information format needs adjustment when moving between brokers

For multi-tenant SaaS platforms and centrally-managed enterprise deployments, service discovery makes sense. For simpler self-hosted setups, peer-to-peer is usually easier.

Architectural patterns unlocked

The MQTT 5 request/response mechanism enables several distinct architectural patterns:

Pattern 1: Classic RPC

One client calls a specific service and gets back a specific result:

Client → Service: PUBLISH "services/orders/create", request_data
Service → Client: PUBLISH on Response Topic, response_data

Use cases: microservices communication, backend integration, tool automation.

Pattern 2: Query-Response

Client requests information from a data provider:

Client → Data Store: PUBLISH "query/customer/lookup", {"customer_id": "C001"}
Data Store → Client: PUBLISH on Response Topic, {"name": "...", "balance": 1000}

Use cases: distributed databases, configuration retrieval, device state queries.

Pattern 3: Command-and-Acknowledgment

Client issues a command and waits for confirmation of execution:

Client → Device: PUBLISH "device/valve-42/close", {}
Device → Client: PUBLISH on Response Topic, {"status": "closed", "duration_ms": 250}

Use cases: industrial control, remote actuation, device management.

Pattern 4: Health Check / Liveness Probe

Monitor checks if a service is alive and responsive:

Monitor → Service: PUBLISH "healthcheck/service-xyz", {"deep": true}
Service → Monitor: PUBLISH on Response Topic, {"status": "healthy", "uptime_s": 12345}

Use cases: system monitoring, load balancer health checks, dependency validation.

Pattern 5: Fire-and-Forget with Optional Response

Publisher sends a message. Some processors respond (subscribed with response capability), others don’t (fire-and-forget subscribers):

Client → Multiple recipients: PUBLISH "event/order-placed", ...
  With Response Topic

Responding subscribers: respond via Response Topic
Non-responding subscribers: process silently

Use cases: hybrid patterns where some receivers report and others just act.

Pattern 6: Broadcast Query

Client asks a question to many potential responders:

Client → Subscribers: PUBLISH "discovery/services/available", ...
  With Response Topic

Each service that matches: PUBLISH response with its identifier
Client aggregates responses until timeout

Use cases: service discovery, device enumeration, capability querying.

Async design patterns

MQTT 5 request/response is inherently asynchronous. Clients typically model this with one of several patterns:

Pattern: Async/Await (Python-style)

python

async def call_service(service, method, params):
    correlation_id = uuid.uuid4().bytes
    future = asyncio.Future()
    pending_requests[correlation_id] = future
    
    await mqtt_client.publish(
        topic=f"services/{service}/{method}",
        payload=json.dumps(params),
        response_topic="my-client/responses",
        correlation_data=correlation_id,
        qos=1
    )
    
    return await asyncio.wait_for(future, timeout=30)

The await cleanly suspends until the response arrives (or timeout fires).

Pattern: Promise/Then (JavaScript-style)

javascript

function callService(service, method, params) {
    const correlationId = crypto.randomUUID();
    return new Promise((resolve, reject) => {
        pendingRequests.set(correlationId, { resolve, reject });
        
        mqttClient.publish(
            `services/${service}/${method}`,
            JSON.stringify(params),
            {
                responseTopic: 'my-client/responses',
                correlationData: correlationId,
                qos: 1
            }
        );
        
        setTimeout(() => {
            if (pendingRequests.has(correlationId)) {
                pendingRequests.delete(correlationId);
                reject(new Error('Timeout'));
            }
        }, 30000);
    });
}

Pattern: Callback (traditional)

call_service("calculator", "add", {"a": 5, "b": 7}, on_result_callback);

function on_result_callback(error, result) {
    if (error) { handle_error(error); }
    else { process_result(result); }
}

The MQTT layer delivers the response via the on_message handler, which looks up the matching callback by Correlation Data.

Pattern: Streaming/Observable

For responses that might arrive multiple times (streamed data):

client.subscribeToResponses(correlationId).forEach(response => {
    processResponse(response);
});

The subscriber processes each response as it arrives until an end-of-stream marker or timeout.

Chained requests and saga workflows

MQTT 5’s request/response mechanism scales up to complex distributed workflows:

Chained requests

One request triggers another:

Client → Service A: PUBLISH "step-1"
Service A → Client: PUBLISH "response-1"

Client → Service B: PUBLISH "step-2" (using data from response-1)
Service B → Client: PUBLISH "response-2"

Client → Service C: PUBLISH "step-3" (using data from response-2)
Service C → Client: PUBLISH "response-3"

Each step is a standard request/response. The client orchestrates the chain.

Saga pattern (distributed workflow)

For workflows spanning multiple services with compensation on failure:

Client → Service A: reserve resource
Service A → Client: reserved (with compensation ID)

Client → Service B: process payment
Service B → Client: succeeded (or failed)

If failure:
  Client → Service A: release resource (compensation)

Correlation Data can carry the saga ID linking all steps. This lets logging/monitoring systems trace the complete saga across services.

Multi-service parallel query

Client queries multiple services in parallel:

Client → Service A: PUBLISH request (Correlation Data = txn_id + "a")
Client → Service B: PUBLISH request (Correlation Data = txn_id + "b")
Client → Service C: PUBLISH request (Correlation Data = txn_id + "c")

All three services respond to the client's response topic.
Client matches each response by its part of the Correlation Data.
Client aggregates all responses once all three arrive.

This is fan-out/fan-in, natural in MQTT because there’s no HTTP-style per-request TCP connection overhead.

Failure recovery

Request/response over unreliable networks requires deliberate failure handling:

Timeout strategy

Always define request timeouts:

Short timeouts (< 1 second): interactive UI
Medium timeouts (1-30 seconds): standard RPC
Long timeouts (30+ seconds): batch operations, long-running processes

Without timeouts, pending request maps grow unboundedly on failed responses.

Late response handling

Responses arriving after their request has timed out should be discarded silently. The pending request entry has already been removed; the on_message handler won’t find a matching callback.

Best practice: log late responses at DEBUG level for observability but don’t act on them — the caller has already given up.

QoS considerations

QoSEffect on request/response
QoS 0Fastest, but requests or responses may be lost silently
QoS 1At-least-once — reliable but responses may be duplicated
QoS 2Exactly-once — guaranteed delivery, higher latency

For most RPC-style patterns, QoS 1 is the right balance. Use QoS 2 only when duplicate processing is unacceptable.

Idempotency

With QoS 1 possibly duplicating messages, responders should handle duplicate requests idempotently. Correlation Data serves as a natural idempotency key:

Responder logic:
  1. Extract Correlation Data from incoming request
  2. Look up in recent-request cache (with TTL)
  3. If found: return cached response (same as first time)
  4. If not found: process request, cache result, return response

The cache TTL should exceed the client’s timeout — typically 60-120 seconds is reasonable.

Connection failure recovery

If the client loses its MQTT connection mid-request, several outcomes are possible:

  1. Request never sent — retry the request on reconnection
  2. Request sent, response lost — timeout fires, retry with same Correlation Data (idempotency prevents duplicates)
  3. Response arrived but not delivered — depends on QoS: QoS 0 lost, QoS 1+ redelivered with Session Expiry preserving state

Session Expiry Interval preserves in-flight state across reconnections when appropriate.

Multi-tenant considerations

For SaaS or multi-tenant deployments, request/response gets more nuanced:

Topic isolation

Different tenants must have isolated topic spaces so their responses don’t collide:

Tenant A responses: "tenants/a/responses/*"
Tenant B responses: "tenants/b/responses/*"

Enforced via topic-based authorization on the Server.

Cross-tenant requests

If a service serves multiple tenants, the request routing must include tenant context. Options:

  • Include tenant ID in the request topic (services/tenant-a/calculator/add)
  • Include tenant ID in Correlation Data (structured binary format)
  • Include tenant ID in User Properties
  • Include tenant ID in payload

Whichever approach, ensure the tenant identity is authenticated (via CIP Security or equivalent) — not just declared by the client.

Authorization complexity

Response topic authorization needs care:

Rule: client X can publish only to topics under "tenants/X/*"
Rule: client X can subscribe only to topics under "tenants/X/*"
Rule: services can publish to any tenant's response topics

Getting authorization rules right is often the hardest part of multi-tenant MQTT deployments.

When to use MQTT 5 request/response vs alternatives

Choose MQTT 5 request/response when:

  • You’re already using MQTT for other purposes (unified protocol stack)
  • Async patterns fit your use case
  • Multiple concurrent requests must share one connection
  • The Server needs to send unsolicited updates (native pub/sub)
  • IoT / edge / constrained network context
  • You need standard MQTT features (retained messages, QoS levels, session expiry) alongside request/response

Choose HTTP when:

  • Synchronous, request-scoped semantics fit
  • Web browsers / standard REST clients are common
  • Load balancers and CDNs need to handle traffic
  • Debugging via curl / Postman is important
  • Rich HTTP semantics (caching, redirects, content negotiation) matter

Choose gRPC when:

  • You need strong typing (protobuf schemas)
  • Streaming patterns are common
  • Multiplexing on HTTP/2 is beneficial
  • Compile-time client/server contracts matter
  • Backend-to-backend communication (not IoT devices)

Choose AMQP when:

  • Enterprise messaging with complex routing (topics, queues, headers)
  • Message durability guarantees are critical
  • Existing enterprise messaging infrastructure (RabbitMQ, IBM MQ)
  • Not primarily IoT or edge use cases

For most industrial IoT scenarios where devices, gateways, and cloud services communicate, MQTT 5 request/response is a strong choice — you get pub/sub for streaming telemetry AND request/response for command/query patterns over a single protocol.

Comparison with alternatives

AspectMQTT 5 Request/ResponseHTTP RESTgRPCAMQP
Connection modelPersistent, long-livedPer-request (or Keep-Alive)Persistent (HTTP/2)Persistent
Sync/AsyncAsync nativeSync + WebSocket for asyncAsync streamingAsync
BidirectionalYes nativeServer push via SSE/WebSocketYesYes
Message correlationCorrelation Data propertyURL + connection stateHTTP/2 stream IDCorrelation ID
Server discoveryResponse InformationDNS + URL pathsDNS + service namingBroker + exchange config
Schema definitionApplication-definedOpenAPI (external)Protobuf (native)Schema registry
AuthenticationTLS + CIP Security / ESTHTTP Auth headersTLS + JWTTLS + SASL
Rate controlFlow ControlHTTP status codesBackpressurePrefetch
IoT device supportExcellentAdequateLimitedRare
Cloud-native supportGrowingUniversalStandardEnterprise

Common design pitfalls

PitfallWhat happensHow to avoid
Race condition on subscriptionRequest sent before subscription active — responses lostSubscribe to response topic BEFORE publishing requests, wait for SUBACK
Per-request response topicsTopic explosion at ServerUse stable response topics + Correlation Data
Missing timeoutPending request maps grow unboundedlyAlways implement client-side timeouts
Non-idempotent respondersQoS 1 retries cause duplicate processingCache responses by Correlation Data
Confusing UUID string with bytesCorrelation Data mismatchTreat Correlation Data as opaque binary bytes
Ignoring Response Topic absentResponder always responds, even to fire-and-forget messagesOnly respond when Response Topic is present
Response topic without authAnyone can subscribe to responsesEnforce topic-level authorization
Assuming Response Information formatClient hardcodes topic formatTreat Response Information as opaque Server-provided value

Frequently asked questions

What is the MQTT 5 request/response pattern?

The MQTT 5 request/response pattern is a protocol-level mechanism for one client to send a specific request to another and receive a specific response back. It uses four PUBLISH-level properties: Response Topic (0x08) tells the responder where to send the answer; Correlation Data (0x09) matches responses to requests; Request Response Information (0x19) in CONNECT opts into service discovery; Response Information (0x1A) in CONNACK provides the Server’s suggested response topic prefix. Together they enable RPC-style patterns on top of MQTT’s pub/sub foundation.

How is MQTT 5 request/response different from MQTT 3.1.1?

MQTT 3.1.1 had no protocol-level request/response support. Engineers had to invent their own topic conventions and payload correlation schemes. This produced topic explosion, complex authorization rules, non-portable code, and DIY correlation logic. MQTT 5 moves correlation into standardized properties, letting request/response work identically across all MQTT 5 implementations without custom conventions.

What is Request Response Information in MQTT 5?

Request Response Information (Property 0x19) is a boolean property (byte value 0 or 1) that a client sends in CONNECT to tell the Server it wants response topic information. If set to 1, the Server may respond in CONNACK with Response Information (Property 0x1A) providing a Server-suggested topic prefix or identifier for the client to use for its response topics. This enables Server-managed response topic conventions for multi-tenant systems and centrally-managed deployments.

What is Response Information in MQTT 5?

Response Information (Property 0x1A) is a UTF-8 string that the Server sends in CONNACK when the client requested it via Request Response Information in CONNECT. The string content is Server-defined — typically a topic prefix like “tenant-acme/responses/client-042” or a unique identifier the client uses to construct response topics. This lets the Server centrally manage response topic conventions instead of clients hardcoding their own.

Do I need both Response Topic and Correlation Data for request/response?

For proper request/response with multiple concurrent requests, yes. Response Topic tells the responder where to publish the response. Correlation Data tells the requester which request the response belongs to. Without Correlation Data, the requester can’t distinguish among multiple in-flight responses on the same response topic. If you only ever have one request in flight at a time, you can skip Correlation Data — but this is rare in real systems.

What’s the difference between peer-to-peer and service discovery patterns?

Peer-to-peer request/response has the client choose its own response topic name (e.g., “client-id/responses”) — self-contained but topic conventions are client-controlled. Service Discovery has the client ask the Server via Request Response Information; the Server tells the client what topic prefix to use via Response Information. Service Discovery enables Server-side control over topic naming, useful for multi-tenant systems where the Server enforces topic isolation policies.

How does MQTT 5 request/response compare to HTTP REST?

MQTT 5 request/response is async-native, uses a persistent connection, supports thousands of concurrent requests on one connection, and integrates with pub/sub for server-initiated notifications. HTTP REST is sync-native, uses per-request or persistent connections, is universal across web infrastructure, and has strong tooling (curl, Postman, browsers). Choose MQTT for IoT/async patterns and unified protocol stacks; choose HTTP for web-style synchronous APIs and browser compatibility.

How do I handle timeouts in MQTT 5 request/response?

Implement client-side timeouts: track outstanding requests with their Correlation Data and start time, and remove entries after a timeout elapses. When a late response arrives, the pending request entry is gone, so the on_message handler silently discards the response. Common timeout values: 1-30 seconds for interactive RPC, longer for batch operations. Without timeouts, pending request maps grow unboundedly on network failures.

How do I subscribe to responses before publishing requests?

Always subscribe to your response topic BEFORE publishing any requests. Wait for the SUBACK confirming the subscription is active. Then publish requests. If you publish before subscribing, responses arriving between the publish and the subscription-active moment are lost. For long-lived clients, subscribe once at connection startup and reuse the same response topic for all requests — combined with Correlation Data for matching.

Can I use request/response with QoS 0?

Technically yes, but QoS 0 risks losing requests or responses silently. For any operational request/response pattern, use QoS 1 (at-least-once) which handles occasional network glitches by retrying. If duplicate processing would be unacceptable, either use QoS 2 (exactly-once, with latency cost) or implement idempotency in the responder using Correlation Data as an idempotency key. QoS 0 is rarely appropriate for request/response.

How do responders know if a PUBLISH is a request or fire-and-forget?

By the presence or absence of Response Topic. If a PUBLISH has Response Topic, treat it as a request and send a response. If not, treat it as fire-and-forget and process without responding. This lets the same service topic handle both patterns transparently — clients decide per-message whether they want a response by including or omitting Response Topic.

What’s the maximum size for Correlation Data?

Correlation Data is Binary Data with a 2-byte length prefix, giving a maximum of 65,535 bytes. In practice, Correlation Data is almost always much smaller — 16 bytes (UUID) or 8 bytes (sequence number) is typical. Large Correlation Data wastes bandwidth on every request AND every response. Use the smallest size that meets your correlation needs.

Should services always respond, even on errors?

Yes. When the responder can’t process the request (validation failure, authorization error, internal error), still publish a response with error indication in the payload. The requester is waiting for SOME response — silent failure just makes them wait until timeout, which is a bad user experience. Include status codes, error types, and human-readable messages in error responses to help requesters diagnose issues.

Author: Zakaria El Intissar

I've spent 13 years in power system automation, electrical protection, and SCADA communication, as an automation and industrial computing engineer. ScadaProtocols.com is where I turn what I've learned on site into plain guides and working tools — so other engineers can decode, analyze, and troubleshoot industrial communication protocols without the guesswork.