KLYRN VM / Blog

Webhooks for Slack, Discord and n8n, signed

Every action KLYRN VM records can be posted to your URL within a second, signed with HMAC-SHA256, or as a ready Slack or Discord message.

· 1 min read · The KLYRN team

Integrations

A virtualisation panel that only talks to its own browser tab makes everything around it poll. KLYRN VM now pushes: every action the Activity page records, and every task that finishes, can be posted to URLs you choose.

What is sent

{"id":"evt_3f2a...","event":"vm.create","result":"ok","target":"vm:41",
           "actor":"ops@example.com","task_id":9001,"at":"2026-09-28T12:00:00Z",
           "controller":"panel.example.com"}

Each webhook has a filter: everything (*), an exact code such as vm.create, or a family such as vm.*, node.* or backup.*. The codes are the same ones the Activity page shows, so there is one list to learn.

Slack and Discord without a relay

Choose Slack or Discord when adding a webhook and paste the incoming-webhook URL those services give you. KLYRN sends one readable line in the field each of them posts as a message, for example vm.create FAILED on vm:41 by ops@example.com. No middleware, no bot.

Signed, so your endpoint can trust it

Every request carries X-Klyrn-Signature: t=<unix time>,v1=<hex>, an HMAC-SHA256 of the timestamp, a dot and the body, keyed with a secret shown once when the webhook is created. Check it and reject a timestamp older than five minutes, and a replayed or forged event is refused.

const [t, v1] = sig.split(',').map((p) => p.split('=')[1])
          const ok = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex') === v1
            && Date.now() / 1000 - Number(t) < 300

Delivery that never slows the panel

  • Delivery is asynchronous: an endpoint that is slow or down delays only its own events.
  • A network error, a 5xx, 408 or 429 is retried after 2 and after 10 seconds. Other 4xx answers are the endpoint saying no, so they are not retried.
  • After 20 failures in a row the webhook switches itself off and says so on its row.
  • The last 100 attempts per webhook are kept with the body and the answer, and a Test button sends one on demand.
  • Link-local addresses such as the cloud metadata service at 169.254.169.254 are refused after DNS, and redirects are not followed.