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.