---
title: "n8n: Respond to a Webhook Instantly and Keep the Workflow Running"
description: "Your n8n form hangs until the whole workflow finishes. Here are the three patterns that return a response immediately — responseMode, an early Respond to Webhook node, and the Form Trigger workaround."
canonical: "https://www.razi.pro/blog/n8n-respond-to-webhook-without-waiting"
date: "2026-09-06"
tags: ["n8n", "Webhooks", "Automation", "Architecture", "API"]
source: "razi.pro"
---

# n8n: Respond to a Webhook Instantly and Keep the Workflow Running

You submit a form or trigger an automation from your app. In n8n, the workflow begins executing: validating the payload, querying an LLM, generating a PDF, and alerting three Slack channels. Meanwhile, in the user's browser, the network spinner spins for 42 seconds before failing with `504 Gateway Timeout`.

Why did the browser wait for a Slack message to be sent?

Because by default, n8n couples the lifecycle of an incoming HTTP request to the lifecycle of the entire workflow. If your downstream steps take 30 seconds, your client waits 30 seconds.

Here is why that happens, the three patterns that return a response in 150ms while letting downstream nodes finish in their own time, and how to handle errors once you have already returned HTTP 200.

---

## Why n8n Holds the HTTP Connection

When a request arrives at an n8n **Webhook** node, n8n assigns an execution context to that request. How and when it closes the connection is governed entirely by the **Response Mode** parameter on the Webhook node:

| Response Mode | When the response is sent | Typical response time | Good for |
|---|---|---|---|
| `On Received` | Instantly upon receiving the payload (HTTP 200) | 10–50ms | Fire-and-forget ingestion, background workers, IoT logs |
| `When Last Node Finishes` (Default) | Only when the entire workflow completes | Seconds to minutes | Simple synchronous transforms (e.g. format JSON and return it) |
| `Using 'Respond to Webhook' Node` | Exactly when an explicit **Respond to Webhook** node is reached | Controlled by you (usually 50–200ms) | Generating an ID or validation check, responding to caller, then doing heavy work |

Most people run into timeouts because their Webhook node is left on the default setting (`When Last Node Finishes`).

---

## Pattern A: Fire and Forget (`responseMode: On Received`)

If the caller does not need any generated data back—they just want to hand you data and move on—change the Webhook setting directly:

1. Double click the **Webhook** node.
2. In the properties panel, locate **Respond**.
3. Change the dropdown from **When Last Node Finishes** to **Immediately**.
4. Set the **Response Code** (default is `200`) and optional message (e.g. `{"status": "received"}`).

```
[Incoming HTTP POST]
       │
       ├───► (Immediately returns HTTP 200 to client in ~15ms)
       ▼
[Validate Payload] ──► [Call AI Service] ──► [Write to DB] ──► [Notify Slack]
```

The client connection is terminated immediately. n8n continues running the workflow nodes in the background until completion.

### When this is wrong
If your frontend needs a generated ID (like a job UUID or receipt token) to poll for progress or show a tracking link, **Pattern A** cannot help you because it returns before any downstream node generates that data. For that, you need **Pattern B**.

---

## Pattern B: Generate Ticket, Respond Early, Then Continue

This is the sweet spot for web applications. The client submits a job, n8n validates the payload and generates a unique tracking ID, responds with `{"status": "queued", "jobId": "..."}` in under 150ms, and *then* continues executing the expensive steps.

The critical concept: **n8n nodes placed after a "Respond to Webhook" node will still execute.** Reaching the Respond node satisfies the HTTP connection, but does not abort workflow execution.

### The Graph Layout

```
[Webhook (Using 'Respond to Webhook' Node)]
             │
             ▼
[Code: Validate & Mint Job ID]
             │
             ├───► [Respond to Webhook: { success: true, jobId }] (HTTP 200 to caller)
             │
             ▼
[Expensive AI / PDF / Heavy Processing]
             │
             ▼
[Save to Supabase / Storage]
             │
             ▼
[Send Confirmation Email]
```

### Configuration Steps
1. Set the **Webhook** node **Respond** setting to **Using 'Respond to Webhook' Node**.
2. Connect your Webhook to a quick validation node or **Code** node. Generate a timestamp or UUID:
```javascript
// In the Code node
const jobId = $json.jobId || "job_" + Date.now();
return [{
  json: {
    ...$json,
    jobId,
    receivedAt: new Date().toISOString()
  }
}];
```
3. Connect the output to a **Respond to Webhook** node.
   - **Respond With**: JSON
   - **Response Body**: `={{ { success: true, jobId: $json.jobId, message: "Job accepted for processing" } }}`
4. Connect the output of the **Respond to Webhook** node (or branch directly from the Code node) to your remaining pipeline (AI summary, external APIs, emails).

The client receives a fast 200 response with the `jobId`, and the rest of the flow continues unimpeded.

---

## Pattern C: The n8n Form Trigger Workaround

A common search in the community is: *"n8n Form Trigger + Respond to Webhook does not work"*.

The reason: **n8n Form Trigger nodes do not support the Respond to Webhook node.** Form Triggers are designed to render a complete HTML page or perform an HTTP redirect when the form is submitted.

If you have a heavy workflow triggered by an n8n Form and you want the submitter to see an instant "Thank you! Processing your submission" screen instead of freezing while 15 nodes execute, use **Workflow Decoupling**:

```
[n8n Form Trigger] ──► [Execute Workflow (Wait for completion: FALSE)] ──► [Show Custom HTML / Redirect]
                                     │
                                     ▼ (Runs in separate background thread)
                            [Heavy Sub-Workflow]
```

### Step-by-step
1. Create your processing logic in a separate workflow with an **Execute Workflow Trigger**.
2. In your Form workflow, after the **Form Trigger**, add an **Execute Workflow** node pointing to your sub-workflow.
3. Open the **Execute Workflow** node settings and ensure **Wait for Sub-Workflow Completion** is toggled **OFF**.
4. End the main form flow with your custom confirmation message or redirect URL.

The user immediately sees their confirmation, and the sub-workflow processes asynchronously in the background.

---

## Error Handling: What if Downstream Nodes Fail?

Once you have responded with HTTP 200, **you cannot change the response code retroactively**. If step 7 crashes on an invalid API key, the caller already received a success message.

To make this production-ready:

1. **Attach an Error Trigger**: In your workflow settings, assign an **Error Workflow**. If any unhandled exception occurs after the Respond node, n8n executes the error workflow to notify your team via Slack or log to an incidents table.
2. **Status Table / Webhook Callback**: Write the initial status (`pending`) to your database in step 2. Update it to `completed` or `failed` at the end of the pipeline. If your client needs to know the final outcome, it can poll an endpoint or listen on a WebSocket.
3. **Graceful Try-Catch**: For nodes that might fail (e.g., third-party AI APIs), enable **Continue On Fail** in the node's settings, and branch into a recovery node if an error occurred.

---

## Full Workflow to Import

Here is a minimal, working n8n workflow implementing **Pattern B**. You can copy this JSON directly, open n8n, and press `Ctrl+V` to import it (or format it in our [JSON Formatter](https://www.razi.pro/tools/json-formatter)):

```json
{
  "name": "Async Webhook with Early Response",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "async-job-demo",
        "responseMode": "responseNode",
        "options": {}
      },
      "name": "Webhook Ingest",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [240, 300]
    },
    {
      "parameters": {
        "jsCode": "const input = $input.first().json;\nconst jobId = 'job_' + Math.random().toString(36).substring(2, 10);\nreturn {\n  json: {\n    ...input,\n    jobId,\n    receivedAt: new Date().toISOString()\n  }\n};"
      },
      "name": "Mint Job ID",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [460, 300]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\n  "status": "queued",\n  "jobId": "{{ $json.jobId }}",\n  "message": "Request received and queued for processing."\n}",
        "options": {}
      },
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [680, 300]
    },
    {
      "parameters": {
        "unit": "seconds",
        "amount": 5
      },
      "name": "Simulated Heavy Task (Wait)",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1,
      "position": [900, 300]
    }
  ],
  "connections": {
    "Webhook Ingest": {
      "main": [
        [
          {
            "node": "Mint Job ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Mint Job ID": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Respond to Webhook": {
      "main": [
        [
          {
            "node": "Simulated Heavy Task (Wait)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
```

---

## Verifying Response Time with cURL

Do not guess whether your workflow is truly decoupled. Test it with `curl` and measure the exact time to first byte:

```bash
curl -X POST https://your-n8n.domain.com/webhook/async-job-demo \
  -H "Content-Type: application/json" \
  -d '{"task": "generate_report", "email": "user@example.com"}' \
  -w "\nHTTP Status: %{http_code}\nTotal Time: %{time_total}s\n"
```

Even if your downstream task runs for 60 seconds, your output will look like this:

```
{"status":"queued","jobId":"job_7f8k21","message":"Request received and queued for processing."}
HTTP Status: 200
Total Time: 0.084s
```

The client was released in 84 milliseconds. n8n does the rest.

---

### Related reading & production workflows

- [Automating Contact Forms with n8n and Real-Time Responses](https://www.razi.pro/blog/automated-contact-form-n8n-real-time-responses) — How razi.pro's production contact system validates, forwards to n8n, and triggers instant alerts.
- [Building Serverless Workflows: Integrating n8n with Next.js](https://www.razi.pro/blog/n8n-nextjs-integration-guide) — Secure server-to-server webhook dispatching from Next.js route handlers.
- [n8n Workflow Automation Examples](https://www.razi.pro/blog/n8n-workflow-automation-examples) — Practical automation recipes for real-world projects.
