WebSocket and MQTT requests

A WS (or WEBSOCKET) region connects to a ws:// or wss:// URL, its headers going with the handshake; an MQTT region connects to an mqtt://host:port broker, or mqtts://host:port (also ssl://) over TLS (MQTT 5, or 3.1.1 when the broker refuses it). Without a port, wss:// uses 443, mqtt:// 1883 and mqtts:// 8883. Over TLS the certificate is checked as for https://: --ca-cert and --insecure apply. The body is the message to send; lines === separate several messages, and === wait-for-server waits for a message of the server before sending the next one, as in httpYac. Then the region waits for the server’s messages until it has # @expect of them or # @timeout (--message-timeout, 5000 ms) has passed. The messages received are the response: its body is their JSON array, each message parsed when it is JSON.

### Request a pickup: the reply carries the request's id
# @expect 1
WS ws://localhost:4010/pickups

{"type":"pickup.request","requestId":"6f1c2b9e-7f8a-4c35-9a51-2d1e8b7f0c11","carrier":"dhl", ...}

?? body $[0].type == pickup.confirmed
?? body $[0].requestId == 6f1c2b9e-7f8a-4c35-9a51-2d1e8b7f0c11

### Report a courier's position
# @expect 0
# @property content-type application/json
# @property payload-format-indicator utf-8
MQTT mqtt://localhost:1883
publish: couriers/cou_4821/position
qos: 1

{"courierId":"cou_4821","at":"2026-10-06T07:41:02Z","position":{"type":"Point","coordinates":[21.0067,52.2319]}}

### Receive a job offer
# @channel courierJobOffers
# @expect 1
MQTT mqtt://localhost:1883
subscribe: couriers/+/offers

?? body $.length == 1
PASS WS ws://localhost:4010/pickups -> 1 sent, 1 received (12 ms) requestPickup
PASS MQTT mqtt://localhost:1883 -> 1 sent, 0 received (3 ms) receiveCourierPositions
PASS MQTT mqtt://localhost:1883 -> 0 sent, 1 received (6 ms) offerPickupJob

The headers of an MQTT region are its settings:

Header Meaning
topic The topic messages are published to, also subscribed to (unless subscribe is given)
publish The topic messages are published to
subscribe The topic filters subscribed to before publishing, comma-separated (couriers/+/offers, hubs/#)
qos 0 (the default), 1 or 2, for publishing and subscribing
retain true to publish retained messages
clientId, keepAlive, clean The connection’s client id (a random one by default), keep-alive in seconds (60) and clean session (true)
username, password The CONNECT’s user name and password; without them, APIGENIE_MQTT_USERNAME and APIGENIE_MQTT_PASSWORD
version auto (the default: MQTT 5, else 3.1.1 when the broker refuses it), 5 or 3.1.1

The client’s own messages coming back on a topic it subscribed to are left out. Retained messages of a subscription count as received.

Settings are substituted like any header, so a password can stay out of the file: password: {{$processEnv BROKER_PASSWORD}}, or a variable of an environment file. A setting the region doesn’t give comes from APIGENIE_MQTT_USERNAME or APIGENIE_MQTT_PASSWORD. MQTT 5 sends a password without a user name; MQTT 3.1.1 sends one only with a user name, so with version: 3.1.1 a password alone is a warning and isn’t sent. A broker that refuses them fails the request with the broker refused the connection (reason code 0x86) (bad user name or password) (MQTT 5), or (return code 4) (bad user name or password) (MQTT 3.1.1). Credentials never show in the output or the reports: wherever they would, they read [redacted], and a URL’s user:password@ is shown as ***@. The user name and password of the URL itself aren’t sent.

In checks and scripts Value
?? body $.length == 2 How many messages came (.length also gives the size of any array or string)
?? body $[0].type == pickup.confirmed A value of the first message
?? status == 101 The handshake’s status (WS); the CONNACK’s return code, 0 (MQTT)
response.messages[i] In scripts: topic (the topic, or the WebSocket’s path), body (parsed when JSON), rawBody, binary

MQTT 5 properties

An MQTT region connects with MQTT 5. A broker that refuses it (reason code 0x84, a 3.1.1 broker’s return code 1, or a connection closed before the CONNACK) gets a second connection with MQTT 3.1.1, and the request notes the broker refused MQTT 5. version: 5 doesn’t fall back; version: 3.1.1 doesn’t try MQTT 5.

# @property gives the properties of every message the region publishes:

Directive Property
# @property content-type application/json Content Type
# @property response-topic clients/{{client}}/quotes Response Topic (no wildcards); the region also subscribes to it
# @property correlation-data {{$uuid}} Correlation Data
# @property payload-format-indicator utf-8 Payload Format Indicator: utf-8 or 1, bytes or 0
# @property message-expiry-interval 60 Message Expiry Interval, in seconds
# @property user replyTo dispatch/d-1/replies A user property (repeat the line for more; the value may be empty)

Values are substituted like headers. An unknown property, a missing value or a bad value is a warning, and that property isn’t sent. Over MQTT 3.1.1 none is sent, and the request notes it.

Over MQTT 5 the messages sent and received are checked with their properties, against the message’s and the operation’s MQTT bindings (0.2.0), as message.properties.invalid (error):

Binding Checked
message payloadFormatIndicator The Payload Format Indicator is the same (absent counts as 0)
message contentType The Content Type is the same, ignoring case
message correlationData Correlation Data is present and matches the schema
message responseTopic A Response Topic is present, the string given or matching the schema
operation messageExpiryInterval A Message Expiry Interval is present and no longer

A request whose reply address is in a header ($message.header#/replyTo) is answered on the topic in that user property; one whose reply channel has address: null is answered on its Response Topic. Either way the region waits for the reply on that topic, checks it as the reply, and compares its correlation id (header or payload) and, when a binding declares correlationData, its Correlation Data with the request’s.

### Ask for a quote: the reply comes to the Response Topic
# @expect 1
# @property content-type application/json
# @property response-topic clients/7/quotes
# @property correlation-data {{$uuid}}
# @property user correlationId c-42
MQTT mqtt://localhost:1883
publish: quotes
qos: 1

{"parcel":"P1"}

?? body $[0].price exists

A message whose bindings state properties counts in the coverage as checked (MQTT 5) or unchecked (MQTT 3.1.1): summary.properties and operations[].properties in the report, MQTT 5 properties: checked on 2 messages, not on 0 (MQTT 3.1.1) in the summary.

Was this helpful?