{
  "name": "Convert HTML and CSS to PNG, JPEG or WebP images with Code to Image",
  "nodes": [
    {
      "parameters": {
        "content": "## Convert HTML and CSS to PNG, JPEG or WebP images with Code to Image\n\n**Who's it for**\nMarketers, developers and makers who need images generated automatically: Open Graph cards for new blog posts, social quotes, certificates, invoices, dashboards or badges. Anything you can describe with HTML and CSS.\n\n**How it works**\n1. Start it manually (edit the **Sample input** node) or POST JSON to the built-in **Webhook**.\n2. **Render with Code to Image** sends your HTML, CSS, size and format to the Code to Image render API and gets back a pixel-perfect image. Google Fonts, flexbox, gradients and emojis all work. No headless browser to run.\n3. Choose the output: the image as a binary file (ready for Slack, Gmail, Google Drive, S3, Telegram) or a temporary link.\n4. A wrong API key, no credits or invalid input comes back as a readable `{ error, message }` instead of a failed execution.\n\n**How to set up**\n1. Get a free API key at https://codetoimage.app/dashboard/keys?utm_source=n8n&utm_medium=template (50 renders/month, no watermark).\n2. Open **Render with Code to Image**, create a *Header Auth* credential: Name `x-api-key`, Value = your key.\n3. Paste your HTML and CSS into **Sample input** and click *Test workflow*.\n\n**Requirements**\n- A Code to Image account (free tier is enough).\n- No community nodes: works on n8n Cloud and self-hosted.\n\n**How to customize**\n- Fields: `html`, `css`, `width`, `height` (1 to 4096), `format` (png, jpeg, webp), `output` (`file` or `url`).\n- Add other render API options (`quality`, `transparent`, `backgroundColor`, templates with variables) to the JSON body of the HTTP Request node.\n- Only need one input or one output? Delete the branch you don't use and reconnect. Details in the notes next to each step.\n\nDocs: https://codetoimage.app/docs?utm_source=n8n&utm_medium=template",
        "height": 1180,
        "width": 760
      },
      "id": "note-overview",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -860,
        -460
      ]
    },
    {
      "parameters": {
        "content": "### Fields\n- `html` (required) – any HTML, inline `<style>` and Google Fonts work\n- `css` – extra stylesheet\n- `width`, `height` – 1 to 4096 px\n- `format` – `png`, `jpeg` or `webp`\n- `output` – `file` (binary image) or `url` (temporary link)\n\nAny other field of the render API (e.g. `quality`, `transparent`, `backgroundColor`) can be added to the JSON body in **Render with Code to Image**.",
        "height": 300,
        "width": 360,
        "color": 7
      },
      "id": "note-fields",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        180,
        -320
      ]
    },
    {
      "parameters": {
        "content": "### Webhook input\nActivate the workflow, then:\n\n```\ncurl -X POST https://YOUR-N8N/webhook/codetoimage \\\n  -H 'content-type: application/json' \\\n  -d '{\"html\":\"<h1>Hi</h1>\",\"css\":\"h1{color:red}\",\"width\":800,\"height\":400,\"format\":\"png\",\"output\":\"file\"}' \\\n  --output image.png\n```\n\nWith `\"output\":\"url\"` the response is JSON: `{ url, expires_at, format }`.",
        "height": 320,
        "width": 360,
        "color": 7
      },
      "id": "note-webhook",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        180,
        700
      ]
    },
    {
      "parameters": {
        "content": "### Outputs\n- **Respond with image file** – binary in the `data` property; connect Slack, Gmail, Google Drive, S3… directly.\n- **Respond with link** – the URL expires (see `expires_at`), download the file if you need to keep it.\n- **Respond with error** – wrong key (401), out of credits (402) or invalid input (422): `{ error, message }`.\n\nOnly need one output? Delete **Link or file?** and the branch you don't use, then reconnect.",
        "height": 320,
        "width": 380,
        "color": 7
      },
      "id": "note-outputs",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1240,
        -460
      ]
    },
    {
      "parameters": {},
      "id": "manual-trigger",
      "name": "When clicking 'Test workflow'",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -40,
        80
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "f-html",
              "name": "html",
              "value": "<div class=\"card\"><p class=\"kicker\">Generated with n8n</p><h1>Hello from Code to Image</h1><p class=\"sub\">Pixel-perfect HTML &amp; CSS → PNG, JPEG or WebP</p></div>",
              "type": "string"
            },
            {
              "id": "f-css",
              "name": "css",
              "value": "@import url('https://fonts.googleapis.com/css2?family=Inter:wght@500;800&display=swap');body{margin:0;font-family:Inter,Arial,sans-serif}.card{width:1200px;height:630px;box-sizing:border-box;padding:80px;display:flex;flex-direction:column;justify-content:center;background:linear-gradient(135deg,#0f172a 0%,#1e3a8a 100%);color:#fff}.kicker{font-size:24px;letter-spacing:.2em;text-transform:uppercase;opacity:.7;margin:0 0 24px}h1{font-size:76px;font-weight:800;line-height:1.05;margin:0 0 24px}.sub{font-size:32px;font-weight:500;opacity:.85;margin:0}",
              "type": "string"
            },
            {
              "id": "f-width",
              "name": "width",
              "value": 1200,
              "type": "number"
            },
            {
              "id": "f-height",
              "name": "height",
              "value": 630,
              "type": "number"
            },
            {
              "id": "f-format",
              "name": "format",
              "value": "png",
              "type": "string"
            },
            {
              "id": "f-output",
              "name": "output",
              "value": "file",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "sample-input",
      "name": "Sample input",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        560,
        80
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "codetoimage",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "webhook-in",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -40,
        420
      ],
      "webhookId": "6f1c2d4e-9a3b-4c5d-8e7f-c0de70104a9e"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "w-html",
              "name": "html",
              "value": "={{ $json.body.html ?? '' }}",
              "type": "string"
            },
            {
              "id": "w-css",
              "name": "css",
              "value": "={{ $json.body.css ?? '' }}",
              "type": "string"
            },
            {
              "id": "w-width",
              "name": "width",
              "value": "={{ Number($json.body.width ?? 1200) }}",
              "type": "number"
            },
            {
              "id": "w-height",
              "name": "height",
              "value": "={{ Number($json.body.height ?? 630) }}",
              "type": "number"
            },
            {
              "id": "w-format",
              "name": "format",
              "value": "={{ $json.body.format ?? 'png' }}",
              "type": "string"
            },
            {
              "id": "w-output",
              "name": "output",
              "value": "={{ $json.body.output ?? 'file' }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "read-webhook-body",
      "name": "Read webhook body",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        560,
        420
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.codetoimage.app/v1/render",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ html: $json.html || undefined, css: $json.css || undefined, width: $json.width, height: $json.height, format: $json.format, output: $json.output === 'url' ? 'url' : 'binary' }) }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "autodetect",
              "neverError": true
            }
          }
        }
      },
      "id": "render",
      "name": "Render with Code to Image",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        860,
        250
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "c-error",
              "leftValue": "={{ $json.error }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "request-failed",
      "name": "Request failed?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        1100,
        250
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ error: $json.error, message: $json.message ?? $json.details?.[0]?.message ?? $json.error }) }}",
        "options": {
          "responseCode": 400,
          "responseHeaders": {
            "entries": [
              {
                "name": "content-type",
                "value": "application/json"
              }
            ]
          }
        }
      },
      "id": "respond-error",
      "name": "Respond with error",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        1360,
        60
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "c-url",
              "leftValue": "={{ $json.url }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "link-or-file",
      "name": "Link or file?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        1360,
        330
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ url: $json.url, expires_at: $json.expires_at, format: $json.format }) }}",
        "options": {
          "responseCode": 200,
          "responseHeaders": {
            "entries": [
              {
                "name": "content-type",
                "value": "application/json"
              }
            ]
          }
        }
      },
      "id": "respond-link",
      "name": "Respond with link",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        1620,
        230
      ]
    },
    {
      "parameters": {
        "respondWith": "binary",
        "responseDataSource": "automatically",
        "options": {}
      },
      "id": "respond-file",
      "name": "Respond with image file",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        1620,
        450
      ]
    }
  ],
  "connections": {
    "When clicking 'Test workflow'": {
      "main": [
        [
          {
            "node": "Sample input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sample input": {
      "main": [
        [
          {
            "node": "Render with Code to Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook": {
      "main": [
        [
          {
            "node": "Read webhook body",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read webhook body": {
      "main": [
        [
          {
            "node": "Render with Code to Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Render with Code to Image": {
      "main": [
        [
          {
            "node": "Request failed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Request failed?": {
      "main": [
        [
          {
            "node": "Respond with error",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Link or file?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Link or file?": {
      "main": [
        [
          {
            "node": "Respond with link",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond with image file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": false
  }
}