Send a Webhook to Discord: Transform & Forward Any Payload
Send any webhook to Discord. Step-by-step guide to forward an incoming webhook to a Discord channel, transforming the raw payload into Discord's { content } format in flight.
You have a service that fires webhooks — a CI pipeline, a contact form, an uptime monitor, a payment provider, an alerting tool — and you want each event to show up in a Discord channel. The problem: Discord doesn't accept arbitrary payloads. It expects a specific JSON body, and the webhook your source sends almost never matches that shape.
Webhook Relay sits in the middle. It receives the incoming webhook at a stable public URL, transforms the payload into the format Discord expects, and delivers it — no glue server, no Lambda, no maintenance.
How it works
The flow is a single hop with a transform in the middle:
Your service ──▶ Webhook Relay ──▶ transform to { content } ──▶ Discord channel
(raw webhook) (public URL) (serverless function) (message appears)
Discord expects a JSON body like this POSTed to a Discord webhook URL:
{ "content": "your message" }
A generic incoming webhook isn't shaped like that, so we add a small transformation function that wraps the raw body into Discord's format before delivery. That one feature is what turns Webhook Relay into a universal "anything → Discord" bridge.
Step 1: Create the Discord webhook URL
In Discord, open Server Settings → Integrations → Webhooks → New Webhook. Pick the channel you want messages to land in, give it a name, and click Copy Webhook URL. It looks like:
https://discord.com/api/webhooks/123456789/AbCdEf...
Keep this URL handy — it's the destination Webhook Relay will deliver to.
Step 2: Create a Webhook Relay public endpoint
Open the new public destination page and paste the Discord webhook URL as the destination. Webhook Relay gives you back an input URL — a public endpoint that you'll point your source service at.
That input URL is now a stable address you can hand to any service. Everything sent to it gets forwarded toward Discord.
Step 3: Add a transformation function
Right now, anything you send to the input URL would be forwarded to Discord as-is — and Discord would reject it, because it isn't in { "content": "..." } shape. Add a transformation to fix that.
Click Transform on the input, open the Functions page, create a function "from scratch", and paste this Lua:
local json = require("json")
local payload = {
content = r.RequestBody,
}
local body, err = json.encode(payload)
if err then error(err) end
r:SetRequestHeader("content-type", "application/json")
r:SetRequestMethod("POST")
r:SetRequestBody(body)
This takes the raw incoming body, wraps it as { "content": "..." }, sets the right headers and method, and lets Webhook Relay deliver it. Discord now accepts the message and posts it to your channel.
Step 4: Point your source at the URL and test
Configure your source service (the CI tool, form, monitor, payment provider — whatever you're integrating) to send its webhook to the Webhook Relay input URL.
To confirm everything works before wiring up the real service, fire a quick test with curl:
curl -X POST https://my.webhookrelay.com/v1/webhooks/your-input-id \
-d 'Deployment finished: build #482 passed on main'
Within a second or two, Deployment finished: build #482 passed on main appears in your Discord channel. If nothing shows up, open the request log in your dashboard to see exactly what was received and delivered.
Going further
Once the basic bridge works, the transform function is where the real power is.
Build a richer message with embeds. If your source sends JSON, parse it and construct a Discord embeds object instead of a flat string — title, description, a colored sidebar, and labelled fields:
local json = require("json")
local event = json.decode(r.RequestBody)
local payload = {
embeds = {
{
title = "Deployment " .. event.status,
description = event.message,
color = 5814783,
},
},
}
local body, err = json.encode(payload)
if err then error(err) end
r:SetRequestHeader("content-type", "application/json")
r:SetRequestMethod("POST")
r:SetRequestBody(body)
Filter out noise. Not every event deserves a ping. Use forwarding rules to only deliver events that match a condition, or drop them in the function with an early return.
Fan out to multiple channels. Add more destinations so one incoming webhook lands in several Discord channels — or in Discord and Slack — at the same time. See forwarding to multiple destinations.
For a complete real-world walkthrough using this exact pattern, see TradingView alerts to Discord, where a plain alert message is transformed into a Discord notification.
FAQ
What does Discord need to receive? A POST with a JSON body of { "content": "your message" } (or an embeds array for rich cards) to a Discord channel webhook URL. The transform function produces exactly that.
Do I need to run a server? No. Webhook Relay receives, transforms, and delivers everything in the cloud. There's nothing to host or keep online.
How do I see the raw payload my source sends? Send it to a free Webhook Bin first to inspect the exact body and headers, then write your transform around that structure.
Get started
Create a free Webhook Relay account and set up your first "anything → Discord" bridge in a few minutes. Not sure what your source sends? Inspect the payload in a Webhook Bin first, then write the transform to match.
