AutomationFlowsEmail & Gmail › Decrypt Legacy PDF Archives with HTML to Pdf, Postgresql and Google Drive

Decrypt Legacy PDF Archives with HTML to Pdf, Postgresql and Google Drive

ByJitesh Dugar @jiteshdugar on n8n.io

This is a high-security enterprise solution for LegalOps and Compliance departments. It automates the high-stakes process of decrypting batches of legacy, password-protected PDF assets to facilitate forensic indexing and search discovery. By utilizing the HTML to PDF (Unlock)…

Manual trigger★★★★☆ complexity19 nodesPostgresSlackGoogle DriveN8N Nodes HtmlcsstopdfGmail
Email & Gmail Trigger: Manual Nodes: 19 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #13032 — we link there as the canonical source.

This workflow follows the Gmail → Google Drive 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 →

Download .json
{
  "nodes": [
    {
      "id": "6030c13d-17ff-4f71-9ce2-0edb90eefe70",
      "name": "Sticky_Quarantine",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        544,
        2000
      ],
      "parameters": {
        "color": 7,
        "width": 578,
        "height": 412,
        "content": "## \u26a0\ufe0f ERROR HANDLING\nQuarantine zone for failed decryptions, invalid keys, and corrupted files. All failures logged for manual review."
      },
      "typeVersion": 1
    },
    {
      "id": "21d5bd3f-de38-42a3-935b-9f0a8851db27",
      "name": "IF: File Validator",
      "type": "n8n-nodes-base.if",
      "position": [
        -64,
        1632
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "string",
                "operation": "contains"
              },
              "leftValue": "={{ $json.mimeType }}",
              "rightValue": "application/pdf"
            },
            {
              "operator": {
                "type": "number",
                "operation": "smaller"
              },
              "leftValue": "={{ $json.fileSize }}",
              "rightValue": "104857600"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "7adf83f8-48ab-421e-aac3-1f2fe3c42c2c",
      "name": "IF: Key Found",
      "type": "n8n-nodes-base.if",
      "position": [
        416,
        1632
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              },
              "leftValue": "={{ $json.password }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "326cdef0-edee-44b2-a7ef-19e77d116c4c",
      "name": "Postgres: Audit Logger",
      "type": "n8n-nodes-base.postgres",
      "position": [
        976,
        1584
      ],
      "parameters": {
        "query": "INSERT INTO audit_log \n(custody_id, file_name, original_hash, unlocked_hash, file_size, \n intake_timestamp, unlock_timestamp, processing_time_ms, \n integrity_status, processing_status, key_id, encryption_type)\nVALUES \n('{{ $json.custodyID }}', '{{ $json.fileName }}', '{{ $json.originalHash }}', \n '{{ $json.unlockedHash }}', {{ $json.fileSize }}, \n '{{ $json.intakeTimestamp }}', '{{ $json.unlockTimestamp }}', \n {{ $json.processingTimeMs }}, '{{ $json.integrityStatus }}', \n '{{ $json.processingStatus }}', '{{ $json.key_id }}', '{{ $json.encryption_type }}')\nRETURNING audit_id;",
        "options": {},
        "operation": "executeQuery"
      },
      "typeVersion": 2.4
    },
    {
      "id": "4ae38931-c4f9-43eb-872f-5c8e4bff6704",
      "name": "Postgres: Quarantine Log",
      "type": "n8n-nodes-base.postgres",
      "position": [
        592,
        2128
      ],
      "parameters": {
        "query": "INSERT INTO quarantine_log \n(custody_id, file_name, failure_reason, failure_timestamp, original_hash, quarantine_status)\nVALUES \n('{{ $json.custodyID }}', '{{ $json.fileName }}', \n '{{ $json.failureReason || \"Key not found in vault\" }}', \n '{{ $now.toISO() }}', '{{ $json.originalHash }}', 'PENDING_REVIEW')\nRETURNING quarantine_id;",
        "options": {},
        "operation": "executeQuery"
      },
      "typeVersion": 2.4
    },
    {
      "id": "4e043139-b7eb-4084-90d6-f18e1b9f395b",
      "name": "Slack: Quarantine Alert",
      "type": "n8n-nodes-base.slack",
      "position": [
        752,
        2192
      ],
      "parameters": {
        "operation": "create"
      },
      "typeVersion": 2.1
    },
    {
      "id": "93291786-5874-46e1-98bf-d604ed8ed951",
      "name": "Google Drive: Quarantine Vault",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        944,
        2128
      ],
      "parameters": {
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {},
        "folderId": "QUARANTINE_VAULT"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "d884a84e-3c81-434f-9d34-e49a1b3d511e",
      "name": "Postgres: Validation Failure",
      "type": "n8n-nodes-base.postgres",
      "position": [
        48,
        1776
      ],
      "parameters": {
        "query": "INSERT INTO file_validation_failures \n(custody_id, file_name, mime_type, file_size, failure_reason, timestamp)\nVALUES \n('{{ $json.custodyID }}', '{{ $json.fileName }}', '{{ $json.mimeType }}', \n {{ $json.fileSize }}, 'Invalid file type or size exceeds limit', '{{ $now.toISO() }}')",
        "options": {},
        "operation": "executeQuery"
      },
      "typeVersion": 2.4
    },
    {
      "id": "861fe3de-d59a-40e3-8815-19e1510b49ef",
      "name": "Sticky_Intake1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -288,
        1312
      ],
      "parameters": {
        "color": 7,
        "width": 450,
        "height": 650,
        "content": "## \ud83d\udee1\ufe0f PHASE 1: Intake & Custody\nFetches encrypted files. Generates an initial SHA-256 hash to track the original state of the legacy data before any modification. Validates file types and sizes."
      },
      "typeVersion": 1
    },
    {
      "id": "35459a7b-b829-429e-bf79-0fbc5b36aab8",
      "name": "Sticky_Unlock1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        192,
        1312
      ],
      "parameters": {
        "color": 7,
        "width": 600,
        "height": 650,
        "content": "## \ud83d\udd10 PHASE 2: Key Vault & Unlock\nMatches file metadata with the SQL Key Vault. **Unlock Node** strips protection. Parallel branch handles 'Key Failure' quarantine logic with retry mechanism."
      },
      "typeVersion": 1
    },
    {
      "id": "b23463f3-f25a-491b-98f7-ae4ce6824d12",
      "name": "Sticky_Audit1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        832,
        1312
      ],
      "parameters": {
        "color": 7,
        "width": 646,
        "height": 650,
        "content": "## \u2696\ufe0f PHASE 3: Audit & Archival\nLogs forensic metadata to Postgres. Moves decrypted assets to the Secure Audit Vault. Notifies Legal of the processed batch with comprehensive reports."
      },
      "typeVersion": 1
    },
    {
      "id": "2f5a4d50-dcf1-42aa-99c1-9d9dfcc145b9",
      "name": "Code: Forensic Intake1",
      "type": "n8n-nodes-base.code",
      "position": [
        -224,
        1632
      ],
      "parameters": {
        "jsCode": "// Code Node: SHA-256 Fingerprinting & Metadata Extraction\nconst crypto = require('crypto');\nconst items = $input.all();\n\nreturn items.map(item => {\n    const binary = item.binary.data;\n    const buffer = Buffer.from(binary.data, 'base64');\n    \n    // Generate forensic hash\n    item.json.originalHash = crypto.createHash('sha256').update(buffer).digest('hex');\n    \n    // Create custody ID with timestamp\n    const timestamp = new Date().toISOString().replace(/[-:]/g, '').split('.')[0];\n    item.json.custodyID = `DISCOVERY-${timestamp}-${Math.random().toString(36).substring(2, 8).toUpperCase()}`;\n    \n    // Extract metadata\n    item.json.fileSize = buffer.length;\n    item.json.intakeTimestamp = new Date().toISOString();\n    item.json.mimeType = binary.mimeType || 'application/pdf';\n    item.json.fileName = item.json.name || 'unknown.pdf';\n    item.json.processingStatus = 'INTAKE_COMPLETE';\n    \n    return item;\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "c51e84c6-2485-4439-9246-60179d39b7aa",
      "name": "Postgres: Key Vault1",
      "type": "n8n-nodes-base.postgres",
      "position": [
        256,
        1632
      ],
      "parameters": {
        "query": "SELECT password, key_id, encryption_type, created_date \nFROM legacy_keys \nWHERE filename = '{{ $json.fileName }}' \nAND active = true \nORDER BY created_date DESC \nLIMIT 1",
        "options": {},
        "operation": "executeQuery"
      },
      "typeVersion": 2.4
    },
    {
      "id": "7592e1f2-15c6-4bb0-a498-37e72068d58d",
      "name": "HTML to PDF: Unlock Engine1",
      "type": "n8n-nodes-htmlcsstopdf.htmlcsstopdf",
      "position": [
        608,
        1520
      ],
      "parameters": {
        "resource": "pdfManipulation",
        "operation": "unlockPdf"
      },
      "credentials": {
        "htmlcsstopdfApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "867b0eda-793b-4c74-9876-048eec7dbf78",
      "name": "Code: Integrity Guard1",
      "type": "n8n-nodes-base.code",
      "position": [
        816,
        1552
      ],
      "parameters": {
        "jsCode": "// Code Node: Post-Unlock Integrity Validation\nconst item = $input.first();\nconst crypto = require('crypto');\n\ntry {\n    const buffer = Buffer.from(item.binary.data.data, 'base64');\n    \n    // Generate hash of unlocked file\n    item.json.unlockedHash = crypto.createHash('sha256').update(buffer).digest('hex');\n    item.json.unlockedSize = buffer.length;\n    item.json.unlockTimestamp = new Date().toISOString();\n    \n    // Verify unlock was successful (hashes should differ)\n    if (item.json.originalHash === item.json.unlockedHash) {\n        item.json.integrityStatus = 'WARNING: Hashes identical - possible unlock failure';\n        item.json.processingStatus = 'UNLOCK_SUSPICIOUS';\n    } else {\n        item.json.integrityStatus = 'VERIFIED: File successfully decrypted';\n        item.json.processingStatus = 'UNLOCK_SUCCESS';\n    }\n    \n    // Calculate processing time\n    const intakeTime = new Date(item.json.intakeTimestamp);\n    const unlockTime = new Date(item.json.unlockTimestamp);\n    item.json.processingTimeMs = unlockTime - intakeTime;\n    \n} catch (error) {\n    item.json.integrityStatus = `ERROR: ${error.message}`;\n    item.json.processingStatus = 'UNLOCK_FAILED';\n}\n\nreturn item;"
      },
      "typeVersion": 2
    },
    {
      "id": "db664b16-1a47-45fc-bacd-c790c2ecea41",
      "name": "Google Drive: Audit Vault1",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        1136,
        1520
      ],
      "parameters": {
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {},
        "folderId": "={{ 'AUDIT_VAULT_' + $json.custodyID.split('-')[1] }}"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "4e4d3ab8-ee2c-470b-92ce-fd51f8f5f8b2",
      "name": "Slack: Compliance Log1",
      "type": "n8n-nodes-base.slack",
      "position": [
        1312,
        1456
      ],
      "parameters": {
        "operation": "create",
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "518a0b1d-c397-4332-b694-c33b3a0310d2",
      "name": "Gmail: Legal Summary1",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1312,
        1648
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "=**Legacy Document Decryption Report**\n\n**Custody ID:** {{ $json.custodyID }}\n**File Name:** {{ $json.fileName }}\n**Original Hash:** {{ $json.originalHash }}\n**Unlocked Hash:** {{ $json.unlockedHash }}\n**File Size:** {{ ($json.fileSize / 1024 / 1024).toFixed(2) }} MB\n\n**Processing Details:**\n- Intake: {{ $json.intakeTimestamp }}\n- Unlock: {{ $json.unlockTimestamp }}\n- Duration: {{ $json.processingTimeMs }}ms\n\n**Status:** {{ $json.processingStatus }}\n**Integrity:** {{ $json.integrityStatus }}\n\n**Audit Trail:** Logged in database as Audit ID {{ $json.audit_id }}\n**Vault Location:** Google Drive folder AUDIT_VAULT_{{ $json.custodyID.split('-')[1] }}\n\nThis file is now available for legal discovery search indexing.\n\n---\nAutomated Legal Compliance System",
        "options": {},
        "subject": "=\u2705 Decryption Complete: {{ $json.custodyID }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "4c0674f1-b0cb-4aef-bf43-401b686e66bd",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -864,
        656
      ],
      "parameters": {
        "width": 464,
        "height": 624,
        "content": "## \u2696\ufe0f Sovereign Decryption & Compliance Gateway v10.0\n\nIndustrial-grade pipeline for the bulk unlocking, indexing, and forensic archival of legacy protected assets.\n\n### \u2699\ufe0f Core Sovereign Logic\n* **PHASE 1: Forensic Intake:** Validates PDF integrity and generates SHA-256 fingerprints to maintain document \"Chain of Custody.\"\n* **PHASE 2: Key Vault Orchestration:** Queries PostgreSQL to match legacy filenames with historical encryption passwords.\n* **PHASE 3: Atomic Unlock:** Compulsory use of **HTML to PDF (Unlock)** to strip passwords and restore full searchability.\n* **PHASE 4: Integrity Guard:** Validates decryption success via binary comparison and logs processing metrics.\n* **PHASE 5: Compliance Archival:** Vaults files in Google Drive and triggers multi-channel alerts (Slack/Gmail).\n\n### \ud83d\udccb Setup & Prerequisites\n1. **Compulsory Node:** **HTML to PDF (Unlock)** for core decryption.\n2. **Databases:** PostgreSQL tables for `legacy_keys`, `audit_log`, and `quarantine_log`.\n3. **Folders:** Define Drive IDs for the Audit Vault and Quarantine Zone.\n\n**Metrics:** `Unlock_Success_Rate`, `Processing_Latency`, `Compliance_Audit_Hash`."
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "IF: Key Found": {
      "main": [
        [
          {
            "node": "HTML to PDF: Unlock Engine1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Postgres: Quarantine Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF: File Validator": {
      "main": [
        [
          {
            "node": "Postgres: Key Vault1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Postgres: Validation Failure",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Postgres: Key Vault1": {
      "main": [
        [
          {
            "node": "IF: Key Found",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: Forensic Intake1": {
      "main": [
        [
          {
            "node": "IF: File Validator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: Integrity Guard1": {
      "main": [
        [
          {
            "node": "Postgres: Audit Logger",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Postgres: Audit Logger": {
      "main": [
        [
          {
            "node": "Google Drive: Audit Vault1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack: Quarantine Alert": {
      "main": [
        [
          {
            "node": "Google Drive: Quarantine Vault",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Postgres: Quarantine Log": {
      "main": [
        [
          {
            "node": "Slack: Quarantine Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Drive: Audit Vault1": {
      "main": [
        [
          {
            "node": "Slack: Compliance Log1",
            "type": "main",
            "index": 0
          },
          {
            "node": "Gmail: Legal Summary1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTML to PDF: Unlock Engine1": {
      "main": [
        [
          {
            "node": "Code: Integrity Guard1",
            "type": "main",
            "index": 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.

Pro

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

About this workflow

This is a high-security enterprise solution for LegalOps and Compliance departments. It automates the high-stakes process of decrypting batches of legacy, password-protected PDF assets to facilitate forensic indexing and search discovery. By utilizing the HTML to PDF (Unlock)…

Source: https://n8n.io/workflows/13032/ — original creator credit. Request a take-down →

More Email & Gmail workflows → · Browse all categories →

Related workflows

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

Email & Gmail

Achieve a high-security, compliant payroll distribution system by using this automation to encrypt sensitive documents and manage delivery failures. This workflow provides an enterprise-grade solution

Google Drive, Google Sheets, N8N Nodes Htmlcsstopdf +2
Email & Gmail

This workflow automates the complete end-to-end processing of daily revenue transactions for finance and accounting teams. It systematically retrieves, validates, and standardizes transaction data fro

HTTP Request, Gmail, Google Drive +2
Email & Gmail

Suspicious_login_detection. Uses postgres, httpRequest, noOp, html. Webhook trigger; 43 nodes.

Postgres, HTTP Request, Gmail +1
Email & Gmail

This n8n workflow is designed for security monitoring and incident response when suspicious login events are detected. It can be initiated either manually from within the n8n UI for testing or automat

Postgres, HTTP Request, Gmail +1
Email & Gmail

Categories: Payments, Project Operations, Client Onboarding

Stripe Trigger, Google Drive, ClickUp +4