{"openapi":"3.1.0","info":{"title":"ModbusCloud API","version":"1.0.0","summary":"Read gateway, register, reading and alert data, and receive events as webhooks.","description":"\nThe ModbusCloud API gives your own systems the data our gateways read from Modbus equipment: gateways, the Modbus devices behind them, registers with their latest values, the reading history and alerts. Webhooks push events to your endpoint the moment they happen.\n\n## Quickstart\n\nEvery request needs an API key. To try the API right away, use the sandbox key below. It is read only and points at our demo organisation, where simulated gateways send new readings every minute. Everyone shares this key, so it allows 60 requests per minute per visitor.\n\n```\nmlk_72718174d83aa7fa5238c5eb8df5adc3e432c72cc80ea3242821f3aaabd9f630\n```\n\nCheck that the key works:\n\n```bash\ncurl https://api.modbuscloud.com/v1/introspect \\\n  -H \"Authorization: Bearer mlk_72718174d83aa7fa5238c5eb8df5adc3e432c72cc80ea3242821f3aaabd9f630\"\n```\n\nList the gateways, then fetch the current values of one of them:\n\n```bash\ncurl https://api.modbuscloud.com/v1/devices \\\n  -H \"Authorization: Bearer mlk_72718174d83aa7fa5238c5eb8df5adc3e432c72cc80ea3242821f3aaabd9f630\"\n\ncurl https://api.modbuscloud.com/v1/devices/ecc5e4e5-da58-4678-92af-d72b09c2b937/registers \\\n  -H \"Authorization: Bearer mlk_72718174d83aa7fa5238c5eb8df5adc3e432c72cc80ea3242821f3aaabd9f630\"\n```\n\nThe **Try it** buttons on this page are filled in with the sandbox key as well.\n\n## Concepts\n\n| Term | Meaning |\n|---|---|\n| Gateway | The ModbusCloud Gateway on site. In the API paths a gateway is a `device`, for example `/devices/{id}`. |\n| Modbus device | Equipment the gateway reads over Modbus RTU or Modbus TCP, such as a climate computer, a meter or a heat pump. One gateway can read many. |\n| Register | One value of a Modbus device, with a name, a unit and a scale factor. |\n| Reading | One stored value of a register at a point in time. |\n| Alert | Raised when a rule is met, for example a value above a limit or a gateway going offline. |\n\n**Values.** `value` and `latest_value` are in engineering units, so with the scale factor applied, exactly as the portal shows them. `raw_value` is the number as read from the register.\n\n**History.** By default the history holds one value per register every five minutes, and at most one per minute. That depends on how often the gateway reads, which you set in the portal. Readings are kept for 12 months by default. Live values in the portal can be faster than that.\n\n**Time.** All timestamps are ISO 8601 in UTC.\n\n## Authentication\n\nSend the key as a bearer token on every request.\n\n```\nAuthorization: Bearer mlk_...\n```\n\nOwners and admins create keys in the portal under **Settings, API keys**. A key is shown once, so store it right away. Each key belongs to one organisation and carries scopes.\n\n| Scope | Gives access to |\n|---|---|\n| `devices:read` | Gateways and their Modbus devices |\n| `registers:read` | Registers, latest values and reading history |\n| `alerts:read` | Alerts |\n\nKeys can expire and can be revoked in the portal at any time.\n\nWriting to registers is off for every organisation until we switch it on at your request. Until then a write is answered with 403 `write_not_enabled`, whatever the key's scopes.\n\n## Pagination\n\nGateways and alerts use `limit` (1 to 100, default 50) and `offset`, and return `meta.total`.\n\nReadings use a cursor, because the history is large. Every page returns `meta.has_more` and `meta.next_cursor`. Pass `next_cursor` as `cursor` to get the next page, with the same other parameters. Rows are never skipped or repeated, also while new readings arrive.\n\n```bash\n# first page\ncurl \"https://api.modbuscloud.com/v1/devices/{id}/readings?since=2026-10-01T00:00:00Z&until=2026-10-02T00:00:00Z&order=asc&limit=1000\" \\\n  -H \"Authorization: Bearer mlk_...\"\n\n# next page\ncurl \"https://api.modbuscloud.com/v1/devices/{id}/readings?since=2026-10-01T00:00:00Z&until=2026-10-02T00:00:00Z&order=asc&limit=1000&cursor=MjAyNi0x...\" \\\n  -H \"Authorization: Bearer mlk_...\"\n```\n\nTo keep a copy of the history in your own database, request `order=asc` with `since` set to the last timestamp you stored, and follow `next_cursor` until `has_more` is false.\n\n## Errors\n\nErrors use the problem details format of RFC 9457, with content type `application/problem+json`.\n\n```json\n{\n  \"type\": \"https://docs.modbuscloud.com/errors#insufficient_scope\",\n  \"title\": \"Insufficient scope\",\n  \"status\": 403,\n  \"detail\": \"This endpoint requires the 'alerts:read' scope.\",\n  \"code\": \"insufficient_scope\",\n  \"request_id\": \"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f\"\n}\n```\n\nBranch on `code`, which is stable. `detail` is meant for people and can change.\n\n| Status | code | When |\n|---|---|---|\n| 400 | `invalid_parameter` | A query parameter is missing, malformed or out of range |\n| 401 | `missing_api_key` | No `Authorization: Bearer` header |\n| 401 | `invalid_api_key` | The key does not exist |\n| 401 | `api_key_revoked` | The key was revoked in the portal |\n| 401 | `api_key_expired` | The key passed its expiry date |\n| 403 | `insufficient_scope` | The key lacks the scope this endpoint needs |\n| 403 | `write_not_enabled` | Writing to installations is not switched on for your organisation |\n| 404 | `not_found` | The gateway, register or alert is not in your organisation |\n| 429 | `rate_limited` | More than 100 requests in the current minute |\n| 500 | `internal_error` | Something failed on our side |\n\nEvery response carries an `X-Request-Id` header. Mention it when you contact us about a request.\n\n## Rate limits\n\nEach key may make 100 requests per minute. The window resets at the start of every minute. The shared sandbox key is counted per visitor, 60 requests per minute each. Every response tells you where you stand.\n\n| Header | Meaning |\n|---|---|\n| `X-RateLimit-Limit` | Requests allowed per minute |\n| `X-RateLimit-Remaining` | Requests left in the current minute |\n| `X-RateLimit-Reset` | Seconds until the window resets |\n| `Retry-After` | On a 429 only, seconds to wait |\n\nPolling the registers of 50 gateways once a minute uses half of the budget. Use webhooks for events instead of polling alerts.\n\n## Webhooks\n\nWebhooks send an HTTP POST to your endpoint when something happens. Owners and admins add endpoints in the portal under **Settings, Webhooks**, pick the events and copy the signing secret. The events and their payloads are listed under **Webhooks** in the menu on this page.\n\nEvery delivery has the same envelope.\n\n```json\n{\n  \"id\": \"msg_2f8c1d0a9b7e4c3d8e1f0a2b3c4d5e6f\",\n  \"type\": \"alert.triggered\",\n  \"timestamp\": \"2026-10-05T12:00:00.000Z\",\n  \"data\": { \"alert_id\": \"06d56ed5-6ddf-4ffd-9dc9-12d6ad5d51cf\", \"device_id\": \"...\", \"severity\": \"warning\", \"value\": 67.2 }\n}\n```\n\n### Verifying the signature\n\nDeliveries are signed according to the [Standard Webhooks](https://www.standardwebhooks.com) specification, so you can use their libraries directly.\n\n| Header | Content |\n|---|---|\n| `webhook-id` | Unique id of the message. The same on every retry. |\n| `webhook-timestamp` | Unix time in seconds of this attempt |\n| `webhook-signature` | `v1,` followed by the base64 HMAC-SHA256 signature |\n| `X-ModbusCloud-Event` | The event type, the same as `type` in the body |\n\nNode.js with `npm install standardwebhooks`:\n\n```js\nimport { Webhook } from 'standardwebhooks'\n\nconst wh = new Webhook(process.env.MODBUSCLOUD_WEBHOOK_SECRET) // whsec_...\n\napp.post('/webhooks/modbuscloud', express.raw({ type: 'application/json' }), (req, res) => {\n  let event\n  try {\n    event = wh.verify(req.body.toString('utf8'), req.headers)\n  } catch {\n    return res.status(400).end()\n  }\n  res.status(200).end()      // answer first\n  queueForProcessing(event)  // then do the work\n})\n```\n\nPython with `pip install standardwebhooks`:\n\n```python\nfrom standardwebhooks.webhooks import Webhook\n\nwh = Webhook(os.environ[\"MODBUSCLOUD_WEBHOOK_SECRET\"])  # whsec_...\n\n@app.post(\"/webhooks/modbuscloud\")\ndef receive():\n    try:\n        event = wh.verify(request.get_data(as_text=True), dict(request.headers))\n    except Exception:\n        return \"\", 400\n    queue_for_processing(event)\n    return \"\", 200\n```\n\nIn any other language, compute HMAC-SHA256 over `{webhook-id}.{webhook-timestamp}.{raw body}` with the secret as key, where the key is the part after `whsec_` decoded from base64. Base64-encode the result and compare it with the value after `v1,` in a constant-time comparison. Reject messages with a timestamp more than five minutes from your clock.\n\nAlways verify against the raw body as received. Parsing and serialising the JSON first changes the bytes and breaks the signature.\n\n### Delivery and retries\n\n- Respond with any 2xx status within 10 seconds. Do the processing after you have answered.\n- A delivery fails on a non-2xx status, a timeout or a redirect. Redirects are not followed.\n- Failed deliveries are retried after 30 seconds, 2 minutes, 10 minutes, 1 hour and 6 hours, so six attempts over a little more than seven hours.\n- Retries carry the same `webhook-id`. Store the ids you processed and skip duplicates.\n- The order of deliveries is not guaranteed. Use `timestamp` and the alert `status` to decide what is current.\n- After ten deliveries in a row that failed every attempt, the endpoint is paused. Switch it back on in the portal.\n- Endpoints must be reachable on the public internet over http or https. Use https.\n\n## Versioning\n\nThis is version 1. We add endpoints, fields and events without notice, so ignore fields you do not know. Changes that could break an integration come as a new version, announced in advance.\n\n## Support\n\nQuestions or something not working? Mail [info@modbuscloud.com](mailto:info@modbuscloud.com) and include the `X-Request-Id`.\n","contact":{"name":"ModbusCloud","email":"info@modbuscloud.com","url":"https://modbuscloud.com"}},"servers":[{"url":"https://api.modbuscloud.com/v1","description":"Production"},{"url":"https://portal.modbuscloud.com/api/v1","description":"Production, alternative address"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Key","description":"Check the key you are using."},{"name":"Gateways","description":"Gateways and the Modbus devices behind them. A gateway is a `device` in the paths."},{"name":"Registers","description":"Registers of a gateway with their latest values."},{"name":"Readings","description":"Reading history of a gateway, paged with a cursor."},{"name":"Alerts","description":"Alerts of your organisation."},{"name":"Webhook events","description":"Events sent to your endpoint. See Webhooks in the introduction for signing and retries."}],"paths":{"/introspect":{"get":{"tags":["Key"],"operationId":"introspect","summary":"Check your API key","description":"Returns the key name, its scopes and the organisation it belongs to. Any valid key may call this.","responses":{"200":{"description":"The key and its organisation.","headers":{"X-Request-Id":{"description":"Id of this request, for support.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current minute.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Introspection"}}}}}},"401":{"description":"Invalid API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#invalid_api_key","title":"Invalid API key","status":401,"detail":"This API key does not exist.","code":"invalid_api_key","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"403":{"description":"Insufficient scope","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#insufficient_scope","title":"Insufficient scope","status":403,"detail":"This endpoint requires the 'registers:read' scope.","code":"insufficient_scope","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"429":{"description":"Rate limit exceeded","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#rate_limited","title":"Rate limit exceeded","status":429,"detail":"Max 100 requests per minute per key. Retry after 12 seconds.","code":"rate_limited","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#internal_error","title":"Internal server error","status":500,"detail":"Something went wrong on our side. Quote the request_id when contacting support.","code":"internal_error","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}}}}},"/devices":{"get":{"tags":["Gateways"],"operationId":"listGateways","summary":"List gateways","description":"All gateways of your organisation, most recently seen first. Scope `devices:read`.","parameters":[{"name":"status","in":"query","description":"Only gateways with this status.","schema":{"type":"string","enum":["online","offline","pending","error"]}},{"name":"limit","in":"query","description":"Page size, 1 to 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"offset","in":"query","description":"Rows to skip.","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"A page of gateways.","headers":{"X-Request-Id":{"description":"Id of this request, for support.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current minute.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Gateway"}},"meta":{"$ref":"#/components/schemas/OffsetMeta"}}}}}},"401":{"description":"Invalid API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#invalid_api_key","title":"Invalid API key","status":401,"detail":"This API key does not exist.","code":"invalid_api_key","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"403":{"description":"Insufficient scope","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#insufficient_scope","title":"Insufficient scope","status":403,"detail":"This endpoint requires the 'registers:read' scope.","code":"insufficient_scope","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"429":{"description":"Rate limit exceeded","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#rate_limited","title":"Rate limit exceeded","status":429,"detail":"Max 100 requests per minute per key. Retry after 12 seconds.","code":"rate_limited","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#internal_error","title":"Internal server error","status":500,"detail":"Something went wrong on our side. Quote the request_id when contacting support.","code":"internal_error","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}}}}},"/devices/{id}":{"get":{"tags":["Gateways"],"operationId":"getGateway","summary":"Get a gateway","description":"One gateway with its customer and the Modbus devices it reads. Scope `devices:read`.","parameters":[{"name":"id","in":"path","required":true,"description":"Gateway id, from `GET /devices`.","schema":{"type":"string","format":"uuid","examples":["ecc5e4e5-da58-4678-92af-d72b09c2b937"]},"example":"ecc5e4e5-da58-4678-92af-d72b09c2b937"}],"responses":{"200":{"description":"The gateway.","headers":{"X-Request-Id":{"description":"Id of this request, for support.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current minute.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/GatewayDetail"}}}}}},"401":{"description":"Invalid API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#invalid_api_key","title":"Invalid API key","status":401,"detail":"This API key does not exist.","code":"invalid_api_key","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"403":{"description":"Insufficient scope","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#insufficient_scope","title":"Insufficient scope","status":403,"detail":"This endpoint requires the 'registers:read' scope.","code":"insufficient_scope","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#not_found","title":"Not found","status":404,"detail":"Gateway not found.","code":"not_found","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"429":{"description":"Rate limit exceeded","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#rate_limited","title":"Rate limit exceeded","status":429,"detail":"Max 100 requests per minute per key. Retry after 12 seconds.","code":"rate_limited","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#internal_error","title":"Internal server error","status":500,"detail":"Something went wrong on our side. Quote the request_id when contacting support.","code":"internal_error","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}}}}},"/devices/{id}/registers":{"get":{"tags":["Registers"],"operationId":"listRegisters","summary":"List registers with latest values","description":"Every register of the gateway with its latest value. Poll this for the current state. Scope `registers:read`.","parameters":[{"name":"id","in":"path","required":true,"description":"Gateway id, from `GET /devices`.","schema":{"type":"string","format":"uuid","examples":["ecc5e4e5-da58-4678-92af-d72b09c2b937"]},"example":"ecc5e4e5-da58-4678-92af-d72b09c2b937"},{"name":"modbus_device_id","in":"query","description":"Only registers of this Modbus device.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Registers of the gateway.","headers":{"X-Request-Id":{"description":"Id of this request, for support.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current minute.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Register"}}}}}}},"401":{"description":"Invalid API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#invalid_api_key","title":"Invalid API key","status":401,"detail":"This API key does not exist.","code":"invalid_api_key","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"403":{"description":"Insufficient scope","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#insufficient_scope","title":"Insufficient scope","status":403,"detail":"This endpoint requires the 'registers:read' scope.","code":"insufficient_scope","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#not_found","title":"Not found","status":404,"detail":"Gateway not found.","code":"not_found","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"429":{"description":"Rate limit exceeded","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#rate_limited","title":"Rate limit exceeded","status":429,"detail":"Max 100 requests per minute per key. Retry after 12 seconds.","code":"rate_limited","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#internal_error","title":"Internal server error","status":500,"detail":"Something went wrong on our side. Quote the request_id when contacting support.","code":"internal_error","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}}}}},"/devices/{id}/readings":{"get":{"tags":["Readings"],"operationId":"listReadings","summary":"List readings","description":"Reading history of the gateway inside a time window, newest first unless `order=asc`. The window is `since` (inclusive) to `until` (exclusive) and defaults to the last 24 hours. It can be at most 7 days, or 366 days when you pass exactly one `register_id`. Follow `meta.next_cursor` for the next page. Scope `registers:read`.","parameters":[{"name":"id","in":"path","required":true,"description":"Gateway id, from `GET /devices`.","schema":{"type":"string","format":"uuid","examples":["ecc5e4e5-da58-4678-92af-d72b09c2b937"]},"example":"ecc5e4e5-da58-4678-92af-d72b09c2b937"},{"name":"register_id","in":"query","description":"One register id, or several separated by commas.","schema":{"type":"string"}},{"name":"since","in":"query","description":"Start of the window, inclusive. Default `until` minus 24 hours.","schema":{"type":"string","format":"date-time"}},{"name":"until","in":"query","description":"End of the window, exclusive. Default now.","schema":{"type":"string","format":"date-time"}},{"name":"order","in":"query","description":"Sort by time.","schema":{"type":"string","enum":["desc","asc"],"default":"desc"}},{"name":"limit","in":"query","description":"Page size, 1 to 1000.","schema":{"type":"integer","minimum":1,"maximum":1000,"default":100}},{"name":"cursor","in":"query","description":"`meta.next_cursor` of the previous page.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of readings.","headers":{"X-Request-Id":{"description":"Id of this request, for support.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current minute.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Reading"}},"meta":{"$ref":"#/components/schemas/CursorMeta"}}}}}},"400":{"description":"Invalid parameter","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#invalid_parameter","title":"Invalid parameter","status":400,"detail":"The window between since and until can be at most 7 days. Pass a single register_id to request up to 366 days.","code":"invalid_parameter","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"401":{"description":"Invalid API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#invalid_api_key","title":"Invalid API key","status":401,"detail":"This API key does not exist.","code":"invalid_api_key","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"403":{"description":"Insufficient scope","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#insufficient_scope","title":"Insufficient scope","status":403,"detail":"This endpoint requires the 'registers:read' scope.","code":"insufficient_scope","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"404":{"description":"Not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#not_found","title":"Not found","status":404,"detail":"Gateway not found.","code":"not_found","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"429":{"description":"Rate limit exceeded","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#rate_limited","title":"Rate limit exceeded","status":429,"detail":"Max 100 requests per minute per key. Retry after 12 seconds.","code":"rate_limited","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#internal_error","title":"Internal server error","status":500,"detail":"Something went wrong on our side. Quote the request_id when contacting support.","code":"internal_error","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}}}}},"/alerts":{"get":{"tags":["Alerts"],"operationId":"listAlerts","summary":"List alerts","description":"Alerts of your organisation, most recently triggered first. Scope `alerts:read`.","parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["active","returned","acknowledged","resolved"]},"description":"Only alerts with this status."},{"name":"severity","in":"query","schema":{"type":"string","enum":["critical","warning","info"]},"description":"Only alerts with this severity."},{"name":"device_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Only alerts of this gateway."},{"name":"limit","in":"query","description":"Page size, 1 to 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"offset","in":"query","description":"Rows to skip.","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"A page of alerts.","headers":{"X-Request-Id":{"description":"Id of this request, for support.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Requests allowed per minute.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current minute.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Alert"}},"meta":{"$ref":"#/components/schemas/OffsetMeta"}}}}}},"401":{"description":"Invalid API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#invalid_api_key","title":"Invalid API key","status":401,"detail":"This API key does not exist.","code":"invalid_api_key","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"403":{"description":"Insufficient scope","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#insufficient_scope","title":"Insufficient scope","status":403,"detail":"This endpoint requires the 'registers:read' scope.","code":"insufficient_scope","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"429":{"description":"Rate limit exceeded","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#rate_limited","title":"Rate limit exceeded","status":429,"detail":"Max 100 requests per minute per key. Retry after 12 seconds.","code":"rate_limited","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}},"500":{"description":"Internal server error","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.modbuscloud.com/errors#internal_error","title":"Internal server error","status":500,"detail":"Something went wrong on our side. Quote the request_id when contacting support.","code":"internal_error","request_id":"req_4f1c0d2e9a8b4c7d8e6f5a4b3c2d1e0f"}}}}}}}},"webhooks":{"device.online":{"post":{"operationId":"webhook_device_online","summary":"device.online","description":"A gateway is reporting again, also when it comes back after it was marked offline.","tags":["Webhook events"],"parameters":[{"name":"webhook-id","in":"header","required":true,"schema":{"type":"string"},"description":"Unique message id, the same on every retry."},{"name":"webhook-timestamp","in":"header","required":true,"schema":{"type":"string"},"description":"Unix time in seconds of this attempt."},{"name":"webhook-signature","in":"header","required":true,"schema":{"type":"string"},"description":"`v1,` followed by the base64 HMAC-SHA256 signature."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["id","type","timestamp","data"],"properties":{"id":{"type":"string","description":"Message id, equal to the `webhook-id` header."},"type":{"type":"string","const":"device.online"},"timestamp":{"type":"string","format":"date-time","description":"When the event happened."},"data":{"$ref":"#/components/schemas/DeviceStatusEvent"}}},"example":{"id":"msg_2f8c1d0a9b7e4c3d8e1f0a2b3c4d5e6f","type":"device.online","timestamp":"2026-10-05T12:00:00.000Z","data":{"device_id":"ecc5e4e5-da58-4678-92af-d72b09c2b937","serial":"MCG-9V63A8M1","status":"online","previous_status":"offline","last_seen":"2026-10-05T12:00:00.000Z"}}}}},"responses":{"2XX":{"description":"Received. Any 2xx status within 10 seconds counts as delivered."}}}},"device.offline":{"post":{"operationId":"webhook_device_offline","summary":"device.offline","description":"A gateway stopped reporting, because it disconnected or because it was silent for longer than its heartbeat interval.","tags":["Webhook events"],"parameters":[{"name":"webhook-id","in":"header","required":true,"schema":{"type":"string"},"description":"Unique message id, the same on every retry."},{"name":"webhook-timestamp","in":"header","required":true,"schema":{"type":"string"},"description":"Unix time in seconds of this attempt."},{"name":"webhook-signature","in":"header","required":true,"schema":{"type":"string"},"description":"`v1,` followed by the base64 HMAC-SHA256 signature."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["id","type","timestamp","data"],"properties":{"id":{"type":"string","description":"Message id, equal to the `webhook-id` header."},"type":{"type":"string","const":"device.offline"},"timestamp":{"type":"string","format":"date-time","description":"When the event happened."},"data":{"$ref":"#/components/schemas/DeviceStatusEvent"}}},"example":{"id":"msg_2f8c1d0a9b7e4c3d8e1f0a2b3c4d5e6f","type":"device.offline","timestamp":"2026-10-05T12:00:00.000Z","data":{"device_id":"ecc5e4e5-da58-4678-92af-d72b09c2b937","serial":"MCG-9V63A8M1","status":"offline","previous_status":"online","last_seen":"2026-10-05T11:56:30.000Z"}}}}},"responses":{"2XX":{"description":"Received. Any 2xx status within 10 seconds counts as delivered."}}}},"alert.triggered":{"post":{"operationId":"webhook_alert_triggered","summary":"alert.triggered","description":"A rule was met and an alert opened, or an open alert fired again. For offline alerts `condition` is `offline` and value fields are absent.","tags":["Webhook events"],"parameters":[{"name":"webhook-id","in":"header","required":true,"schema":{"type":"string"},"description":"Unique message id, the same on every retry."},{"name":"webhook-timestamp","in":"header","required":true,"schema":{"type":"string"},"description":"Unix time in seconds of this attempt."},{"name":"webhook-signature","in":"header","required":true,"schema":{"type":"string"},"description":"`v1,` followed by the base64 HMAC-SHA256 signature."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["id","type","timestamp","data"],"properties":{"id":{"type":"string","description":"Message id, equal to the `webhook-id` header."},"type":{"type":"string","const":"alert.triggered"},"timestamp":{"type":"string","format":"date-time","description":"When the event happened."},"data":{"$ref":"#/components/schemas/AlertTriggeredEvent"}}},"example":{"id":"msg_2f8c1d0a9b7e4c3d8e1f0a2b3c4d5e6f","type":"alert.triggered","timestamp":"2026-10-05T12:00:00.000Z","data":{"alert_id":"06d56ed5-6ddf-4ffd-9dc9-12d6ad5d51cf","rule_id":"bacb8015-2c7c-49dc-bbd9-7ca2da90b6de","rule_name":"Sludge temperature too high","device_id":"ecc5e4e5-da58-4678-92af-d72b09c2b937","severity":"warning","value":45.1,"threshold":45,"condition":"gt","message":"Sludge temperature too high: 45.1 C","source":"rule","register_id":"a8607f2a-6979-4aa4-96a9-ffb5f1ab49b1","modbus_device_id":"d82b9a58-92b0-4653-b869-19fe9d6c2f24"}}}}},"responses":{"2XX":{"description":"Received. Any 2xx status within 10 seconds counts as delivered."}}}},"alert.returned":{"post":{"operationId":"webhook_alert_returned","summary":"alert.returned","description":"The value is back within the limit, or the gateway is back online. The alert stays open until someone resolves it.","tags":["Webhook events"],"parameters":[{"name":"webhook-id","in":"header","required":true,"schema":{"type":"string"},"description":"Unique message id, the same on every retry."},{"name":"webhook-timestamp","in":"header","required":true,"schema":{"type":"string"},"description":"Unix time in seconds of this attempt."},{"name":"webhook-signature","in":"header","required":true,"schema":{"type":"string"},"description":"`v1,` followed by the base64 HMAC-SHA256 signature."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["id","type","timestamp","data"],"properties":{"id":{"type":"string","description":"Message id, equal to the `webhook-id` header."},"type":{"type":"string","const":"alert.returned"},"timestamp":{"type":"string","format":"date-time","description":"When the event happened."},"data":{"$ref":"#/components/schemas/AlertReturnedEvent"}}},"example":{"id":"msg_2f8c1d0a9b7e4c3d8e1f0a2b3c4d5e6f","type":"alert.returned","timestamp":"2026-10-05T12:00:00.000Z","data":{"alert_id":"06d56ed5-6ddf-4ffd-9dc9-12d6ad5d51cf","rule_id":"bacb8015-2c7c-49dc-bbd9-7ca2da90b6de","rule_name":"Sludge temperature too high","device_id":"ecc5e4e5-da58-4678-92af-d72b09c2b937","severity":"warning","value":43.8,"source":"rule"}}}}},"responses":{"2XX":{"description":"Received. Any 2xx status within 10 seconds counts as delivered."}}}},"alert.acknowledged":{"post":{"operationId":"webhook_alert_acknowledged","summary":"alert.acknowledged","description":"Someone acknowledged the alert in the portal.","tags":["Webhook events"],"parameters":[{"name":"webhook-id","in":"header","required":true,"schema":{"type":"string"},"description":"Unique message id, the same on every retry."},{"name":"webhook-timestamp","in":"header","required":true,"schema":{"type":"string"},"description":"Unix time in seconds of this attempt."},{"name":"webhook-signature","in":"header","required":true,"schema":{"type":"string"},"description":"`v1,` followed by the base64 HMAC-SHA256 signature."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["id","type","timestamp","data"],"properties":{"id":{"type":"string","description":"Message id, equal to the `webhook-id` header."},"type":{"type":"string","const":"alert.acknowledged"},"timestamp":{"type":"string","format":"date-time","description":"When the event happened."},"data":{"$ref":"#/components/schemas/AlertStatusEvent"}}},"example":{"id":"msg_2f8c1d0a9b7e4c3d8e1f0a2b3c4d5e6f","type":"alert.acknowledged","timestamp":"2026-10-05T12:00:00.000Z","data":{"alert_id":"06d56ed5-6ddf-4ffd-9dc9-12d6ad5d51cf","device_id":"ecc5e4e5-da58-4678-92af-d72b09c2b937","rule_id":"bacb8015-2c7c-49dc-bbd9-7ca2da90b6de","severity":"warning","message":"Sludge temperature too high: 45.1 C","status":"acknowledged","value":45.1,"acknowledged_at":"2026-10-05T12:04:10.000Z","resolved_at":null,"note":"Checking the heat exchanger"}}}}},"responses":{"2XX":{"description":"Received. Any 2xx status within 10 seconds counts as delivered."}}}},"alert.resolved":{"post":{"operationId":"webhook_alert_resolved","summary":"alert.resolved","description":"The alert was closed.","tags":["Webhook events"],"parameters":[{"name":"webhook-id","in":"header","required":true,"schema":{"type":"string"},"description":"Unique message id, the same on every retry."},{"name":"webhook-timestamp","in":"header","required":true,"schema":{"type":"string"},"description":"Unix time in seconds of this attempt."},{"name":"webhook-signature","in":"header","required":true,"schema":{"type":"string"},"description":"`v1,` followed by the base64 HMAC-SHA256 signature."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["id","type","timestamp","data"],"properties":{"id":{"type":"string","description":"Message id, equal to the `webhook-id` header."},"type":{"type":"string","const":"alert.resolved"},"timestamp":{"type":"string","format":"date-time","description":"When the event happened."},"data":{"$ref":"#/components/schemas/AlertStatusEvent"}}},"example":{"id":"msg_2f8c1d0a9b7e4c3d8e1f0a2b3c4d5e6f","type":"alert.resolved","timestamp":"2026-10-05T12:00:00.000Z","data":{"alert_id":"06d56ed5-6ddf-4ffd-9dc9-12d6ad5d51cf","device_id":"ecc5e4e5-da58-4678-92af-d72b09c2b937","rule_id":"bacb8015-2c7c-49dc-bbd9-7ca2da90b6de","severity":"warning","message":"Sludge temperature too high: 45.1 C","status":"resolved","value":45.1,"acknowledged_at":"2026-10-05T12:04:10.000Z","resolved_at":"2026-10-05T13:30:00.000Z","note":"Valve replaced"}}}}},"responses":{"2XX":{"description":"Received. Any 2xx status within 10 seconds counts as delivered."}}}},"automation.triggered":{"post":{"operationId":"webhook_automation_triggered","summary":"automation.triggered","description":"An automation with a webhook action ran.","tags":["Webhook events"],"parameters":[{"name":"webhook-id","in":"header","required":true,"schema":{"type":"string"},"description":"Unique message id, the same on every retry."},{"name":"webhook-timestamp","in":"header","required":true,"schema":{"type":"string"},"description":"Unix time in seconds of this attempt."},{"name":"webhook-signature","in":"header","required":true,"schema":{"type":"string"},"description":"`v1,` followed by the base64 HMAC-SHA256 signature."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["id","type","timestamp","data"],"properties":{"id":{"type":"string","description":"Message id, equal to the `webhook-id` header."},"type":{"type":"string","const":"automation.triggered"},"timestamp":{"type":"string","format":"date-time","description":"When the event happened."},"data":{"$ref":"#/components/schemas/AutomationEvent"}}},"example":{"id":"msg_2f8c1d0a9b7e4c3d8e1f0a2b3c4d5e6f","type":"automation.triggered","timestamp":"2026-10-05T12:00:00.000Z","data":{"rule_id":"9b1d2c3e-4f50-4a61-8b72-9c83d4e5f6a7","rule_name":"Notify ERP on high load","actions":[{"type":"webhook"}]}}}}},"responses":{"2XX":{"description":"Received. Any 2xx status within 10 seconds counts as delivered."}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"mlk_...","description":"An API key from Settings, API keys in the portal. The sandbox key is filled in for you on this page."}},"schemas":{"Problem":{"type":"object","description":"Error in the RFC 9457 problem details format.","required":["type","title","status","code","request_id"],"properties":{"type":{"type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"code":{"type":"string","enum":["invalid_parameter","bad_request","missing_api_key","invalid_api_key","api_key_revoked","api_key_expired","insufficient_scope","write_not_enabled","not_found","conflict","rate_limited","internal_error"]},"request_id":{"type":"string"}}},"OffsetMeta":{"type":"object","properties":{"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"}}},"CursorMeta":{"type":"object","properties":{"since":{"type":"string","format":"date-time"},"until":{"type":"string","format":"date-time"},"order":{"type":"string","enum":["asc","desc"]},"limit":{"type":"integer"},"has_more":{"type":"boolean"},"next_cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page. Null on the last page."}}},"Introspection":{"type":"object","properties":{"key":{"type":"object","properties":{"name":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"created_at":{"type":"string","format":"date-time"},"expires_at":{"type":["string","null"],"format":"date-time"}}},"organization":{"type":"object","properties":{"name":{"type":"string"}}},"stats":{"type":"object","properties":{"gateways":{"type":"integer"},"devices":{"type":"integer","description":"Same as gateways, kept for older clients."},"open_alerts":{"type":"integer"}}}}},"Customer":{"type":"object","description":"The site or customer a gateway is assigned to.","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"city":{"type":["string","null"]}}},"Gateway":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"serial":{"type":"string","examples":["MCG-9V63A8M1"]},"name":{"type":"string"},"status":{"type":"string","enum":["online","offline","pending","error"]},"firmware_ver":{"type":["string","null"]},"ip_eth":{"type":["string","null"],"description":"Address on the wired network."},"ip_wifi":{"type":["string","null"]},"ip_4g":{"type":["string","null"],"description":"Address on the 4G network."},"signal_4g":{"type":["integer","null"],"description":"Signal strength in dBm."},"last_seen":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"customer":{"type":["object","null"],"$ref":"#/components/schemas/Customer"}}},"ModbusDevice":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":["string","null"]},"slave_id":{"type":"integer","description":"Modbus unit id."},"protocol":{"type":"string","enum":["rtu","tcp"]},"ip_address":{"type":["string","null"],"description":"For Modbus TCP."},"port":{"type":["integer","null"],"description":"For Modbus TCP."},"poll_interval_ms":{"type":["integer","null"]},"template":{"type":["object","null"],"properties":{"id":{"type":"string","format":"uuid"},"brand":{"type":"string"},"model":{"type":"string"}}}}},"GatewayDetail":{"allOf":[{"$ref":"#/components/schemas/Gateway"},{"type":"object","properties":{"claimed_at":{"type":["string","null"],"format":"date-time"},"modbus_devices":{"type":"array","items":{"$ref":"#/components/schemas/ModbusDevice"}}}}]},"Register":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"modbus_device_id":{"type":"string","format":"uuid"},"modbus_device_name":{"type":["string","null"]},"name":{"type":"string","examples":["Supply temperature"]},"address":{"type":"integer","description":"Register address."},"function_code":{"type":["integer","null"],"description":"3 holding register, 4 input register."},"data_type":{"type":"string","examples":["int16","uint16","int32","float32"]},"unit":{"type":["string","null"],"examples":["°C"]},"scale_factor":{"type":["number","null"],"description":"value = raw_value × scale_factor"},"is_writable":{"type":"boolean"},"description":{"type":["string","null"]},"latest_value":{"type":["number","null"],"description":"Latest value in engineering units."},"latest_raw_value":{"type":["number","null"],"description":"Latest value as read from the register."},"latest_ts":{"type":["string","null"],"format":"date-time"}}},"Reading":{"type":"object","properties":{"register_id":{"type":"string","format":"uuid"},"modbus_device_id":{"type":["string","null"],"format":"uuid"},"name":{"type":["string","null"]},"unit":{"type":["string","null"]},"value":{"type":["number","null"],"description":"In engineering units, scale factor applied."},"raw_value":{"type":["number","null"],"description":"As read from the register."},"ts":{"type":"string","format":"date-time"}}},"Alert":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"device_id":{"type":["string","null"],"format":"uuid"},"rule_id":{"type":["string","null"],"format":"uuid"},"severity":{"type":"string","enum":["critical","warning","info"]},"message":{"type":"string"},"status":{"type":"string","enum":["active","returned","acknowledged","resolved"]},"value":{"type":["number","null"]},"created_at":{"type":"string","format":"date-time"},"last_triggered_at":{"type":["string","null"],"format":"date-time"},"returned_at":{"type":["string","null"],"format":"date-time"},"ack_at":{"type":["string","null"],"format":"date-time"},"ack_note":{"type":["string","null"]},"resolved_at":{"type":["string","null"],"format":"date-time"},"resolve_note":{"type":["string","null"]},"entry_count":{"type":"integer","description":"How often the alert fired while open."}}},"DeviceStatusEvent":{"type":"object","properties":{"device_id":{"type":"string","format":"uuid"},"serial":{"type":"string"},"status":{"type":"string","enum":["online","offline"]},"previous_status":{"type":["string","null"]},"last_seen":{"type":["string","null"],"format":"date-time","description":"Last time the gateway reported."}}},"AlertTriggeredEvent":{"type":"object","properties":{"alert_id":{"type":"string","format":"uuid"},"rule_id":{"type":"string","format":"uuid"},"rule_name":{"type":"string"},"device_id":{"type":"string","format":"uuid"},"severity":{"type":"string","enum":["critical","warning","info"]},"value":{"type":"number"},"threshold":{"type":"number"},"condition":{"type":"string","description":"Comparison of the rule, or `offline`."},"message":{"type":"string"},"source":{"type":"string","enum":["rule","detector"]},"register_id":{"type":["string","null"],"format":"uuid"},"modbus_device_id":{"type":["string","null"],"format":"uuid"}}},"AlertReturnedEvent":{"type":"object","properties":{"alert_id":{"type":"string","format":"uuid"},"rule_id":{"type":"string","format":"uuid"},"rule_name":{"type":"string"},"device_id":{"type":"string","format":"uuid"},"severity":{"type":"string"},"value":{"type":"number"},"condition":{"type":"string","description":"`offline` when the gateway is back online."},"source":{"type":"string","enum":["rule","detector"]}}},"AlertStatusEvent":{"type":"object","properties":{"alert_id":{"type":"string","format":"uuid"},"device_id":{"type":["string","null"],"format":"uuid"},"rule_id":{"type":["string","null"],"format":"uuid"},"severity":{"type":"string"},"message":{"type":"string"},"status":{"type":"string","enum":["acknowledged","resolved"]},"value":{"type":["number","null"]},"acknowledged_at":{"type":["string","null"],"format":"date-time"},"resolved_at":{"type":["string","null"],"format":"date-time"},"note":{"type":["string","null"]}}},"AutomationEvent":{"type":"object","properties":{"rule_id":{"type":"string","format":"uuid"},"rule_name":{"type":"string"},"actions":{"type":"array","items":{"type":"object"}}}}}}}