This workflow corresponds to n8n.io template #6558 — we link there as the canonical source.
This workflow follows the Chainllm → OpenAI Chat 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 →
{
"id": "gQhYUHmv0KdIsS6n",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Analytics",
"tags": [],
"nodes": [
{
"id": "67a02659-4fe1-4394-89e8-03c4819dc1ff",
"name": "Email Trigger (IMAP)",
"type": "n8n-nodes-base.emailReadImap",
"disabled": true,
"position": [
-3552,
-144
],
"parameters": {
"options": {},
"downloadAttachments": true
},
"credentials": {
"imap": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "c3ebd896-96af-4c81-a916-aa19df57c8d3",
"name": "Discord",
"type": "n8n-nodes-base.discord",
"position": [
-832,
-208
],
"parameters": {
"files": {
"values": [
{}
]
},
"content": "=# :rotating_light: New Invoice: {{ $json.output.invoiceDetails.invoiceNumber }}\nBuyer: {{ $json.output.invoiceDetails.recipient.name }}\n Address: {{ $json.output.invoiceDetails.recipient.address }}\n TAX ID: {{ $json.output.invoiceDetails.recipient.taxNumber }}\n\nSeller: {{ $json.output.invoiceDetails.issuer.name }}\n Address: {{ $json.output.invoiceDetails.issuer.address }}\n TAX ID: {{ $json.output.invoiceDetails.issuer.taxNumber }}\n\nBank: {{ $json.output.invoiceDetails.bankAccountDetails.bankName }}\nAccount Number: {{ $json.output.invoiceDetails.bankAccountDetails.accountNumber }}\n\nPayment Due Date: {{ $json.output.invoiceDetails.paymentDueDate }}\n:dollar: Total Due: {{ $json.output.invoiceDetails.totalToPay }} {{ $json.output.invoiceDetails.currency }} ",
"options": {},
"authentication": "webhook"
},
"credentials": {
"discordWebhookApi": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "e3754a1f-ea3b-4027-91ee-0d96c38270f2",
"name": "Basic LLM Chain",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
-2144,
-272
],
"parameters": {
"text": "Determine whether the provided text is an invoice. If it is, extract the following details: the invoice number, the items listed on the invoice along with their prices, the recipient and the issuer, the payment due date in the format dd-MM-yyyy, bank account details, the total net and gross amounts, the total amount to pay, and the currency of the invoice",
"messages": {
"messageValues": [
{
"type": "HumanMessagePromptTemplate",
"message": "= Filename: {{ $binary.data.fileName }} Data: {{ $json.text }}"
}
]
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 1.5
},
{
"id": "0e75a30b-b631-45c1-a957-71e8a20c9f9b",
"name": "OpenAI Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
-2192,
64
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4o-mini"
},
"options": {}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.2
},
{
"id": "c9555958-80ce-40d9-81f7-095f5fd59225",
"name": "Structured Output Parser",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
-1920,
64
],
"parameters": {
"jsonSchemaExample": "{\n \"fileName\": \"test.pdf\",\n \"isInvoice\": true,\n \"invoiceDetails\": {\n \"invoiceNumber\": \"INV-2025-00123\",\n \"issuer\": {\n \"name\": \"ABC Company\",\n \"address\": \"123 Business St, Warsaw, Poland\",\n \"taxNumber\": \"547-896-544-52\"\n },\n \"recipient\": {\n \"name\": \"XYZ Ltd.\",\n \"address\": \"456 Client Rd, Krakow, Poland\",\n \"taxNumber\": \"54789654452\"\n },\n \"items\": [\n {\n \"description\": \"Consulting Services\",\n \"quantity\": 10,\n \"unitPrice\": 100.00,\n \"totalPrice\": 1000.00\n }\n ],\n \"totalNet\": 1500.00,\n \"totalGross\": 1845.00,\n \"totalToPay\": 1845.00,\n \"currency\": \"PLN\",\n \"paymentDueDate\": \"2025-03-15\",\n \"bankAccountDetails\": {\n \"bankName\": \"Example Bank\",\n \"accountNumber\": \"PL6110901014+1234567890\"\n }\n }\n}"
},
"typeVersion": 1.2
},
{
"id": "9444f054-ab31-4723-ad9b-43907f71d81a",
"name": "Code",
"type": "n8n-nodes-base.code",
"position": [
-2928,
-160
],
"parameters": {
"jsCode": "const newItems = [];\n\nfor (const key of Object.keys(items[0].binary)) {\n const binaryData = items[0].binary[key];\n\n // Check if the file is a PDF\n if (binaryData.mimeType === \"application/pdf\") {\n newItems.push({\n json: { fileName: key },\n binary: {\n data: binaryData\n }\n });\n }\n}\n\nreturn newItems;\n"
},
"typeVersion": 2
},
{
"id": "42bf86b9-14f3-4ba1-b0f8-60ae8650e5f0",
"name": "Extract from File1",
"type": "n8n-nodes-base.extractFromFile",
"position": [
-2464,
-176
],
"parameters": {
"options": {
"keepSource": "binary"
},
"operation": "pdf"
},
"typeVersion": 1
},
{
"id": "eeefc5f4-3c7f-49d7-8b21-3a98462c702e",
"name": "If",
"type": "n8n-nodes-base.if",
"position": [
-3216,
-144
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "09333af2-d5b3-4525-8f30-568b666263d9",
"operator": {
"type": "object",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $binary }}",
"rightValue": ""
},
{
"id": "a9cdfc35-c960-49dc-aad6-861814cee8ff",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "f7a051ed-f99a-4a5a-b6b7-b32f8e2f5fdf",
"name": "Merge",
"type": "n8n-nodes-base.merge",
"position": [
-1584,
-192
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineByPosition"
},
"typeVersion": 3
},
{
"id": "7e57500b-1a8f-4854-8221-7edc2336a9dd",
"name": "If1",
"type": "n8n-nodes-base.if",
"position": [
-2704,
-160
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "636c5c8b-abf2-4da0-84e0-a11aad1daee4",
"operator": {
"type": "object",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $binary }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "250cf1ac-90bc-4868-a8c6-7898c393d790",
"name": "If2",
"type": "n8n-nodes-base.if",
"position": [
-1296,
-192
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "c75b9dda-ce11-41f1-8bcc-48b32406c4f4",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.output.isInvoice }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "3406be94-e2e0-4a3d-af3d-d6c5c8cd1279",
"name": "Prepare Company Data",
"type": "n8n-nodes-base.set",
"position": [
-1280,
192
],
"parameters": {
"values": {
"string": [
{
"name": "issuer_tax_number",
"value": "={{ $json.invoiceDetails.issuer.taxNumber }}"
},
{
"name": "issuer_name",
"value": "={{ $json.invoiceDetails.issuer.name }}"
},
{
"name": "issuer_address",
"value": "={{ $json.invoiceDetails.issuer.address }}"
}
]
},
"options": {}
},
"typeVersion": 2
},
{
"id": "7b540304-d0aa-40c9-b40b-6c6dc15ea557",
"name": "Check Company",
"type": "n8n-nodes-base.postgres",
"position": [
-1072,
192
],
"parameters": {
"query": "=SELECT id FROM company WHERE tax_number = '{{ $json.issuer_tax_number }}';",
"options": {},
"operation": "executeQuery"
},
"credentials": {
"postgres": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "ee7ca336-4f6b-4f2e-94e6-7526896e5c52",
"name": "IF Company Does Not Exist",
"type": "n8n-nodes-base.if",
"position": [
-880,
192
],
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $items('Check Company')[0].json.length }}"
}
]
}
},
"typeVersion": 1
},
{
"id": "180d3810-a537-447b-b6db-78fe01daebe7",
"name": "Add Company",
"type": "n8n-nodes-base.postgres",
"position": [
-672,
80
],
"parameters": {
"table": "company",
"schema": "public",
"options": {}
},
"credentials": {
"postgres": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "e91420fb-0b15-4067-99e9-bf1f5c493bc6",
"name": "Set Existing Company ID",
"type": "n8n-nodes-base.set",
"position": [
-576,
288
],
"parameters": {
"values": {
"number": [
{
"name": "company_id",
"value": "={{ $items('Check Company')[0].json[0].id }}"
}
]
},
"options": {}
},
"typeVersion": 2
},
{
"id": "4dfdc187-084e-4500-9249-fa2d0df5064c",
"name": "Set New Company ID",
"type": "n8n-nodes-base.set",
"position": [
-480,
80
],
"parameters": {
"values": {
"number": [
{
"name": "company_id",
"value": "={{ $json.id }}"
}
]
},
"options": {}
},
"typeVersion": 2
},
{
"id": "a854c3c2-3375-4f80-84e1-fc327c30dce8",
"name": "Merge Company ID",
"type": "n8n-nodes-base.merge",
"position": [
-272,
192
],
"parameters": {},
"typeVersion": 2
},
{
"id": "76edc407-fac3-43f2-902e-c8e3de3031c8",
"name": "Check Invoice",
"type": "n8n-nodes-base.postgres",
"position": [
-80,
192
],
"parameters": {
"query": "=SELECT id FROM invoice WHERE invoice_number = '{{ $json.invoiceDetails.invoiceNumber }}' AND company_id = {{ $json.company_id }};",
"options": {},
"operation": "executeQuery"
},
"credentials": {
"postgres": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "e82706a5-0415-49d1-aa10-280d8b0ed9a9",
"name": "IF Invoice Does Not Exist",
"type": "n8n-nodes-base.if",
"position": [
144,
192
],
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $items('Check Invoice')[0].json.length }}",
"value2": 0
}
]
}
},
"typeVersion": 1
},
{
"id": "cf030538-7ab2-4199-979e-82fabc83a8a6",
"name": "Add Invoice",
"type": "n8n-nodes-base.postgres",
"position": [
416,
80
],
"parameters": {
"table": "invoice",
"schema": "public",
"options": {}
},
"credentials": {
"postgres": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "a25f62a0-4334-46aa-8ad7-fbcfb59b8910",
"name": "Invoice Exists",
"type": "n8n-nodes-base.set",
"position": [
416,
288
],
"parameters": {
"values": {
"string": [
{
"name": "status",
"value": "Invoice already exists"
}
]
},
"options": {}
},
"typeVersion": 2
},
{
"id": "5e8aa135-8ee3-4ced-b239-f35aeb5eacbb",
"name": "Merge End",
"type": "n8n-nodes-base.merge",
"position": [
752,
192
],
"parameters": {},
"typeVersion": 2
},
{
"id": "d4dd9cd0-2ef9-4038-9cb8-8779edbf1887",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3712,
-560
],
"parameters": {
"color": 6,
"width": 432,
"height": 672,
"content": "### \ud83d\udce5 Email Trigger (IMAP)\n\nThis node listens for new emails in your inbox and fetches PDF attachments.\n\n**Setup Instructions:**\n- Go to **Credentials** and create new IMAP credentials \n- Fill in: IMAP host, port, email address, and password \n- Optionally filter emails (e.g. only those with PDF attachments)\n"
},
"typeVersion": 1
},
{
"id": "33a8dc7b-e181-495d-bfb3-f352ff883c17",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2256,
-576
],
"parameters": {
"color": 6,
"width": 816,
"height": 800,
"content": "### \ud83e\udd16 AI Data Extraction (Basic LLM Chain)\n\nThis node sends the text from the PDF to an AI model (e.g. GPT-4o) to extract structured invoice data.\n\n**Setup Instructions:**\n- Provide a clear prompt describing which data to extract \n- Example: invoice number, dates, seller, buyer, tax number, total, currency \n- Use JSON as the expected response format\n\n**Note:** \nIf the model fails to read some invoices (e.g. unusual layout, languages, or formats), try improving or customizing the prompt.\n"
},
"typeVersion": 1
},
{
"id": "bacc660a-5fd2-463b-ba2e-54cb94a1bdde",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2256,
256
],
"parameters": {
"color": 3,
"width": 432,
"height": 304,
"content": "### \ud83d\udd11 OpenAI API (LLM Provider)\n\nThis node connects to OpenAI (or another AI provider) and sends the invoice content for analysis.\n\n**Setup Instructions:**\n- Log in to [OpenAI](https://platform.openai.com) or your provider \n- Generate an API key \n- In **n8n**, create new credentials for the OpenAI node \n- Select the correct model (`gpt-4o`, `gpt-3.5-turbo`, etc.)\n\n**Tip:** You can replace OpenAI with Anthropic, Mistral, or any compatible LLM node.\n"
},
"typeVersion": 1
},
{
"id": "970d4c31-8c78-482f-b10b-e3350bea6634",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1056,
-576
],
"parameters": {
"color": 6,
"width": 560,
"height": 544,
"content": "### \ud83d\udce4 Chat Notification (Discord Webhook)\n\nThis node sends a summary of the extracted invoice to your Discord channel.\n\n**Setup Instructions:**\n- In Discord: \n Go to Server Settings \u2192 Integrations \u2192 Webhooks \u2192 Create Webhook \n- Choose the target channel and copy the webhook URL \n- Paste the URL into this node in n8n\n\n**Alternative:** \nSwap with Slack, Telegram, MS Teams, or another notification service.\n"
},
"typeVersion": 1
},
{
"id": "7f9798aa-bc2c-4653-9521-d2530e3917d9",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
-368,
384
],
"parameters": {
"color": 3,
"width": 704,
"height": 304,
"content": "### \u2757 Don't forget to set your PostgreSQL credentials\n\nIn every PostgreSQL-related node, you must manually select your own credentials from the dropdown.\n\n**Instructions:**\n- Click on the node \n- Go to the **\"Credentials\"** section \n- Select your custom PostgreSQL connection (e.g. `My Production DB`) \n- Make sure the credentials have access to the correct database and tables\n\n\u26a0\ufe0f The workflow will fail if no credentials are selected or if the connection is invalid.\n"
},
"typeVersion": 1
},
{
"id": "27a3576a-2946-41ee-98af-be1e68a1011a",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"position": [
544,
384
],
"parameters": {
"color": 6,
"width": 544,
"height": 320,
"content": "### \ud83d\udd00 Merge Node\n\nThis node merges metadata from the email (e.g., subject, sender) with the AI-extracted invoice content.\n\n**Setup Instructions:**\n- Input 1 \u2192 AI output \n- Input 2 \u2192 Email data \n- Set **Merge Strategy** to \u201cMerge By Index\u201d (if both inputs are aligned)\n\n**Use case:** \nAttach additional metadata (e.g., sender email) to your final record or notification.\n"
},
"typeVersion": 1
},
{
"id": "ccb75df0-4db9-40e1-ab87-11914e81d36f",
"name": "Sticky Note10",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1328,
384
],
"parameters": {
"color": 6,
"width": 624,
"height": 512,
"content": "### \ud83d\uddc3\ufe0f PostgreSQL Insert\n\nThis node writes the extracted data into the `company` and `invoice` tables in your PostgreSQL database.\n\n**Setup Instructions:**\n- Ensure your database has the correct schema (see SQL setup) \n- Create credentials in **n8n** for your database \n- Map each extracted field to the corresponding column \n- Use \u201cInsert\u201d or \u201cInsert & Update\u201d depending on your logic\n\n**Alternative:** \nYou can replace this with MySQL, Airtable, Supabase, or Google Sheets.\n"
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "5729a984-3945-4991-a780-0077ab8f919f",
"connections": {
"If": {
"main": [
[
{
"node": "Code",
"type": "main",
"index": 0
}
]
]
},
"If1": {
"main": [
[
{
"node": "Extract from File1",
"type": "main",
"index": 0
}
]
]
},
"If2": {
"main": [
[
{
"node": "Discord",
"type": "main",
"index": 0
},
{
"node": "Prepare Company Data",
"type": "main",
"index": 0
}
]
]
},
"Code": {
"main": [
[
{
"node": "If1",
"type": "main",
"index": 0
}
]
]
},
"Merge": {
"main": [
[
{
"node": "If2",
"type": "main",
"index": 0
}
]
]
},
"Add Company": {
"main": [
[
{
"node": "Set New Company ID",
"type": "main",
"index": 0
}
]
]
},
"Add Invoice": {
"main": [
[
{
"node": "Merge End",
"type": "main",
"index": 0
}
]
]
},
"Check Company": {
"main": [
[
{
"node": "IF Company Does Not Exist",
"type": "main",
"index": 0
}
]
]
},
"Check Invoice": {
"main": [
[
{
"node": "IF Invoice Does Not Exist",
"type": "main",
"index": 0
}
]
]
},
"Invoice Exists": {
"main": [
[
{
"node": "Merge End",
"type": "main",
"index": 0
}
]
]
},
"Basic LLM Chain": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"Merge Company ID": {
"main": [
[
{
"node": "Check Invoice",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "Basic LLM Chain",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Extract from File1": {
"main": [
[
{
"node": "Basic LLM Chain",
"type": "main",
"index": 0
},
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
},
"Set New Company ID": {
"main": [
[
{
"node": "Merge Company ID",
"type": "main",
"index": 0
}
]
]
},
"Email Trigger (IMAP)": {
"main": [
[
{
"node": "If",
"type": "main",
"index": 0
}
]
]
},
"Prepare Company Data": {
"main": [
[
{
"node": "Check Company",
"type": "main",
"index": 0
}
]
]
},
"Set Existing Company ID": {
"main": [
[
{
"node": "Merge Company ID",
"type": "main",
"index": 0
}
]
]
},
"Structured Output Parser": {
"ai_outputParser": [
[
{
"node": "Basic LLM Chain",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"IF Company Does Not Exist": {
"main": [
[
{
"node": "Add Company",
"type": "main",
"index": 0
}
],
[
{
"node": "Set Existing Company ID",
"type": "main",
"index": 0
}
]
]
},
"IF Invoice Does Not Exist": {
"main": [
[
{
"node": "Add Invoice",
"type": "main",
"index": 0
}
],
[
{
"node": "Invoice Exists",
"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.
discordWebhookApiimapopenAiApipostgres
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Automatically process PDF invoices directly from your email inbox. This workflow uses AI to extract key data, saves it to a PostgreSQL database, and instantly notifies you about the new document in your preferred chat application.
Source: https://n8n.io/workflows/6558/ — 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.
Self-Hosted
This workflow automates the process of analyzing emails and their attachments (PDFs and images) using AI models (DeepSeek, Gemini, and OpenRouter). It extracts and summarizes the content of emails and
This workflow is designed for Customer Success Managers (CSM), sales, support, or marketing teams using HubSpot CRM who want to automate customer engagement tracking when new emails arrive. It’s ideal
Complete PostgreSQL-backed system: Keyword scoring → AI research → Multi-part content generation → fal.ai Nano Banana image generation → WordPress publishing
This workflow is perfect for creators, solopreneurs, and personal brands who want to consistently publish bold, high-performing content on X (Twitter) — without writing a single line themselves. After