Skip to main content
3 min read

Discord webhook

Pipe Zenovay events into a Discord channel — useful for engineering team rooms or community-growth war rooms.

Discord's webhooks are channel-scoped. No Discord bot or OAuth needed.

Outbound webhooks are a Pro+ feature. Upgrade your plan to enable them.


1. Create the Discord webhook

  1. In Discord, open the channel that should receive Zenovay alerts.
  2. Click the gear icon (Edit Channel) → IntegrationsWebhooksNew Webhook.
  3. Give it a name ("Zenovay Alerts") and an avatar if you want.
  4. Click Copy Webhook URL — it looks like https://discord.com/api/webhooks/123456789012345678/XXXXXXXX....

2. Add the webhook in Zenovay

  1. In app.zenovay.com, go to Settings → Webhooks.
  2. Pick the website you want to receive notifications for.
  3. Click Add webhook.
  4. Fill in:
    • Name: Discord #ops (or whatever's meaningful)
    • URL: paste the Discord webhook URL from Step 1
    • Events: pick the ones you care about
  5. Click Create webhook.
  6. Click the Send test event (paper-plane icon) — within ~5 seconds a message should appear in your Discord channel.

Discord accepts arbitrary JSON, but it'll render the raw payload as plain text. For pretty messages, see Step 3.


3. (Optional) Format with Discord embeds

Discord supports rich embeds with colours, fields, and timestamps. Zenovay sends a generic JSON event — to render it as an embed, run a small relay:

// Cloudflare Workers / Node / Vercel — anywhere that runs JS
export default {
  async fetch(req) {
    const event = await req.json();
    const colour = event.event_type === 'website_down' ? 0xdc2626
                 : event.event_type === 'traffic_spike' ? 0x10b981
                 : 0x3b82f6;
    const discordPayload = {
      embeds: [{
        title: `Zenovay: ${event.event_type}`,
        description: `Event for site ${event.website_id}`,
        color: colour,
        fields: [
          { name: 'Event type', value: event.event_type, inline: true },
          { name: 'Time', value: event.timestamp, inline: true },
        ],
        footer: { text: 'Zenovay Analytics' },
      }],
    };
    await fetch(DISCORD_WEBHOOK_URL, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(discordPayload),
    });
    return new Response('ok');
  },
};

Point Zenovay's webhook URL at this relay instead of Discord directly.


4. Verify Zenovay's signature

For production relays, verify the HMAC-SHA256 signature so only genuine Zenovay events reach Discord:

import { createHmac } from 'crypto';

const signature = req.headers.get('x-zenovay-signature'); // 'sha256=<hex>'
const provided = signature.replace(/^sha256=/, '');
const expected = createHmac('sha256', YOUR_WEBHOOK_SECRET)
  .update(rawBody)
  .digest('hex');

if (provided !== expected) {
  return new Response('invalid signature', { status: 401 });
}

Your webhook secret is shown in Settings → Webhooks → click the eye icon on the webhook card.


Troubleshooting

  • Discord rejects with HTTP 400: Discord's webhook receiver is strict — payloads with unknown top-level keys may get 400. Use the relay in Step 3 to reshape.
  • Rate limits: Discord webhooks are rate-limited per channel (~30 messages per minute). For chatty event types like traffic_spike, batch them in your relay or filter at the Zenovay end.
  • Test event arrives but real events don't: confirm the right event types are selected in the Zenovay webhook config. The test event uses event_type: 'test' regardless of what's selected.

Was this page helpful?