# Respond to Webhook

> Send a custom HTTP status code, headers, JSON or text body, or redirect back to the caller of a Webhook trigger.

**Respond to Webhook** (`respondToWebhook`) sends a custom HTTP response to the caller of a [Webhook Trigger](https://docs.getglow.ai/build/triggers/webhook). Set the trigger's **When to respond** mode to **Using a Respond to Webhook step**.

**At a glance**

- **Use it to:** Return a custom HTTP status code, headers, JSON/text body, or redirect to a waiting Webhook caller
- **Find it:** Tools → Data → Dev tools → Respond to Webhook
- **Cost:** 1 Step credit per execution
- **Limits:** 1 MiB max response body · Caller waits up to 60 seconds
- **Output:** {{ N.statusCode }}, {{ N.headers }}, {{ N.body }}
- **Instead:** Webhook Trigger in When the last step finishes mode (when the final Step's output is the reply)

When the first passing **Respond to Webhook** Step finishes, Glow sends its status code, headers, and body to the waiting caller immediately. Any Steps after **Respond to Webhook** continue running in the background.

Lookup or transform data → Respond to Webhook → Background follow-up Steps

## Setting it up

### Set the Webhook Trigger to wait for this Step

Select your [Webhook Trigger](https://docs.getglow.ai/build/triggers/webhook) at Step 1 and set **When to respond** (`responseMode`) to **Using a Respond to Webhook step**.

### Add Respond to Webhook

Open **Tools → Data → Dev tools** in the dock and select **Respond to Webhook**. Connect it on the branch that should reply to the caller.

### Configure the response

Choose how to reply under **Respond with** (`respondWith`):

| Option             | Value        | Fields & behavior                                                                                                                                                                         |
| :----------------- | :----------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **JSON** (default) | `"json"`     | Sends **Body** (`body`) as `application/json` (parses JSON text or serializes mapped objects and lists). Set **Status code** from `200` to `599` (default `200`).                         |
| **Text**           | `"text"`     | Sends **Body** as `text/plain; charset=utf-8` (unless you set a custom `Content-Type` header).                                                                                            |
| **Redirect**       | `"redirect"` | Sends a `Location` header to **Redirect URL** (`redirectUrl`, must start with `https://`, up to `2,048` characters) with status `301`, `302`, `303`, `307`, or `308` (defaults to `302`). |
| **No body**        | `"noData"`   | Sends status and headers with an empty body (`Status code` `204`, `205`, or `304` also send no body automatically).                                                                       |

Under **Optional Props**, add up to 50 **Headers** (`{ name, value }` pairs). Allowed headers include `content-type`, `cache-control`, `content-language`, `etag`, `last-modified`, `expires`, `vary`, `retry-after`, and custom `x-*` headers. Hop-by-hop, cookie, CORS, and security headers are refused.

> **Do not return credentials in a webhook response.** Workspace secrets and API keys (`{{ $secret.* }}`, `{{ $key.* }}`, or sealed variables) are refused with `SECRET_IN_UNSAFE_SINK`.

## What it passes on

The Step records the response it sent so downstream Steps (or your test trace) can inspect it:

| Reference            | Type           | What it holds                                                             |
| :------------------- | :------------- | :------------------------------------------------------------------------ |
| `{{ N.statusCode }}` | Number         | The HTTP status code sent to the caller (for example `200` or `302`).     |
| `{{ N.headers }}`    | Record         | Normalized lowercase map of the headers sent with the reply.              |
| `{{ N.body }}`       | Text or `null` | The exact response text sent, or `null` for **No body** and **Redirect**. |

If multiple **Respond to Webhook** Steps run, the **first** passing Step sends the HTTP reply. Later executions record their output without overwriting the sent response.

## Limits and held-request statuses

| Limit or situation                                           | Behavior                                                                                                                                           |
| :----------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Maximum held request wait**                                | **60 seconds**. If the Step has not run within 60 seconds, the caller receives `504 {"error": "timeout"}` and the run continues in the background. |
| **Maximum response body size**                               | **1 MiB** (`1,048,576` bytes). Larger bodies fail the reply with `500 {"error": "response_not_sendable"}`.                                         |
| **Run finishes without reaching Respond to Webhook**         | The caller receives `500 {"error": "no_response"}`.                                                                                                |
| **Run fails before reaching Respond to Webhook**             | The caller receives `500 {"error": "workflow_failed"}`.                                                                                            |
| **Duplicate `x-glow-correlation-id` on a held webhook mode** | Returns `409 {"error": "duplicate"}` immediately without starting a duplicate run.                                                                 |

## Examples to copy

  

**Return a JSON lookup**

    Look up a customer record in a [Data Store](https://docs.getglow.ai/build/action-steps/data-store) and return it synchronously as JSON, then log the access in the background:

    1. Set the **Webhook Trigger** at Step 1 to **When to respond: Using a Respond to Webhook step**.
    2. Add **Data Store → Get a record** at Step 2 using `{{ 1.body.customerId }}`.
    3. Add **Respond to Webhook** at Step 3 with **Respond with: JSON**, **Status code: `200`**, and **Body**:
       ```json
       {
         "customerId": "{{ 1.body.customerId }}",
         "profile": "{{ 2 }}"
       }
       ```
    4. Connect a Slack or analytics Step after Step 3. The caller gets the `200` response as soon as Step 3 finishes.

  

  

**Validate and return 400 or 200**

    Branch with [Conditions](https://docs.getglow.ai/build/action-steps/conditions) to return `400` when a required field is missing or `200` when valid:

    1. Configure **Webhook Trigger** with **Using a Respond to Webhook step**.
    2. Add **Conditions** at Step 2 checking whether `{{ 1.body.email }}` **is not empty**.
    3. On the matching branch, run your action and end with **Respond to Webhook** (`Status code: 200`, `Body: {"ok": true}`).
    4. On the **Else** branch, add a second **Respond to Webhook** Step with **Status code: `400`** and **Body**: `{"error": "email is required"}`.

  

**Verify it works**

- **Run:** Set the Webhook Trigger to Using a Respond to Webhook step, configure Status code 200 and a JSON body, and run a test
- **You should see:** The Step outputs {{ N.statusCode }}, {{ N.headers }}, and {{ N.body }}, and the test trace records the webhook response.

## What's Next?

- Configure signature verification and response modes on the [Webhook Trigger](https://docs.getglow.ai/build/triggers/webhook).
- Make outbound API calls with [HTTP Request](https://docs.getglow.ai/build/action-steps/http-request).
