Set Up Discord Bot Webhooks Locally with Python — No Server Required

Webhooks offer an efficient way to interact with Discord servers without the need to run a persistent bot or constantly poll for events. Whether you're building a simple notification system or a more complex bot feature, webhooks provide a straightforward approach to send data into Discord channels or receive interactions.

In this guide, you’ll learn how to set up and test Discord bot webhooks locally using Python (Flask) and Pinggy, a secure tunneling tool that lets you expose your local development server to the internet.

What Are Discord Webhooks?

A Discord webhook is an HTTP endpoint that allows external applications to send messages into a Discord text channel. They’re commonly used for:

  • Posting alerts (e.g., GitHub pushes, CI/CD updates)

  • Integrating with APIs or scripts

  • Enabling bot-like behavior without a full bot framework

Unlike a typical bot, webhooks don’t require a WebSocket connection and are much easier to implement for one-way communication.

Why Use Pinggy?

For local development, Discord’s webhook service needs to reach your Flask server. Pinggy provides a secure tunnel to your localhost, allowing your server to receive HTTP requests from the outside world without deploying to a cloud service.

Step 1: Create a Discord Server and Channel

  1. Open Discord and create a new server.

    Create your own server

    Create a new channel

  2. Add a text channel for webhook messages (e.g., #alerts).

    Create channel and get channel ID

  3. Enable Developer Mode under Settings → Advanced.

  4. Right-click your new channel → Copy Channel ID.

Step 2: Create a Discord Application and Bot

  1. Go to the Discord Developer Portal.

  2. Create a new application and add a bot.

    Create new application

    Name your application

  3. Copy the Bot Token for authentication.

    Add a bot to your application

    Copy your bot token

  4. Under OAuth2 → URL Generator, choose:

    • Scope: bot

    • Permissions: Manage Webhooks

      Bot permissions

Copy ClientId

  1. Authorize the bot to your server using the generated URL.

OAuth2 URL Generator

Step 3: Create a Webhook via the Discord API

Use the following command to create a webhook:

curl -X POST "https://discord.com/api/channels/<CHANNEL_ID>/webhooks" \
  -H "Authorization: Bot <BOT_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"name": "My Webhook"}'

After running the command, note down the id and token from the response:

https://discord.com/api/webhooks/<WEBHOOK_ID>/<WEBHOOK_TOKEN>

Calling Api using Postman

Step 4: Build a Local Server with Flask

Create a file discord_webhook.py:

from flask import Flask, request, jsonify
import requests

app = Flask(__name__)
WEBHOOK_URL = "https://discord.com/api/webhooks/WEBHOOK_ID/WEBHOOK_TOKEN"

@app.route('/send', methods=['POST'])
def send_message():
    data = request.get_json()
    if 'content' not in data:
        return jsonify({'error': 'Missing message content'}), 400

    payload = {
        "content": data['content'],
        "username": data.get('username', 'Webhook Bot'),
        "avatar_url": data.get('avatar_url', '')
    }

    response = requests.post(WEBHOOK_URL, json=payload)
    return jsonify({'status': 'sent' if response.status_code == 204 else 'failed'}), response.status_code

@app.route('/health', methods=['GET'])
def health_check():
    return jsonify({'status': 'ok'})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000)

Install the required dependencies:

pip install flask requests

Run your Flask server:

python discord_webhook.py

Flask server running on port 8000

Step 5: Expose Your Local Server with Pinggy

To make your Flask server accessible from Discord:

ssh -p 443 -R0:localhost:8000 a.pinggy.io

Pinggy will generate a public HTTPS URL (e.g., https://randomname.a.pinggy.link).

Pinggy public URL

Step 6: Test the Webhook

Send a test POST request:

curl -X POST "https://your-subdomain.a.pinggy.link/send" \
  -H "Content-Type: application/json" \
  -d '{"content": "Hello from Pinggy!", "username": "PinggyBot"}'

If successful, the message will appear in your Discord channel.

Send message using Postman

Server logs showing successful response

(Optional) Handling Discord Interactions

For two-way interaction (e.g., responding to slash commands), Discord can send HTTP requests to your endpoint. Here’s a basic Flask route to handle that:

@app.route('/discord-interactions', methods=['POST'])
def interaction():
    data = request.get_json()
    if data.get("type") == 1:
        return jsonify({"type": 1})  # PING
    if data.get("type") == 2:
        return jsonify({
            "type": 4,
            "data": {"content": f"You triggered /{data['data']['name']}"}
        })

Make sure to set your Interactions Endpoint URL in the Developer Portal to your Pinggy link (e.g., https://your-subdomain.a.pinggy.link/discord-interactions).

(Optional) Verify Discord Signatures

Discord signs requests to ensure authenticity. Here's how to verify them:

import nacl.signing
from nacl.exceptions import BadSignatureError

@app.route('/discord-interactions', methods=['POST'])
def verify_and_handle():
    signature = request.headers['X-Signature-Ed25519']
    timestamp = request.headers['X-Signature-Timestamp']
    body = request.data.decode()

    verify_key = nacl.signing.VerifyKey(bytes.fromhex(PUBLIC_KEY))
    try:
        verify_key.verify(f"{timestamp}{body}".encode(), bytes.fromhex(signature))
    except BadSignatureError:
        return jsonify({"error": "Invalid signature"}), 401

    # Process the interaction...

Replace PUBLIC_KEY with your app's public key from the Developer Portal.

Conclusion

Setting up and testing Discord webhooks locally doesn’t require deploying to a cloud server or paying for hosting. With Flask and Pinggy, you can quickly prototype and debug your bot’s webhook functionality in real-time.

This method is especially useful for:

  • Developers building Discord bots

  • Automation scripts that notify users

  • Quick integration tests before deployment

References

0
Subscribe to my newsletter

Read articles from Lightning Developer directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Lightning Developer
Lightning Developer