This workflow corresponds to n8n.io template #7880 — we link there as the canonical source.
This workflow follows the Chainllm → Google Sheets 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": "usFtj9NVTTqcIUFw",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "TyphoonV2.2 Multi-File & Multi-Page & with AI",
"tags": [],
"nodes": [
{
"id": "bcda99fe-5a10-4c43-af9d-3510ff00fb5e",
"name": "When clicking \u2018Test workflow\u2019",
"type": "n8n-nodes-base.manualTrigger",
"position": [
912,
208
],
"parameters": {},
"typeVersion": 1
},
{
"id": "06b15c69-b854-4956-a6fc-3355f1a72427",
"name": "Extract Text with Typhoon OCR",
"type": "n8n-nodes-base.executeCommand",
"position": [
752,
640
],
"parameters": {
"command": "=python -c \"import sys, os; os.environ['TYPHOON_OCR_API_KEY'] = '<Please update your Typhoon OCR>'; from typhoon_ocr import ocr_document; sys.stdout.reconfigure(encoding='utf-8'); input_path = sys.argv[1]; text = ocr_document(input_path); print(text)\" \"/doc/tmp/pages/{{$json[\"fileName\"]}}\"",
"executeOnce": false
},
"typeVersion": 1
},
{
"id": "1c8ac652-4ac9-424e-847b-5b845753c621",
"name": "Load PDFs from doc Folder",
"type": "n8n-nodes-base.readWriteFile",
"position": [
1184,
208
],
"parameters": {
"options": {},
"fileSelector": "/doc/multipage/*"
},
"typeVersion": 1,
"alwaysOutputData": true
},
{
"id": "291f466a-3928-47bb-8cfb-48de2ad0256f",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
192,
176
],
"parameters": {
"width": 1352,
"height": 968,
"content": "## Thai OCR to Sheet (V2.2 \u2013 Multi-File & Multi-Page with AI)\n\nWorkflow \u0e19\u0e35\u0e49\u0e2d\u0e2d\u0e01\u0e41\u0e1a\u0e1a\u0e21\u0e32\u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e1b\u0e23\u0e30\u0e21\u0e27\u0e25\u0e1c\u0e25\u0e40\u0e2d\u0e01\u0e2a\u0e32\u0e23 PDF \u0e2b\u0e25\u0e32\u0e22\u0e44\u0e1f\u0e25\u0e4c\u0e17\u0e35\u0e48\u0e2d\u0e22\u0e39\u0e48\u0e43\u0e19\u0e42\u0e1f\u0e25\u0e40\u0e14\u0e2d\u0e23\u0e4c\u0e40\u0e14\u0e35\u0e22\u0e27\u0e01\u0e31\u0e19 \n\u0e23\u0e2d\u0e07\u0e23\u0e31\u0e1a\u0e40\u0e2d\u0e01\u0e2a\u0e32\u0e23\u0e17\u0e35\u0e48\u0e21\u0e35\u0e2b\u0e25\u0e32\u0e22\u0e2b\u0e19\u0e49\u0e32 \u0e42\u0e14\u0e22\u0e08\u0e30\u0e41\u0e22\u0e01\u0e17\u0e38\u0e01\u0e2b\u0e19\u0e49\u0e32\u0e2d\u0e2d\u0e01\u0e21\u0e32\u0e41\u0e25\u0e49\u0e27\u0e17\u0e33 OCR \u0e17\u0e35\u0e25\u0e30\u0e2b\u0e19\u0e49\u0e32 \u0e08\u0e32\u0e01\u0e19\u0e31\u0e49\u0e19\u0e23\u0e27\u0e21\u0e02\u0e49\u0e2d\u0e04\u0e27\u0e32\u0e21 \n\u0e41\u0e25\u0e30\u0e43\u0e0a\u0e49 AI \u0e41\u0e1b\u0e25\u0e07\u0e40\u0e19\u0e37\u0e49\u0e2d\u0e2b\u0e32\u0e43\u0e2b\u0e49\u0e2d\u0e22\u0e39\u0e48\u0e43\u0e19\u0e23\u0e39\u0e1b\u0e41\u0e1a\u0e1a\u0e42\u0e04\u0e23\u0e07\u0e2a\u0e23\u0e49\u0e32\u0e07 \u0e01\u0e48\u0e2d\u0e19\u0e1a\u0e31\u0e19\u0e17\u0e36\u0e01\u0e25\u0e07 Google Sheet\n\n### \u0e01\u0e23\u0e30\u0e1a\u0e27\u0e19\u0e01\u0e32\u0e23\n1. **Load PDFs from doc Folder** \n \u0e14\u0e36\u0e07\u0e44\u0e1f\u0e25\u0e4c PDF \u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14\u0e08\u0e32\u0e01\u0e42\u0e1f\u0e25\u0e40\u0e14\u0e2d\u0e23\u0e4c\u0e17\u0e35\u0e48\u0e15\u0e49\u0e2d\u0e07\u0e01\u0e32\u0e23\n\n2. **Loop Over Items** \n \u0e27\u0e19\u0e17\u0e33\u0e07\u0e32\u0e19\u0e17\u0e35\u0e25\u0e30\u0e44\u0e1f\u0e25\u0e4c \u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e43\u0e2b\u0e49 process \u0e04\u0e23\u0e1a\u0e17\u0e38\u0e01\u0e44\u0e1f\u0e25\u0e4c\n\n3. **Write File to Disk** \n \u0e40\u0e02\u0e35\u0e22\u0e19\u0e44\u0e1f\u0e25\u0e4c\u0e25\u0e07\u0e14\u0e34\u0e2a\u0e01\u0e4c\u0e0a\u0e31\u0e48\u0e27\u0e04\u0e23\u0e32\u0e27\u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e43\u0e0a\u0e49 split \u0e41\u0e25\u0e30 OCR\n\n4. **Split PDF page** \n \u0e41\u0e22\u0e01\u0e44\u0e1f\u0e25\u0e4c PDF \u0e2d\u0e2d\u0e01\u0e21\u0e32\u0e40\u0e1b\u0e47\u0e19\u0e2b\u0e19\u0e49\u0e32 \u0e46\n\n5. **Read Splitted PDF Page** \n \u0e42\u0e2b\u0e25\u0e14\u0e44\u0e1f\u0e25\u0e4c\u0e2b\u0e19\u0e49\u0e32\u0e17\u0e35\u0e48\u0e41\u0e15\u0e01\u0e2d\u0e2d\u0e01\u0e21\u0e32\n\n6. **Extract Text with Typhoon OCR** \n OCR \u0e17\u0e35\u0e25\u0e30\u0e2b\u0e19\u0e49\u0e32\u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e43\u0e2b\u0e49\u0e44\u0e14\u0e49\u0e02\u0e49\u0e2d\u0e04\u0e27\u0e32\u0e21\u0e14\u0e34\u0e1a\u0e2d\u0e2d\u0e01\u0e21\u0e32\n\n7. **Aggregate** \n \u0e23\u0e27\u0e21\u0e02\u0e49\u0e2d\u0e04\u0e27\u0e32\u0e21\u0e08\u0e32\u0e01\u0e17\u0e38\u0e01\u0e2b\u0e19\u0e49\u0e32\u0e02\u0e2d\u0e07\u0e44\u0e1f\u0e25\u0e4c\u0e19\u0e31\u0e49\u0e19\u0e40\u0e02\u0e49\u0e32\u0e14\u0e49\u0e27\u0e22\u0e01\u0e31\u0e19 \u0e40\u0e1b\u0e47\u0e19\u0e02\u0e49\u0e2d\u0e04\u0e27\u0e32\u0e21\u0e40\u0e14\u0e35\u0e22\u0e27\n\n8. **Structure Text to JSON with LLM** \n \u0e43\u0e0a\u0e49 AI/LLM \u0e0a\u0e48\u0e27\u0e22\u0e41\u0e22\u0e01\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e08\u0e32\u0e01\u0e02\u0e49\u0e2d\u0e04\u0e27\u0e32\u0e21\u0e14\u0e34\u0e1a\u0e43\u0e2b\u0e49\u0e40\u0e1b\u0e47\u0e19 JSON \u0e17\u0e35\u0e48\u0e21\u0e35\u0e42\u0e04\u0e23\u0e07\u0e2a\u0e23\u0e49\u0e32\u0e07 \u0e40\u0e0a\u0e48\u0e19 \u0e27\u0e31\u0e19\u0e17\u0e35\u0e48 \u0e40\u0e25\u0e02\u0e17\u0e35\u0e48\u0e40\u0e2d\u0e01\u0e2a\u0e32\u0e23 \u0e22\u0e2d\u0e14\u0e23\u0e27\u0e21 \u0e40\u0e1b\u0e47\u0e19\u0e15\u0e49\u0e19\n\n9. **Parse JSON to Sheet Format** \n \u0e41\u0e1b\u0e25\u0e07 JSON \u0e17\u0e35\u0e48\u0e44\u0e14\u0e49\u0e43\u0e2b\u0e49\u0e2d\u0e22\u0e39\u0e48\u0e43\u0e19\u0e23\u0e39\u0e1b\u0e41\u0e1a\u0e1a row/column \u0e17\u0e35\u0e48\u0e2a\u0e2d\u0e14\u0e04\u0e25\u0e49\u0e2d\u0e07\u0e01\u0e31\u0e1a Google Sheet\n\n10. **Save to Google Sheet** \n \u0e1a\u0e31\u0e19\u0e17\u0e36\u0e01\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e25\u0e07\u0e43\u0e19 Google Sheet \u0e40\u0e1b\u0e47\u0e19 1 \u0e41\u0e16\u0e27\u0e15\u0e48\u0e2d 1 \u0e44\u0e1f\u0e25\u0e4c \n (\u0e41\u0e15\u0e48\u0e25\u0e30 field \u0e02\u0e2d\u0e07 JSON \u0e08\u0e30\u0e16\u0e39\u0e01\u0e41\u0e22\u0e01\u0e40\u0e1b\u0e47\u0e19 column)\n\n11. **Execute Command (Cleanup)** \n \u0e25\u0e1a\u0e44\u0e1f\u0e25\u0e4c temp \u0e41\u0e25\u0e30\u0e22\u0e49\u0e32\u0e22\u0e44\u0e1f\u0e25\u0e4c PDF \u0e17\u0e35\u0e48\u0e17\u0e33\u0e40\u0e2a\u0e23\u0e47\u0e08\u0e41\u0e25\u0e49\u0e27\u0e44\u0e1b\u0e40\u0e01\u0e47\u0e1a\u0e43\u0e19\u0e42\u0e1f\u0e25\u0e40\u0e14\u0e2d\u0e23\u0e4c `Completed`\n\n### \u0e2b\u0e21\u0e32\u0e22\u0e40\u0e2b\u0e15\u0e38\n- \u0e21\u0e35\u0e01\u0e32\u0e23\u0e43\u0e0a\u0e49 AI/LLM \u0e0a\u0e48\u0e27\u0e22\u0e08\u0e31\u0e14\u0e42\u0e04\u0e23\u0e07\u0e2a\u0e23\u0e49\u0e32\u0e07\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e2d\u0e31\u0e15\u0e42\u0e19\u0e21\u0e31\u0e15\u0e34 \n- \u0e40\u0e2b\u0e21\u0e32\u0e30\u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e07\u0e32\u0e19\u0e17\u0e35\u0e48\u0e15\u0e49\u0e2d\u0e07\u0e01\u0e32\u0e23 OCR \u0e40\u0e2d\u0e01\u0e2a\u0e32\u0e23\u0e2b\u0e25\u0e32\u0e22\u0e44\u0e1f\u0e25\u0e4c \u0e41\u0e25\u0e49\u0e27\u0e41\u0e22\u0e01\u0e1c\u0e25\u0e25\u0e31\u0e1e\u0e18\u0e4c\u0e43\u0e2b\u0e49\u0e2d\u0e22\u0e39\u0e48\u0e43\u0e19\u0e23\u0e39\u0e1b\u0e41\u0e1a\u0e1a column \u0e17\u0e35\u0e48\u0e2d\u0e48\u0e32\u0e19\u0e07\u0e48\u0e32\u0e22 \n- \u0e1c\u0e25\u0e25\u0e31\u0e1e\u0e18\u0e4c: PDF \u0e2b\u0e19\u0e36\u0e48\u0e07\u0e44\u0e1f\u0e25\u0e4c \u2192 1 \u0e41\u0e16\u0e27\u0e43\u0e19 Google Sheet \u0e42\u0e14\u0e22\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e16\u0e39\u0e01\u0e41\u0e22\u0e01\u0e2d\u0e2d\u0e01\u0e40\u0e1b\u0e47\u0e19\u0e2b\u0e25\u0e32\u0e22 column \u0e15\u0e32\u0e21\u0e1f\u0e34\u0e25\u0e14\u0e4c\n"
},
"typeVersion": 1
},
{
"id": "d585efcd-9b42-4bea-b128-1e0042e6b67f",
"name": "Read/Write Files from Disk",
"type": "n8n-nodes-base.readWriteFile",
"position": [
848,
416
],
"parameters": {
"options": {},
"fileName": "/doc/tmp/in.pdf",
"operation": "write"
},
"typeVersion": 1
},
{
"id": "8fff15da-ea36-4b56-83da-011ae227964b",
"name": "Set_Input_Path",
"type": "n8n-nodes-base.code",
"position": [
1008,
416
],
"parameters": {
"jsCode": "// \u0e23\u0e31\u0e1a items \u0e08\u0e32\u0e01\u0e42\u0e2b\u0e19\u0e14\u0e01\u0e48\u0e2d\u0e19\u0e2b\u0e19\u0e49\u0e32 \u0e41\u0e25\u0e49\u0e27\u0e40\u0e15\u0e34\u0e21\u0e1f\u0e34\u0e25\u0e14\u0e4c inputPath \u0e43\u0e2b\u0e49\u0e40\u0e23\u0e35\u0e22\u0e1a\u0e23\u0e49\u0e2d\u0e22\n// \u0e01\u0e15\u0e34\u0e01\u0e32: \u0e43\u0e0a\u0e49 filePath \u0e16\u0e49\u0e32\u0e21\u0e35; \u0e16\u0e49\u0e32\u0e44\u0e21\u0e48\u0e21\u0e35 \u0e43\u0e2b\u0e49\u0e1b\u0e23\u0e30\u0e01\u0e2d\u0e1a\u0e08\u0e32\u0e01 directory + fileName; \u0e16\u0e49\u0e32\u0e44\u0e21\u0e48\u0e21\u0e35 directory \u0e01\u0e47\u0e43\u0e0a\u0e49 fileName\nreturn items.map(({ json, binary }) => {\n const filePath =\n json.filePath\n ? json.filePath\n : (json.directory\n ? `${json.directory.replace(/\\/$/, '')}/${json.fileName}`\n : json.fileName);\n\n return {\n json: {\n ...json,\n inputPath: filePath,\n },\n binary, // \u0e40\u0e1c\u0e37\u0e48\u0e2d\u0e2d\u0e22\u0e32\u0e01\u0e1e\u0e01 binary \u0e15\u0e48\u0e2d\u0e44\u0e1b\u0e14\u0e49\u0e27\u0e22\n };\n});\n"
},
"typeVersion": 2
},
{
"id": "e4d8e648-e26b-4707-adf5-525e88a09ec8",
"name": "Split PDF page",
"type": "n8n-nodes-base.executeCommand",
"position": [
1184,
416
],
"parameters": {
"command": "=sh -lc '\nset -e\nIN=\"{{ $json.inputPath }}\"\nOUT=\"/doc/tmp/pages\"\nrm -rf \"$OUT\" && mkdir -p \"$OUT\"\n\n# \u0e19\u0e31\u0e1a\u0e08\u0e33\u0e19\u0e27\u0e19\u0e2b\u0e19\u0e49\u0e32 (\u0e15\u0e49\u0e2d\u0e07\u0e21\u0e35 poppler-utils)\nPAGES=$(pdfinfo \"$IN\" 2>/dev/null | awk -F\": *\" \"/^Pages/{print \\$2}\")\n[ -n \"$PAGES\" ] || { echo \"Cannot detect page count for: $IN\" >&2; exit 1; }\n\npdfseparate -f 1 -l \"$PAGES\" \"$IN\" \"$OUT/page_%d.pdf\"\nls -1 \"$OUT\"/page_*.pdf\n'\n",
"executeOnce": false
},
"typeVersion": 1
},
{
"id": "57bb55c3-07e2-4ace-9678-8fafd5b420c6",
"name": "Read Splite PDF Page",
"type": "n8n-nodes-base.readWriteFile",
"position": [
1360,
416
],
"parameters": {
"options": {},
"fileSelector": "/doc/tmp/pages/*.pdf"
},
"typeVersion": 1
},
{
"id": "33589105-6b44-4d83-bd68-50b381d681e1",
"name": "Aggregate",
"type": "n8n-nodes-base.aggregate",
"position": [
944,
640
],
"parameters": {
"options": {},
"fieldsToAggregate": {
"fieldToAggregate": [
{
"fieldToAggregate": "stdout"
}
]
}
},
"typeVersion": 1
},
{
"id": "a65d7907-3744-4629-a8fa-37c36b31e3f3",
"name": "Loop Over Items",
"type": "n8n-nodes-base.splitInBatches",
"position": [
672,
400
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "eadb2b7e-4ad3-4a46-8ecd-da1702470f1a",
"name": "Parse JSON to Sheet Format",
"type": "n8n-nodes-base.code",
"position": [
752,
864
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const raw = $json[\"text\"];\n\n// 1. \u0e25\u0e1a ```json \u0e41\u0e25\u0e30 ``` \u0e17\u0e35\u0e48 LLM \u0e2d\u0e32\u0e08\u0e43\u0e2a\u0e48\u0e21\u0e32\nconst cleaned = raw.replace(/```json\\n?|```/g, \"\").trim();\n\nlet parsed;\ntry {\n // 2. \u0e41\u0e1b\u0e25\u0e07\u0e40\u0e1b\u0e47\u0e19 object\n parsed = JSON.parse(cleaned);\n} catch (err) {\n throw new Error(\"JSON parsing failed: \" + err.message + \"\\n\\nRaw text:\\n\" + cleaned);\n}\n\n// 3. \u0e2b\u0e32\u0e01 contact \u0e40\u0e1b\u0e47\u0e19 object \u0e41\u0e22\u0e01 field \u0e2d\u0e2d\u0e01\u0e21\u0e32\nconst contact = parsed.contact || {};\n\nreturn {\n book_id: parsed.book_id || \"\",\n date: parsed.date || \"\",\n subject: parsed.subject || \"\",\n to: parsed.to || \"\",\n attach: parsed.attach || \"\",\n detail: parsed.detail || \"\",\n signed_by: parsed.signed_by || \"\",\n signed_by2: parsed.signed_by2 || \"\",\n contact_phone: contact.phone || \"\",\n contact_email: contact.email || \"\",\n contact_fax: contact.fax || \"\",\n download_url: parsed.download_url || \"\"\n};\n"
},
"typeVersion": 2
},
{
"id": "8f2fb4c3-bbb3-49aa-8796-f85219241f47",
"name": "Structure Text to JSON with LLM",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
1136,
640
],
"parameters": {
"text": "=\u0e02\u0e49\u0e2d\u0e04\u0e27\u0e32\u0e21\u0e14\u0e49\u0e32\u0e19\u0e25\u0e48\u0e32\u0e07\u0e19\u0e35\u0e49\u0e40\u0e1b\u0e47\u0e19\u0e40\u0e19\u0e37\u0e49\u0e2d\u0e2b\u0e32 OCR \u0e08\u0e32\u0e01\u0e2b\u0e19\u0e31\u0e07\u0e2a\u0e37\u0e2d\u0e23\u0e32\u0e0a\u0e01\u0e32\u0e23 \u0e01\u0e23\u0e38\u0e13\u0e32\u0e41\u0e22\u0e01\u0e2b\u0e31\u0e27\u0e02\u0e49\u0e2d\u0e2a\u0e33\u0e04\u0e31\u0e0d\u0e2d\u0e2d\u0e01\u0e21\u0e32\u0e43\u0e19\u0e23\u0e39\u0e1b\u0e41\u0e1a\u0e1a JSON:\n\n1. book_id: \u0e40\u0e25\u0e02\u0e17\u0e35\u0e48\u0e2b\u0e19\u0e31\u0e07\u0e2a\u0e37\u0e2d\n2. date: \u0e27\u0e31\u0e19\u0e17\u0e35\u0e48\u0e43\u0e19\u0e40\u0e2d\u0e01\u0e2a\u0e32\u0e23\n3. subject: \u0e2b\u0e31\u0e27\u0e40\u0e23\u0e37\u0e48\u0e2d\u0e07\n4. to: \u0e40\u0e23\u0e35\u0e22\u0e19\n5. attach: \u0e2a\u0e34\u0e48\u0e07\u0e17\u0e35\u0e48\u0e2a\u0e48\u0e07\u0e21\u0e32\u0e14\u0e49\u0e27\u0e22\n6. detail: \u0e40\u0e19\u0e37\u0e49\u0e2d\u0e04\u0e27\u0e32\u0e21\u0e43\u0e19\u0e2b\u0e19\u0e31\u0e07\u0e2a\u0e37\u0e2d\n7. signed_by: \u0e1c\u0e39\u0e49\u0e25\u0e07\u0e19\u0e32\u0e21\n8. signed_by2: \u0e15\u0e33\u0e41\u0e2b\u0e19\u0e48\u0e07\u0e1c\u0e39\u0e49\u0e25\u0e07\u0e19\u0e32\u0e21\n9. contact: \u0e0a\u0e48\u0e2d\u0e07\u0e17\u0e32\u0e07\u0e15\u0e34\u0e14\u0e15\u0e48\u0e2d (\u0e40\u0e0a\u0e48\u0e19 \u0e40\u0e1a\u0e2d\u0e23\u0e4c\u0e42\u0e17\u0e23 \u0e2d\u0e35\u0e40\u0e21\u0e25)\n10. download_url: \u0e25\u0e34\u0e07\u0e01\u0e4c\u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e14\u0e32\u0e27\u0e19\u0e4c\u0e42\u0e2b\u0e25\u0e14 (\u0e16\u0e49\u0e32\u0e21\u0e35)\n\nOCR_TEXT:\n\"\"\"\n{{ $json[\"stdout\"] }}\n\"\"\"",
"promptType": "define"
},
"typeVersion": 1.6
},
{
"id": "205beaeb-8532-4768-a2db-e4ab773a2fc8",
"name": "OpenRouter Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
"position": [
1136,
784
],
"parameters": {
"model": "openai/gpt-4o-mini",
"options": {}
},
"credentials": {
"openRouterApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "aa0f180d-9ae8-4a09-ad09-d409d92b4c6b",
"name": "Clear tmp files",
"type": "n8n-nodes-base.executeCommand",
"position": [
1248,
864
],
"parameters": {
"command": "=sh -lc '\nset -e\n\n# \u0e25\u0e1a\u0e42\u0e1f\u0e25\u0e40\u0e14\u0e2d\u0e23\u0e4c temp \u0e17\u0e35\u0e48\u0e43\u0e0a\u0e49\u0e40\u0e01\u0e47\u0e1a\u0e44\u0e1f\u0e25\u0e4c\u0e17\u0e35\u0e48 split\nrm -rf /doc/tmp/pages\n\n# \u0e22\u0e49\u0e32\u0e22\u0e44\u0e1f\u0e25\u0e4c\u0e15\u0e49\u0e19\u0e09\u0e1a\u0e31\u0e1a (input PDF) \u0e44\u0e1b\u0e40\u0e01\u0e47\u0e1a\u0e44\u0e27\u0e49\u0e43\u0e19 Completed\nmkdir -p /doc/multipage/Completed\n\n# \u0e43\u0e0a\u0e49 $json.fileName \u0e02\u0e2d\u0e07 node \u0e15\u0e49\u0e19\u0e17\u0e32\u0e07 (Load PDFs from doc Folder)\n# \u0e16\u0e49\u0e32 fileName \u0e40\u0e1b\u0e47\u0e19 path \u0e2d\u0e22\u0e39\u0e48\u0e41\u0e25\u0e49\u0e27 \u0e43\u0e2b\u0e49 basename \u0e2d\u0e2d\u0e01\u0e21\u0e32\u0e01\u0e48\u0e2d\u0e19\nsrc=\"/doc/multipage/{{ $('Load PDFs from doc Folder').item.json.fileName }}\"\ndst=\"/doc/multipage/Completed/{{ $('Load PDFs from doc Folder').item.json.fileName }}\"\n\nmv \"$src\" \"$dst\"\necho \"Moved $src \u2192 $dst\"\n'\n"
},
"typeVersion": 1
},
{
"id": "32acc4b4-3bb3-49d7-8a50-984e02f31077",
"name": "Save to Google Sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
992,
864
],
"parameters": {
"columns": {
"value": {
"to": "={{ $json.to }}",
"date": "={{ $json.date }}",
"attach": "={{ $json.attach }}",
"detail": "={{ $json.detail }}",
"book_id": "={{ $json.book_id }}",
"subject": "={{ $json.subject }}",
"signed_by": "={{ $json.signed_by }}",
"signed_by2": "={{ $json.signed_by2 }}",
"contact_fax": "={{ $json.contact_fax }}",
"download_url": "={{ $json.download_url }}",
"contact_email": "={{ $json.contact_email }}",
"contact_phone": "={{ $json.contact_phone }}"
},
"schema": [
{
"id": "book_id",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "book_id",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "date",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "date",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "subject",
"type": "string",
"display": true,
"required": false,
"displayName": "subject",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "to",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "to",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "attach",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "attach",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "detail",
"type": "string",
"display": true,
"required": false,
"displayName": "detail",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "signed_by",
"type": "string",
"display": true,
"required": false,
"displayName": "signed_by",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "signed_by2",
"type": "string",
"display": true,
"required": false,
"displayName": "signed_by2",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "contact_phone",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "contact_phone",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "contact_email",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "contact_email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "contact_fax",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "contact_fax",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "download_url",
"type": "string",
"display": true,
"required": false,
"displayName": "download_url",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"book_id"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1h70cJyLj5i2j0Ag5kqp93ccZjjhJnqpLmz-ee5r4brU/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1h70cJyLj5i2j0Ag5kqp93ccZjjhJnqpLmz-ee5r4brU",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1h70cJyLj5i2j0Ag5kqp93ccZjjhJnqpLmz-ee5r4brU/edit?usp=drivesdk",
"cachedResultName": "TyphoonOCR_Extracted_Data"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.5
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "7166e717-0700-4f73-872d-a771d37b1e65",
"connections": {
"Aggregate": {
"main": [
[
{
"node": "Structure Text to JSON with LLM",
"type": "main",
"index": 0
}
]
]
},
"Set_Input_Path": {
"main": [
[
{
"node": "Split PDF page",
"type": "main",
"index": 0
}
]
]
},
"Split PDF page": {
"main": [
[
{
"node": "Read Splite PDF Page",
"type": "main",
"index": 0
}
]
]
},
"Clear tmp files": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Loop Over Items": {
"main": [
[],
[
{
"node": "Read/Write Files from Disk",
"type": "main",
"index": 0
}
]
]
},
"Read Splite PDF Page": {
"main": [
[
{
"node": "Extract Text with Typhoon OCR",
"type": "main",
"index": 0
}
]
]
},
"Save to Google Sheet": {
"main": [
[
{
"node": "Clear tmp files",
"type": "main",
"index": 0
}
]
]
},
"OpenRouter Chat Model": {
"ai_languageModel": [
[
{
"node": "Structure Text to JSON with LLM",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Load PDFs from doc Folder": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Parse JSON to Sheet Format": {
"main": [
[
{
"node": "Save to Google Sheet",
"type": "main",
"index": 0
}
]
]
},
"Read/Write Files from Disk": {
"main": [
[
{
"node": "Set_Input_Path",
"type": "main",
"index": 0
}
]
]
},
"Extract Text with Typhoon OCR": {
"main": [
[
{
"node": "Aggregate",
"type": "main",
"index": 0
}
]
]
},
"Structure Text to JSON with LLM": {
"main": [
[
{
"node": "Parse JSON to Sheet Format",
"type": "main",
"index": 0
}
]
]
},
"When clicking \u2018Test workflow\u2019": {
"main": [
[
{
"node": "Load PDFs from doc Folder",
"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.
googleSheetsOAuth2ApiopenRouterApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This template is designed for developers, back-office teams, and automation builders (especially in Thailand or Thai-speaking environments) who need to process multi-file, multi-page Thai PDFs and automatically export structured results to Google Sheets.
Source: https://n8n.io/workflows/7880/ — 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.
⚠️ Note: This template requires a community node and works only on self-hosted n8n installations. It uses the Typhoon OCR Python package and custom command execution. Make sure to install required dep
This template is ideal for B2B founders, solopreneurs, growth marketers, SDRs, or anyone looking to scale their lead generation and enrichment with no-code tools to low-code tools.
This workflow demonstrates a simple way to run evals on a set of test cases stored in a Google Sheet.
This workflow demonstrates a simple way to run evals on a set of test cases stored in a Google Sheet.
This workflow is designed to automate the generation and updating of SEO meta titles and descriptions for WooCommerce products using n8n. It leverages Google Sheets for data input, a FREE language mod