AutomationFlowsGeneral › ComfyUI Image Generation via Webhook

ComfyUI Image Generation via Webhook

Original n8n title: Comfyui Image Generation Pipeline

ComfyUI Image Generation Pipeline. Uses httpRequest, respondToWebhook, noOp. Webhook trigger; 12 nodes.

Webhook trigger★★★★☆ complexity12 nodesHTTP Request
General Trigger: Webhook Nodes: 12 Complexity: ★★★★☆ Added:

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 →

Download .json
{
  "name": "ComfyUI Image Generation Pipeline",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "generate-image",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "webhook-trigger",
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        250,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Extract prompt from webhook body or use default\nconst input = $input.first().json;\nconst prompt = input.body?.prompt || input.prompt || 'a beautiful sunset over mountains, highly detailed, 4k';\nconst negativePrompt = input.body?.negative_prompt || input.negative_prompt || 'blurry, low quality, distorted';\nconst seed = input.body?.seed || input.seed || Math.floor(Math.random() * 1000000000);\nconst steps = input.body?.steps || input.steps || 20;\nconst cfg = input.body?.cfg || input.cfg || 7;\nconst width = input.body?.width || input.width || 512;\nconst height = input.body?.height || input.height || 512;\n\n// ComfyUI API workflow format\n// This is a basic txt2img workflow - customize as needed\nconst comfyWorkflow = {\n  "3": {\n    "inputs": {\n      "seed": seed,\n      "steps": steps,\n      "cfg": cfg,\n      "sampler_name": "euler",\n      "scheduler": "normal",\n      "denoise": 1,\n      "model": ["4", 0],\n      "positive": ["6", 0],\n      "negative": ["7", 0],\n      "latent_image": ["5", 0]\n    },\n    "class_type": "KSampler",\n    "_meta": { "title": "KSampler" }\n  },\n  "4": {\n    "inputs": {\n      "ckpt_name": "v1-5-pruned-emaonly.safetensors"\n    },\n    "class_type": "CheckpointLoaderSimple",\n    "_meta": { "title": "Load Checkpoint" }\n  },\n  "5": {\n    "inputs": {\n      "width": width,\n      "height": height,\n      "batch_size": 1\n    },\n    "class_type": "EmptyLatentImage",\n    "_meta": { "title": "Empty Latent Image" }\n  },\n  "6": {\n    "inputs": {\n      "text": prompt,\n      "clip": ["4", 1]\n    },\n    "class_type": "CLIPTextEncode",\n    "_meta": { "title": "CLIP Text Encode (Positive)" }\n  },\n  "7": {\n    "inputs": {\n      "text": negativePrompt,\n      "clip": ["4", 1]\n    },\n    "class_type": "CLIPTextEncode",\n    "_meta": { "title": "CLIP Text Encode (Negative)" }\n  },\n  "8": {\n    "inputs": {\n      "samples": ["3", 0],\n      "vae": ["4", 2]\n    },\n    "class_type": "VAEDecode",\n    "_meta": { "title": "VAE Decode" }\n  },\n  "9": {\n    "inputs": {\n      "filename_prefix": "n8n_generated",\n      "images": ["8", 0]\n    },\n    "class_type": "SaveImage",\n    "_meta": { "title": "Save Image" }\n  }\n};\n\nreturn {\n  json: {\n    prompt: comfyWorkflow,\n    original_prompt: prompt,\n    negative_prompt: negativePrompt,\n    seed: seed,\n    steps: steps,\n    cfg: cfg,\n    width: width,\n    height: height\n  }\n};"
      },
      "id": "prepare-workflow",
      "name": "Prepare ComfyUI Workflow",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        470,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://comfyui:8188/prompt",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ prompt: $json.prompt }) }}",
        "options": {}
      },
      "id": "submit-to-comfyui",
      "name": "Submit to ComfyUI",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        690,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const response = $input.first().json;\nconst promptId = response.prompt_id;\n\nif (!promptId) {\n  throw new Error('No prompt_id received from ComfyUI');\n}\n\nreturn {\n  json: {\n    prompt_id: promptId,\n    status: 'queued',\n    check_count: 0,\n    max_checks: 60,\n    ...$input.first().json\n  }\n};"
      },
      "id": "extract-prompt-id",
      "name": "Extract Prompt ID",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        910,
        300
      ]
    },
    {
      "parameters": {
        "amount": 2,
        "unit": "seconds"
      },
      "id": "wait-node",
      "name": "Wait 2 Seconds",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        1130,
        300
      ]
    },
    {
      "parameters": {
        "url": "=http://comfyui:8188/history/{{ $json.prompt_id }}",
        "options": {}
      },
      "id": "check-history",
      "name": "Check Generation Status",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1350,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const input = $input.first().json;\nconst promptId = $('Extract Prompt ID').first().json.prompt_id;\nconst historyData = input;\n\n// Check if our prompt_id exists in history and has outputs\nconst promptHistory = historyData[promptId];\n\nif (promptHistory && promptHistory.outputs) {\n  // Find the SaveImage node output (node 9 in our workflow)\n  const outputs = promptHistory.outputs;\n  let images = [];\n  \n  for (const nodeId in outputs) {\n    if (outputs[nodeId].images) {\n      images = images.concat(outputs[nodeId].images);\n    }\n  }\n  \n  if (images.length > 0) {\n    return {\n      json: {\n        status: 'completed',\n        prompt_id: promptId,\n        images: images,\n        image_urls: images.map(img => \n          `http://comfyui:8188/view?filename=${img.filename}&subfolder=${img.subfolder || ''}&type=${img.type || 'output'}`\n        ),\n        original_prompt: $('Extract Prompt ID').first().json.original_prompt,\n        generation_params: {\n          seed: $('Extract Prompt ID').first().json.seed,\n          steps: $('Extract Prompt ID').first().json.steps,\n          cfg: $('Extract Prompt ID').first().json.cfg\n        }\n      }\n    };\n  }\n}\n\n// Not ready yet - increment check count\nconst checkCount = ($('Extract Prompt ID').first().json.check_count || 0) + 1;\nconst maxChecks = $('Extract Prompt ID').first().json.max_checks || 60;\n\nif (checkCount >= maxChecks) {\n  throw new Error(`Generation timed out after ${maxChecks} checks`);\n}\n\nreturn {\n  json: {\n    status: 'pending',\n    prompt_id: promptId,\n    check_count: checkCount,\n    max_checks: maxChecks,\n    original_prompt: $('Extract Prompt ID').first().json.original_prompt,\n    seed: $('Extract Prompt ID').first().json.seed,\n    steps: $('Extract Prompt ID').first().json.steps,\n    cfg: $('Extract Prompt ID').first().json.cfg\n  }\n};"
      },
      "id": "check-completion",
      "name": "Check if Complete",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1570,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "condition-complete",
              "leftValue": "={{ $json.status }}",
              "rightValue": "completed",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "if-complete",
      "name": "Generation Complete?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1790,
        300
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json) }}",
        "options": {}
      },
      "id": "respond-success",
      "name": "Respond with Image",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        2050,
        200
      ]
    },
    {
      "parameters": {
        "amount": 2,
        "unit": "seconds"
      },
      "id": "wait-retry",
      "name": "Wait and Retry",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        2050,
        400
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ error: 'Generation failed', details: $json }) }}",
        "options": {
          "responseCode": 500
        }
      },
      "id": "respond-error",
      "name": "Respond with Error",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        2270,
        500
      ]
    },
    {
      "parameters": {},
      "id": "no-op",
      "name": "No Operation",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        2270,
        400
      ]
    }
  ],
  "connections": {
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Prepare ComfyUI Workflow",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare ComfyUI Workflow": {
      "main": [
        [
          {
            "node": "Submit to ComfyUI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Submit to ComfyUI": {
      "main": [
        [
          {
            "node": "Extract Prompt ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Prompt ID": {
      "main": [
        [
          {
            "node": "Wait 2 Seconds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 2 Seconds": {
      "main": [
        [
          {
            "node": "Check Generation Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Generation Status": {
      "main": [
        [
          {
            "node": "Check if Complete",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check if Complete": {
      "main": [
        [
          {
            "node": "Generation Complete?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generation Complete?": {
      "main": [
        [
          {
            "node": "Respond with Image",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait and Retry",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait and Retry": {
      "main": [
        [
          {
            "node": "Check Generation Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "versionId": "1",
  "triggerCount": 0,
  "tags": [
    {
      "name": "ComfyUI",
      "id": "1"
    },
    {
      "name": "Image Generation",
      "id": "2"
    },
    {
      "name": "AI Stack",
      "id": "3"
    }
  ]
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

How this works

Generate high-quality images effortlessly from text prompts using this ComfyUI pipeline, saving hours of manual design work for creators and marketers. Ideal for content teams needing quick visuals for social media or prototypes without investing in complex tools. The process starts with a webhook trigger that sends your prompt to ComfyUI via an HTTP request, followed by polling for completion to deliver the final image seamlessly.

Use this workflow for on-demand image creation integrated with chatbots or forms, such as producing custom artwork during customer interactions. Avoid it for real-time applications requiring sub-second responses, as the built-in wait and status checks add slight delays. Common variations include adding post-processing nodes for resizing or integrating with storage services like Google Drive for automatic uploads.

About this workflow

ComfyUI Image Generation Pipeline. Uses httpRequest, respondToWebhook, noOp. Webhook trigger; 12 nodes.

Source: https://github.com/Zie619/n8n-workflows/blob/main/ai-stack/workflows/comfyui-image-generation.json — original creator credit. Request a take-down →

More General workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

General

Portfolio Orchestrator. Uses httpRequest. Webhook trigger; 59 nodes.

HTTP Request
General

jump-section: Comment Fix Pipeline. Uses httpRequest. Webhook trigger; 24 nodes.

HTTP Request
General

GitHub Issues Router (Linear / Jira / ClickUp). Uses stickyNote, httpRequest, respondToWebhook. Webhook trigger; 23 nodes.

HTTP Request
General

Form to CRM Lead Router (Pipedrive / HubSpot / Salesforce). Uses stickyNote, httpRequest, respondToWebhook. Webhook trigger; 22 nodes.

HTTP Request
General

Calendly to CRM Sync (Pipedrive / HubSpot / Salesforce). Uses stickyNote, httpRequest, respondToWebhook. Webhook trigger; 22 nodes.

HTTP Request