# How to move a Pipedream HTTP trigger to an n8n Webhook

> Source: https://workflowexit.com/pipedream-http-trigger-to-n8n-webhook · Updated 2026-10-13

Replace the Pipedream HTTP trigger with an n8n Webhook node. The URL changes from `https://<id>.m.pipedream.net` (or your Pipedream custom domain) to `https://<your-n8n-host>/webhook/<path>`, so every service that calls the old URL has to be updated. If the workflow sends its own response with `$.respond()` or the Return HTTP Response action, set the Webhook's **Respond** option to **Using 'Respond to Webhook' Node** and add a Respond to Webhook node. A Pipedream custom token becomes Header Auth on the Webhook node. Pipedream stops running workflows on **March 31, 2027**, so leave time to move every sender.

## What changes between the two

| | Pipedream HTTP trigger | n8n Webhook node |
|---|---|---|
| URL | `https://<id>.m.pipedream.net`, any path and query | `https://<host>/webhook/<path>`, plus a separate test URL |
| Methods | Any method | One method, or several with one output each |
| Default response | `200` with a short Pipedream HTML page | Respond Immediately: a short message that the workflow started |
| Custom response | `$.respond()` or the Return HTTP Response action | Respond to Webhook node |
| Authorization | None by default, a custom token, or Pipedream OAuth | None, Basic, Header or JWT auth |
| Request body size | 512 KB by default | 16 MB by default |
| Event data | `body`, `headers`, `method`, `path`, `query`, `url`, `client_ip` | `body`, `headers`, `params`, `query`, `webhookUrl`, `executionMode` |

Both lower-case header names, so code that reads `headers["content-type"]` keeps working.

## Step 1: Set up the Webhook node

1. **Add a Webhook node and name it `trigger`.** Pipedream code reads `steps.trigger.event`, so the same name keeps references easy to follow: `steps.trigger.event.body.email` becomes `$('trigger').first().json.body.email`.
2. **Pick the HTTP method.** Pipedream accepted any method. If your senders only POST, choose POST. Otherwise allow multiple methods, and n8n gives each method its own output.
3. **Set the path.** Use something stable, such as `orders-created`. Route parameters like `orders/:id` work too. Two published workflows can't share the same method and path.
4. **Choose how to respond.** Leave Respond on **Immediately** if Pipedream sent its default response. Pick **Using 'Respond to Webhook' Node** if any step called `$.respond()` or used the Return HTTP Response action.
5. **Set authentication**, as in step 2.
6. **Publish the workflow.** n8n registers the production URL only when you publish. Until then, only the test URL answers.

Workflow Exit does this when it converts a workflow. The path is the workflow name plus a short code, for example `new-order-to-slack-3f9a1c`, and it stays the same if you convert again, so senders only change once. If the input doesn't say which methods the endpoint accepted, the Webhook listens on GET, POST, PUT, PATCH, DELETE and HEAD, and a pass-through node named `trigger` joins the outputs.

## Step 2: Carry over authorization

Pipedream HTTP triggers are public unless you set authorization, so check the trigger's configuration in Pipedream before you rebuild it:

- **Custom token.** Senders pass `Authorization: Bearer <token>`. In n8n, set the Webhook's Authentication to **Header Auth** and create a credential with the name `Authorization` and the value `Bearer <token>`. Senders keep the header they already send. No Pipedream export includes the token, so copy it from a sender's settings or your password manager.
- **Pipedream OAuth.** Callers sent access tokens issued by your Pipedream OAuth client, and n8n has no way to check those. Move these callers to Header Auth or JWT auth.
- **Checks in code.** A code step that verified a token or signature from the headers keeps working, because the Webhook output has the same lower-cased `headers`.

The IP(s) Allowlist option adds a second layer when senders have fixed addresses. When the Pipedream endpoint required authorization, a converted workflow gets a checklist item to set Header Auth, because otherwise anyone with the URL can call it.

## Step 3: Rebuild custom responses

In Pipedream, `$.respond()` took a `status`, optional `headers` and a `body`. By default the response went out when the workflow finished. With `immediate: true` it went out at once, and the rest of the workflow kept running. If a workflow that uses `$.respond()` ended without calling it, Pipedream answered `400` with "No $.respond called in workflow".

n8n's Respond to Webhook node sends the response when it runs, and the nodes after it keep running. Put it where your code called `$.respond()`: at the end for the default timing, or earlier for `immediate: true`. Choose JSON or Text under Respond With, then set the Response Code and Response Headers options.

Take this Pipedream step:

```javascript
export default defineComponent({
  async run({ steps, $ }) {
    const { email } = steps.trigger.event.body ?? {};
    if (!email) {
      await $.respond({ status: 422, body: { error: "email is required" } });
      return $.flow.exit("missing email");
    }
    await $.respond({ immediate: true, status: 202, body: { queued: true } });
    return { email: email.toLowerCase() };
  },
});
```

Built by hand in n8n:

1. Webhook node `trigger`, with Respond set to Using 'Respond to Webhook' Node.
2. An [If node](/pipedream-if-else-switch-to-n8n) that checks that `{{ $('trigger').first().json.body.email }}` is not empty.
3. On the false output, a Respond to Webhook node with the JSON body `{"error": "email is required"}` and Response Code `422`. Nothing comes after it.
4. On the true output, a Respond to Webhook node with the JSON body `{"queued": true}` and Response Code `202`, followed by the rest of the workflow.

Watch these differences:

- **A path with no response.** If an n8n run finishes without reaching a Respond to Webhook node, the caller gets a standard `200`, not Pipedream's `400`. An error before the Respond node returns `500`.
- **One response per request.** The node answers once, from the first item it receives. A second Respond node in the same run is ignored.
- **Mismatched settings fail.** A Webhook set to respond through a node, with no Respond to Webhook node after it, stops with an error. So does a Respond node behind a Webhook set to Immediately.
- **HTML pages.** Since n8n 1.103.0, HTML responses are wrapped in a sandboxed iframe. Scripts can't reach the parent window and relative links break, so use absolute URLs.
- **Files and streams.** A step that answered with a Buffer or a stream needs the Respond to Webhook node's Binary File option instead.

When Workflow Exit converts a code step that calls `$.respond()`, it adds two nodes after it: an If node, `<step> responded?`, and a Respond to Webhook node, `<step> respond`. The Respond node sends the status the code set (`200` if none) and its body. It takes the Content-Type from the code's headers, or uses `text/plain` for text and `application/json` for objects, and it forwards every other header name the code sets literally. When the step also calls `$.flow.exit()` after responding, another If node stops the run there. A Return HTTP Response action becomes a Respond to Webhook node, and a static response set on the trigger becomes the Webhook's own Response Code, Response Data and Response Headers options.

## Step 4: Map the event data

Pipedream gave every step a `steps.trigger.event` object. The Webhook node's output has a similar shape, with a few gaps:

| Pipedream | n8n |
|---|---|
| `steps.trigger.event.body.email` | `$('trigger').first().json.body.email` |
| `steps.trigger.event.headers["x-signature"]` | `$('trigger').first().json.headers["x-signature"]` |
| `steps.trigger.event.query.page` | `$('trigger').first().json.query.page` |
| `steps.trigger.event.url` | `$('trigger').first().json.webhookUrl` |
| `steps.trigger.event.method` | The method you set, or the output the request arrived on |
| `steps.trigger.event.path` | No field. Use separate paths or route parameters (`params`). |
| `steps.trigger.event.client_ip` | No field. Read `x-forwarded-for` if your proxy sets it. |

**Body-only events.** A Pipedream HTTP trigger could pass just the request body as the event, so code read `steps.trigger.event.email` rather than `steps.trigger.event.body.email`. n8n always puts the body under `body`. Workflow Exit reads that setting from the export, or from Pipedream's payload-only HTTP source, and maps `steps.trigger.event` to the body for you. GitHub Sync repositories don't store trigger settings, so check those expressions by hand.

**Large payloads.** Pipedream limited request bodies to 512 KB unless the sender added `pipedream_upload_body=1`, in which case a step downloaded the body from `raw_body_url`. n8n accepts up to 16 MB directly, so drop the download step and the extra query parameter.

**Form data.** n8n puts `multipart/form-data` fields in `body` and uploaded files in binary data.

Inside converted code steps, the compatibility layer rebuilds Pipedream's event: `method` from the output the request came in on, `url` from `webhookUrl`, `client_ip` from the `x-forwarded-for` or `x-real-ip` header, and `path` as `/`. If a workflow branches on the path, split it into one Webhook per path. There's more on code in [code steps on n8n Cloud](/pipedream-code-steps-n8n-cloud).

## Step 5: Test, then move the senders

1. **List every sender.** Payment providers, Git hosts, form tools, internal apps and scheduled scripts can all hold the old URL. The events in Pipedream's inspector show who called recently.
2. **Test with the test URL.** Click Listen for Test Event in the Webhook node and send a saved request:

```bash
curl -i -X POST "https://n8n.example.com/webhook-test/orders-created" \
  -H "Authorization: Bearer $ORDERS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email": "ana@example.com", "total": 120}'
```

3. **Compare responses.** Check the status code, headers and body against what Pipedream returned for the same request.
4. **Publish, then switch one sender at a time.** Senders use the production URL, with `/webhook/` instead of `/webhook-test/`. Watch the workflow's Executions tab after each change.
5. **Disable the Pipedream workflow.** A disabled Pipedream HTTP workflow answers `404`, so any sender you missed starts logging errors, which shows you what's left.
6. **Finish before March 31, 2027.** After that, the old URLs stop answering for good.

If your senders call a Pipedream custom domain that you own, there's one more option: point that hostname at a reverse proxy that forwards requests to n8n. Senders keep their URL, and you take on running the proxy.

The [free migration report](/report) lists every HTTP trigger in your export, which ones need a Respond to Webhook node and which used authorization. It runs in your browser. For the rest of the move, see the [Pipedream to n8n guide](/pipedream-to-n8n), and finish before the [shutdown](/pipedream-shutdown).

## Frequently asked questions

### Can I keep my Pipedream webhook URL?

No. Pipedream endpoints stop answering when Workflows shuts down on March 31, 2027, and each n8n Webhook node has its own URL on your n8n instance. If your senders use a Pipedream custom domain you own, you can point that domain at a proxy that forwards to n8n.

### What is the n8n equivalent of $.respond()?

The Respond to Webhook node. Set the Webhook node's Respond option to Using 'Respond to Webhook' Node, then place a Respond to Webhook node where your code called $.respond(), with the same status, headers and body.

### Why does n8n return 200 where Pipedream returned 400?

When a workflow that uses $.respond() ended without calling it, Pipedream answered 400. In n8n, a run that ends without reaching a Respond to Webhook node gets a standard 200, so add a Respond node on every path where callers expect an error code.

### How do I protect an n8n webhook like Pipedream's custom token?

Set the Webhook node's Authentication to Header Auth and create a credential with the name Authorization and the value Bearer followed by your token. Senders keep sending the header they already use.

### How large can a request to an n8n webhook be?

16 MB by default, and self-hosted instances can change it with N8N_PAYLOAD_SIZE_MAX. Pipedream's default limit for a request body was 512 KB.
