Générer un portail API auto-documenté pour les Webhooks avec n8n API et Bootstrap

Avancé

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

Prérequis
  • Point de terminaison HTTP Webhook (généré automatiquement par n8n)
Aperçu du workflow
Visualisation des connexions entre les nœuds, avec support du zoom et du déplacement
Exporter le workflow
Copiez la configuration JSON suivante dans n8n pour importer et utiliser ce workflow
{
  "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![](https://cdn-icons-png.flaticon.com/512/4028/4028647.png)"
      },
      "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
          }
        ]
      ]
    }
  }
}
Foire aux questions

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.

Informations sur le workflow
Niveau de difficulté
Avancé
Nombre de nœuds17
Catégorie2
Types de nœuds10
Description de la difficulté

Adapté aux utilisateurs avancés, avec des workflows complexes contenant 16+ nœuds

Auteur
Matheus Pedrosa

Matheus Pedrosa

@julinho

I 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.

Liens externes
Voir sur n8n.io

Partager ce workflow

Catégories

Catégories: 34