This workflow follows the Error Trigger → HTTP Request recipe pattern — see all workflows that pair these two integrations.
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 →
{
"name": "Subflow \u2014 PDF Generate",
"nodes": [
{
"id": "pd000001-0001-0001-0001-000000000001",
"name": "PDF Generate Trigger",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1,
"position": [
200,
300
],
"parameters": {}
},
{
"id": "pd000002-0002-0002-0002-000000000002",
"name": "Render HTML Template",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
440,
300
],
"parameters": {
"language": "javaScript",
"jsCode": "// Render HTML for the requested template_kind.\n// v1 supports LANDLORD_MONTHLY_REPORT only.\n// Expand with additional cases as new template kinds are added.\n\nconst t = $('PDF Generate Trigger').first().json;\nconst kind = t.template_kind || '';\nconst data = t.data || {};\nconst outputFilename = t.output_filename || 'report';\n\nif (kind !== 'LANDLORD_MONTHLY_REPORT') {\n return [{ json: { html: '', error: 'UNSUPPORTED_TEMPLATE_KIND', template_kind: kind, output_filename: outputFilename } }];\n}\n\nconst landlordName = data.landlord_name || 'Landlord';\nconst reportMonth = data.report_month || '';\nconst properties = Array.isArray(data.properties) ? data.properties : [];\nconst financials = data.financials || {};\n\nconst propertyRows = properties.map(function(p) {\n return '<tr><td>' + (p.address || '') + '</td><td>' + (p.type || '') + '</td><td>' + (p.tenant || 'Vacant') + '</td><td>' + (p.rent_ghs !== undefined ? 'GHS ' + Number(p.rent_ghs).toLocaleString() : '') + '</td><td>' + (p.status || '') + '</td></tr>';\n}).join('');\n\nconst html = '<!DOCTYPE html>\\n<html lang=\"en\">\\n<head>\\n<meta charset=\"UTF-8\">\\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\\n<title>Monthly Landlord Report</title>\\n<style>\\nbody { font-family: Arial, sans-serif; margin: 40px; color: #333; }\\nh1 { color: #1a1a2e; border-bottom: 2px solid #e4a400; padding-bottom: 8px; }\\nh2 { color: #1a1a2e; margin-top: 32px; }\\ntable { width: 100%; border-collapse: collapse; margin-top: 16px; }\\nth { background: #1a1a2e; color: white; padding: 10px 12px; text-align: left; }\\ntd { padding: 8px 12px; border-bottom: 1px solid #ddd; }\\ntr:nth-child(even) td { background: #f9f9f9; }\\n.financials-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-top: 16px; }\\n.fin-card { background: #f0f4ff; border-radius: 8px; padding: 16px; }\\n.fin-card .label { font-size: 12px; color: #666; text-transform: uppercase; }\\n.fin-card .value { font-size: 22px; font-weight: bold; color: #1a1a2e; margin-top: 4px; }\\n.footer { margin-top: 48px; font-size: 12px; color: #888; border-top: 1px solid #eee; padding-top: 16px; }\\n</style>\\n</head>\\n<body>\\n<h1>Monthly Property Report</h1>\\n<p>Prepared for: <strong>' + landlordName + '</strong></p>\\n<p>Period: <strong>' + reportMonth + '</strong></p>\\n\\n<h2>Your Properties</h2>\\n<table>\\n<thead><tr><th>Address</th><th>Type</th><th>Tenant</th><th>Rent</th><th>Status</th></tr></thead>\\n<tbody>' + propertyRows + '</tbody>\\n</table>\\n\\n<h2>Financial Summary</h2>\\n<div class=\"financials-grid\">\\n<div class=\"fin-card\"><div class=\"label\">Total Rent Collected</div><div class=\"value\">GHS ' + Number(financials.total_rent_collected_ghs || 0).toLocaleString() + '</div></div>\\n<div class=\"fin-card\"><div class=\"label\">Outstanding Rent</div><div class=\"value\">GHS ' + Number(financials.outstanding_rent_ghs || 0).toLocaleString() + '</div></div>\\n<div class=\"fin-card\"><div class=\"label\">Occupancy Rate</div><div class=\"value\">' + (financials.occupancy_rate_pct || 0) + '%</div></div>\\n</div>\\n\\n<div class=\"footer\">Generated ' + new Date().toLocaleDateString('en-GH', {timeZone: 'Africa/Accra'}) + ' • Real Estate Automation Platform • Confidential</div>\\n</body>\\n</html>';\n\nreturn [{ json: { html, template_kind: kind, output_filename: outputFilename } }];"
}
},
{
"id": "pd000003-0003-0003-0003-000000000003",
"name": "Render Error?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
680,
300
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "cond-no-error",
"leftValue": "={{ $json.error ?? '' }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "equals"
}
}
]
},
"options": {}
}
},
{
"id": "pd000004-0004-0004-0004-000000000004",
"name": "Return Template Error",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
920,
420
],
"parameters": {
"mode": "manual",
"duplicateItem": false,
"assignments": {
"assignments": [
{
"id": "tpl-err-success",
"name": "success",
"value": false,
"type": "boolean"
},
{
"id": "tpl-err-error",
"name": "error",
"value": "={{ $json.error ?? 'UNSUPPORTED_TEMPLATE_KIND' }}",
"type": "string"
},
{
"id": "tpl-err-pdf",
"name": "pdf_base64",
"value": "",
"type": "string"
}
]
},
"options": {}
}
},
{
"id": "pd000005-0005-0005-0005-000000000005",
"name": "Generate PDF via Browserless",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"onError": "continueErrorOutput",
"position": [
920,
200
],
"parameters": {
"method": "POST",
"url": "http://re-browserless:3000/pdf",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "={{ 'Token ' + $env.BROWSERLESS_TOKEN }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"contentType": "json",
"jsonBody": "={{ { html: $json.html, options: { format: 'A4', printBackground: true } } }}",
"options": {
"timeout": 30000,
"retry": {
"enabled": true,
"maxTries": 2,
"waitBetweenTries": 2000
},
"response": {
"response": {
"responseFormat": "file"
}
}
}
}
},
{
"id": "pd000006-0006-0006-0006-000000000006",
"name": "Log Browserless Error",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"alwaysOutputData": true,
"position": [
1160,
340
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
},
"parameters": {
"operation": "executeQuery",
"query": "INSERT INTO workflow_errors (workflow_name, execution_id, node_name, error_message, input_payload) VALUES ('subflow-pdf-generate', $1, 'Generate PDF via Browserless', $2, $3::jsonb)",
"options": {
"queryReplacement": "={{ [$execution.id, $json.message || 'Browserless HTTP error', '{\"node\":\"Generate PDF via Browserless\"}'] }}"
}
}
},
{
"id": "pd000007-0007-0007-0007-000000000007",
"name": "Return Browserless Error",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1400,
340
],
"parameters": {
"mode": "manual",
"duplicateItem": false,
"assignments": {
"assignments": [
{
"id": "bl-err-success",
"name": "success",
"value": false,
"type": "boolean"
},
{
"id": "bl-err-error",
"name": "error",
"value": "BROWSERLESS_ERROR",
"type": "string"
},
{
"id": "bl-err-pdf",
"name": "pdf_base64",
"value": "",
"type": "string"
}
]
},
"options": {}
}
},
{
"id": "pd000008-0008-0008-0008-000000000008",
"name": "Encode PDF Base64",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1160,
200
],
"parameters": {
"language": "javaScript",
"jsCode": "// The Browserless /pdf endpoint returns binary PDF data.\n// n8n surfaces binary responses as base64 in the binary property.\n// We extract it and compute byte size for the caller.\nconst binaryData = $input.item.binary;\nlet pdfBase64 = '';\nlet byteSize = 0;\n\nif (binaryData && binaryData.data) {\n pdfBase64 = binaryData.data.data || '';\n // Base64 length * 3/4 approximates byte size\n byteSize = Math.floor(pdfBase64.length * 3 / 4);\n} else if (binaryData && binaryData.file) {\n pdfBase64 = binaryData.file.data || '';\n byteSize = Math.floor(pdfBase64.length * 3 / 4);\n}\n\nconst trigger = $('PDF Generate Trigger').first().json;\nreturn [{ json: { pdf_base64: pdfBase64, byte_size: byteSize, kind: trigger.template_kind || '', output_filename: trigger.output_filename || '' } }];"
}
},
{
"id": "pd000009-0009-0009-0009-000000000009",
"name": "Return PDF Result",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1400,
200
],
"parameters": {
"mode": "manual",
"duplicateItem": false,
"assignments": {
"assignments": [
{
"id": "pdf-success",
"name": "success",
"value": true,
"type": "boolean"
},
{
"id": "pdf-base64",
"name": "pdf_base64",
"value": "={{ $json.pdf_base64 ?? '' }}",
"type": "string"
},
{
"id": "pdf-byte-size",
"name": "byte_size",
"value": "={{ $json.byte_size ?? 0 }}",
"type": "number"
},
{
"id": "pdf-kind",
"name": "kind",
"value": "={{ $json.kind ?? '' }}",
"type": "string"
}
]
},
"options": {}
}
},
{
"id": "pd000010-0010-0010-0010-000000000010",
"name": "Error Trigger",
"type": "n8n-nodes-base.errorTrigger",
"typeVersion": 1,
"position": [
200,
600
],
"parameters": {}
},
{
"id": "pd000011-0011-0011-0011-000000000011",
"name": "Log Unhandled Error",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.6,
"alwaysOutputData": true,
"position": [
440,
600
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
},
"parameters": {
"operation": "executeQuery",
"query": "INSERT INTO workflow_errors (workflow_name, execution_id, node_name, error_message, error_stack, input_payload) VALUES ('subflow-pdf-generate', $1, $2, $3, $4, $5::jsonb)",
"options": {
"queryReplacement": "={{ [$json.execution.id, $json.execution.lastNodeExecuted || 'unknown', $json.execution.error.message || 'unknown error', ($json.execution.error.stack ? $json.execution.error.stack.split('\\n')[0] : ''), '{\"source\":\"error_trigger\"}'] }}"
}
}
}
],
"connections": {
"PDF Generate Trigger": {
"main": [
[
{
"node": "Render HTML Template",
"type": "main",
"index": 0
}
]
]
},
"Render HTML Template": {
"main": [
[
{
"node": "Render Error?",
"type": "main",
"index": 0
}
]
]
},
"Render Error?": {
"main": [
[
{
"node": "Generate PDF via Browserless",
"type": "main",
"index": 0
}
],
[
{
"node": "Return Template Error",
"type": "main",
"index": 0
}
]
]
},
"Generate PDF via Browserless": {
"main": [
[
{
"node": "Encode PDF Base64",
"type": "main",
"index": 0
}
],
[
{
"node": "Log Browserless Error",
"type": "main",
"index": 0
}
]
]
},
"Encode PDF Base64": {
"main": [
[
{
"node": "Return PDF Result",
"type": "main",
"index": 0
}
]
]
},
"Log Browserless Error": {
"main": [
[
{
"node": "Return Browserless Error",
"type": "main",
"index": 0
}
]
]
},
"Error Trigger": {
"main": [
[
{
"node": "Log Unhandled Error",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"tags": [
{
"name": "real-estate-automation"
},
{
"name": "subflow"
},
{
"name": "version-1.0"
}
]
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
postgres
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Subflow — PDF Generate. Uses executeWorkflowTrigger, httpRequest, postgres, errorTrigger. Event-driven trigger; 11 nodes.
Source: https://github.com/jameszokah/real-estate-n8n-workflow/blob/ff6c75dac6b332a260a52efcb695ac4f3c962ed3/n8n-workflows/subflows/pdf-generate.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Agendamiento_v2. Uses n8n-nodes-evolution-api, redis, httpRequest, executeWorkflowTrigger. Event-driven trigger; 59 nodes.
Cancelacion_v2. Uses executeWorkflowTrigger, redis, httpRequest, n8n-nodes-evolution-api. Event-driven trigger; 46 nodes.
Youtube Searcher. Uses splitInBatches, httpRequest, manualTrigger, executeWorkflowTrigger. Event-driven trigger; 21 nodes.
Log errors and avoid sending too many emails. Uses errorTrigger, postgres, stickyNote, emailSend. Event-driven trigger; 16 nodes.
Most of the time, it’s necessary to log all errors that occur. However, in some cases, a scheduled task or service consuming excessive resources might trigger a surge of errors.