
WebhookDocs: generate swagger preview of your active workflows
Description
Categories
🔧 Engineering
Nodes Used
n8n-nodes-base.n8nn8n-nodes-base.coden8n-nodes-base.webhookn8n-nodes-base.stickyNoten8n-nodes-base.respondToWebhook
PriceGratuit
Views0
Last Updated11/28/2025
workflow.json
{
"meta": {
"instanceId": "d68b0885df4f6057c27649c0cc1cdbf154a8c3c6de34051d82d8f9164d66f031",
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "1f397969-aa2e-435b-b0e9-9ef66face5fc",
"name": "n8n",
"type": "n8n-nodes-base.n8n",
"position": [
220,
100
],
"parameters": {
"filters": {
"activeWorkflows": true
},
"requestOptions": {}
},
"credentials": {
"n8nApi": {
"id": "23",
"name": "n8n account"
}
},
"typeVersion": 1
},
{
"id": "37b03898-8b7b-4511-8a54-b6777118ea76",
"name": "Code",
"type": "n8n-nodes-base.code",
"position": [
440,
100
],
"parameters": {
"jsCode": "function safe(x) \n{\n return x.replaceAll(\":\",\"\").replaceAll(\"/\",\"|\")\n}\n\nfunction findValidTargets(connections, sourceNode, potentialTargets) {\n const visited = new Set();\n const foundTargets = new Set();\n\n function dfs(node) {\n if (visited.has(node)) return;\n visited.add(node);\n\n if (potentialTargets.includes(node)) {\n foundTargets.add(node);\n return; // Optionally continue search if multiple paths to same target are needed\n }\n\n const nodeConnections = connections[node]?.main || [];\n for (const path of nodeConnections) {\n for (const next of path) {\n dfs(next.node);\n }\n }\n }\n\n dfs(sourceNode);\n return Array.from(foundTargets);\n}\n\nconst nodes = []\nfor (const item of $input.all()) \n{\n let webhook = {\n name: item.json.name,\n id: item.json.id,\n webhooks: []\n }\n const webhook_call = item.json.nodes.filter(node =>node.type=='n8n-nodes-base.webhook');\n const webhook_resp = item.json.nodes.filter(node =>node.type=='n8n-nodes-base.respondToWebhook');\n const targets = webhook_resp.map(wr => wr.name);\n if(webhook_call.length)\n {\n for(const call of webhook_call)\n {\n const callName = call.name;\n if(call?.parameters?.responseMode == \"responseNode\")\n {\n const valid = findValidTargets(item.json.connections, callName, targets);\n call.responses = webhook_resp.filter(wr => valid.includes(wr.name))\n }\n }\n \n webhook.webhooks.push(...webhook_call);\n nodes.push(webhook);\n }\n}\n\nlet s = `\nswagger: \"2.0\"\ninfo:\n title: N8N Instance API\n description: API description in Markdown.\n version: 1.0.0\n \nhost: ${$('Get Swagger').first().json?.headers?.host || 'n8n.instance.com'}\n\nbasePath: /webhook\nschemes:\n - https\npaths:\n`\n\nfor(const node of nodes)\n{\n const used_path = {};\n for(const w of node.webhooks)\n {\n let path = '';\n if(!(w?.parameters?.path in used_path))\n {\n path = w?.parameters?.path;\n used_path[path] = true;\n }\n\n let produces = \"application/json\"\n let code = 200;\n if(w?.responses?.length)\n {\n for(const r of w.responses)\n {\n switch(r?.parameters?.respondWith)\n {\n case 'text':\n produces ='text/plain';\n break\n case 'redirect':\n produces = 'redirect';\n code = 301\n break\n case 'json':\n produces = 'application/json';\n break\n }\n }\n }\n let parameters = []\n if(w?.notes)\n {\n console.log({notes: w.notes})\n const params = w?.notes?.split('\\n').filter(p => p.startsWith('@'));\n for(const p of params)\n {\n const parts = p.split(' ');\n const [position, name, type] = parts;\n const description = parts.slice(3).join(' ');\n console.log(p.split(' '))\n switch(position)\n {\n case '@query':\n parameters.push(`\n - name: ${name}\n in: ${position.replaceAll('@', '')}\n description: ${description || 'no description'}\n type: ${type}\n required: true\n`)\n break\n case '@body':\n parameters.push(`\n - name: ${name}\n in: ${position.replaceAll('@', '')}\n description: ${description || 'no description'}\n schema:\n type: object\n required:\n - ${name}\n properties:\n ${name}:\n type: ${type}\n`)\n }\n \n }\n\n if(parameters.length)\n {\n parameters.unshift(`\n parameters:\n`)\n }\n }\n s+=`\n ${path ? `/${path}:` : ''}\n ${w?.parameters?.httpMethod?.toLowerCase() || 'get'} :\n summary: ${safe(w?.name) || safe(node.name)}.\n description: Related to workflow [${node.id}].\n tags:\n - ${safe(node.name)}\n ${parameters.join('\\n')}\n produces:\n - ${produces}\n responses:\n ${code}:\n description: OK`\n }\n}\n\nreturn {json:{text: s, nodes}};"
},
"typeVersion": 2
},
{
"id": "872dbe8f-a4e2-4828-98d5-73f06ae116ad",
"name": "Respond to Webhook",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
660,
100
],
"parameters": {
"options": {},
"respondWith": "text",
"responseBody": "=<!DOCTYPE html>\n<html>\n<head>\n <title>Swagger UI from YAML Text</title>\n <link rel=\"stylesheet\" href=\"https://unpkg.com/swagger-ui-dist/swagger-ui.css\">\n</head>\n<body>\n <div id=\"swagger-ui\"></div>\n\n <script src=\"https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js\"></script>\n <script src=\"https://unpkg.com/[email protected]/dist/js-yaml.min.js\"></script>\n <script>\n // Your YAML as a string (replace this with your actual variable)\n const yamlText = `{{ $json.text }}`;\n\n // Parse the YAML into a JavaScript object\n const spec = jsyaml.load(yamlText);\n\n // Initialize Swagger UI with the parsed spec\n const ui = SwaggerUIBundle({\n spec: spec,\n dom_id: \"#swagger-ui\"\n });\n </script>\n</body>\n</html>\n"
},
"typeVersion": 1.2
},
{
"id": "7868c867-85c4-422d-a9f3-7424af49fd14",
"name": "Get Swagger",
"type": "n8n-nodes-base.webhook",
"position": [
0,
100
],
"webhookId": "b6873bae-3e61-4a93-9dc2-0100b497390e",
"parameters": {
"path": "swagger",
"options": {},
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "30aa0403-c828-48eb-862b-4b258249c1e1",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
0,
-160
],
"parameters": {
"width": 480,
"height": 180,
"content": "## Configure webhooks\n\nIn order to support parameter labels you have to open the note sections of every webhook and add the following text\n\n//@body field_name string description\n//@query field_name string description\n\n"
},
"typeVersion": 1
}
],
"pinData": {},
"connections": {
"n8n": {
"main": [
[
{
"node": "Code",
"type": "main",
"index": 0
}
]
]
},
"Code": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
},
"Get Swagger": {
"main": [
[
{
"node": "n8n",
"type": "main",
"index": 0
}
]
]
}
}
}