🎉 Launch offer: the next 5 servers get 30 days free. Claim a spot →
All topics & search

Guide/Developers/Outgoing webhooks: receiving and verifying

Outgoing webhooks: receiving and verifying — Discord Bot…

How to receive signed security events (raids, nukes, escalations) in your own service and verify the signature.

Quick answer

Check an endpoint

Dashboard → Security → Webhooks → Test      # signed sample event; the row shows status, latency and the last 20 deliveries

Ask the guide

Step by step

!security webhook add https://hooks.example.com/sweet raid.detected,nuke.detected,antinuke.faststrike
!security webhook add https://hooks.example.com/all *
!security webhook list · !security webhook remove <id>

Dashboard: Security → Webhooks. The secret is shown once, at creation. URLs must be https and resolve to a public address — checked when added and again before every delivery, so a hostname re-pointed at a private network stops receiving events. Redirects are not followed. After 25 consecutive failures the hook is disabled.

Events

nuke.detected, nuke.emergency, antinuke.faststrike, antinuke.damageReport, antinuke.suspicious, antinuke.selfAttack, raid.detected, spam.bot, escalation.changed, lockdown.started, lockdown.ended, channel.nuked, or * for all.

Request

POST /your/path
content-type: application/json
x-sweet-event: nuke.detected
x-sweet-signature: t=1758540000000,v1=<hex hmac-sha256>

{ "event": "nuke.detected", "guildId": "…", "timestamp": "2026-09-22T08:10:02.000Z", "data": { "severity": "critical", "title": "…", "description": "…", "actorId": "…", … } }

Verify (Node)

import { createHmac, timingSafeEqual } from 'node:crypto';

export function verify(secret, header, rawBody, toleranceSec = 300) {
  const m = /t=(\d+),v1=([a-f0-9]{64})/.exec(header ?? '');
  if (!m) return false;
  const [, t, sig] = m;
  if (Math.abs(Date.now() - Number(t)) > toleranceSec * 1000) return false;   // replay window
  const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  return expected.length === sig.length && timingSafeEqual(Buffer.from(expected), Buffer.from(sig));
}

Sign over the raw body bytes exactly as received, not a re-serialised object. Respond with any 2xx quickly; do the work afterwards.

Copy and try

Check an endpoint

Dashboard → Security → Webhooks → Test      # signed sample event; the row shows status, latency and the last 20 deliveries

Good to know

  • Payloads carry ids, counts and short reasons only; message content, evidence and long text are stripped before signing.
  • Sign over the raw body bytes; re-serialising breaks the signature.
  • Respond 2xx fast; 25 consecutive failures disable the hook.

Still stuck? Ask in the support server.

Outgoing webhooks: receiving and verifying — Discord Bot… · Sweet