{
  "name": "Tally form response to PDF (html-pdf.com)",
  "nodes": [
    {
      "parameters": {
        "content": "## Tally form response to PDF\n1. Copy the **Production URL** of *Tally webhook* into Tally: **Integrations → Webhooks**.\n2. **Build summary HTML** turns every answer into a row (choice answers show their text).\n3. **Create PDF** calls html-pdf.com and gets a 24h download link.\n4. **Email the PDF link** sends it to the respondent's email answer.\n\nGuide: https://html-pdf.com/tally-to-pdf",
        "height": 340,
        "width": 440
      },
      "id": "f44da133-966b-5d5b-a451-b110a7eb8a31",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -80,
        -300
      ]
    },
    {
      "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\n\nYour form needs an **Email** question for step 4.",
        "height": 340,
        "width": 340,
        "color": 5
      },
      "id": "b3af506f-913e-5426-a598-8a74c3c0d5b4",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        400,
        -300
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "tally-to-pdf",
        "options": {}
      },
      "id": "f97dfc17-c421-5ba5-b55c-3fcd6a1f5ceb",
      "name": "Tally webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        120
      ],
      "webhookId": "dcba0ee7-cbe6-54c6-9c79-9b2177eb9adf"
    },
    {
      "parameters": {
        "jsCode": "// Turns a Tally submission (webhook body) into a one page summary PDF.\n// Edit BRAND and the CSS to match your style.\nconst BRAND = { name: 'Your Company', email: 'hello@yourcompany.com' };\n\nconst esc = (v) => String(v ?? '').replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c]));\n// Choice questions send option ids; look up their text in `options`.\nconst text = (f) => Array.isArray(f.value)\n  ? f.value.map((id) => (f.options || []).find((o) => o.id === id)?.text ?? id).join(', ')\n  : f.value;\n\nreturn $input.all().map(({ json }) => {\n  const data = json.body?.data || {};\n  const fields = data.fields || [];\n  const rows = fields.map((f) => `<tr><th>${esc(f.label)}</th><td>${esc(text(f))}</td></tr>`).join('');\n  const date = new Date(data.createdAt || Date.now()).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });\n\n  const html = `<!doctype html><html><head><meta charset=\"utf-8\"><style>\n    @page { size: A4; margin: 18mm; }\n    body { font-family: Helvetica, Arial, sans-serif; color: #111; font-size: 12.5px; margin: 0; }\n    .top { display: flex; justify-content: space-between; align-items: flex-end; border-bottom: 2px solid #111; padding-bottom: 12px; margin-bottom: 24px; }\n    h1 { font-size: 24px; margin: 0; }\n    .muted { color: #666; }\n    table { width: 100%; border-collapse: collapse; }\n    th { text-align: left; color: #666; font-weight: 600; width: 35%; padding: 9px 12px 9px 0; vertical-align: top; }\n    td { padding: 9px 0; white-space: pre-wrap; }\n    tr { border-bottom: 1px solid #eee; break-inside: avoid; }\n    .foot { margin-top: 32px; font-size: 11px; color: #888; }\n  </style></head><body>\n    <div class=\"top\"><div><h1>${esc(data.formName || 'Form response')}</h1><div class=\"muted\">${esc(date)}</div></div><strong>${esc(BRAND.name)}</strong></div>\n    <table>${rows}</table>\n    <div class=\"foot\">Submission ${esc(data.submissionId || data.responseId || '')} · ${esc(BRAND.email)}</div>\n  </body></html>`;\n\n  const email = fields.find((f) => f.type === 'INPUT_EMAIL')?.value || '';\n  const slug = String(data.formName || 'response').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');\n  return { json: { html, email, formName: data.formName || 'your form', filename: `${slug}.pdf` } };\n});"
      },
      "id": "0cd9889a-ad32-56cb-98f5-eaa0c2727b3c",
      "name": "Build summary HTML",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        220,
        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', filename: $json.filename, response: 'url' }) }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          },
          "timeout": 60000
        }
      },
      "id": "190d0a17-a9e1-554d-bbed-0a086eab19ba",
      "name": "Create PDF",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        440,
        120
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID",
          "name": "html-pdf.com API key"
        }
      }
    },
    {
      "parameters": {
        "sendTo": "={{ $('Build summary HTML').item.json.email }}",
        "subject": "=Your copy of {{ $('Build summary HTML').item.json.formName }}",
        "emailType": "html",
        "message": "=<p>Thanks for your submission. Here is a PDF copy of your answers:</p><p><a href=\"{{ $json.url }}\">Download PDF</a></p><p>The link is valid for 24 hours.</p>",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "e78d8551-351f-5314-8190-ef0d2550a23f",
      "name": "Email the PDF link",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        660,
        120
      ],
      "credentials": {
        "gmailOAuth2": {
          "id": "REPLACE_WITH_YOUR_CREDENTIAL_ID",
          "name": "Gmail account"
        }
      },
      "webhookId": "7e390200-0dfa-55b0-99fd-8f39b10a3d5e"
    }
  ],
  "connections": {
    "Tally webhook": {
      "main": [
        [
          {
            "node": "Build summary HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build summary HTML": {
      "main": [
        [
          {
            "node": "Create PDF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create PDF": {
      "main": [
        [
          {
            "node": "Email the PDF link",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}
