{
  "name": "Google Sheets rows to PDF in Google Drive (html-pdf.com)",
  "nodes": [
    {
      "parameters": {
        "content": "## Google Sheets rows to PDFs in Google Drive\n1. **Every day at 8am** runs the workflow (change the schedule, or swap in a *Google Sheets Trigger* on row added).\n2. **Read rows** loads the sheet. Set your spreadsheet ID and sheet name.\n3. **Build HTML per row** makes one HTML document per row and skips rows with a value in `pdf_done`.\n4. **Convert to PDF** returns each PDF as a binary file.\n5. **Upload to Google Drive** saves it. Pick your folder.\n\nTip: add a Google Sheets *Update row* node at the end to set `pdf_done`.",
        "height": 400,
        "width": 600
      },
      "id": "db603e5c-cd0a-556d-8fd5-57723f5d5052",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -80,
        -360
      ]
    },
    {
      "parameters": {
        "content": "## html-pdf.com credential\nCreate a **Header Auth** credential:\n- Name: `Authorization`\n- Value: `Bearer hp_live_...`\n\nFree key (100 pages, no card): https://html-pdf.com/login\nDocs: https://html-pdf.com/docs\n\nAlso connect: Google Sheets and Google Drive (OAuth2).",
        "height": 400,
        "width": 380,
        "color": 5
      },
      "id": "fbb65950-d17c-561d-a70d-1bd9c896a448",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        540,
        -360
      ]
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 8
            }
          ]
        }
      },
      "id": "04a907d6-3247-54c9-ba34-1c3c84e604b2",
      "name": "Every day at 8am",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        120
      ]
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "REPLACE_WITH_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Sheet1",
          "mode": "name"
        },
        "options": {}
      },
      "id": "d10d7a3f-6388-52fb-a395-72de6fc9b344",
      "name": "Read rows",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        220,
        120
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID",
          "name": "Google Sheets account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// One PDF per sheet row. Column headers become fields, e.g. name, email, amount.\n// Rows that already have a value in a \"pdf_done\" column are skipped.\nconst esc = (v) => String(v ?? '').replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c]));\n\nreturn $input.all()\n  .filter(({ json: row }) => !row.pdf_done)\n  .map(({ json: row }) => {\n    const fields = Object.entries(row)\n      .filter(([k]) => !['row_number', 'pdf_done'].includes(k))\n      .map(([k, v]) => `<tr><th>${esc(k)}</th><td>${esc(v)}</td></tr>`)\n      .join('');\n\n    const title = row.name || row.title || `Row ${row.row_number}`;\n    const html = `<!doctype html><html><head><meta charset=\"utf-8\"><style>\n      body { font-family: Helvetica, Arial, sans-serif; color: #111; margin: 0; }\n      h1 { font-size: 24px; margin-bottom: 4px; }\n      .muted { color: #666; margin-bottom: 24px; }\n      table { width: 100%; border-collapse: collapse; }\n      th, td { text-align: left; padding: 8px; border-bottom: 1px solid #e5e5e5; vertical-align: top; }\n      th { width: 30%; color: #444; text-transform: capitalize; }\n    </style></head><body>\n      <h1>${esc(title)}</h1>\n      <div class=\"muted\">Generated ${new Date().toISOString().slice(0, 10)}</div>\n      <table>${fields}</table>\n    </body></html>`;\n\n    const slug = String(title).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '') || 'row';\n    return { json: { html, filename: `${slug}-${row.row_number}.pdf` } };\n  });"
      },
      "id": "3b0daff0-c531-55a2-baaf-a0813f46b106",
      "name": "Build HTML per row",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        440,
        120
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://html-pdf.com/v1/pdf",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ html: $json.html, format: 'A4', margin: '15mm', print_background: true, filename: $json.filename }) }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file",
              "outputPropertyName": "data"
            }
          },
          "timeout": 60000
        }
      },
      "id": "81692168-8001-52e9-857f-9af715058108",
      "name": "Convert to PDF",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        660,
        120
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID",
          "name": "html-pdf.com API key"
        }
      }
    },
    {
      "parameters": {
        "name": "={{ $('Build HTML per row').item.json.filename }}",
        "driveId": {
          "__rl": true,
          "value": "My Drive",
          "mode": "list",
          "cachedResultName": "My Drive"
        },
        "folderId": {
          "__rl": true,
          "value": "root",
          "mode": "list",
          "cachedResultName": "/ (Root folder)"
        },
        "options": {}
      },
      "id": "15b8453f-e27b-5553-beac-af4880badae2",
      "name": "Upload to Google Drive",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        880,
        120
      ],
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID",
          "name": "Google Drive account"
        }
      }
    }
  ],
  "pinData": {},
  "connections": {
    "Every day at 8am": {
      "main": [
        [
          {
            "node": "Read rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read rows": {
      "main": [
        [
          {
            "node": "Build HTML per row",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build HTML per row": {
      "main": [
        [
          {
            "node": "Convert to PDF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert to PDF": {
      "main": [
        [
          {
            "node": "Upload to Google Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}
