Signing in with a key
Another system does not sign in with a person's username and password. A superadmin creates an API key on the API Keys page, and every request carries it:
curl -H "X-API-Key: mikr_…" http://your-manager:3000/api/devices
Use your HTTPS address instead if the manager has one. A client that can only set an Authorization header can send the same key as Authorization: Bearer mikr_….
The key is shown once, when you create it. It has its own role and its own list of sites, set the same way as a user's, and it can have an expiry date. A key is a viewer, an operator or an admin — never a superadmin, so no key can reach user accounts or issue more keys. Every request is answered as that key: a key limited to one site lists only that site's devices, and a device outside it is refused. For reading state, which is everything on this page, a viewer key is enough. See Users & Access for what each role allows.
One thing to know before scoping a key: an admin key limited to sites also sees the devices that belong to no site at all, where a scoped operator or viewer key sees none of them.
Finding a device
Every device has an id, and the requests below take that id — its name or its address is not accepted in place of it. A ticket usually names a device instead, so start by fetching the list:
GET /api/devices
It returns every device the key can see, sorted by name. There is no search parameter: match on the fields your ticket carries, then keep the id. The fields most useful for that:
| Field | What it holds |
|---|---|
id | What the other requests take. |
name | The name given to the device in the manager. |
host | The IP address or hostname the manager connects to. |
status.identity | The RouterOS identity, as set on the router itself. |
tags, siteId | Tags and the site the device belongs to. |
Add ?grouped=true to get the same devices grouped under their sites. The answer is then an object holding sites, each of which carries its own devices; anything belonging to no site comes last, in a group named Ungrouped.
Reading a device
GET /api/devices/{id}
Returns what the manager knows from its last poll, without contacting the device. The status object is where diagnosis starts:
| Field | What it holds |
|---|---|
status.online | true if the last poll succeeded, false if it failed, null if the device has not been polled yet. |
status.offlineSince | When it stopped answering. |
status.lastChecked | When the manager last tried to poll it. A failed poll writes this too, so a recent timestamp means the manager is still trying, not that the device answered — that is what status.online is for. |
status.uptime, status.cpuLoad, status.freeMemory, status.totalMemory, status.temperature | The basics from that poll. |
status.routerOsVersion, status.latestRouterOs, status.model | What it runs, what is available on its channel, and what it is. |
These ask the device directly, so each one takes as long as a round trip to the router:
| Request | Returns |
|---|---|
GET /api/devices/{id}/interfaces | Ports in interfaces, and the neighbours discovered on them in neighbors. |
GET /api/devices/{id}/routes | How large the routing table is, not the routes themselves: total and a count per protocol in protocols, with ipv6Total and ipv6Protocols beside them. |
GET /api/devices/{id}/dhcp-leases | DHCP leases in leases, and how full each pool is in pools. |
GET /api/devices/{id}/wireless-clients | Connected wireless clients, in clients. |
A router that cannot be reached answers 502, with error carrying the reason and the list it would have filled left empty beside it. An empty list with 200 means the opposite: the device answered and has nothing of that kind to report. Reading the status code is enough to tell the two apart.
History comes from the manager's own records instead:
GET /api/devices/{id}/health-history?range=24h
GET /api/devices/{id}/health-history?range=24h&metric=cpu-load
Without metric it lists the metrics that have samples in that range. With one, it returns points carrying ts, avg, min and max. The range is one of 1h, 6h, 24h, 7d or 30d, and anything else is refused rather than rounded to the nearest.
Logs
GET /api/syslog?deviceId={id}&limit=100
Syslog the manager has collected from the device, newest first, in rows. It narrows further with search for text, topic, and maxSeverity — a number from 0, emergency, to 7, debug, keeping that level and everything more severe. siteId, since and until work here too, the last two as ISO timestamps. limit defaults to 200 and goes up to 1000; to walk further back, send the oldest receivedAt you were given as before and ask again. The device has to be sending its log to the manager; Logs explains how to set that up.
A device in a site the key cannot see is not an error on this request: the answer is an empty rows.
GET /api/syslog/export?deviceId={id}&format=csv&tz=Europe/Warsaw
The same entries as a file, oldest first, for a scheduled dump or an archive. format is csv or txt. tz is an IANA time zone for the local time column, and UTC is used when it is missing. The filters above all apply. There is no limit: one export holds up to the newest 100,000 entries, and when it stops there the response carries X-Export-Truncated with that ceiling as its value.
When a request fails
Errors come back as JSON with an error field and the usual status code:
| Code | Means |
|---|---|
401 | The key is missing, mistyped, revoked or past its expiry date. |
403 | The key's role does not allow this request, or the device is in a site the key is not scoped to. |
404 | There is no device with that id. |
422 | The device is disabled in the manager, so requests that ask the device directly are refused. |
502 | The manager could not reach the device, so the reading could not be taken. The body still carries the field the reading would have arrived in, empty, alongside error. |
Missing something your integration needs? Write to support@mikr.app and say what it has to do.