BlogDiscord Webhooks

How to Test Discord Webhooks

Discord webhooks let you send automated messages and receive interaction events from your bot in Discord channels.

Looking for the broader picture? See the 7 best webhook testing tools (2026), or if you're already on Webhook.site, the 60-second migration to HookRay.

Discord Official Webhook Docs

1. Discord Webhook Events

Discord can send the following webhook events to your endpoint:

MESSAGE_CREATE
INTERACTION_CREATE
GUILD_MEMBER_ADD
GUILD_MEMBER_REMOVE
MESSAGE_REACTION_ADD
CHANNEL_CREATE
VOICE_STATE_UPDATE

2. Set Up a Test Endpoint with HookRay

Follow these steps to start receiving Discord webhooks for testing:

  1. Go to HookRay and click "Start Testing — Free" to get your unique webhook URL.
  2. Copy the URL (e.g., https://h.hookray.com/abc123).
  3. In your Discord dashboard, navigate to the webhook settings and paste the HookRay URL as your endpoint.
  4. Select the events you want to receive (see list above).
  5. Trigger a test event — HookRay will show the incoming webhook in real-time.

3. Sample Discord Webhook Payload

Here's an example of what a Discord webhook payload looks like:

payload.json
{
  "type": 1,
  "data": {
    "name": "ping",
    "type": 1
  },
  "member": {
    "user": {
      "id": "123456789",
      "username": "developer"
    }
  },
  "channel_id": "987654321"
}

4. How to Verify Discord Webhook Signatures

Signing details
Algorithm
Ed25519
Header
X-Signature-Ed25519
Encoding
hex

Discord uses Ed25519 (not HMAC). The signed string is `{X-Signature-Timestamp}{raw_request_body}`. Both headers — `X-Signature-Ed25519` (signature) and `X-Signature-Timestamp` (timestamp) — are required.

Node.js (Express)

// npm i tweetnacl
import nacl from 'tweetnacl';
import express from 'express';

const PUBLIC_KEY = process.env.DISCORD_PUBLIC_KEY!; // hex string from Dev Portal

const app = express();

app.post(
  '/webhooks/discord',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const signature = req.headers['x-signature-ed25519'] as string | undefined;
    const timestamp = req.headers['x-signature-timestamp'] as string | undefined;
    if (!signature || !timestamp) {
      return res.status(401).send('missing signature headers');
    }

    const isValid = nacl.sign.detached.verify(
      Buffer.concat([Buffer.from(timestamp), req.body]),
      Buffer.from(signature, 'hex'),
      Buffer.from(PUBLIC_KEY, 'hex'),
    );

    if (!isValid) return res.status(401).send('invalid signature');

    const interaction = JSON.parse(req.body.toString('utf8'));
    // type 1 is PING — Discord uses it to verify the endpoint at registration
    if (interaction.type === 1) return res.json({ type: 1 });
    res.json({ type: 4, data: { content: 'ack' } });
  },
);

Python (FastAPI)

# pip install pynacl
import os
from nacl.signing import VerifyKey
from nacl.exceptions import BadSignatureError
from fastapi import FastAPI, Request, HTTPException

verify_key = VerifyKey(bytes.fromhex(os.environ['DISCORD_PUBLIC_KEY']))
app = FastAPI()

@app.post("/webhooks/discord")
async def discord_webhook(request: Request):
    body = await request.body()
    signature = request.headers.get('x-signature-ed25519')
    timestamp = request.headers.get('x-signature-timestamp')
    if not signature or not timestamp:
        raise HTTPException(status_code=401, detail='missing signature headers')
    try:
        verify_key.verify(timestamp.encode() + body, bytes.fromhex(signature))
    except BadSignatureError:
        raise HTTPException(status_code=401, detail='invalid signature')
    import json
    payload = json.loads(body)
    if payload.get('type') == 1:  # PING
        return {'type': 1}
    return {'type': 4, 'data': {'content': 'ack'}}
Watch out: Discord uses Ed25519 elliptic-curve signatures, not HMAC. You verify with Discord's public key (from the Developer Portal), and you don't have a shared secret. If you copy a Stripe-style verifier and try to swap in `crypto.createHmac`, it will silently produce signatures that never match.

Capture a real Discord webhook with HookRay first, then replay the captured request against your verifier locally — that way you can iterate on the verification code without re-triggering events in Discord. Read Discord's official signing docs for the canonical reference, or see the cross-service signature verification guide for Ruby and timing-safe comparison patterns.

5. Frequently Asked Questions

How do I test Discord webhooks without deploying?

Use HookRay to get an instant public webhook URL. Paste it into your Discord dashboard's webhook configuration, trigger an event, and watch the payload arrive in real time. No code, no ngrok, no deployment required. The free tier captures 100 requests per month and works on all Discord event types.

Why aren't my Discord webhooks arriving?

The four most common causes: (1) the endpoint URL isn't publicly accessible — Discord can't reach localhost; (2) the wrong events are subscribed in your Discord dashboard; (3) signature verification is rejecting the request before your handler runs; (4) Discord can't reach your server because of a firewall, expired SSL certificate, or wrong DNS. Use HookRay's URL to isolate which of these four is failing — if HookRay receives the webhook, the problem is in your handler. If HookRay doesn't, the problem is in Discord configuration.

Why am I getting 400 or 500 errors from my Discord webhook?

Discord reports the response status your endpoint returned. HookRay accepts any payload and returns 200 OK by default, so if you see 400/500 in your Discord dashboard while pointing at HookRay, the issue is in Discord's configuration (wrong event, malformed signing secret, etc.). If you point at your own endpoint and get 400/500, the issue is in your handler — capture the request with HookRay, replay it locally, and debug from the captured payload.

How do I verify Discord webhook signatures?

Discord signs each webhook request with a shared secret. Capture the raw headers and body using HookRay, then verify the signature in your application using Discord's SDK or a standard HMAC library. Once verification works against HookRay-captured data, you can safely deploy. Discord's docs (linked above) cover the exact signing algorithm.

Can I replay a captured Discord webhook?

Yes — HookRay's replay feature re-sends any captured webhook to a different endpoint with one click. This is the fastest way to fix a buggy handler: capture the payload once, fix your code, and replay until it works. No need to re-trigger the event in Discord.

6. Next Steps

  • Use HookRay's webhook replay feature to re-send captured webhooks while building your handler
  • Enable smart parsing (Pro plan) to see Discord-specific fields highlighted automatically
  • Check the Discord webhook documentation for the complete event reference

Ready to test Discord webhooks?

Get a free webhook URL in 5 seconds. No signup required.

Start Testing Discord Webhooks — Free

Free PDF: Webhook Testing Cheat Sheet 2026

One-page reference for 50+ APIs — canonical events, signing methods, sample payloads. Print it, pin it, share it.

📄 Download the cheat sheet (PDF, 180KB)