Gareth Wilson Gareth Wilson

Guide to Kibana Webhooks: Features and Best Practices

Published


Kibana webhooks let your Elastic alerting rules call any service you run. When a rule detects something, such as an error rate threshold crossed, a log pattern matched or a host gone quiet, its action can send an HTTP request through a Webhook connector, so you can open tickets, trigger remediation or notify a tool that Kibana has no dedicated connector for.

This guide covers how the Webhook connector and rule actions fit together, how to authenticate requests with Basic authentication, how to design the body template, and the practices that keep a Kibana receiver reliable.

What are Kibana webhooks?

Kibana separates connectors from actions. A Webhook connector holds the connection details: the URL, the HTTP method, the authentication and any extra headers. An alerting rule's action uses a connector and supplies the request body, written as a Mustache template that Kibana fills in with details of the rule and the alert when the action runs.

Kibana does not sign these requests and adds no event type. Everything your endpoint learns about the alert comes from the template you write. The connector's authentication setting is None, Basic, SSL (a client certificate) or, since Elastic Stack 9.2, OAuth 2.0. With Basic, Kibana sends your username and password in an Authorization header with each request.

This guide covers the Webhook connector, not the separate Webhook - Case Management connector.

Kibana webhook features

FeatureDetails
ConfigurationStack Management > Connectors, or while creating a rule
MethodPOST (default), PUT, and since Elastic Stack 9.3 PATCH, GET and DELETE
URLhttp:// or https://. Must be in xpack.actions.allowedHosts if you use that setting
AuthenticationNone, Basic, SSL client certificate, or OAuth 2.0 (9.2+)
Custom headersConfig type (plain text) or, since 9.2, secret type
BodyA Mustache template per rule action, sent with POST, PUT or PATCH
EscapingVariables are escaped so the result stays valid JSON
Signature and timestampNone
Event typeNone. Defined by your template
TestingTest the connector while creating or editing it
Timeout, retries, source IPsNot documented on the connector page

Designing the body

The body is whatever your action's template renders. Kibana's own example is:

{
  "short_description": "{{context.rule.name}}",
  "description": "{{context.rule.description}}",
  ...
}

Every rule type passes general variables such as rule.id, rule.name, rule.url, rule.tags and date. Actions that run for each alert also get alert.id, alert.uuid, alert.actionGroup and alert.flapping, and each rule type adds its own context variables. A template for your own receiver might be:

{
  "source": "kibana",
  "rule_id": "{{rule.id}}",
  "rule_name": "{{rule.name}}",
  "rule_url": "{{rule.url}}",
  "alert_uuid": "{{alert.uuid}}",
  "action_group": "{{alert.actionGroup}}",
  "scheduled_at": "{{date}}",
  "reason": "{{context.reason}}"
}

Kibana escapes characters that are invalid in JSON string values, so a rule name with quotes in it does not break the body. Triple braces, such as {{{context.reason}}}, turn escaping off and can produce invalid JSON, so avoid them in webhook bodies. To see every variable available to a rule, temporarily set the body to {{{.}}}, which renders the whole context as JSON. rule.url is empty unless server.publicBaseUrl is configured.

alert.uuid stays the same each time the rule runs while the alert is active, which makes it the natural key for tracking one alert across notifications.

Inspect Kibana webhooks as they arrive. Point a Webhook connector at a Hookdeck Inspect URL and run a rule, or render {{{.}}}, to see exactly what your template produces, with no account or setup required.

Setting up Kibana webhooks

Choose a username and a long random password first, for example with openssl rand -hex 32 for the password.

  1. In Kibana, go to Stack Management > Connectors and create a Webhook connector.
  2. Choose the Method. Keep POST unless your receiver needs another.
  3. Enter the URL of your HTTPS endpoint.
  4. Choose Basic authentication and enter the username and password.
  5. Save, and test the connector.
  6. Create or edit an alerting rule, add an action that uses the connector, and write the body template.

To route through Hookdeck, create a Hookdeck source with the Kibana type and use its URL as the connector URL. Choose Basic authentication on the Kibana Webhook connector. The source asks for a Username and Password: enter the same pair. Hookdeck never answers with a 401 challenge, so the credentials must arrive with each request, which is what the Webhook connector does with Basic authentication.

The source pre-selects POST but leaves the method open, because the connector lets you choose. If your connector uses PUT, PATCH, GET or DELETE, add that method to the source's allowed HTTP methods as well. GET and DELETE requests carry no body, so the template only applies to POST, PUT and PATCH.

Securing Kibana webhooks

With Basic authentication, every request carries Authorization: Basic <base64(username:password)>. Decode it, split on the first colon, and compare the username and password with your stored values in constant time. Check it for every method your connector uses.

const crypto = require('crypto');
const express = require('express');

const USERNAME = process.env.KIBANA_WEBHOOK_USERNAME;
const PASSWORD = process.env.KIBANA_WEBHOOK_PASSWORD;

function safeEqual(a, b) {
  // timingSafeEqual throws on unequal lengths, so check first.
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

function verifyBasicAuth(header) {
  if (!USERNAME || !PASSWORD || typeof header !== 'string') return false;
  const match = /^basic\s+([A-Za-z0-9+/]+={0,2})\s*$/i.exec(header);
  if (!match) return false;
  const decoded = Buffer.from(match[1], 'base64');
  const colon = decoded.indexOf(0x3a); // split on the first colon
  if (colon === -1) return false;
  const userOk = safeEqual(decoded.subarray(0, colon), Buffer.from(USERNAME, 'utf8'));
  const passOk = safeEqual(decoded.subarray(colon + 1), Buffer.from(PASSWORD, 'utf8'));
  return userOk && passOk;
}

const app = express();

app.post('/webhooks/kibana', express.raw({ type: '*/*' }), (req, res) => {
  if (!verifyBasicAuth(req.get('authorization'))) {
    return res.sendStatus(401);
  }

  let alert;
  try {
    alert = JSON.parse(req.body.toString('utf8'));
  } catch {
    return res.sendStatus(400);
  }

  // Fields are whatever your template defines, e.g. alert.rule_id and alert.alert_uuid.
  res.sendStatus(200);
});

The same check in Python:

import base64
import binascii
import hmac
import os

USERNAME = os.environ.get("KIBANA_WEBHOOK_USERNAME", "")
PASSWORD = os.environ.get("KIBANA_WEBHOOK_PASSWORD", "")


def verify_basic_auth(header) -> bool:
    if not USERNAME or not PASSWORD or not header:
        return False
    scheme, _, encoded = header.partition(" ")
    if scheme.lower() != "basic":
        return False
    try:
        decoded = base64.b64decode(encoded.strip(), validate=True)
    except (binascii.Error, ValueError):
        return False
    user, sep, password = decoded.partition(b":")
    if not sep:
        return False
    user_ok = hmac.compare_digest(user, USERNAME.encode("utf-8"))
    pass_ok = hmac.compare_digest(password, PASSWORD.encode("utf-8"))
    return user_ok and pass_ok

Basic credentials are static. They authenticate the sender, not the body, so a modified payload still passes, and with no timestamp a captured request can be replayed. Serve the endpoint over HTTPS only, use a long random password, and change it on the connector and in your environment together if it might have leaked.

Make Kibana webhooks production-ready. Hookdeck Event Gateway checks the Basic credentials on every request, queues each alert notification durably, and retries delivery to your service on a schedule you control.

Kibana webhook limitations and pain points

Only Basic authentication fits the source type

The Problem: A connector set up with OAuth 2.0, an SSL client certificate or a secret header never verifies in Hookdeck's Kibana source.

Why It Happens: The Kibana source checks HTTP Basic credentials only. OAuth 2.0 tokens change as they are refreshed, a client certificate is checked during the TLS handshake rather than in a header, and custom headers are not part of this source type.

Workarounds:

  • Choose Basic authentication on connectors that point at Hookdeck.
  • If you are tied to another method, check it in your own receiver instead.

How Hookdeck Can Help: With Basic authentication, the Kibana source compares the username and password you enter. Hookdeck answers Kibana with a 200 either way, but requests without matching credentials are recorded as failed verification and never reach your service.

The template is the contract

The Problem: Someone edits a rule's action and your receiver starts failing, or two rules send the same endpoint different shapes.

Why It Happens: The body is defined per action, so every rule that uses the connector can send something different, and a variable that does not exist for a rule type renders as an empty string.

Workarounds:

  • Agree one template for every action that targets the same endpoint, and treat changes to it like an API change.
  • Include constant fields such as "source": "kibana" and a schema version.
  • Use {{{.}}} once per rule type to check which context variables really exist.

How Hookdeck Can Help: Hookdeck keeps every request with its full body, so you can see exactly what a changed template sent and replay it against your fixed handler. Transformations can normalise differing shapes before they reach your service.

No documented delivery guarantees

The Problem: You cannot tell from the connector docs how long Kibana waits for your endpoint, whether it retries, or what happens to a notification when your service is down.

Why It Happens: The Webhook connector page covers configuration and the body, not delivery behaviour.

Workarounds:

  • Respond quickly and process afterwards, so a slow handler never becomes a lost alert.
  • Watch the rule's execution history in Kibana for failed actions.

How Hookdeck Can Help: Hookdeck accepts each request as soon as it arrives and stores it, then delivers to your service with retries you configure. You can replay any notification after a fix, and issues alert you when deliveries to your service fail.

Repeated notifications for one alert

The Problem: One alert produces a request every time the rule runs, and your receiver opens duplicate tickets.

Why It Happens: Depending on the action's frequency settings, an action can run on every rule execution while an alert stays active.

Workarounds:

  • Include alert.uuid in the template and upsert on it.
  • Set the action to run only when the alert status changes, if that fits your workflow.

How Hookdeck Can Help: Hookdeck deduplication can drop repeated notifications based on fields you choose, such as alert_uuid and action_group.

Best practices

Design the template for your receiver

Include the rule ID, the alert UUID, the action group, the date and a link back to Kibana. Keep double braces so Kibana escapes the values.

Make handlers idempotent

Key on alert.uuid plus the action group, and store that key before you act. See our guide to webhook idempotency.

Keep secrets out of plain headers

If you do add custom headers, use the secret type (9.2+) for anything sensitive. Config-type header values are stored and sent as plain text.

Acknowledge fast and process asynchronously

Verify the credentials, store the notification, return 200, and do the work afterwards. See why to process webhooks asynchronously.

Conclusion

Kibana's Webhook connector sends whatever body your rule action's Mustache template renders, with no signature and no event type. Choose Basic authentication, compare the decoded username and password in constant time over HTTPS, put the rule ID and alert UUID in the template, allow every method your connector uses, and build the handler to cope with repeats and undocumented delivery behaviour.

Hookdeck Event Gateway checks the Basic credentials, keeps every notification, and retries and replays delivery to your service, so an outage on your side does not mean a missed alert.

Get started with Hookdeck for free and handle Kibana webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.