---
title: "Alerting from functions | WebhookRelay"
meta:
  "og:description": "Inspect webhook requests and delivery responses in Webhook Relay functions, and POST an alert to any URL when something looks wrong."
  "og:title": "Alerting from functions"
  description: "Inspect webhook requests and delivery responses in Webhook Relay functions, and POST an alert to any URL when something looks wrong."
---

![Stripes](https://webhookrelay.com/docs/webhooks/functions/alerting/images/stripes.svg)

Documentation

**Fundamentals**

# **Alerting from functions**

Inspect webhook requests and delivery responses in Webhook Relay functions, and POST an alert to any URL when something looks wrong.

Functions can do more than reshape payloads — they can **inspect** the request or the delivery response and **alert** an external system when something is off.

Two common patterns:

1. **Request function** (runs _before_ delivery) — parse the body; if it is not valid JSON (or a required field is missing), POST an alert to a URL.
2. **Response function** (runs _after_ delivery) — if the destination returned an empty body, POST an alert to a URL.

Set `alert_webhook_url` as a [config variable](https://webhookrelay.com/docs/webhooks/functions/alerting/docs/webhooks/functions/modify-request/#getting-configuration-values) on the function so the alert endpoint is not hard-coded in the source.

> For built-in delivery-failure incidents (policies, Slack/email destinations, auto-resolve), see [Alerts](https://webhookrelay.com/docs/webhooks/functions/alerting/docs/webhooks/alerts/). For a deeper look at post-delivery hooks, status codes, and rewriting the recorded response, see [Response (post-delivery) functions](https://webhookrelay.com/docs/webhooks/functions/alerting/docs/webhooks/functions/response-functions/).

## [Example 1: alert when the request is not valid JSON](#example-1-alert-when-the-request-is-not-valid-json)

Attach this as a **request** function (before delivery). It tries to parse `r.body` as JSON. If parsing fails, it posts an alert to your URL and [stops forwarding](https://webhookrelay.com/docs/webhooks/functions/alerting/docs/webhooks/functions/modify-request/#filtering-requests) so a bad payload never reaches the destination.

```
// Request function — runs BEFORE delivery.
// Alert if the body is missing or is not valid JSON.

var url = cfg.get("alert_webhook_url");

if (!r.body || r.body.trim() === "") {
  if (url) {
    http.post(url, JSON.stringify({
      type: "invalid_request",
      reason: "empty body",
      output: r.metadata["output_name"],
      path: r.path
    }), { headers: { "Content-Type": "application/json" } });
  }
  r.stopForwarding();
  return;
}

var payload;
try {
  payload = JSON.parse(r.body);
} catch (e) {
  if (url) {
    http.post(url, JSON.stringify({
      type: "invalid_request",
      reason: "body is not valid JSON",
      error: String(e),
      output: r.metadata["output_name"],
      path: r.path
    }), { headers: { "Content-Type": "application/json" } });
  }
  r.stopForwarding();
  return;
}

// Optional: also require a field
if (!payload || payload.id == null) {
  if (url) {
    http.post(url, JSON.stringify({
      type: "invalid_request",
      reason: "missing required field: id",
      output: r.metadata["output_name"]
    }), { headers: { "Content-Type": "application/json" } });
  }
  r.stopForwarding();
  return;
}

// Body looks fine — continue delivery as-is (or call r.setBody(...) to reshape it).
```

```
-- Request function — runs BEFORE delivery.
-- Alert if the body is missing or is not valid JSON.

local http = require("http")
local json = require("json")

local url = cfg:GetValue("alert_webhook_url")

local function alert(reason, extra)
  if url == "" then return end
  local body = {
    type = "invalid_request",
    reason = reason,
    output = r.metadata["output_name"] or "",
    path = r.RequestPath or ""
  }
  if extra then
    for k, v in pairs(extra) do body[k] = v end
  end
  local payload, err = json.encode(body)
  if err then error(err) end
  http.request("POST", url, {
    headers = { ["Content-Type"] = "application/json" },
    body = payload
  })
end

if r.RequestBody == nil or r.RequestBody == "" then
  alert("empty body")
  r:StopForwarding()
  return
end

local payload, err = json.decode(r.RequestBody)
if err then
  alert("body is not valid JSON", { error = tostring(err) })
  r:StopForwarding()
  return
end

if payload == nil or payload.id == nil then
  alert("missing required field: id")
  r:StopForwarding()
  return
end

-- Body looks fine — continue delivery as-is
-- (or call r:SetRequestBody(...) to reshape it).
```

## [Example 2: alert when the response body is empty](#example-2-alert-when-the-response-body-is-empty)

Attach this as a **response** function (after delivery). When the destination answers with a success status but an **empty** body — a common silent-failure pattern — post an alert to your URL.

```
// Response function — runs AFTER delivery.
// Alert if the destination returned an empty body.

var empty = !r.responseBody || r.responseBody.trim() === "";
if (!empty) {
  return; // body present — nothing to do
}

var url = cfg.get("alert_webhook_url");
if (!url) {
  return;
}

http.post(url, JSON.stringify({
  type: "empty_response",
  reason: "destination returned an empty body",
  status: r.responseStatus,
  output: r.metadata["output_name"],
  destination: r.metadata["output_url"]
}), { headers: { "Content-Type": "application/json" } });
```

```
-- Response function — runs AFTER delivery.
-- Alert if the destination returned an empty body.

local http = require("http")
local json = require("json")

local empty = r.ResponseBody == nil or r.ResponseBody == ""
if not empty then
  return -- body present — nothing to do
end

local url = cfg:GetValue("alert_webhook_url")
if url == "" then
  return
end

local payload, err = json.encode({
  type = "empty_response",
  reason = "destination returned an empty body",
  status = r.ResponseStatus,
  output = r.metadata["output_name"] or "",
  destination = r.metadata["output_url"] or ""
})
if err then error(err) end

http.request("POST", url, {
  headers = { ["Content-Type"] = "application/json" },
  body = payload
})
```

You can combine this with a status check (`r.responseStatus === 0` or `>= 400`) if you also want alerts on timeouts and HTTP errors. See [Response (post-delivery) functions](https://webhookrelay.com/docs/webhooks/functions/alerting/docs/webhooks/functions/response-functions/) for failure-status examples and for rewriting the recorded response when a `200` should count as failed.

## [Setup checklist](#setup-checklist)

1. Create a function in the dashboard (use the **REQUEST** tab for example 1, **RESPONSE** tab for example 2).
2. Add a config variable `alert_webhook_url` pointing at your Slack incoming webhook, PagerDuty, Discord, or any HTTP endpoint that accepts POST JSON.
3. Attach the function to an output:
  - Request function → `function_id` (before delivery)
  - Response function → `response_function_id` (after delivery)

## [Related](#related)

- [Read, write request data](https://webhookrelay.com/docs/webhooks/functions/alerting/docs/webhooks/functions/modify-request/) — `r.body`, `r.setBody`, `r.stopForwarding`, config variables.
- [Make HTTP requests](https://webhookrelay.com/docs/webhooks/functions/alerting/docs/webhooks/functions/make-http-request/) — `http.post` / `http.request` used to send the alert.
- [Response (post-delivery) functions](https://webhookrelay.com/docs/webhooks/functions/alerting/docs/webhooks/functions/response-functions/) — full response API, empty-200 handling, flagging the recorded response.
- [Accessing metadata](https://webhookrelay.com/docs/webhooks/functions/alerting/docs/webhooks/functions/accessing-metadata/) — `output_name`, `output_url`, and other fields useful in alerts.

Did this page help you?