This workflow corresponds to n8n.io template #9165 — we link there as the canonical source.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"nodes": [
{
"id": "e6445f6f-1087-4e51-b8e5-ddb99456f868",
"name": "HEAD",
"type": "n8n-nodes-base.set",
"position": [
-336,
1440
],
"parameters": {
"mode": "raw",
"options": {},
"jsonOutput": "{\n \"method\": \"HEAD\"\n}\n"
},
"typeVersion": 3.4
},
{
"id": "bb48dadb-e960-4e34-bb51-f0a0528c0369",
"name": "Execute Workflow",
"type": "n8n-nodes-base.executeWorkflow",
"onError": "continueErrorOutput",
"position": [
896,
672
],
"parameters": {
"mode": "each",
"options": {
"waitForSubWorkflow": true
},
"workflowId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Resolve').item.json.workflowIdToExecute }}",
"cachedResultUrl": "/workflow/=%7B%7B%20$('Resolve').item.json.workflowIdToExecute%20%7D%7D"
},
"workflowInputs": {
"value": {},
"schema": [],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": true
}
},
"typeVersion": 1.3
},
{
"id": "050ed363-ea07-4301-a53b-28c5c6328857",
"name": "Success",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
1088,
672
],
"parameters": {
"options": {
"responseCode": 200
},
"respondWith": "json",
"responseBody": "={{ $json }}"
},
"typeVersion": 1.4
},
{
"id": "1659fc43-7139-49c8-a5a8-e1557dc89bae",
"name": "Aggregate Method Branches",
"type": "n8n-nodes-base.set",
"position": [
-128,
672
],
"parameters": {
"mode": "raw",
"options": {},
"jsonOutput": "={{ $json }}"
},
"typeVersion": 3.4
},
{
"id": "3bdd165b-3ce6-4292-b696-bfc99a984ee3",
"name": "PUT",
"type": "n8n-nodes-base.set",
"position": [
-336,
1280
],
"parameters": {
"mode": "raw",
"options": {},
"jsonOutput": "{\n \"method\": \"PUT\"\n}\n"
},
"typeVersion": 3.4
},
{
"id": "3aeaa1f4-9b71-46da-ae4f-277a356c1dc2",
"name": "PATCH",
"type": "n8n-nodes-base.set",
"position": [
-336,
1120
],
"parameters": {
"mode": "raw",
"options": {},
"jsonOutput": "{\n \"method\": \"PATCH\"\n}\n"
},
"typeVersion": 3.4
},
{
"id": "56b8e967-39b4-43d4-b610-77e009227d07",
"name": "DELETE",
"type": "n8n-nodes-base.set",
"position": [
-336,
960
],
"parameters": {
"mode": "raw",
"options": {},
"jsonOutput": "{\n \"method\": \"DELETE\"\n}\n"
},
"typeVersion": 3.4
},
{
"id": "6e369b93-0468-4cb3-8198-09bcd21a5b17",
"name": "POST",
"type": "n8n-nodes-base.set",
"position": [
-336,
816
],
"parameters": {
"mode": "raw",
"options": {},
"jsonOutput": "{\n \"method\": \"POST\"\n}\n"
},
"typeVersion": 3.4
},
{
"id": "cb22c4e7-1a77-455d-a4c8-d6be32d6136e",
"name": "GET",
"type": "n8n-nodes-base.set",
"position": [
-336,
672
],
"parameters": {
"mode": "raw",
"options": {},
"jsonOutput": "{\n \"method\": \"GET\"\n}\n"
},
"typeVersion": 3.4
},
{
"id": "42dcf978-3dbc-40bc-b514-a0fb5c01f5df",
"name": "Routes Config",
"type": "n8n-nodes-base.set",
"position": [
288,
672
],
"parameters": {
"mode": "raw",
"options": {},
"jsonOutput": "{\n \"routes\": [\n {\n \"action\": \"doSomething\",\n \"allowedMethods\": [\"GET\"],\n \"subflowId\": -1\n },\n {\n \"action\": \"doSomethingElse\",\n \"allowedMethods\": [\"GET\", \"POST\"],\n \"subflowId\": -1\n },\n {\n \"action\": \"deleteSomething\",\n \"allowedMethods\": [\"DELETE\"],\n \"subflowId\": -1\n }\n ]\n}\n"
},
"typeVersion": 3.4
},
{
"id": "91b1d0a9-431f-4806-b291-fd76973301d7",
"name": "Query param exists?",
"type": "n8n-nodes-base.if",
"position": [
64,
672
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "d010b9f7-91c1-45b8-8f57-0dd17c4db7d2",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $('Universal Receiver').item.json.query?.action }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "4a9798ab-c963-4e42-9a0b-8d8e9f2ac2ed",
"name": "Universal Receiver",
"type": "n8n-nodes-base.webhook",
"position": [
-544,
672
],
"parameters": {
"path": "universalReceiver",
"options": {},
"httpMethod": [
"GET",
"POST",
"DELETE",
"PATCH",
"PUT",
"HEAD"
],
"responseMode": "responseNode",
"multipleMethods": true
},
"typeVersion": 2.1
},
{
"id": "116a35d5-68cc-478c-8630-4f9a36bdba36",
"name": "Sticky: Method detection",
"type": "n8n-nodes-base.stickyNote",
"position": [
-336,
496
],
"parameters": {
"color": 7,
"width": 308,
"height": 152,
"content": "Method branches: Each branch hardcodes the HTTP method because n8n\u2019s Webhook node does not expose the method as a property directly. We aggregate these branches so other nodes can reference a single `method` value."
},
"typeVersion": 1
},
{
"id": "1664cfd5-d8fb-4417-a99e-43e7ccf7568b",
"name": "Sticky: Routes config",
"type": "n8n-nodes-base.stickyNote",
"position": [
208,
496
],
"parameters": {
"color": 7,
"width": 228,
"height": 152,
"content": "Route configuration: Map action names to subflow IDs and allowed HTTP methods. You can override this with other methods for receiving data, such as database queries or similar."
},
"typeVersion": 1
},
{
"id": "b95edefc-f4cf-4696-8d09-5271233f1d38",
"name": "Sticky: Resolver",
"type": "n8n-nodes-base.stickyNote",
"position": [
464,
496
],
"parameters": {
"color": 7,
"width": 180,
"height": 152,
"content": "Resolve Route: Reads the query action and method, checks the config and decides whether to run a subflow. "
},
"typeVersion": 1
},
{
"id": "784a1ad8-d454-41fe-ab3b-801e81cce923",
"name": "Sticky: Execute & respond",
"type": "n8n-nodes-base.stickyNote",
"position": [
880,
496
],
"parameters": {
"color": 7,
"width": 308,
"height": 152,
"content": "Execution & Response: Executes the target subflow when valid and returns its output."
},
"typeVersion": 1
},
{
"id": "c712a136-97f8-49fe-b01a-77ed001fe10f",
"name": "[Error] Required query param missing",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
288,
848
],
"parameters": {
"options": {
"responseCode": 400
},
"respondWith": "json",
"responseBody": "{\n \"error\": \"Required information not provided!\"\n}"
},
"typeVersion": 1.4
},
{
"id": "d10087c4-96d7-4df6-9918-81b95a95cd6c",
"name": "Resolve",
"type": "n8n-nodes-base.code",
"position": [
496,
672
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// inputs\nconst method = $('Aggregate Method Branches').item.json.method;\nconst action = $(\"Universal Receiver\").item.json.query?.action;\nconst cfg = $(\"Routes Config\").item.json;\nconst routes = Array.isArray(cfg?.routes) ? cfg.routes : [];\nconst body = $(\"Universal Receiver\").item.json.query?.body || null;\n\n// Try to find a matching route (action + method)\nconst routeByAction = routes.find(r => String(r.action || '').toLowerCase() === String(action || '').toLowerCase());\nconst routeByActionAndMethod = routes.find(r => {\n const actionFound = String(r.action || '').toLowerCase() === String(action || '').toLowerCase();\n const methodAllowed = Array.isArray(r.allowedMethods) && r.allowedMethods.includes(method);\n return (actionFound && methodAllowed)\n});\n\nlet error = null;\nlet status = 200;\nlet allowHeader = null;\nlet workflowId = null;\n\nif (!action) {\n error = 'MISSING_ACTION';\n status = 400;\n} else if (!routeByAction) {\n error = 'ROUTE_NOT_FOUND';\n status = 404;\n} else if (!routeByActionAndMethod) {\n error = 'METHOD_NOT_ALLOWED';\n status = 405;\n allowHeader = Array.isArray(routeByAction.allowedMethods) ? routeByAction.allowedMethods.join(', ') : '';\n} else {\n workflowId = routeByActionAndMethod.subflowId ?? null;\n if (workflowId == null) {\n error = 'MISSING_SUBFLOW_ID';\n status = 500;\n }\n}\n\nreturn {\n action,\n method,\n error,\n status,\n allow: allowHeader,\n workflowIdToExecute: workflowId,\n body\n};"
},
"typeVersion": 2
},
{
"id": "80b31830-b665-4f01-a2d6-4a08779e3ebb",
"name": "Route is OK?",
"type": "n8n-nodes-base.if",
"position": [
688,
672
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "d010b9f7-91c1-45b8-8f57-0dd17c4db7d2",
"operator": {
"type": "number",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $('Resolve').item.json.workflowIdToExecute }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "99fb75ed-9d41-4396-b4e6-ecead6e005d9",
"name": "Status = 405?",
"type": "n8n-nodes-base.if",
"position": [
896,
992
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "9928d071-7d13-4d05-9843-9f2f1b00b60e",
"operator": {
"type": "number",
"operation": "equals"
},
"leftValue": "={{ $('Resolve').item.json.status }}",
"rightValue": 405
}
]
}
},
"typeVersion": 2.2
},
{
"id": "c1882d8f-bfd1-482c-9f52-113c6111868f",
"name": "[Error] Method Not Allowed",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
1088,
992
],
"parameters": {
"options": {
"responseCode": 405,
"responseHeaders": {
"entries": [
{
"name": "Allow",
"value": "={{ $('Resolve').item.json.allow || '' }}"
}
]
}
},
"respondWith": "json",
"responseBody": "={\n \"error\": \"{{ $('Resolve').item.json.error }}\"\n}"
},
"typeVersion": 1
},
{
"id": "6325c215-2175-4703-8746-f18e5e8dd208",
"name": "Error - Not OK",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
1088,
1168
],
"parameters": {
"options": {
"responseCode": "={{ $('Resolve').item.json.status || 400 }}"
},
"respondWith": "json",
"responseBody": "={ \"error\": \"{{ $('Resolve').item.json.error || 'Bad request' }}\" }"
},
"typeVersion": 1
},
{
"id": "b4f491b6-ea8b-470c-ad4b-d1c8922ae479",
"name": "Error - Subflow",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
1088,
832
],
"parameters": {
"options": {
"responseCode": 500
},
"respondWith": "json",
"responseBody": "{\n \"error\": \"Unexpected error in the executed subflow!\"\n}"
},
"typeVersion": 1
},
{
"id": "2fe4296a-f97f-4dce-b49e-983a25720dd8",
"name": "Sticky: Method detection1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1104,
496
],
"parameters": {
"color": 7,
"width": 500,
"height": 520,
"content": "This flow acts as a centralized API router using a single webhook URL to receive incoming HTTP requests with different methods (GET, POST, DELETE, PATCH, PUT, HEAD).\n\n- The flow separates and hardcodes each HTTP method into dedicated branches (Set nodes for GET, POST, etc.) since the webhook node doesn't expose the method directly. These branches are then consolidated in \"Aggregate Method Branches\" for easier reference.\n- It checks if the required query parameter action exists. If missing, it returns an error response.\n- The `Routes Config` node defines a list of routes mapping the action parameter and allowed HTTP methods to specific subflow IDs. This allows controlling which workflow to execute based on the request.\n- The `Resolve` code node matches the incoming action and method against the configured routes, returns error codes like 404 or 405 if no route/method matches, or identifies the subflow to execute.\n- Based on this result, the flow either executes the matched subflow or returns an appropriate error message and status code (400, 404, 405, 500).\n- After executing a subflow, it returns the subflow result as the response. If the subflow fails, a generic error is sent back.\n- This setup provides a flexible and maintainable way to route multiple API actions via a single webhook endpoint, with centralized management of routes, error handling, and response management."
},
"typeVersion": 1
}
],
"connections": {
"GET": {
"main": [
[
{
"node": "Aggregate Method Branches",
"type": "main",
"index": 0
}
]
]
},
"PUT": {
"main": [
[
{
"node": "Aggregate Method Branches",
"type": "main",
"index": 0
}
]
]
},
"HEAD": {
"main": [
[
{
"node": "Aggregate Method Branches",
"type": "main",
"index": 0
}
]
]
},
"POST": {
"main": [
[
{
"node": "Aggregate Method Branches",
"type": "main",
"index": 0
}
]
]
},
"PATCH": {
"main": [
[
{
"node": "Aggregate Method Branches",
"type": "main",
"index": 0
}
]
]
},
"DELETE": {
"main": [
[
{
"node": "Aggregate Method Branches",
"type": "main",
"index": 0
}
]
]
},
"Resolve": {
"main": [
[
{
"node": "Route is OK?",
"type": "main",
"index": 0
}
]
]
},
"Route is OK?": {
"main": [
[
{
"node": "Execute Workflow",
"type": "main",
"index": 0
}
],
[
{
"node": "Status = 405?",
"type": "main",
"index": 0
}
]
]
},
"Routes Config": {
"main": [
[
{
"node": "Resolve",
"type": "main",
"index": 0
}
]
]
},
"Status = 405?": {
"main": [
[
{
"node": "[Error] Method Not Allowed",
"type": "main",
"index": 0
}
],
[
{
"node": "Error - Not OK",
"type": "main",
"index": 0
}
]
]
},
"Execute Workflow": {
"main": [
[
{
"node": "Success",
"type": "main",
"index": 0
}
],
[
{
"node": "Error - Subflow",
"type": "main",
"index": 0
}
]
]
},
"Universal Receiver": {
"main": [
[
{
"node": "GET",
"type": "main",
"index": 0
}
],
[
{
"node": "POST",
"type": "main",
"index": 0
}
],
[
{
"node": "DELETE",
"type": "main",
"index": 0
}
],
[
{
"node": "PATCH",
"type": "main",
"index": 0
}
],
[
{
"node": "PUT",
"type": "main",
"index": 0
}
],
[
{
"node": "HEAD",
"type": "main",
"index": 0
}
]
]
},
"Query param exists?": {
"main": [
[
{
"node": "Routes Config",
"type": "main",
"index": 0
}
],
[
{
"node": "[Error] Required query param missing",
"type": "main",
"index": 0
}
]
]
},
"Aggregate Method Branches": {
"main": [
[
{
"node": "Query param exists?",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow provides a universal webhook endpoint that dynamically routes incoming requests to different subflows. It allows you to manage multiple API-like endpoints from a single entry point, while ensuring proper error handling and consistent responses. Webhook Receiver – A…
Source: https://n8n.io/workflows/9165/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
A production-ready authentication workflow implementing secure user registration, login, token verification, and refresh token mechanisms. Perfect for adding authentication to any application without
Portfolio Orchestrator. Uses httpRequest. Webhook trigger; 59 nodes.
This n8n template demonstrates how a simple Multi-Layer Perceptron (MLP) neural network can predict housing prices. The prediction is based on four key features, processed through a three-layer model.
github code Try yourself
This workflow contains community nodes that are only compatible with the self-hosted version of n8n.