What is a Discord Webhook?
A Discord Webhook is a unique URL that lets any external service post messages to a specific Discord channel without needing a bot or user account. The webhook URL contains an ID and a token — together they authorize HTTP POST requests against Discord's API on behalf of one channel.
Mechanically, a webhook is a thin one-way bridge: HTTP POST in, message rendered in Discord. No login, no OAuth, no gateway connection. You can send plain text, rich embeds, file uploads, and even interactive components — all from a curl one-liner or a CI pipeline.
A typical webhook URL looks like:
https://discord.com/api/webhooks/123456789012345678/aBcDeF...secret-token The first numeric segment is the webhook ID (public). The second segment is the secret token — guard it like a password. Anyone with this URL can post to your channel.
Webhook vs Bot — When to Choose What
Webhooks and bots overlap in messaging capability but have very different scopes. The cheapest way to decide is to check whether your use-case is one-way notification or interactive.
| Capability | Webhook | Bot |
|---|---|---|
| Send messages | ✅ | ✅ |
| Send embeds | ✅ (up to 10/msg) | ✅ |
| Send files | ✅ (≤25 MB) | ✅ |
| Custom username + avatar per message | ✅ | ❌ |
| Receive messages / handle commands | ❌ | ✅ |
| Handle button / select clicks | ❌ (post only) | ✅ |
| React to events (joins, deletes) | ❌ | ✅ |
| Run across many servers | ❌ (1 channel each) | ✅ |
| Setup time | ~30 seconds | App registration + hosting |
Use a webhook when: you need to push notifications, deploy alerts, scheduled reports, RSS feeds, GitHub events, monitoring pings, or any one-way data flow into Discord.
Use a bot when: you need slash commands, message reactions, role automation, button callbacks, member events, or anything Discord considers an "interaction."
Many production setups combine both: a webhook fires fast notifications, a bot listens for follow-up clicks. See interactive buttons with bot pairing for the hybrid pattern.
Creating a Webhook URL
You need Manage Webhooks permission in the target channel (server owners and admins always have it). The flow takes ~30 seconds:
- Open Server Settings (drop-down next to your server name → Server Settings).
- Click Integrations in the left sidebar.
- Click Webhooks → New Webhook.
- Set the display name, target channel, and (optionally) upload an avatar.
- Click Copy Webhook URL.
That URL is now your one-and-only credential. Anyone with it posts as the webhook. Never commit it to git, never paste it into a public chat, never embed it in client-side JavaScript that a browser will execute. See the Security section for the full hardening checklist.
Need a screenshot walkthrough? Read the deep-dive: Discord webhook setup guide.
Sending Your First Message
The simplest webhook call is a POST with a JSON body containing a content field. Using curl:
curl -X POST -H "Content-Type: application/json" \
-d '{"content":"Hello from a webhook!"}' \
"https://discord.com/api/webhooks/ID/TOKEN"
Discord returns 204 No Content on success and the message appears in the channel. The full payload schema accepts:
content— plain text up to 2000 charactersembeds— array of up to 10 embed objectsusername— override the default webhook name for this messageavatar_url— override the avatar imagetts— text-to-speech flag (rarely useful)allowed_mentions— control who gets pinged (see below)components— interactive buttons and select menusflags— bitfield (e.g. Components V2, suppress notifications)
For language-specific code, jump to Code Examples below.
Embeds — Rich Message Formatting
Embeds are the colored card-style messages Discord renders with a left-side accent bar. They support a title, description (Markdown), URL, color, fields (key-value pairs), thumbnail, image, footer, author block, and timestamp.
{
"embeds": [{
"title": "Build #2341 — passed",
"description": "All checks green ✓",
"url": "https://example.com/builds/2341",
"color": 5763719,
"fields": [
{ "name": "Branch", "value": "main", "inline": true },
{ "name": "Duration", "value": "2m 14s", "inline": true }
],
"footer": { "text": "CI bot" },
"timestamp": "2026-05-07T10:00:00.000Z"
}]
}
The color field is a decimal RGB integer. Discord ignores hex strings — convert #5865F2 → 5793266. Reference: Discord embed color codes.
The full embed object reference, with every field and constraint, lives in the embed builder guide. Or skip the manual JSON entirely and use the visual embed builder — design with drag-and-drop, copy generated JSON.
Components V2 — Modern Layouts
Components V2 is Discord's newer message layout system that replaces embeds for complex content. Instead of a fixed embed structure, you compose messages from sections, text displays, media galleries, separators, and containers — much closer to a real UI framework.
To use Components V2, set the flags bitfield to include the V2 flag (1 << 15 = 32768) and provide a components array with V2 component types instead of content/embeds.
You cannot mix V2 components with classic embeds in a single message — choose one mode per message. The Components V2 guide covers every component type with examples; the visual builder supports both modes.
Sending Files & Attachments
File uploads switch the request body from JSON to multipart/form-data. Add a payload_json form field with the JSON body, then add file fields named files[0], files[1], etc.
curl -X POST \
-F 'payload_json={"content":"Logs attached"}' \
-F 'files[0]=@./error.log' \
"https://discord.com/api/webhooks/ID/TOKEN"
Files cap at 25 MB per attachment for non-Nitro servers. To embed an uploaded image inside an embed, reference it with attachment://filename in image.url. Full reference: Send files via webhook.
Editing & Deleting Messages
To edit or delete a webhook message later, you need its message ID. The default POST returns no body — append ?wait=true to the URL and Discord responds with the full message object including id:
POST /api/webhooks/ID/TOKEN?wait=true
→ { "id": "987...", "content": "Hello", ... }
PATCH /api/webhooks/ID/TOKEN/messages/987...
{ "content": "Updated content" }
DELETE /api/webhooks/ID/TOKEN/messages/987...
Persist the id if you want to edit across process restarts. For thread messages, pass ?thread_id=... on the PATCH/DELETE URL. Deep dive: Edit and delete webhook messages.
Mentions & allowed_mentions
Webhook messages can ping users (<@USER_ID>), roles (<@&ROLE_ID>), and @everyone / @here exactly like normal messages. By default, every mention in your content fires a notification — including ones that came from user-generated input you didn't sanitize.
The safe default is to set allowed_mentions with parse: []. This makes mention syntax visible in the message body without firing pings:
{
"content": "Hi @everyone, please check <@&123>",
"allowed_mentions": { "parse": [] }
}
Then opt-in to specific pings via users: ["123"], roles: ["456"], or parse: ["roles"]. Full matrix: Control mentions in webhooks.
Threads & Forum Channels
To post inside an existing thread, append ?thread_id=THREAD_ID to the webhook URL. To create a new post in a forum channel, send a top-level thread_name in the JSON body — Discord creates the post and uses your content/embeds as the first message.
POST /api/webhooks/ID/TOKEN
{
"thread_name": "Daily build report",
"content": "Build #2341 ✅"
}
Edit/delete operations on thread messages need the same thread_id on the URL. Reference: Threads and forum channels.
Rate Limits & 429 Handling
Webhooks are rate-limited per route. Each response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset-After headers. Hitting zero remaining returns 429 Too Many Requests with a Retry-After header (seconds).
The conservative budget for a single webhook is roughly 5 requests per 2 seconds (~30/min sustained). Bursts above that get queued by Discord; sustained bursts hit 429.
Production code must:
- Respect
Retry-Afteron 429 (sleep, then retry once) - Track
X-RateLimit-Remainingand self-throttle before hitting 0 - Use exponential backoff on 5xx responses
- Serialize hot loops through a queue or semaphore
Both discord.js and discord.py handle this automatically. For raw HTTP, see webhook rate limits guide with Python and JavaScript retry implementations.
Embed & Payload Limits
Discord enforces strict per-field character caps and a global payload cap. Exceeding any returns 400 Bad Request with error code 50035:
| Field | Limit |
|---|---|
content | 2000 chars |
embed.title | 256 chars |
embed.description | 4096 chars |
embed.fields | 25 max |
embed.field.name | 256 chars |
embed.field.value | 1024 chars |
embed.footer.text | 2048 chars |
embed.author.name | 256 chars |
| embeds per message | 10 max |
| total chars across all embeds | 6000 max |
| file size | 25 MB (non-Nitro) |
Full validator implementations in Python and TypeScript: embed limits cheat sheet.
Security — Protecting Your Webhook
Webhook URLs are bearer credentials. Anyone with the URL can:
- Post arbitrary messages, embeds, and files (with custom username/avatar)
- Edit and delete every message the webhook has sent
- Ping
@everyoneif your channel allows it - Spam the channel until you delete the webhook
Hardening checklist:
- Never commit URLs to git. Use
.env+.gitignore, GitHub Actions secrets, or your platform's secret manager. - Never embed in client-side JavaScript. The browser DevTools will leak it instantly.
- Use a server-side proxy if browsers must trigger webhook sends — let your backend hold the secret and forward sanitized requests.
- Enable GitHub secret scanning — Discord webhook URLs are a recognized pattern; GitHub will alert on accidental commits.
- Rotate immediately if a URL leaks: delete the webhook in Discord settings (this revokes the token), create a new one, update your secret store.
- Disable @everyone at the channel level if your webhook posts user-generated content — combined with
allowed_mentions: { parse: [] }.
Full incident response procedure and proxy pattern code: Discord webhook security guide.
Code Examples by Language
Pick the language that matches your stack. Each guide covers send, embed, files, edit/delete, and rate-limit handling specific to that ecosystem.
Python (requests)
Sync HTTP via the requests library. Best for scripts and CLI tools.
Python (discord.py)
Async via aiohttp. Best when you already run discord.py.
JavaScript / Node.js (fetch)
Native fetch in Node 18+. No dependencies.
discord.js (WebhookClient)
Built-in rate limit handling, EmbedBuilder, AttachmentBuilder.
PHP (cURL)
Native PHP, Laravel and WordPress integration patterns.
C# / .NET (HttpClient)
Strongly-typed records, async/await, ASP.NET Core.
cURL / Bash
CI hooks, cron jobs, one-liners. The lingua franca of webhooks.
GitHub Actions
Workflow notifications on push, deploy, release events.
Real-World Patterns
Webhooks shine in production when paired with a solid pattern. The most common:
Scheduled Messages
Daily reports, reminders, weekly digests via cron.
Automation Workflows
No-code platforms, n8n, custom workers.
Notifications & Alerts
Monitoring, error reporting, SLA breach pages.
Polls via Webhook
Native Discord polls, multiple choice, expiry.
Interactive Buttons
Approval flows, role assignment, link buttons.
CI/CD Integrations
GitHub Actions, GitLab CI, deploy notifications.
Discohook vs Discord Webhook
Discohook was the canonical visual webhook builder for years; it's been unmaintained since the original author paused work. Discord Webhook (this site) is the modern continuation: same goals, current Discord features, no signup, free forever.
The big differences:
- Components V2 support — Discohook is stuck on classic embeds; we support both modes.
- Bot integration — send via webhook OR via a bot token with custom features.
- Active development — new Discord features land here within weeks of release.
- Templates & presets — changelogs, status pages, welcomes — start fast.
- Privacy — no analytics, no tracking, no data collection. Your webhook URL never leaves your browser.
Side-by-side feature comparison: Discohook vs Discord Webhook and why we're the best Discohook alternative.
Stop Reading. Start Sending.
The visual builder generates valid webhook JSON for every payload covered above — embeds, Components V2, files, components — with live preview.
Open the Builder