Skip to content

Discord Webhook Setup Guide

Guide to create the 4 Discord channels and configure webhooks for the Freeze Design alerting system.

Overview

The alerting system uses 4 Discord channels, each with a specific purpose:

Channel Purpose Notification Source
#errors Error spikes, new issues, regressions Sentry Discord integration (bot)
#payments Payment-related errors Sentry Discord integration (bot)
#backups Daily backup summaries, backup failures Django code via webhook
#admin Admin audit trail (delete actions) Django code via webhook

Two notification mechanisms are used:

  • Sentry Discord bot (for #errors and #payments): Configured in Sentry UI, routes alerts directly. See the Monitoring Runbook for Sentry alert configuration.
  • Custom Discord webhooks (for #backups and #admin): Configured as env vars, called by Django application code.

All 4 channels need to be created. Webhooks are only needed for #backups and #admin.

Channel Setup

Organizing alert channels under a category keeps your Discord server tidy.

  1. Right-click your server name at the top of the channel list
  2. Select Create Category
  3. Name it FREEZE DESIGN ALERTS (or similar)
  4. Click Create Category

Step 2: Create the 4 Channels

For each channel in the table below:

  1. Click the + next to the category name (or next to "Text Channels")
  2. Select Text Channel
  3. Enter the channel name
  4. Click Create Channel
Channel Name Description to Set
errors Sentry error alerts - new issues, regressions, error spikes
payments Payment error alerts from Sentry
backups Daily backup summaries and failure alerts
admin Admin audit trail - delete action notifications

To prevent accidental messages in alert channels:

  1. Click the gear icon next to the channel name (Edit Channel)
  2. Go to Permissions
  3. Under @everyone, deny Send Messages
  4. Add the Sentry bot role and grant Send Messages and Embed Links (for #errors and #payments)
  5. For #backups and #admin, the webhook will bypass role permissions automatically

Webhook Creation

Webhooks are needed for #backups and #admin only. The #errors and #payments channels use Sentry's native Discord bot instead.

Create Webhook for #backups

  1. Right-click the #backups channel
  2. Select Edit Channel
  3. Go to Integrations in the left menu
  4. Click Webhooks
  5. Click New Webhook
  6. Set the name to Freeze Design Backups (or similar)
  7. Optionally upload an avatar image
  8. Click Copy Webhook URL
  9. Save the URL -- you will need it for the environment variable

Create Webhook for #admin

  1. Right-click the #admin channel
  2. Select Edit Channel
  3. Go to Integrations > Webhooks
  4. Click New Webhook
  5. Set the name to Freeze Design Admin
  6. Click Copy Webhook URL
  7. Save the URL

Environment Variables

New Environment Variables

Add these to your .env.production file:

# Discord Webhook URLs (custom notifications from Django)
DISCORD_WEBHOOK_BACKUPS=https://discord.com/api/webhooks/YOUR_BACKUPS_WEBHOOK_ID/YOUR_TOKEN
DISCORD_WEBHOOK_ADMIN=https://discord.com/api/webhooks/YOUR_ADMIN_WEBHOOK_ID/YOUR_TOKEN

# Discord Webhook URLs (for future use if Sentry native integration is not available)
DISCORD_WEBHOOK_ERRORS=https://discord.com/api/webhooks/YOUR_ERRORS_WEBHOOK_ID/YOUR_TOKEN
DISCORD_WEBHOOK_PAYMENTS=https://discord.com/api/webhooks/YOUR_PAYMENTS_WEBHOOK_ID/YOUR_TOKEN

Legacy Environment Variable Migration

The codebase previously used different env var names. The following mapping applies:

Old Variable New Variable Used By
BACKUP_DISCORD_WEBHOOK DISCORD_WEBHOOK_BACKUPS backup_notifications.py, backup_database.py
ADMIN_DISCORD_WEBHOOK DISCORD_WEBHOOK_ADMIN discord_admin_notifications.py, health_checks.py
DISK_ALERT_DISCORD_WEBHOOK DISCORD_WEBHOOK_ERRORS health_checks.py (disk space alerts)

During migration, set both old and new env vars to avoid breaking existing notifications:

# Keep old vars until code is fully migrated
BACKUP_DISCORD_WEBHOOK=https://discord.com/api/webhooks/...
ADMIN_DISCORD_WEBHOOK=https://discord.com/api/webhooks/...

# Add new vars (same webhook URLs or new ones for the dedicated channels)
DISCORD_WEBHOOK_BACKUPS=https://discord.com/api/webhooks/...
DISCORD_WEBHOOK_ADMIN=https://discord.com/api/webhooks/...

Once the code migration is complete (all services use the new DISCORD_WEBHOOK_* env vars), you can remove the old variables.

Sentry Discord Bot vs Custom Webhooks

Feature Sentry Discord Bot Custom Webhooks
Setup Installed via Sentry Integrations Created per-channel in Discord
Trigger Sentry alert rules fire automatically Django code calls webhook URL
Formatting Sentry-formatted messages with links Custom embed payloads from code
Channels #errors, #payments #backups, #admin
Config location Sentry UI (alert rule actions) .env.production (webhook URLs)
Failure mode Sentry retries delivery Code logs warning, does not retry

Testing Webhooks

Test with curl

After creating webhooks, verify they work before deploying:

Test #backups Webhook

curl -H "Content-Type: application/json" \
  -X POST \
  -d '{"embeds": [{"title": "Test: Backup Webhook", "description": "If you see this, the #backups webhook is working.", "color": 3066993}]}' \
  "https://discord.com/api/webhooks/YOUR_BACKUPS_WEBHOOK_ID/YOUR_TOKEN"

You should see a green-colored embed message appear in #backups.

Test #admin Webhook

curl -H "Content-Type: application/json" \
  -X POST \
  -d '{"embeds": [{"title": "Test: Admin Webhook", "description": "If you see this, the #admin webhook is working.", "color": 3447003}]}' \
  "https://discord.com/api/webhooks/YOUR_ADMIN_WEBHOOK_ID/YOUR_TOKEN"

You should see a blue-colored embed message appear in #admin.

Test from Django

On the production server, verify Django can send notifications:

docker compose exec backend python manage.py shell
import os
import requests

# Test backups webhook
url = os.getenv('DISCORD_WEBHOOK_BACKUPS')
if url:
    resp = requests.post(url, json={
        "embeds": [{"title": "Test from Django", "description": "Backup webhook works!", "color": 3066993}]
    }, timeout=10)
    print(f"Status: {resp.status_code}")
else:
    print("DISCORD_WEBHOOK_BACKUPS not set")

Verification Checklist

Check Command / Action Expected Result
#backups webhook works curl test (above) Green embed appears in channel
#admin webhook works curl test (above) Blue embed appears in channel
Sentry bot can post to #errors Create test Sentry alert (see Sentry guide) Alert message appears in channel
Sentry bot can post to #payments Create test payment alert (see Sentry guide) Alert message appears in channel
Django can reach webhook Django shell test (above) Status 200 printed
Env vars are set docker compose exec backend env \| grep DISCORD All webhook URLs shown

Security Notes

  • Never commit webhook URLs to version control. They are secrets that allow anyone to post to your channels.
  • Store webhook URLs only in .env.production (which should have chmod 600 permissions).
  • If a webhook URL is compromised, delete the webhook in Discord and create a new one.
  • Regenerating a webhook URL invalidates the old one immediately.