{
  "name": "Convert PDFs from Google Drive to Markdown for RAG with Apify",
  "nodes": [
    {
      "parameters": {
        "pollTimes": {
          "item": [
            {
              "mode": "everyHour"
            }
          ]
        },
        "triggerOn": "specificFolder",
        "folderToWatch": {
          "__rl": true,
          "value": "REPLACE_WITH_INPUT_FOLDER_ID",
          "mode": "id"
        },
        "event": "fileCreated",
        "options": {}
      },
      "id": "b2a8a72b-15e8-5433-b169-b1d867a573db",
      "name": "New file in the Drive folder",
      "type": "n8n-nodes-base.googleDriveTrigger",
      "typeVersion": 1,
      "position": [
        0,
        0
      ],
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "REPLACE_WITH_YOUR_GOOGLE_DRIVE_CREDENTIAL",
          "name": "Google Drive account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Skip anything that isn't a PDF, and make a safe record key for Apify's key-value store.\n// The key keeps the .pdf extension, which the converter uses to recognise the format.\nreturn $('New file in the Drive folder').all()\n  .map((i) => i.json)\n  .filter((f) => f.mimeType === 'application/pdf' || /\\.pdf$/i.test(f.name ?? ''))\n  .map((f) => {\n    const base = String(f.name ?? 'document').replace(/\\.pdf$/i, '').replace(/[^a-zA-Z0-9!_.'()-]+/g, '-').slice(0, 120);\n    return { json: { fileId: f.id, name: f.name, key: `${base}-${String(f.id).slice(0, 10)}.pdf` } };\n  });"
      },
      "id": "a59da403-9fb6-5883-a805-87430eba53ac",
      "name": "Keep PDFs and make keys",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        440,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.apify.com/v2/key-value-stores?name=n8n-drive-documents",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "apifyApi",
        "options": {}
      },
      "id": "f90e92c5-0db7-5605-ba6e-5b8070406e93",
      "name": "Get or create the Apify store",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        220,
        0
      ],
      "credentials": {
        "apifyApi": {
          "id": "REPLACE_WITH_YOUR_APIFY_CREDENTIAL",
          "name": "Apify account"
        }
      },
      "executeOnce": true
    },
    {
      "parameters": {
        "operation": "download",
        "fileId": {
          "__rl": true,
          "value": "={{ $json.fileId }}",
          "mode": "id"
        },
        "options": {}
      },
      "id": "40bbb809-f601-517a-ae51-87b8649c8df6",
      "name": "Download the PDF",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        660,
        0
      ],
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "REPLACE_WITH_YOUR_GOOGLE_DRIVE_CREDENTIAL",
          "name": "Google Drive account"
        }
      }
    },
    {
      "parameters": {
        "method": "PUT",
        "url": "=https://api.apify.com/v2/key-value-stores/{{ $('Get or create the Apify store').first().json.data.id }}/records/{{ encodeURIComponent($('Keep PDFs and make keys').item.json.key) }}",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "apifyApi",
        "sendBody": true,
        "contentType": "binaryData",
        "inputDataFieldName": "data",
        "options": {}
      },
      "id": "f461eb83-59ca-5d26-882d-8a9d8f463a05",
      "name": "Upload the PDF to Apify",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        880,
        0
      ],
      "credentials": {
        "apifyApi": {
          "id": "REPLACE_WITH_YOUR_APIFY_CREDENTIAL",
          "name": "Apify account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Gather every uploaded file into one Apify run (one start fee instead of one per file).\nconst files = $('Keep PDFs and make keys').all().map((i) => i.json);\nif (files.length === 0) return [];\nreturn [{ json: {\n  storeId: $('Get or create the Apify store').first().json.data.id,\n  keys: files.map((f) => f.key),\n  names: Object.fromEntries(files.map((f) => [f.key, f.name])),\n} }];"
      },
      "id": "b414dd89-1eb2-51c0-aed6-ac962aa6ae7f",
      "name": "Collect the uploaded files",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1100,
        0
      ]
    },
    {
      "parameters": {
        "resource": "Actors",
        "operation": "Run actor and get dataset",
        "actorId": {
          "__rl": true,
          "value": "bKpZkfwwvswi4uEig",
          "mode": "id"
        },
        "customBody": "={{ JSON.stringify({ keyValueStore: $json.storeId, keyValueStoreRecords: $json.keys, outputMode: 'document', keepLinks: true, includePageMarkers: true, maxPagesPerDocument: 0 }) }}",
        "maxTotalChargeUsd": 2,
        "memory": 2048
      },
      "id": "fbc359e1-6155-59cf-9023-6fc48b4bb728",
      "name": "Convert to Markdown",
      "type": "@apify/n8n-nodes-apify.apify",
      "typeVersion": 2,
      "position": [
        1320,
        0
      ],
      "credentials": {
        "apifyApi": {
          "id": "REPLACE_WITH_YOUR_APIFY_CREDENTIAL",
          "name": "Apify account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// One Markdown file per converted document. Failed documents are skipped (and not charged).\nconst names = $('Collect the uploaded files').first().json.names;\nreturn $input.all()\n  .map((i) => i.json)\n  .filter((d) => d.markdown && !d.error)\n  .map((d) => {\n    const original = names[d.fileName] ?? d.fileName ?? d.title ?? 'document';\n    const header = `---\\ntitle: ${JSON.stringify(d.title ?? original)}\\nsource: ${JSON.stringify(original)}\\npages: ${d.pageCount ?? ''}\\nconverted: ${d.processedAt ?? new Date().toISOString()}\\n---\\n\\n`;\n    return { json: { fileName: String(original).replace(/\\.pdf$/i, '') + '.md', content: header + d.markdown } };\n  });"
      },
      "id": "3b91f1ff-a245-504b-804e-695017e45b0a",
      "name": "Make Markdown files",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1540,
        0
      ]
    },
    {
      "parameters": {
        "operation": "createFromText",
        "content": "={{ $json.content }}",
        "name": "={{ $json.fileName }}",
        "driveId": {
          "__rl": true,
          "value": "My Drive",
          "mode": "list",
          "cachedResultName": "My Drive"
        },
        "folderId": {
          "__rl": true,
          "value": "REPLACE_WITH_OUTPUT_FOLDER_ID",
          "mode": "id"
        },
        "options": {}
      },
      "id": "e2cb5c1e-c9aa-57f9-a2b1-37dc25b3d7c1",
      "name": "Save Markdown to Drive",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        1760,
        0
      ],
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "REPLACE_WITH_YOUR_GOOGLE_DRIVE_CREDENTIAL",
          "name": "Google Drive account"
        }
      }
    },
    {
      "parameters": {
        "content": "## Collect new PDFs\nNew files in your input folder, PDFs only.",
        "height": 260,
        "width": 640,
        "color": 7
      },
      "id": "fdf95880-4d5d-52f7-9a8d-1ffe356db47f",
      "name": "Section 1",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -50,
        -130
      ]
    },
    {
      "parameters": {
        "content": "## Upload to your Apify storage\nPrivate files, no public links.",
        "height": 260,
        "width": 420,
        "color": 7
      },
      "id": "bb870f77-7956-5110-ac75-ff83dd55b7a7",
      "name": "Section 2",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        610,
        -130
      ]
    },
    {
      "parameters": {
        "content": "## Convert and save\nOne Apify run, one `.md` file per PDF.",
        "height": 260,
        "width": 860,
        "color": 7
      },
      "id": "ad0a316a-a49d-5daf-baff-8e08c9f51e16",
      "name": "Section 3",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1050,
        -130
      ]
    },
    {
      "parameters": {
        "content": "## Convert PDFs from Google Drive to Markdown for RAG\n\nFor teams building a chatbot, search or AI assistant over their own documents who need clean text first, with headings, lists and tables kept.\n\n### How it works\n1. Every hour the Drive trigger checks your input folder for new files. Only PDFs go on.\n2. Each PDF is downloaded and uploaded to a private key-value store in **your** Apify account (`n8n-drive-documents`), so files never need a public link.\n3. One run of the **doc-to-markdown** Actor converts them all. Tables become Markdown tables and page numbers are marked.\n4. Each result is saved to your output folder as a `.md` file with a short header (title, source file, pages), ready for a vector store or an AI assistant.\n\n### Setup\n1. Add your own Apify API token as an *Apify API* credential. Both HTTP Request nodes use it too.\n2. Pick your Google Drive credential in the trigger and both Drive nodes.\n3. Paste the input and output folder IDs (the last part of each folder's URL).\n\n### Requirements\nApify account (free plan: up to 50 documents per run) and Google Drive. The Actor charges $1 per 1,000 pages, skips image-only pages without charging, and the node caps each run at $2.\n\n**Community node:** uses the verified Apify node (`@apify/n8n-nodes-apify`). On n8n Cloud it installs from the canvas; on self-hosted n8n install it under Settings > Community nodes.\n\n### Customization tips\nSet `outputMode` to `chunks` for embedding-ready chunks with heading paths and page numbers, then send them to a vector store node.\n\nMade by locaihost, a small studio that builds its tools with AI agents.",
        "height": 901,
        "width": 560
      },
      "id": "3aba16a5-9d38-5e21-81eb-f39a48fc2f47",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -670,
        -130
      ]
    }
  ],
  "connections": {
    "New file in the Drive folder": {
      "main": [
        [
          {
            "node": "Get or create the Apify store",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get or create the Apify store": {
      "main": [
        [
          {
            "node": "Keep PDFs and make keys",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Keep PDFs and make keys": {
      "main": [
        [
          {
            "node": "Download the PDF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download the PDF": {
      "main": [
        [
          {
            "node": "Upload the PDF to Apify",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload the PDF to Apify": {
      "main": [
        [
          {
            "node": "Collect the uploaded files",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Collect the uploaded files": {
      "main": [
        [
          {
            "node": "Convert to Markdown",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert to Markdown": {
      "main": [
        [
          {
            "node": "Make Markdown files",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Make Markdown files": {
      "main": [
        [
          {
            "node": "Save Markdown to Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}
