What a webhook does here
A webhook is an HTTP request the manager sends when something happens to your fleet — a device stopping answering, a backup failing, a CVE matching a version you run. You give it a URL, tick the events it should carry, and choose the shape of the body. Nothing else is needed: there is no queue to run and no agent to install.
Any number of webhooks can subscribe to the same event, and each is delivered independently — a dashboard that wants everything and a phone that wants only the serious things are two webhooks, not one with rules. A webhook switched off stops receiving events without losing its configuration.
The Webhooks page is where they live, and it is offered to admins only. Creating, editing, testing and deleting all sit behind the same role, so an operator or a viewer never sees the page and is refused if they ask for it another way.
An admin limited to certain sites sees a narrower page. A webhook is theirs only when its site list is not empty and every site in it is one of their own: a fleet-wide webhook belongs to nobody in particular, and one spanning their site plus someone else's could be repointed at that other site. They also have to name at least one of their sites when creating one. An admin with no site limits sees and does everything. (v1.94.0+)
The events
Twenty-six, and each one names what fired it. On the form they sit in groups, and an event and its counterpart share a row, such as Offline and Online:
| Event | Fires when |
|---|---|
| Device Offline | A poll failed and the host did not answer on the network either. |
| Device Not Accessible | The host answers on the network but the poll failed — usually a management service that has stopped listening rather than a device that is down. |
| Device Online | A device that was not online answers again. That is a device coming back from an outage, and also the first successful poll of a device you have just added, whose state was not yet known. |
| Device Rebooted | A reboot started with the Reboot action, or one a poll noticed because the device's uptime went down: power loss, a watchdog, a crash, a reboot from the console (v1.102.0+). A reboot during an outage is reported on the first poll after the device answers again, and the message gives the uptime it found and the one before. The restart a RouterOS upgrade does on its way through is reported as Device Upgraded. A reboot the manager started with the Reboot action or an upgrade is reported once, by that action, and the poll stays quiet about it. A reboot sent by a command or a template, such as /system reboot, is reported by the poll and says it came from a command. A second reboot after that, a crash for example, is reported as usual (v1.103.0+). Devices polled over SNMP get only the Reboot action's message, because the uptime SNMP reports belongs to the SNMP service and can reset without a reboot. |
| Device Upgraded | A RouterOS or firmware upgrade finished on a device, with the versions it moved between. |
| Bulk Upgrade Complete | A whole batch or queue finished, with the counts of succeeded, skipped and failed, the names of the devices that failed, and whether a safety stop ended the run early. |
| Backup Complete / Backup Failed | A backup finished or failed, whether started by hand or by a schedule. |
| New RouterOS Version | MikroTik published a version on a channel the manager watches. Not tied to a device. The first check after an install only records what is current, so the first message comes on the next change. |
| Security (CVE) Alert | A published CVE matches the RouterOS version a device actually runs. Only a CVE that was not already matching fires, and the first download of the feed after an install is silent — otherwise switching the feature on would empty the whole backlog into your channel at once. |
| Security Patch Available | A release whose changelog fixes a security issue is newer than what a device runs. |
| LTE Data Warning / LTE Data Limit Reached | Metered LTE transfer passed the warning percentage, or the monthly limit set on the device. Each fires once in a billing period, and the warning percentage is a fleet-wide setting — 80% unless you change it. |
| Metric Threshold Exceeded / Metric Recovered | CPU, memory or temperature crossed its alert threshold and stayed there for the configured number of samples, and later came back. |
| BGP Session Down | A BGP peer that was up is not up any more, or its session reset between two samples — its uptime went backwards, so it dropped and returned inside five minutes. A peer you disabled never fires it, one that was already down when the manager started does not either, and neither does any peer on a router that rebooted between the two samples — a restart resets every session on the device at once, and Device Rebooted already reports that cause once instead of once per peer. |
| BGP Session Up | A BGP peer that was reported down is up again (v1.99.0+). It is sent only after a BGP Session Down for the same peer, so a peer that was down before the manager started, or one that dropped with a reboot, never gets an "up" on its own. A reset needs no pair, it was already up again when it was reported. |
| ISP Link Down / ISP Link Up | One of a device's ISP links stopped running, or came back. Every link is watched, backup links included, because a backup nobody paid for is worth knowing about before it is needed. Down waits for the offline delay set on the webhook, like Device Offline, and Up goes only to a webhook that was told the link went down. A link someone disabled on the router is reported at once as administratively disabled. The first time a link is seen, nothing fires. |
| Port Down / Port Up | A port listed under Watched ports on the device form stopped running, was disabled, or came back (v1.107.0+). The device picks its ports; which webhooks hear about them is the webhook's own site and tag filter. Down waits for the offline delay set on the webhook and is sent once per outage; a port someone disabled is reported at once; Up goes only to a webhook that was told the port went down. The first time a port is seen, nothing fires, and a port missing from the router fires nothing. Devices polled over SNMP are read over SNMP for this too. Each change is also written to the activity log. |
| ISP Address Changed | The address on an ISP link changed while the link stayed up. It is the address on the router's own interface, public or private. Only for devices with Alert when an ISP link's address changes switched on. A link that loses its address while it is down and gets the same one back does not fire it. |
| Log Alert | A log line matched a rule under Logs → Alerts (v1.102.0+). The message names the rule, the device and the line, and how many lines it took when the rule needs more than one. See Log alerts. |
| New Device Discovered | A discovery profile on the Scan Network page found MikroTik devices that are not in the fleet and that this profile had not found before (v1.104.0+). One message per run, with the profile's name, how many there are, and the address, identity, model, RouterOS version and serial number of up to 50 of them. A device found again on the next run is not announced twice, and a scan started by hand sends nothing. See Discovery profiles. |
| Verified Link Mismatch / Verified Link Restored | A link marked verified on the Topology map is not found by the scan, or another device is on one of its ports, and later it is found again (v1.104.0+). While a webhook takes either event, every site with a verified link is scanned every 15 minutes. Mismatch is sent after two scans in a row and once per mismatch, with both ends and their ports and, when it moved, what is on the port now. Restored goes only to a webhook that was told about the mismatch. A link with an end that is offline is not judged. See Verified links. |
Metric thresholds
Most events describe something that happened. The metric ones describe something that is still happening, so they need a rule about when a number becomes news. That rule is set once, fleet-wide, under Settings, and overridden per device where a device deserves different treatment.
Three metrics are watched, from the same five-minute samples the device page charts:
| Metric | Default threshold |
|---|---|
| CPU load | 90% |
| Memory usage | 90% |
| Temperature | Off. Normal temperature varies too much by model for a fleet-wide number to mean anything, and plenty of boards have no sensor at all — set it per device, or fleet-wide if your fleet is uniform |
A threshold of 0 disables that metric, and that works in both directions: 0 on a device switches the metric off for it even when the fleet has a threshold, and a value on a device switches the metric on even when the fleet-wide one is disabled. Anything left unset on a device inherits the fleet value. A reading that lands exactly on the threshold counts as a breach, not as being under it.
Why it does not fire immediately
A metric has to breach for three consecutive samples — about fifteen minutes — before anything is sent. That number is configurable, anywhere from one sample to twelve. A router briefly pinned at 100% while it does something you asked it to do is not an incident, and an alert per spike trains people to ignore the channel.
Each crossing then fires once. The condition is latched while it lasts, so a device sitting at 95% for a day produces one message, not two hundred and eighty. When the value drops back under the threshold, a recovery event fires — once — and the latch clears.
A sensor a device does not report is left alone rather than treated as zero: a board with no temperature sensor neither alerts nor recovers on temperature.
The latch lives in memory. Restarting the manager while a device is still over its threshold can produce one repeat notification, and that is the intended answer: a live over-threshold condition is a state, not a counter, and the state is still true.
Formats and what gets sent
Four shapes, picked per webhook:
- JSON — the default. One envelope for every event:
event,timestamp, a one-linesummaryalready written for a human, anddatawith the device, its site and whatever the event carries. - ntfy — the summary as plain text, with the event's title in an
X-Titleheader. Point it at your ntfy topic URL and you are done. - Discord — a rich embed, coloured by severity, for a channel webhook URL.
- Custom — for an API that dictates its own shape. Choose the method (POST, GET or PUT) and write the URL and body as templates:
{{device.name}},{{device.host}},{{device.siteName}},{{error}},{{upgrade.type}},{{upgrade.from}},{{upgrade.to}},{{status.routerOsVersion}}, and in the body{{summary}}and{{event}}as well. GET sends no body at all, so on that method the message has to go in the URL as a query parameter; every other method wants a body template and the form says so before it saves.
Placeholders in a body are escaped to match the Content-Type you set on the webhook, JSON when you set none — so a device name with a quote in it does not break the JSON you are building. Form-encoded bodies are escaped too; any other content type is inserted exactly as it stands, which is worth knowing if you are building XML by hand. Placeholders in the URL are always percent-encoded, while your own separators stay verbatim.
The one-line summary can be rewritten per event, on the same form, if the default wording does not suit the target that reads it. The form offers a message field only for the events you ticked, each with the variables that event carries; click one to insert it. (v1.107.0+) It is the same string in all four shapes: the JSON envelope carries it, ntfy sends it as the message, Discord prints it in the embed, and a custom body can place it wherever the target wants it.
Limiting a webhook to sites or tags
By default a webhook is fleet-wide: every device, every subscribed event. It can be narrowed to sites, to device tags, or to both. Within one kind the test is "any of" — two sites means either site. Between the two kinds it is "and" — sites plus tags means a device in one of those sites that also carries one of those tags.
An event about a device that the filter excludes is simply not delivered to that webhook. Nothing is queued and nothing is reported; every other webhook subscribed to the same event still receives it.
Events that do not name a device — a new RouterOS release, a finished bulk upgrade — are delivered to a narrowed webhook anyway. There is no device to test against, and silently dropping them would be worse than sending one message too many.
One exception: a Log Alert for a line from an address no device owns. Nothing says which site it belongs to, so it goes only to webhooks that are not limited to sites or tags. (v1.102.0+) New Device Discovered is handled the same way: a device that is not in the fleet has no site yet. (v1.104.0+)
Verified Link Mismatch and Verified Link Restored name the managed device at one end of the link, so the site and tag filters apply to them like to any device event. (v1.104.0+)
Tags are the way to say "only the gateways" or "only the customer sites". There is no device type in the manager to filter on, and inferring one from configuration is guesswork; a tag is something you decided. A site has to exist before it can be picked, and the form refuses one that does not; a tag no device carries yet is accepted, so a webhook can be armed for a tag you are about to start using.
Holding an offline alert
A device is polled every minute unless you changed that, globally or on the device itself, and the offline event fires on the first failed poll. For a dashboard that is right. For a phone at three in the morning it is not: a link that flaps and comes back is indistinguishable from an outage.
Hold offline alerts for takes a number of minutes, per webhook, up to twenty-four hours. The device must still be down when that time has passed for the alert to go out; a flap that clears in between produces nothing at all. Zero — the default on every webhook, existing ones included — keeps the immediate behaviour.
Two things follow from that, and both are deliberate. A target that was never told the device went down is not told it came back, so a quiet night does not end with a recovery message about an outage you never heard about. And the wait is not a countdown in memory: the manager records when the outage started and asks on every poll whether it has lasted long enough, so restarting the manager mid-wait delays nothing and loses nothing.
Quiet hours are deliberately not offered. A schedule silences real outages at eleven on a Sunday morning just as effectively as it silences flapping at three in the morning; the delay attacks the flap instead of the clock.
Signing, headers and secrets
Every request carries X-Webhook-Event with the event name and X-Webhook-Delivery with an id unique to that one delivery, which gives a receiver something to log each request against. It is not a key for discarding repeats: nothing is ever re-sent, and a second message about the same condition is a new delivery with a new id.
Set a secret and the body is signed: X-Webhook-Signature: sha256=<HMAC>, computed over the exact bytes sent. Verify it on your side before acting on anything — a webhook URL is a URL, and anyone who learns it can post to it. With no secret set, the header is absent altogether rather than empty, so a receiver can insist on its presence.
A GET target carries no body, so there is nothing to sign and no signature is sent, secret or not. Signing is for POST and PUT. (v1.94.0+) For a GET target, authenticate with a custom header or a token in the URL instead.
Once saved, the secret is not shown again (v1.92.0+). When you edit the webhook, leave the field empty to keep it, enter a new one to replace it, or tick Remove the secret and stop signing.
Custom headers can be added for targets that authenticate with one, an API token for instance. They are stored encrypted, the same way device passwords are, and the values are never shown again after saving — the form shows that a value is set, not what it is. A header you name yourself wins over the ones the manager sets, Content-Type included.
Delivery, failures and testing
Delivery is a single attempt with a ten-second timeout. There is no retry: a target that is down misses that event, and the manager does not build a backlog to replay at it later. The result of the last attempt — the HTTP status, or the error — is kept and shown on the webhook, which is where to look when messages stop arriving.
Sending never holds anything else up. The poll, the upgrade or the backup that produced the event carries on without waiting for the target to answer, so a slow endpoint costs you nothing but its own ten seconds.
Test sends a fixed test payload to the URL with the current settings, including the signature and any custom headers, and reports what came back — the status and the start of the response body, because a target that answers 200 with an error string in it would otherwise read as a pass. Use it after every change: the common failures are a target that wants a header you have not set, and a URL that quietly returns a 4xx.
A test ignores the event list and the scope, so it reaches a narrowed webhook that nothing else would have reached today. It carries its own event name rather than one of the sixteen, which is worth knowing if your target branches on that header, and its result lands in the status column like a real delivery.
Webhooks are managed by admins, and the events they carry are fleet-wide unless you scope them.