Générer un portail API auto-documenté pour les Webhooks avec n8n API et Bootstrap
Ceci est unDocument Extraction, Multimodal AIworkflow d'automatisation du domainecontenant 17 nœuds.Utilise principalement des nœuds comme N8n, Set, Code, Html, Webhook. Utiliser n8n API et Bootstrap pour générer un portail d'API auto-documenté pour Webhooks
- •Point de terminaison HTTP Webhook (généré automatiquement par n8n)
Nœuds utilisés (17)
{
"meta": {
"instanceId": "e860732ed76ff1de8212e780b2d62bd140dee0b71e6ccf7172d54e965acae43d",
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "a59b516a-e188-4364-8722-f83fd00ed0f0",
"name": "Agréger",
"type": "n8n-nodes-base.aggregate",
"position": [
120,
-300
],
"parameters": {
"options": {},
"aggregate": "aggregateAllItemData"
},
"typeVersion": 1
},
{
"id": "3ab41431-6298-4380-aaec-d0e29f7f9cb5",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"position": [
-700,
460
],
"webhookId": "e98f9bf5-f5ad-46fb-a93c-08218eea8d5e",
"parameters": {
"path": "api-doc",
"options": {},
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "286e23f0-44c8-46c4-bfd2-8aab6eae245b",
"name": "Configurations",
"type": "n8n-nodes-base.set",
"position": [
-480,
460
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "9a243289-a359-47fd-9155-a809a910b8f7",
"name": "name_doc",
"type": "string",
"value": "N8N Example Doc"
},
{
"id": "59992f15-d27d-454c-9c1f-a66f0674c7bb",
"name": "version",
"type": "string",
"value": "v.0.1"
},
{
"id": "f0633ecb-b0c8-4e55-8285-51aa05492420",
"name": "description",
"type": "string",
"value": "Example description"
},
{
"id": "2da37a16-f97a-4ec8-9d9c-13f09bea1d39",
"name": "url_base",
"type": "string",
"value": "n8n.io/"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "b910c088-c3c6-4bfc-9856-5294891e4cd0",
"name": "Note adhésive",
"type": "n8n-nodes-base.stickyNote",
"position": [
-370,
-400
],
"parameters": {
"color": 2,
"width": 860,
"height": 260,
"content": "## Get Workflow and filter\n"
},
"typeVersion": 1
},
{
"id": "add44468-ffba-401e-a119-3e25c4870ece",
"name": "Note adhésive1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-360,
-120
],
"parameters": {
"color": 6,
"width": 760,
"height": 400,
"content": "## webhook for testing"
},
"typeVersion": 1
},
{
"id": "1de3f20f-152c-4f81-b42d-cc9e03f5bf0d",
"name": "Répondre",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
120,
-40
],
"parameters": {
"options": {},
"respondWith": "allIncomingItems"
},
"typeVersion": 1.1
},
{
"id": "0a7a9d55-5b34-4129-952d-c982ebcc3d7c",
"name": "RépondreHTML",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
180,
460
],
"parameters": {
"options": {},
"respondWith": "text",
"responseBody": "={{$json.html}}"
},
"typeVersion": 1.1
},
{
"id": "f85d91a0-16ec-4f79-870a-dfc24f954469",
"name": "CréerHTMLComplet",
"type": "n8n-nodes-base.html",
"position": [
-40,
460
],
"parameters": {
"html": "<html>\n<head>\n <title>{{$('Configs').item.json.name_doc}}</title>\n <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css\">\n <link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css\" rel=\"stylesheet\">\n <style>\n :root {\n --bs-body-bg: #1a1a1a;\n --bs-body-color: #e0e0e0;\n --bs-border-color: #444;\n --bs-accordion-bg: #2b2b2b;\n --bs-accordion-color: #e0e0e0;\n --bs-accordion-button-color: #e0e0e0;\n --bs-accordion-border-color: #444;\n --bs-accordion-button-active-bg: #3c3c3c;\n --bs-accordion-button-active-color: #ffffff;\n --bs-accordion-button-focus-border-color: #007bff;\n --bs-accordion-button-focus-box-shadow: 0 0 0 0.25rem rgba(0, 123, 255, 0.25);\n }\n .method {\n display: inline-block;\n padding: 0.3em 0.8em;\n border-radius: 0.25rem;\n font-weight: bold;\n font-size: 0.9em;\n color: white;\n text-transform: uppercase;\n margin-right: 1rem;\n }\n .method-post { background-color: #49cc90; }\n .method-get { background-color: #61affe; }\n .endpoint-path {\n font-family: monospace;\n font-size: 1.1em;\n color: #ccc;\n }\n pre {\n background-color: #161B22;\n padding: 1rem;\n border-radius: 0.25rem;\n white-space: pre-wrap;\n word-break: break-all;\n border: 1px solid var(--bs-border-color);\n }\n code { color: #c9d1d9; }\n .accordion-button:not(.collapsed) {\n box-shadow: inset 0 -1px 0 var(--bs-accordion-border-color);\n }\n </style>\n</head>\n<body class=\"p-4\">\n <div class=\"container\">\n <div class=\"d-flex align-items-baseline mb-4\">\n <h1 class=\"text-white me-3\">{{$('Configs').item.json.name_doc}}</h1>\n <p class=\"text-white-50 me-3\">{{$('Configs').item.json.description}}</p>\n <span class=\"badge bg-secondary\">{{$('Configs').item.json.version}}</span>\n </div>\n \n {{ $json.htmlContent || `\n<div class=\"alert alert-danger d-flex align-items-center mt-4\" role=\"alert\">\n <i class=\"bi bi-x-octagon-fill me-3\" style=\"font-size: 2rem;\"></i>\n <div>\n <h4 class=\"alert-heading\">Documentation Generation Failed!</h4>\n <p>An error occurred while trying to generate the API documentation from the sub-workflow.</p>\n <hr>\n <p class=\"mb-1\"><strong>Please check the following:</strong></p>\n <ul>\n <li>Ensure the sub-workflow (the one that generates the accordion) executed successfully and produced an output named <code>htmlContent</code>.</li>\n <li>Verify that at least one of your workflows is active and contains a correctly configured <code>API_DOCS</code> node.</li>\n <li>Check the n8n execution logs for more detailed error messages from the sub-workflow.</li>\n </ul>\n </div>\n</div>\n`}}\n\n\n </div>\n <footer class=\"mt-5 pt-4 text-center text-white-50\">\n <p>\n Created by <a href=\"https://www.linkedin.com/in/matheus-pedrosa-custodio/\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"link-light text-decoration-none\">\n <i class=\"bi bi-linkedin\"></i> Matheus Pedrosa\n </a>\n </p>\n</footer>\n \n <script src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js\"></script>\n</body>\n</html>"
},
"typeVersion": 1.2
},
{
"id": "0bd66854-cf1b-46b5-ad75-e0d7ef9b71d4",
"name": "ExécuterSousWorkflow",
"type": "n8n-nodes-base.executeWorkflow",
"onError": "continueRegularOutput",
"position": [
-260,
460
],
"parameters": {
"options": {
"waitForSubWorkflow": true
},
"workflowId": {
"__rl": true,
"mode": "list",
"value": "y2xOdk7RyRoS2sgF",
"cachedResultName": "Documentation"
},
"workflowInputs": {
"value": {},
"schema": [
{
"id": "url_base",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "url_base",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": true
}
},
"typeVersion": 1.2
},
{
"id": "43a02f1b-9f18-4148-9705-2c3347997cfa",
"name": "Exécuter",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"position": [
-320,
-300
],
"parameters": {
"workflowInputs": {
"values": [
{
"name": "url_base"
}
]
}
},
"typeVersion": 1.1
},
{
"id": "91ad9f92-adf3-4a8c-af48-3d1e5cd06678",
"name": "ObtenirWorkflows",
"type": "n8n-nodes-base.n8n",
"position": [
-100,
-300
],
"parameters": {
"filters": {
"tags": "",
"activeWorkflows": true
},
"requestOptions": {}
},
"credentials": {
"n8nApi": {
"id": "fTE1pX6nL40KSIdW",
"name": "n8n account"
}
},
"retryOnFail": true,
"typeVersion": 1,
"alwaysOutputData": false
},
{
"id": "c28819b2-2e5d-4732-9a68-0d5045dcb59e",
"name": "FiltrerWorkflows",
"type": "n8n-nodes-base.code",
"position": [
340,
-300
],
"parameters": {
"jsCode": "const allWorkflows = $('Aggregate').last().json.data;\nlet html = '';\n\nconst documentedWebhookWorkflows = allWorkflows.filter(item => {\n if (!item.nodes || item.nodes.length === 0) {\n return false;\n }\n const hasWebhook = item.nodes.some(node => node.type === 'n8n-nodes-base.webhook');\n const hasDocsNote = item.nodes.some(node => node.type === 'n8n-nodes-base.set' && node.name === 'API_DOCS');\n return hasWebhook && hasDocsNote;\n});\n\n\nif (documentedWebhookWorkflows.length > 0) {\n html += '<div class=\"accordion\" id=\"docsAccordion\">';\n\n const n8nBaseUrl = $(\"Execute\").last().json.url_base || 'https://n8n.io/';\n\n for (const workflow of documentedWebhookWorkflows) {\n const docsNode = workflow.nodes.find(n => n.name === 'API_DOCS');\n\n if (docsNode) {\n const jsonString = docsNode.parameters.jsonOutput || JSON.stringify(docsNode.parameters.docsData);\n let docsData = {};\n let webhookNode = null;\n\n try {\n docsData = JSON.parse(jsonString);\n webhookNode = workflow.nodes.find(n => { return n.type === 'n8n-nodes-base.webhook' && n.name === docsData.webhook });\n } catch (e) {\n console.error(`Erro ao parsear JSON no workflow: ${workflow.name}`, e);\n continue;\n }\n\n if(webhookNode){\n const method = (docsData.method || \"POST\").toUpperCase();\n const uniqueId = webhookNode.parameters.path.replace(/[^a-zA-Z0-9]/g, '');\n\n // --- make CURL\n const fullUrl = `${n8nBaseUrl}webhook/${webhookNode.parameters.path}`;\n\n let curlCommand = `curl -X ${method}\\\\`;\n if (method === 'POST' || method === 'PUT' || method === 'PATCH') {\n const requestBodyString = JSON.stringify(docsData.requestBody || {});\n curlCommand += `\n -H 'Content-Type: application/json' \\\\\n -d '${requestBodyString}' \\\\`;\n }\n curlCommand += `\n ${fullUrl}`;\n\n html += `\n <div class=\"accordion-item\">\n <h2 class=\"accordion-header\" id=\"heading-${uniqueId}\">\n <button class=\"accordion-button collapsed\" type=\"button\" data-bs-toggle=\"collapse\" data-bs-target=\"#collapse-${uniqueId}\" aria-expanded=\"false\" aria-controls=\"collapse-${uniqueId}\">\n <span class=\"method method-${method.toLowerCase()}\">${method}</span>\n <span class=\"endpoint-path\">/webhook/${webhookNode.parameters.path}</span>\n </button>\n </h2>\n <div id=\"collapse-${uniqueId}\" class=\"accordion-collapse collapse\" aria-labelledby=\"heading-${uniqueId}\" data-bs-parent=\"#docsAccordion\">\n <div class=\"accordion-body\">\n <p><strong>${docsData.summary || 'No summary'}</strong></p>\n <p>${docsData.description || 'No description.'}</p>\n <hr style=\"border-color: var(--bs-border-color);\">\n \n <h6>cURL Command:</h6>\n <pre><code>${curlCommand}</code></pre>\n \n <h6>Request Body:</h6>\n <pre><code>${JSON.stringify(docsData.requestBody || {}, null, 2)}</code></pre>\n <h6>Success Response (${docsData.successCode || 'N/A'}):</h6>\n <pre><code>${JSON.stringify(docsData.successResponse || {}, null, 2)}</code></pre>\n <h6>Error Response (${docsData.errorCode || 'N/A'}):</h6>\n <pre><code>${JSON.stringify(docsData.errorResponse || {}, null, 2)}</code></pre>\n </div>\n </div>\n </div>\n `;\n }\n }\n }\n html += '</div>';\n} else {\n html += '<div class=\"alert alert-info\">Nenhum endpoint documentado foi encontrado.</div>';\n}\n\nreturn [{\n json: {\n htmlContent: html\n }\n}];"
},
"typeVersion": 2
},
{
"id": "78526a12-613c-4530-9a6f-bb612036addf",
"name": "Note adhésive2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-940,
-400
],
"parameters": {
"color": 4,
"width": 540,
"height": 700,
"content": "### Live n8n API Documentation Generator\n\n**Author:** Matheus Pedrosa | https://www.linkedin.com/in/matheus-pedrosa-custodio/\n**Version:** 1.0\n**Date:** 2025-08-21\n\n## Summary\nThis is a \"meta-workflow\" that acts as an engine to automatically generate a live, interactive HTML documentation page for your n8n instance's webhooks. It scans all active workflows, finds the ones designated for documentation, and renders a single, professional, dark-themed page.\n\n## Key Feature\nThe core logic relies on a **convention-based discovery**. To include a webhook in the documentation, you simply add a `Set` node named **`API_DOCS`** to that workflow and fill in a JSON object with the endpoint's metadata (summary, request body, responses, etc.). This generator then automatically parses that data to build the documentation page.\n\n## Setup (Quick Checklist)\n1. **`API_DOCS` Nodes:** In every workflow you want to document, add a `Set` node named `API_DOCS` and provide the necessary JSON metadata (especially the `webhookPath`).\n2. **`GetWorkflows` Node:** Configure this node with your `n8n API` credentials. It needs permission to read all workflows.\n3. **`Configs` Node:** Customize the global settings for your documentation page, such as the main title (`name_doc`), description, and version.\n4. **`Webhook` Trigger:** The URL of this workflow's trigger is the public URL for your final documentation page."
},
"typeVersion": 1
},
{
"id": "602d0cd7-558e-4360-b385-bf9824f9278b",
"name": "Webhook1",
"type": "n8n-nodes-base.webhook",
"position": [
-320,
-40
],
"webhookId": "15105da5-96ca-472f-a7db-2ddc2e8b0891",
"parameters": {
"path": "15105da5-96ca-472f-a7db-2ddc2e8b0891",
"options": {},
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "6283f30d-d1eb-4a7b-9f97-73834d7c060f",
"name": "API_DOCS",
"type": "n8n-nodes-base.set",
"position": [
-320,
120
],
"parameters": {
"mode": "raw",
"options": {},
"jsonOutput": "{\n \"expose\": true,\n \"webhook\": \"Webhook1\",\n \"summary\": \"Title test\",\n \"method\":\"get\",\n \"description\": \"Hey community, look at this cool workflow I'm doing.\",\n \"tags\": [\n \"test\",\n \"tag_0101\"\n ],\n \"requestBody\": {},\n \"successCode\": 200,\n \"successResponse\": {\n \"status\": 200,\n \"message\": \"Workflow start\"\n },\n \"errorCode\": 400,\n \"errorResponse\": {\n \"status\": 500,\n \"message\": \"Workflow error\"\n }\n}"
},
"typeVersion": 3.4
},
{
"id": "57843c34-afb5-401e-86dc-8aa22c242263",
"name": "FausseRéponse",
"type": "n8n-nodes-base.code",
"position": [
-100,
-40
],
"parameters": {
"jsCode": "return {\n \"status\": 200,\n \"message\": \"Workflow start\"\n }"
},
"typeVersion": 2
},
{
"id": "29b6b6ea-917b-47a3-b8a1-ff017b0a10a5",
"name": "Note adhésive3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-940,
400
],
"parameters": {
"width": 150,
"height": 200,
"content": "### Create documentation page\n\n"
},
"typeVersion": 1
}
],
"pinData": {},
"connections": {
"286e23f0-44c8-46c4-bfd2-8aab6eae245b": {
"main": [
[
{
"node": "0bd66854-cf1b-46b5-ad75-e0d7ef9b71d4",
"type": "main",
"index": 0
}
]
]
},
"43a02f1b-9f18-4148-9705-2c3347997cfa": {
"main": [
[
{
"node": "91ad9f92-adf3-4a8c-af48-3d1e5cd06678",
"type": "main",
"index": 0
}
]
]
},
"3ab41431-6298-4380-aaec-d0e29f7f9cb5": {
"main": [
[
{
"node": "286e23f0-44c8-46c4-bfd2-8aab6eae245b",
"type": "main",
"index": 0
}
]
]
},
"602d0cd7-558e-4360-b385-bf9824f9278b": {
"main": [
[
{
"node": "57843c34-afb5-401e-86dc-8aa22c242263",
"type": "main",
"index": 0
}
]
]
},
"a59b516a-e188-4364-8722-f83fd00ed0f0": {
"main": [
[
{
"node": "c28819b2-2e5d-4732-9a68-0d5045dcb59e",
"type": "main",
"index": 0
}
]
]
},
"57843c34-afb5-401e-86dc-8aa22c242263": {
"main": [
[
{
"node": "1de3f20f-152c-4f81-b42d-cc9e03f5bf0d",
"type": "main",
"index": 0
}
]
]
},
"91ad9f92-adf3-4a8c-af48-3d1e5cd06678": {
"main": [
[
{
"node": "a59b516a-e188-4364-8722-f83fd00ed0f0",
"type": "main",
"index": 0
}
]
]
},
"f85d91a0-16ec-4f79-870a-dfc24f954469": {
"main": [
[
{
"node": "0a7a9d55-5b34-4129-952d-c982ebcc3d7c",
"type": "main",
"index": 0
}
]
]
},
"c28819b2-2e5d-4732-9a68-0d5045dcb59e": {
"main": [
[]
]
},
"0bd66854-cf1b-46b5-ad75-e0d7ef9b71d4": {
"main": [
[
{
"node": "f85d91a0-16ec-4f79-870a-dfc24f954469",
"type": "main",
"index": 0
}
]
]
}
}
}Comment utiliser ce workflow ?
Copiez le code de configuration JSON ci-dessus, créez un nouveau workflow dans votre instance n8n et sélectionnez "Importer depuis le JSON", collez la configuration et modifiez les paramètres d'authentification selon vos besoins.
Dans quelles scénarios ce workflow est-il adapté ?
Avancé - Extraction de documents, IA Multimodale
Est-ce payant ?
Ce workflow est entièrement gratuit et peut être utilisé directement. Veuillez noter que les services tiers utilisés dans le workflow (comme l'API OpenAI) peuvent nécessiter un paiement de votre part.
Workflows recommandés
Matheus Pedrosa
@julinhoI am a software engineer specializing in automations, with extensive experience on the N8N platform. With solid skills in JavaScript, Go, .NET, and C#, I am equipped to develop efficient and scalable solutions.
Partager ce workflow