Sends Discord notifications for Render webhook events via a Discord bot.
Built on top of the Render Webhook Discord Bot template.
Grove's webhook is configured here.
The discord bot is configured here.
The alerts are sent to the #alerts channel in the Grove Discord server.
- Discord API Proxy (Cloudflare Worker)
- Supported Webhook Events
- Adding a New Webhook Event
- Discord Bot Permissions
- Original README.md
- Deploy to Render
- Developing
- Building
Render uses shared outbound IPs. Other tenants hitting Discord from the same IP can trigger Cloudflare's rate limiter (Error 1015), which blocks all Discord API calls from that IP — including ours.
To work around this, Discord REST API calls are routed through a Cloudflare Worker (free tier: 100k req/day), giving us a dedicated outbound IP.
- Worker URL:
https://discord-proxy.buildwithgrove.workers.dev - Worker source:
discord-proxy/src/index.ts - Cloudflare dashboard:
https://dash.cloudflare.com/343857f02bb4bd0b157b88ff59e7d867/workers - Render env var:
DISCORD_API_PROXY=https://discord-proxy.buildwithgrove.workers.dev
How it works:
- The Worker is a simple reverse proxy that forwards requests to
discord.com - discord.js REST calls (channel.fetch, channel.send) and the
/healthcheck both route through it - The gateway WebSocket (
client.login) still connects directly — it's a single persistent connection that doesn't trigger rate limits - Without the env var set, everything falls back to
discord.comdirectly
Redeploying the Worker:
cd discord-proxy && npx wrangler deployIf the bot can't connect to Discord (Error 1015 / login timeout):
- Check if
DISCORD_API_PROXYis set in Render's environment variables - Verify the Worker is deployed:
curl https://discord-proxy.buildwithgrove.workers.dev/api/v10/gateway - If the Render IP is currently Cloudflare-banned, redeploy the Render service to get a new IP
The full list of supported webhook events is available here.
Unrecognized event types are logged and ignored.
| Category | Events |
|---|---|
| Deploy Lifecycle | build_started, build_ended, deploy_started, deploy_ended, pre_deploy_started, pre_deploy_ended, image_pull_failed, commit_ignored, branch_deleted |
| Service Availability | server_available, server_failed, server_hardware_failure, server_restarted, service_suspended, service_resumed |
| Scaling | instance_count_changed, autoscaling_started, autoscaling_ended, autoscaling_config_changed |
| Jobs / Cron | job_run_ended, cron_job_run_started, cron_job_run_ended |
| Config | plan_changed |
| Maintenance | maintenance_started, maintenance_ended, maintenance_mode_enabled, maintenance_mode_uri_updated |
| Zero Downtime Redeploy | zero_downtime_redeploy_started, zero_downtime_redeploy_ended |
| Render Postgres | postgres_available, postgres_unavailable, postgres_restarted, postgres_created, postgres_backup_started, postgres_backup_completed, postgres_backup_failed, postgres_cluster_leader_changed, postgres_credentials_created, postgres_credentials_deleted, postgres_disk_size_changed, postgres_disk_autoscaling_enabled_changed, postgres_ha_status_changed, postgres_pitr_checkpoint_started, postgres_pitr_checkpoint_completed, postgres_pitr_checkpoint_failed, postgres_restore_succeeded, postgres_restore_failed, postgres_upgrade_started, postgres_upgrade_succeeded, postgres_upgrade_failed, postgres_read_replica_stale, postgres_read_replicas_changed, postgres_wal_archive_failed |
| Render Key Value | key_value_available, key_value_config_restart, key_value_unhealthy |
| Persistent Disks | disk_created, disk_updated, disk_deleted |
| Other | pipeline_minutes_exhausted |
There are two steps:
- Subscribe to the event on Render by clicking the
Enabledbutton and editing the events. - Add a single entry to the
webhookMetamap inrender.ts:
export const webhookMeta = {
// ...existing entries
new_event_type: { color: 0x3498db, label: "New Event", emoji: "🆕" },
};The generic handler in app.ts picks it up automatically — no other changes needed unless the event requires custom formatting (see server_failed for an example).
The bot is managed via the Discord Developer Portal.
Required OAuth2 settings:
- Scope:
bot - Permissions:
SendMessagesandViewChannels - Requires OAuth2 Code Grant: Leave disabled — this is only needed for apps that use user OAuth2 tokens (e.g., "Login with Discord"). This bot only needs the bot token.
If the bot gets a Missing Access (50001) error:
- Verify the bot's role has
View ChannelandSend Messagesin the target channel's permission settings - If the channel was deleted/recreated, update
DISCORD_CHANNEL_IDwith the new channel ID - Re-invite the bot using the install link from OAuth2 > Installation in the Developer Portal
Below is the original README from the forked repo.
- Sign up for a Render account (Professional plan or higher required for webhooks)
- View and upgrade your plan in the Render Dashboard
-
Follow Render webhook instructions to create a webhook with the URL:
https://webhook-discord-bot-ruv6-ruv6.onrender.com/webhook -
Follow instructions to create a Render API Key
-
Follow instructions to create a Discord App and copy the token
-
Navigate to the installation settings for your app and
- add
botscope - add
SendMessagesandViewChannelspermissions
- add
-
Set the following env vars
RENDER_WEBHOOK_SECRETenvironment variable to the secret from the webhook created in step 2RENDER_API_KEYto the key created in step 3DISCORD_TOKENto the token created in step 4DISCORD_CHANNEL_IDto the channel id you want messages sent toDISCORD_API_PROXY(optional) to a Cloudflare Worker URL that proxies Discord API calls (see Discord API Proxy)
Once you've created a project and installed dependencies with pnpm install, start a development server:
pnpm run devpnpm run build