Webhook delivery
Webhook delivery
When an IOC matches on one of your endpoints, Mimir creates a row on the Alerts page. If your operator has also configured a webhook URL, Mimir POSTs a JSON notification to that URL at the same time. This page is the reference for what gets sent, what guarantees Mimir makes about delivery, and what to look for when your downstream integration (SIEM, Slack, ticketing system) doesn’t match the in-product feed.
You can’t enable or configure the webhook from the Alerts page —
it’s a server-side setting your sysadmin sets at deploy time
(webhook_url + optional webhook_secret in mimir-server.yaml).
This doc is for the analyst on the receiving end of those POSTs.
What gets sent
The payload is a single JSON object. Five string fields, no nesting:
{ "alert_id": "0fe1f2c3-...", "host_id": "9a8b7c6d-...", "ioc_value": "44d88612fea8a8f36de82e1278abb02f", "severity": "high", "detected_at": "2026-05-11T14:23:09Z"}- alert_id — the UUID of the alert row. Use this to deep-link
back to Mimir:
https://<your-mimir-host>/alerts?alert=<alert_id>. - host_id — the UUID of the endpoint that triggered. Pair with the Hosts detail page for context.
- ioc_value — the literal indicator value that matched (hash, filename, registry key, etc.).
- severity — the IOC’s severity at match time:
critical,high,medium,low, orinfo. - detected_at — an RFC 3339 UTC timestamp.
That’s it. Mimir deliberately keeps the payload narrow so the downstream parser is simple. Richer context (IOC type, source feed, evidence rows, hostname) is on the Mimir side; deep-link to the alert UUID if you need it.
Which alerts fire a webhook
Only IOC alerts. The other categories (tamper, compliance, fleet change, host state) currently don’t fan out to the webhook channel. If you’re triaging compliance failures or host-state transitions, the Alerts page is the source of truth, not your SIEM.
Within the IOC category, every alert fires a webhook regardless
of the match source (watchlist_pack, historical_match,
hunt, or campaign). The first time the same indicator hits
the same host inside a 24-hour dedup window, you get a webhook;
subsequent matches inside that window are suppressed at the
alert layer (no new row, no new webhook).
Signing and verification
If your operator set webhook_secret, Mimir signs every payload
with HMAC-SHA256 and sends the signature in the
X-Mimir-Signature header:
X-Mimir-Signature: sha256=<hex>Verify on your side by recomputing HMAC-SHA256 of the raw body with the shared secret and comparing in constant time. Reject any request whose signature doesn’t verify; treat unsigned requests as untrusted if you provisioned a secret. Without the secret, only the URL’s secrecy itself acts as the auth boundary, which is fine for low-stakes channels (an internal Slack incoming-webhook) but isn’t suitable for anything that triggers remediation actions.
The Content-Type is always application/json. The body is a
single JSON object; no batching.
Delivery semantics
Three properties of the delivery loop worth knowing about as a consumer:
- At-most-three retries, exponential backoff. Mimir tries up to 3 times: immediate, then 1 second, then 2 seconds. A 2xx or 4xx response stops retries (4xx is treated as non-retryable “you reject this, don’t pile on”). A 5xx or transport error triggers the next attempt.
- Concurrency cap. Outbound deliveries are bounded by a semaphore (default 10 in-flight). A burst of alerts will serialize through that slot count rather than fan out unboundedly.
- Best-effort. If all three attempts fail, Mimir logs a warning server-side (with the URL redacted) and moves on. The in-product Alerts row is unaffected. Webhook delivery is a convenience channel; the database is the system of record.
That means your integration cannot assume exactly-once
delivery. Build the receiver idempotent on alert_id — if
the same UUID arrives twice, treat the second one as a no-op.
Mapping webhook events back to the Alerts page
The cleanest pattern: render https://<mimir>/alerts?alert=<alert_id>
as a “View in Mimir” link wherever you surface the payload.
Mimir treats the ?alert=<uuid> query parameter as a “select this
alert” anchor.
If your downstream tool needs a hostname instead of a UUID, do
one round-trip to GET /api/v1/hosts/<host_id> to resolve.
Webhook payloads don’t include hostnames precisely because
those can change; the UUID is stable.
Troubleshooting
“I see the row in the Alerts page but no webhook arrived.” Three likely causes:
- Your operator hasn’t set
webhook_url. Confirm with your admin. Without it, webhooks are silently off. - The alert isn’t an IOC alert (tamper / compliance / etc.). Only IOC alerts fanout.
- The same indicator already hit the same host inside the 24-hour dedup window. The alert may be the dedup’d “first” that’s been quiet for hours.
“The webhook signature doesn’t verify.” Most common cause is re-serializing the body before hashing — HMAC must be computed over the raw bytes Mimir sent, not over a re-serialized JSON that happens to be equivalent. Hash before any framework middleware reparses the request.
“I’m receiving duplicate POSTs for the same alert_id.” Either
the previous delivery returned a 5xx Mimir interpreted as
“retry me,” or your endpoint’s load balancer is forwarding the
same request to multiple receivers. Make your handler idempotent
on alert_id and the issue disappears.
“The webhook stopped firing.” Mimir’s server log carries any
delivery-failure warnings. Ask your operator to grep server
logs for alert webhook delivery failed after 3 attempts — the
URL is redacted in the log line for safety, but the alert_id
isn’t, so you can correlate against the page.
Where to next
- The alert feed — Mimir’s own surface, the system of record.
- Acknowledging alerts — acknowledging in Mimir doesn’t propagate to your webhook receiver; the webhook is one-way, fire-on-create.
- Matching modes — which match paths fire a webhook (all three) and how dedup works.