Agent skill

Kibana Webhooks Skill

Receive and authenticate Elastic Kibana Webhook connector requests (alerting rule actions). Use when setting up a Kibana webhook endpoint, checking HTTP Basic authentication, debugging 401s, accepting POST, PUT, PATCH, GET or DELETE, or handling a rule action's mustache-template body.

Install this skill

npx skills add hookdeck/webhook-skills --skill kibana-webhooks


The Kibana Webhook connector (alerting rule actions) sends an HTTP request to your server whenever a rule action runs. Requests are not signed: there is no signature header and no timestamp. The connector's authentication setting is none, basic, SSL or OAuth 2.0. Hookdeck's Kibana source verifies HTTP Basic auth. Custom headers and OAuth 2.0 are not covered by Hookdeck's Kibana source. This skill covers Basic auth only (not the Webhook - Case Management connector).

When to Use This Skill

  • How do I receive Kibana webhooks?
  • How do I verify the Kibana Webhook connector's Basic authentication?
  • Does Kibana sign webhook payloads? (No: Basic auth only)
  • Why does my Kibana webhook endpoint return 401?
  • How do I accept PUT, PATCH, GET or DELETE from Kibana, not just POST?
  • How do I handle a Kibana rule action's body template?

Hookdeck Setup

Choose Basic authentication on the Kibana Webhook connector.

Hookdeck's Kibana source pre-selects POST without managing the method, so any other method the connector uses (PUT, PATCH, GET, DELETE) must also be allowed on the source. Hookdeck never sends a 401 challenge (no WWW-Authenticate round trip), so the credentials must arrive with every request.

Verification (core)

Kibana sends Authorization: Basic base64(username:password) on every request. Split on the first colon and compare username and password each in constant time, evaluating both:

const crypto = require('crypto');
const STRICT_BASE64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;

const digest = (s) => crypto.createHash('sha256').update(s).digest();
const safeEqual = (a, b) => crypto.timingSafeEqual(digest(a), digest(b)); // equal length, never throws

function verifyBasicAuth(header, username, password) {
  if (!username || !password) return false;                  // fail closed
  const m = /^basic\s+(\S+)$/i.exec(header || '');           // scheme is case-insensitive
  if (!m || !STRICT_BASE64.test(m[1])) return false;         // junk after valid base64 fails
  const decoded = Buffer.from(m[1], 'base64').toString('utf8');
  const i = decoded.indexOf(':');
  if (i === -1) return false;
  const userOk = safeEqual(decoded.slice(0, i), username);   // evaluate both, no short-circuit
  const passOk = safeEqual(decoded.slice(i + 1), password);
  return userOk && passOk;
}

Return 401 for a missing or malformed header or a mismatch, for every method. In Python use base64.b64decode(token, validate=True) and hmac.compare_digest on bytes (never str, which raises on non-ASCII). Read the body raw (express.raw({ type: '*/*' })) so any content type works, and parse it only after the check.

A static shared credential authenticates the sender, not the body, and gives no replay protection (no signature, no timestamp). Use HTTPS, and rotate the credential if it leaks.

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Methods and Body

The connector method is POST (default), PUT, PATCH, GET or DELETE (PATCH, GET and DELETE are generally available since 9.3). The examples accept all five on /webhooks/kibana, check the credential first, and handle an empty body.

There is no vendor event field. The body is the rule action's mustache template, so its shape is whatever you configure. The examples use this illustrative payload (not a vendor schema):

{"rule": "{{rule.name}}", "status": "{{alert.actionGroup}}", "context": "{{context.message}}"}

Parse JSON when it parses, otherwise keep the raw text.

Environment Variables

KIBANA_WEBHOOK_USERNAME=kibana                         # user name on the Webhook connector
KIBANA_WEBHOOK_PASSWORD=change-me-to-a-long-random-password

Local Development

npx hookdeck-cli listen 3000 kibana --path /webhooks/kibana

No account required. Use the printed URL as the connector URL (FastAPI: port 8000).

Resources


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 11, 2026

View on GitHub →