DocumentationWebhook transformations (functions): modify body, headers, method and path

Webhook transformations (functions): modify body, headers, method and path

Transform webhooks in flight with Webhook Relay functions: rewrite the JSON body, add or change headers, change the HTTP method or path, filter events and call external APIs, in JavaScript or Lua, with no server to host.

A Webhook Relay function is a short JavaScript (or Lua) program that runs on every webhook after it is received and before it is forwarded, so you can transform the payload, modify headers, change the method or path, filter unwanted requests, or call external APIs, without hosting a server. Attach a function to an input to affect every destination, or to a single output to shape the request for that destination only. The provider keeps sending its normal webhook; the destination receives exactly the request it expects.

The minimal transformation, reading the incoming body and setting a new one:

const payload = JSON.parse(r.body)          // incoming webhook body

r.setBody(JSON.stringify({                  // new body for the destination
  id: payload.id,
  status: payload.data.object.status,
}))
r.setHeader("Content-Type", "application/json")
r.setHeader("Authorization", "Bearer " + cfg.get("API_TOKEN"))
r.setMethod("POST")
r.setPath("/v2/events")                     // destination path override

Task-shaped guides: transform a webhook payload, modify headers, method and path, filter or drop webhooks, call an external API. Comparison with other tools: webhook transformation tools.

Functions can be written in JavaScript or Lua. Both languages have access to the same request object (r) and built-in modules for HTTP requests, JSON, cryptography, and more.

What can you do with functions?

  • Transform payloads — reshape webhook data from one format to another, for example converting a GitHub push event into a Slack message.
  • Filter requests — inspect incoming webhooks and reject ones that don't match your criteria.
  • Modify headers and method — add authentication headers, change the HTTP method, or set a custom path before forwarding.
  • Make HTTP requests — call external APIs to enrich data, fetch tokens, or send notifications to multiple services.
  • Alert on bad requests or responses — post to Slack, Discord, or any URL when JSON is missing or a destination returns an empty body. See Alerting from functions.
  • Validate webhooks — verify HMAC signatures, check shared secrets, and ensure webhook authenticity.
  • Customize responses — return custom status codes and response bodies to the webhook sender.

Quick example: transform a payload

This function takes an incoming JSON webhook and reshapes it into a Slack message format:

const payload = JSON.parse(r.body)

const slackMessage = {
    text: `New event from ${payload.source}: ${payload.message}`
}

r.setBody(JSON.stringify(slackMessage))
r.setHeader("Content-Type", "application/json")
local json = require("json")

local payload, err = json.decode(r.RequestBody)
if err then error(err) end

local slack_message = {
    text = "New event from " .. payload.source .. ": " .. payload.message
}

local encoded, err = json.encode(slack_message)
if err then error(err) end

r:SetRequestBody(encoded)
r:SetRequestHeader("Content-Type", "application/json")

Quick example: filter requests

This function only forwards webhooks that have an action field set to "completed":

const payload = JSON.parse(r.body)

if (payload.action !== "completed") {
    r.stopForwarding()
    return
}
local json = require("json")

local payload, err = json.decode(r.RequestBody)
if err then error(err) end

if payload.action ~= "completed" then
    r:StopForwarding()
    return
end

Quick example: call an external API

Functions can make HTTP requests to enrich webhook data or notify other services:

const payload = JSON.parse(r.body)

// look up additional data from an external API
const resp = http.request("GET", "https://api.example.com/users/" + payload.user_id, {
    headers: {
        Authorization: "Bearer " + cfg.get("API_TOKEN")
    }
})

const user = JSON.parse(resp.body)

// enrich the original payload with user data
payload.user_name = user.name
payload.user_email = user.email

r.setBody(JSON.stringify(payload))
local json = require("json")
local http = require("http")

local payload, err = json.decode(r.RequestBody)
if err then error(err) end

-- look up additional data from an external API
local resp, err = http.request("GET", "https://api.example.com/users/" .. payload.user_id, {
    headers = {
        Authorization = "Bearer " .. cfg:GetValue("API_TOKEN")
    }
})
if err then error(err) end

local user, err = json.decode(resp.body)
if err then error(err) end

-- enrich the original payload with user data
payload.user_name = user.name
payload.user_email = user.email

local encoded, err = json.encode(payload)
if err then error(err) end

r:SetRequestBody(encoded)

Next steps

Explore the guides in this section for detailed examples:

  • JSON encoding — parse and construct JSON payloads.
  • Helper librariesrequire() curated modules for JMESPath queries, JSON Schema validation, CSV/XML/YAML conversion, JWT and HMAC verification, Mustache templates, and more.
  • Provider-aware helpers — build Slack, Discord, Telegram, Notion, Airtable, Sheets and Supabase deliveries, or add bounded AI enrichment through a stored provider connection.
  • Secrets — store API tokens and signing secrets as write-only service connections and import them with require("secret:alias").
  • Make HTTP requests — call external APIs from your functions.
  • Read, write request data — access and modify headers, body, method, and query.
  • Base64, encryption — hash, sign, and encode data.
  • Working with time — parse and format timestamps.
  • Send emails — send email notifications from functions.
  • Response (post-delivery) functions — run code on the delivery outcome to alert on failures or flag bad responses.
Did this page help you?