Data Delivery: Webhooks & Real-Time Events
MQcentral delivers device data and gateway events to your endpoints in real time through two push mechanisms: Output Profiles (for device uplinks) and the Output Alert Service (for gateway connectivity events). This page covers the delivery formats, delivery semantics, and patterns for building receivers that hold up in production.
Before You Begin
You need:
$MQ_TOKENexported from the authentication step. See Authentication & API Access.- At least one push delivery target configured:
- Device uplinks: an Output Profile with a
RestParamstarget assigned to your devices. See Data Delivery: Output Profiles. - Gateway connectivity events: an Output Alert Service destination. See Gateway & Network Provisioning.
- Device uplinks: an Output Profile with a
Delivery Mechanisms
MQcentral uses two distinct push mechanisms:
| Mechanism | Data delivered | Where to configure |
|---|---|---|
Output Profile: RestParams (v1) |
Device uplinks as they are received | Data Delivery: Output Profiles |
| Output Alert Service: webhook destination (v2) | Gateway online/offline events | Gateway & Network Provisioning |
Each mechanism uses a different base URL and configuration model. They are not interchangeable.
Uplink Payload Format
When an Output Profile RestParams target fires, your endpoint receives an HTTP POST with a JSON body. The payload structure matches the uplink format documented in Working with Device Data:
extendedformat includes decodedApplicationDataalongside raw network metadatasimpleformat includes only the decoded payload
Use the combination (DevEUI, FCnt) as your idempotency key; this pair is unique per uplink and survives retries and multi-gateway duplicate delivery.
Gateway Event Payload Format
When a gateway's connectivity changes, the Output Alert Service sends a POST HTTP request to all configured webhook destinations:
{
"timestamp": "2022-09-12T14:49:01.964Z",
"type": "gateway_connectivity",
"data": {
"timestamp": "2022-09-12T14:49:01.919Z",
"node_id": "2CC4070000001234",
"status": "online"
}
}
data.status is "online" or "offline". Gateway connectivity alerts include a 15-minute debounce; see Gateway & Network Provisioning for full details.
Delivery Semantics
- At-least-once. A given uplink or event may be delivered more than once, most commonly when your endpoint times out or returns 5xx. Your receiver must be idempotent.
- Best-effort ordering. Events for the same device usually arrive in order, but retries can cause reordering. Don't rely on order for correctness; if it matters, sort by timestamp or
FCnt. - Retries on failure. Failed deliveries retry on 5xx responses or timeouts. Monitor your endpoint's error rate; a sustained failure rate means data is accumulating in the retry queue.
Building a Resilient Receiver
Acknowledge fast, process async. Your handler should do as little as possible: validate the request, drop the event onto an internal queue (SQS, Pub/Sub, RabbitMQ), and return 200. Processing synchronously converts every downstream slowdown into a delivery timeout and retry storm.
Idempotency on (DevEUI, FCnt). Track recently-seen (DevEUI, FCnt) pairs in a short-TTL cache (for example, a Redis key per pair with a five-minute expiry, SET deveui:fcnt 1 EX 300) and drop any delivery whose key already exists. The TTL keeps memory bounded while still catching duplicates within the platform's retry window. This is non-negotiable in production.
Dead-letter handling. Decide in advance what happens to events that exhaust retries. Silently dropping them is how data quality issues compound undetected. At minimum, log them durably and alert on sustained delivery failures.
Return 5xx for backpressure. If your downstream system is overloaded, return 5xx and let the platform back off rather than accepting and losing the delivery internally. The retry mechanism is the only thing between your incident and permanent data loss.
MQTT as an Alternative
For high-volume telemetry streams where HTTP overhead is a concern, configure an Output Profile with a MqttParams target instead. MQTT trades complexity (you operate a long-lived connection with reconnect logic) for lower per-message overhead and a single delivery channel.
A practical split: use REST/webhook delivery for lower-volume, event-driven integrations; use MQTT for continuous high-frequency sensor streams.
Both are configured through Output Profiles. See Data Delivery: Output Profiles.
Testing Locally
Testing Output Profile Receivers
Output Profiles can deliver device uplinks to four target types: a REST endpoint (RestParams), an MQTT broker (MqttParams), Azure IoT Hub (AzureParams), or AWS IoT Core (AWSParams). The approach to local testing depends on which target you use.
HTTP / REST receiver. Start your webhook handler on localhost:8080 (or whichever port you choose) so it's ready to accept POST requests. The platform delivers uplinks in the format returned by the v1 Logs API. Here's the full payload shape:
{
"Timestamp": "2026-05-10T01:32:28.290Z",
"DevEUI": "2CC407FFFE500D87",
"DevAddr": "446BC633",
"Fport": "2",
"FCnt": "721",
"MessageType": "2",
"MessageTypeText": "UnconfirmedDataUp",
"PayloadHex": "4d",
"MICHex": "41de6fe9",
"PrimaryGatewayRSSI": "0.0",
"PrimaryGatewaySNR": "0.0",
"PrimaryGatewayESP": "0.0",
"SpreadingFactor": "7",
"Airtime": "0.046336",
"SubBand": "G0",
"Channel": "LC1",
"GatewayID": "Not Owned",
"GatewayLatitide": "null",
"GatewayLongitude": "null",
"GatewayCount": "1",
"GatewayList": [
{
"Gateway": "07020000",
"RSSI": "-85.0",
"SNR": "13.25",
"ESP": "-85.600769",
"Time": "2026-05-10T01:32:28.285Z",
"GatewayNodeID": "2CC4070000020000"
}
],
"DeviceLatitude": "null",
"DeviceLongitude": "null",
"DeviceLocationRadius": "null",
"MacCommands": "",
"DecodedMacCommands": [],
"ADRbit": "1",
"ADRAckReq": "0",
"AckRequested": "0",
"ACKbit": "0",
"FPending": "null",
"Late": "0",
"DevNonce": "null",
"JoinEUI": "null",
"GatewayUnowned": true,
"GatewayNodeID": "Not Owned",
"PayloadDecoded": {
"data": {
"humidity": 38.5,
"payload_type": "sensor_data"
}
}
}
Simulate a delivery with curl against your running server:
curl -X POST http://localhost:8080/webhook \
-H "Content-Type: application/json" \
-d '{"Timestamp":"2026-05-10T01:32:28.290Z","DevEUI":"2CC407FFFE500D87","FCnt":"721","PayloadHex":"4d","PayloadDecoded":{"data":{"humidity":38.5,"payload_type":"sensor_data"}}, ...}'
Send the same payload twice to confirm your deduplication cache rejects the second delivery. To receive real platform traffic locally, expose your server with ngrok or cloudflared and point your Output Profile at the tunnel URL (e.g., https://f6e0-160-154-163-253.ngrok-free.app):
ngrok http 8080
MQTT receiver. Run a broker locally (e.g., Mosquitto in a container), then expose it with ngrok so the platform can reach it:
ngrok tcp 1883
Copy the public URL that ngrok returns (e.g., tcp://0.tcp.ngrok.io:12345) and set it as the broker URL in your Output Profile's MqttParams. The platform will publish device uplinks for the devices in that Output Profile to the ngrok address, which forwards them to your local broker. Once validated, swap the MqttParams URL to your production broker.
Azure IoT Hub / AWS IoT Core. These targets deliver directly into your cloud provider's IoT ingestion service; there is no local server to run. Test by pointing the Output Profile at a development IoT Hub or IoT Core instance and verifying messages arrive in the corresponding device stream or rule action.
Production Readiness Checklist. Before moving to production, verify your Output Profile receiver handles:
- A successful end-to-end delivery with the payload parsed correctly
- A duplicate event (same
DevEUI+FCntarriving twice) rejected by your idempotency cache - A
5xxreturn followed by a platform retry and eventual success (HTTP targets) - An MQTT disconnect and automatic reconnect (MQTT targets)
Testing Gateway Alert Receivers
Gateway alerts are delivered exclusively via webhook; your receiver is always an HTTP endpoint. The payload shape is defined in the v2 API specification and follows this structure:
{
"timestamp": "2023-01-10T14:58:35.455152Z",
"type": "gateway_connectivity",
"data": {
"timestamp": "2023-01-10T14:58:29.076699Z",
"node_id": "FFFFA15A9C20F79F",
"status": "offline"
}
}
type is always "gateway_connectivity". data.status is "online" or "offline".
Simulate a gateway alert locally with curl:
curl -X POST http://localhost:8080/alerts \
-H "Content-Type: application/json" \
-d '{
"timestamp": "2023-01-10T14:58:35.455152Z",
"type": "gateway_connectivity",
"data": {
"timestamp": "2023-01-10T14:58:29.076699Z",
"node_id": "FFFFA15A9C20F79F",
"status": "offline"
}
}'
To receive real alerts locally, expose your server with ngrok or cloudflared and configure the tunnel URL (e.g., https://f6e0-160-154-163-253.ngrok-free.app) as an Output Alert destination.
Production Readiness Checklist. Before moving to production, verify your alert receiver:
- Parses the
gateway_connectivityevent correctly - Returns
200promptly to acknowledge receipt - Handles a
5xxscenario followed by a platform retry and eventual success
General Tips
Log every inbound request during development. Dump the full request body and headers to stdout or a file. When something doesn't parse, having the raw payload makes debugging immediate instead of speculative.
What's Next?
- Operations, Monitoring & Best Practices: Observability, alerting, and production readiness guidance.