From 88f4ee6ae2619b343e2d35d2be8c48b8f17f3397 Mon Sep 17 00:00:00 2001 From: nusquama Date: Wed, 12 Nov 2025 13:21:25 +0100 Subject: [PATCH] creation --- ...onitor_file_changes_with_google_drive_push_notifications.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 workflows/Monitor File Changes with Google Drive Push Notifications-6106/monitor_file_changes_with_google_drive_push_notifications.json diff --git a/workflows/Monitor File Changes with Google Drive Push Notifications-6106/monitor_file_changes_with_google_drive_push_notifications.json b/workflows/Monitor File Changes with Google Drive Push Notifications-6106/monitor_file_changes_with_google_drive_push_notifications.json new file mode 100644 index 000000000..53bf35b09 --- /dev/null +++ b/workflows/Monitor File Changes with Google Drive Push Notifications-6106/monitor_file_changes_with_google_drive_push_notifications.json @@ -0,0 +1 @@ +{"meta":{"instanceId":"408f9fb9940c3cb18ffdef0e0150fe342d6e655c3a9fac21f0f644e8bedabcd9"},"nodes":[{"id":"d8c73782-4560-4cce-bd59-767defcf3317","name":"Set Variables","type":"n8n-nodes-base.set","position":[-2576,320],"parameters":{"options":{},"assignments":{"assignments":[{"id":"5cba7460-3ff9-44ac-80d8-a6d649d19dd5","name":"driveId","type":"string","value":"REPLACE_ME_DRIVE_ID"},{"id":"667d142b-d36c-4ee4-a672-dcc247649960","name":"channelId","type":"string","value":"REPLACE_ME_CUSTOM_CHANNEL_NAME"},{"id":"8f1cc4e7-2dbc-47a8-bdd4-222598531684","name":"channelToken","type":"string","value":"REPLACE_ME_CUSTOM_CHANNEL_TOKEN"},{"id":"c4446141-516d-4f44-b39e-eaff25356db6","name":"webhookUrl","type":"string","value":"={{ 'https://' + $execution.resumeUrl.extractDomain() + '/webhook/' +$('Webhook').params.path }}"}]}},"typeVersion":3.4},{"id":"e3f4a25d-f75a-49f3-b935-8822e9465f87","name":"Get StartPageToken","type":"n8n-nodes-base.httpRequest","position":[-1856,144],"parameters":{"url":"https://www.googleapis.com/drive/v3/changes/startPageToken","options":{},"sendQuery":true,"authentication":"predefinedCredentialType","queryParameters":{"parameters":[{"name":"supportsAllDrives","value":"true"},{"name":"driveId","value":"={{ $('Set Variables').first().json.driveId }}"}]},"nodeCredentialType":"googleDriveOAuth2Api"},"credentials":{"googleDriveOAuth2Api":{"id":"yOwz41gMQclOadgu","name":"Google Drive account"}},"typeVersion":4.2},{"id":"0fe67ac8-2166-42bc-be0d-9cb99b9091b2","name":"Register Webhook","type":"n8n-nodes-base.httpRequest","position":[-1680,144],"parameters":{"url":"https://www.googleapis.com/drive/v3/changes/watch","method":"POST","options":{},"jsonBody":"={\n \"id\": \"{{ $('Set Variables').first().json.channelId }}\",\n \"type\": \"web_hook\",\n \"address\": \"{{ $('Set Variables').first().json.webhookUrl }}\",\n \"token\": \"{{ $('Set Variables').first().json.channelToken }}\",\n \"expiration\": {{ $now.plus({ \"days\": 7 }).toMillis() }}\n}","sendBody":true,"sendQuery":true,"specifyBody":"json","authentication":"predefinedCredentialType","queryParameters":{"parameters":[{"name":"pageToken","value":"={{ $json.startPageToken }}"},{"name":"supportsAllDrives","value":"true"},{"name":"includeItemsFromAllDrives","value":"true"},{"name":"driveId","value":"={{ $('Set Variables').first().json.driveId }}"}]},"nodeCredentialType":"googleDriveOAuth2Api"},"credentials":{"googleDriveOAuth2Api":{"id":"yOwz41gMQclOadgu","name":"Google Drive account"}},"typeVersion":4.2},{"id":"caefc780-c8e4-47c9-a8ce-4656e0f73264","name":"Save Registration to WorkflowStaticData","type":"n8n-nodes-base.code","position":[-1504,144],"parameters":{"mode":"runOnceForEachItem","jsCode":"// Get the global workflow static data\nconst workflowStaticData = $getWorkflowStaticData('global');\n\nconst registration = $input.item.json;\n\n// Update its data\nconst lastPageToken = $('Get StartPageToken').first().json.startPageToken;\n\nworkflowStaticData.registration = registration;\nworkflowStaticData.lastPageToken = lastPageToken;\n\nreturn { workflowStaticData }"},"typeVersion":2},{"id":"655de901-1a33-4bd3-b2b7-441f5ec9f4ec","name":"Is Valid Request?","type":"n8n-nodes-base.if","position":[-816,304],"parameters":{"options":{},"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"a490cbb7-febd-4266-8fb1-4e557ee325d2","operator":{"name":"filter.operator.equals","type":"string","operation":"equals"},"leftValue":"={{ $json.headers['x-goog-channel-id'] }}","rightValue":"={{ $json.workflowStaticData.registration.id }}"},{"id":"0ce9621d-f200-4093-8b22-bfbb422790c0","operator":{"name":"filter.operator.equals","type":"string","operation":"equals"},"leftValue":"={{ $json.headers['x-goog-channel-token'] }}","rightValue":"={{ $json.workflowStaticData.registration.token }}"},{"id":"4403843f-5e74-4e11-929b-1085726f12bf","operator":{"name":"filter.operator.equals","type":"string","operation":"equals"},"leftValue":"={{ $json.headers['x-goog-resource-state'] }}","rightValue":"change"}]}},"typeVersion":2.2},{"id":"bfc39f92-6196-47f2-947c-05e728bb4853","name":"Invalid Request","type":"n8n-nodes-base.noOp","position":[-592,400],"parameters":{},"typeVersion":1},{"id":"03d5afa2-42eb-4a08-a387-8291bed5245a","name":"Get Changes List","type":"n8n-nodes-base.httpRequest","position":[-592,224],"parameters":{"url":"https://www.googleapis.com/drive/v3/changes","options":{},"sendQuery":true,"authentication":"predefinedCredentialType","queryParameters":{"parameters":[{"name":"pageToken","value":"={{ $json.workflowStaticData.lastPageToken || 10000 }}"},{"name":"supportsAllDrives","value":"true"},{"name":"includeItemsFromAllDrives","value":"true"},{"name":"driveId","value":"={{ $json.driveId }}"}]},"nodeCredentialType":"googleDriveOAuth2Api"},"credentials":{"googleDriveOAuth2Api":{"id":"yOwz41gMQclOadgu","name":"Google Drive account"}},"typeVersion":4.2},{"id":"6484466f-868b-480b-b022-753a4cd19738","name":"Change Events to Items","type":"n8n-nodes-base.splitOut","position":[-192,656],"parameters":{"options":{},"fieldToSplitOut":"changes"},"typeVersion":1},{"id":"36530836-1816-4483-a2b5-6bb98ea31e2c","name":"Filter Changed Events","type":"n8n-nodes-base.filter","position":[0,656],"parameters":{"options":{},"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"b76abb68-b859-47d9-b952-cedd0055a4f9","operator":{"type":"boolean","operation":"false","singleValue":true},"leftValue":"={{ $json.removed }}","rightValue":""},{"id":"24878191-ad0d-4572-95b5-878f1eed5f28","operator":{"name":"filter.operator.equals","type":"string","operation":"equals"},"leftValue":"={{ $json.type }}","rightValue":"file"},{"id":"1b1fcda9-25e7-4b1b-ae9a-1899bbacc359","operator":{"name":"filter.operator.equals","type":"string","operation":"equals"},"leftValue":"={{ $json.changeType }}","rightValue":"file"},{"id":"daf1be34-d87f-479c-adc2-e155ab3d4474","operator":{"name":"filter.operator.equals","type":"string","operation":"equals"},"leftValue":"={{ $json.file.mimeType }}","rightValue":"application/pdf"}]}},"typeVersion":2.2},{"id":"94b0b1e2-2bc5-4de2-a32c-4f28b71806e9","name":"Get Registration From WorkflowStaticData","type":"n8n-nodes-base.code","position":[-1008,304],"parameters":{"mode":"runOnceForEachItem","jsCode":"const staticData = $getWorkflowStaticData('global');\nreturn {\n ...$input.item.json,\n workflowStaticData: staticData,\n driveId: staticData.registration.resourceUri.match(/driveId=([^&]+)&/)[1]\n}"},"typeVersion":2},{"id":"ed3fc6b5-a952-4805-be6e-359b6d54e7a9","name":"Sticky Note","type":"n8n-nodes-base.stickyNote","position":[-2880,0],"parameters":{"color":7,"width":1552,"height":640,"content":"## 1. Register a Google Drive Notification Channel (Webhook)\n[Learn more about Google Drive Notification Channels](https://developers.google.com/workspace/drive/api/guides/push)\n\nWith Push Notifications, Google Drive can create a POST request to any URL when a file or folder change is detected.\nThis is greatly superior to polling as it's really efficient and reliable. Here are some notes of the variables to set:\n\n**driveId** - Unless defined, changes are monitored for all drives which is slow. Defining a drive speeds up receiving notifications considerably.\n**ChannelId** - Set a name of your notification (channel).\n**ChannelToken** - Set an auth token value for notification messages. Google will send this along with the notification payload to the webhook.\n**webhookUrl** - this is the webhook URL of the webhook trigger in this template. Feel free to change this if your webhook is somewhere else.\n\n"},"typeVersion":1},{"id":"e573fe58-f2fb-4c92-bd88-2dfc473e5392","name":"Schedule Trigger","type":"n8n-nodes-base.scheduleTrigger","position":[-2800,320],"parameters":{"rule":{"interval":[{"daysInterval":6,"triggerAtHour":6}]}},"typeVersion":1.2},{"id":"2d48f008-c590-4e53-9569-611be082d5c6","name":"Sticky Note2","type":"n8n-nodes-base.stickyNote","position":[-1280,0],"parameters":{"color":7,"width":896,"height":640,"content":"## 2. Listening For Push Notifications\n[Learn more about Receiving Google Drive Push Notifications](https://developers.google.com/workspace/drive/api/guides/push#receive-notifications)\n\nOnce the Google Notification Channel is set, we can now use a webhook trigger to accept the actual notification messages.\nBe aware that push notification can fire multiple times and in quick succession - especially true when working with bulk actions or file changes. You'll want to keep your calls idempotent and use something like the \"remove duplicates\" node to prevent duplicate changes being processed again.\n\n**📋 Remember to Activate the workflow to enable the Webhook trigger!**"},"typeVersion":1},{"id":"2a02d923-bd2d-48b3-975d-a03949eb50dc","name":"Get Files Details","type":"n8n-nodes-base.httpRequest","position":[192,656],"parameters":{"url":"=https://www.googleapis.com/drive/v3/files/{{ $json.file.id }}","options":{},"sendQuery":true,"authentication":"predefinedCredentialType","queryParameters":{"parameters":[{"name":"fields","value":"id,kind,name,createdTime,modifiedTime,mimeType,trashed,driveId,parents"},{"name":"supportsAllDrives","value":"true"}]},"nodeCredentialType":"googleDriveOAuth2Api"},"credentials":{"googleDriveOAuth2Api":{"id":"yOwz41gMQclOadgu","name":"Google Drive account"}},"typeVersion":4.2},{"id":"193af0b7-26b7-4ef4-b727-f222f58dc323","name":"Sticky Note3","type":"n8n-nodes-base.stickyNote","position":[-320,0],"parameters":{"color":7,"width":720,"height":416,"content":"## 3. Update LastPageToken For Next Push Notification\n[Learn more about WorkflowStaticData](https://docs.n8n.io/code/cookbook/builtin/get-workflow-static-data/)\n\nThe page token is like a \"cursor\" and is used to bookmark where changes were last checked. We need to save and reference this token every time we perform a new check to ensure we're only getting the latest changes. For this template, we're using the `WorkflowStaticData` for our data storage but feel free to use Redis, PostgreSQL or similar."},"typeVersion":1},{"id":"44bae3f1-ac15-4313-a229-733921c80660","name":"Sticky Note4","type":"n8n-nodes-base.stickyNote","position":[-320,448],"parameters":{"color":7,"width":720,"height":416,"content":"## 4. Post-Filter the Changes List\nThe **changes list** contains events for the entire drive and can't be pre-filtered by folder. It is therefore important to post-filter the changes list to ensure you're only actioning on the events you actually want.\n\nOnce you have your events, you may then want to make the extra Google Drive API call to get the files details as the event only contains basic meta information."},"typeVersion":1},{"id":"cbaee96d-e408-4c02-b05c-14a8d5348d56","name":"Sticky Note1","type":"n8n-nodes-base.stickyNote","position":[64,208],"parameters":{"width":384,"height":144,"content":"### ⚠️ Only Works In Production Mode!\nTo use workflowStaticData, the workflow must be run in production mode and not editor mode.\nIf you need this to work in both modes, consider switching to another data storage solution."},"typeVersion":1},{"id":"2cbebd1f-a927-40b1-989f-8e960669de51","name":"Sticky Note5","type":"n8n-nodes-base.stickyNote","position":[-3552,-416],"parameters":{"width":608,"height":1344,"content":"## Watch for Google Drive File Changes with Google Push Notifications!\n\n**Tired of being let down by the Google Drive Trigger? Rather not exhaust system resources by polling every minute? Then this workflow is for you!**\n\nGoogle drive is a great storage option for automation due to its relative simplicity, cheap costs and readily-available integrations. Using Google Drive as a trigger is the next logically step but many n8n users quickly realise the built-in Google Drive trigger just isn't that reliable. Disaster!\n\nTypically, the workaround is to poll the Google Drive search API in short intervals but the trade off is wasted server resources during inactivity. The ideal solution is of course, push notifications but they seem quite complicated to implement... or are they?\n\nThis template demonstrates that setting up **Google Push Notifications for Google Drive File Changes** actually isn't that hard! Using this approach, Google sends a POST request every time something in a drive changes which solves reliability of events and efficiency of resources.\n\n### How it works\n1. We begin with registering a **Notification channel (webhook)** with the Google Drive API. 2 key pieces of information is (a) the webhook URL which notifications will be pushed to and (b) because we want to scope to a single location, the driveId. Good to know that you can register as many as you like using http calls but you have to manage them yourself, there's no google dashboard for notification channels!\n2. The registration data along with the startPageToken are saved in `workflowStaticData` - This is a convenient persistence which we can use to hold small bits of data between executions.\n3. Now, whenever files or folders are created or updated in our target Google Drive, Google will send push notifications to our webhook trigger in this template.\n4. Once triggered, we need still need to call Google Drive's `Changes.list` to get the actual change events which were detected. we can do this with the HTTP request node.\n5. The Changes API will also return the `nextPageToken` - a marker to establish where next to get the new batch of changes. It's important that we use this token the next time we request from the changes API and so, we'll update the `workflowStaticData` with this updated value.\n6. Unfortunately, the `changes.list` API isn't able to filter change events by folder or action and so be sure to do your own set of filtering steps to get the files you want.\n7. Finally with the valid change events, optionally fetch the file metadata which gives you more attributes to play with. For example, you may want to know if the change event was triggered by n8n, in which case you'll want to check \"ModifiedByMe\" value.\n\n### How to use\n* Start with Step 1 and fill in the \"Set Variables\" node and Click on the Manual Execute Trigger. This will create a single Google Drive Notification Channel for a specific drive.\n* Activate the workflow to start recieving events from Google Drive.\n* To test, perform an action eg. create a file, on the target drive. Watch the webhook calls come pouring in!\n* Once you have the desired events, finish off this template to do something with the changed files.\n\n### Requirements\n* Google Drive Credentials. Note this workflow also works on Shared Drives.\n\n### Need Help?\nJoin the [Discord](https://discord.com/invite/XPKeKXeB7d) or ask in the [Forum](https://community.n8n.io/)!\n\nHappy Hacking!"},"typeVersion":1},{"id":"719e5e52-7424-409e-b9cf-dc746f8af3f7","name":"Do Something With These Files!","type":"n8n-nodes-base.noOp","position":[496,656],"parameters":{},"typeVersion":1},{"id":"902b64b1-0acf-4922-8ffe-a4114e6a2fdd","name":"Sticky Note6","type":"n8n-nodes-base.stickyNote","position":[-2016,352],"parameters":{"color":7,"width":608,"content":"\n### Renewing the Notification Channel\n* All Notifications Channels have a maximum active duration of 1 week.\n* To renew an existing notification channel, you can simply call the `changes.watch` API (See \"Register Webhook\") with the same variables.\n* This is why we use a scheduled trigger here to run the registration every 6 days."},"typeVersion":1},{"id":"5e7ca713-097e-4e50-9ca3-6c5faaea1a02","name":"Save NextPageToken to WorkflowStaticData","type":"n8n-nodes-base.code","position":[-160,224],"parameters":{"mode":"runOnceForEachItem","jsCode":"// Get the global workflow static data\nconst workflowStaticData = $getWorkflowStaticData('global');\n\n// Update its data\nconst lastPageToken = $json.newStartPageToken || $json.nextPageToken;\n\nworkflowStaticData.lastPageToken = lastPageToken;\n\nreturn { workflowStaticData }"},"typeVersion":2},{"id":"e2972a38-5c2f-40b5-965b-4ab7bc10df6a","name":"Get Registration","type":"n8n-nodes-base.code","position":[-2400,320],"parameters":{"mode":"runOnceForEachItem","jsCode":"return $getWorkflowStaticData('global') ?? {};"},"typeVersion":2},{"id":"12893d4d-0a38-4f1c-b5d5-fc5d76c4889c","name":"Sticky Note7","type":"n8n-nodes-base.stickyNote","position":[-2624,272],"parameters":{"width":192,"height":304,"content":"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n### ⚠️ Edit me!\nAdd your details here and the template will do the rest."},"typeVersion":1},{"id":"24fd55eb-d91f-413d-97e8-636e4e19c494","name":"First Run? Click Here!","type":"n8n-nodes-base.manualTrigger","position":[-2800,704],"parameters":{},"typeVersion":1},{"id":"947ad96f-d564-4943-9deb-cc93a92c63fb","name":"Sticky Note8","type":"n8n-nodes-base.stickyNote","position":[-3552,960],"parameters":{"width":608,"height":336,"content":"![](https://cdn.subworkflow.ai/n8n-templates/banner_595x311.png#full-width)"},"typeVersion":1},{"id":"bdfbaa00-65f0-4392-86d0-2213301737a9","name":"Webhook","type":"n8n-nodes-base.webhook","position":[-1216,304],"webhookId":"44fac054-fc68-4360-9069-377bd3c6bc40","parameters":{"path":"44fac054-fc68-4360-9069-377bd3c6bc40","options":{},"httpMethod":"POST"},"typeVersion":2},{"id":"e06f30f0-9eaf-4098-bbd7-7ed6fbf3a2b9","name":"Subworkflow Trigger","type":"n8n-nodes-base.executeWorkflowTrigger","position":[-2800,464],"parameters":{"inputSource":"passthrough"},"typeVersion":1.1},{"id":"2e74455d-d95a-4575-afe4-e57c2da3e9d3","name":"Stop Any Existing Notifications","type":"n8n-nodes-base.httpRequest","onError":"continueRegularOutput","position":[-2224,320],"parameters":{"url":"https://www.googleapis.com/drive/v3/channels/stop","method":"POST","options":{},"sendBody":true,"authentication":"predefinedCredentialType","bodyParameters":{"parameters":[{"name":"id","value":"={{ $json.registration?.id ?? $('Set Variables').first().json.channelId }}"},{"name":"resourceId","value":"={{ $json.registration?.resourceId }}"},{"name":"token","value":"={{ $json.registration?.token ?? $('Set Variables').first().json.channelToken }}"}]},"nodeCredentialType":"googleDriveOAuth2Api"},"credentials":{"googleDriveOAuth2Api":{"id":"yOwz41gMQclOadgu","name":"Google Drive account"}},"typeVersion":4.2},{"id":"466e4f97-c801-43f5-a90f-3a493e47d912","name":"Register Notification Channel","type":"n8n-nodes-base.executeWorkflow","position":[-2592,704],"parameters":{"mode":"each","options":{},"workflowId":{"__rl":true,"mode":"id","value":"={{ $workflow.id }}"},"workflowInputs":{"value":{},"schema":[],"mappingMode":"defineBelow","matchingColumns":[],"attemptToConvertTypes":false,"convertFieldsToString":true}},"typeVersion":1.2}],"pinData":{},"connections":{"Webhook":{"main":[[{"node":"Get Registration From WorkflowStaticData","type":"main","index":0}]]},"Set Variables":{"main":[[{"node":"Get Registration","type":"main","index":0}]]},"Get Changes List":{"main":[[{"node":"Save NextPageToken to WorkflowStaticData","type":"main","index":0},{"node":"Change Events to Items","type":"main","index":0}]]},"Get Registration":{"main":[[{"node":"Stop Any Existing Notifications","type":"main","index":0}]]},"Register Webhook":{"main":[[{"node":"Save Registration to WorkflowStaticData","type":"main","index":0}]]},"Schedule Trigger":{"main":[[{"node":"Set Variables","type":"main","index":0}]]},"Get Files Details":{"main":[[{"node":"Do Something With These Files!","type":"main","index":0}]]},"Is Valid Request?":{"main":[[{"node":"Get Changes List","type":"main","index":0}],[{"node":"Invalid Request","type":"main","index":0}]]},"Get StartPageToken":{"main":[[{"node":"Register Webhook","type":"main","index":0}]]},"Subworkflow Trigger":{"main":[[{"node":"Set Variables","type":"main","index":0}]]},"Filter Changed Events":{"main":[[{"node":"Get Files Details","type":"main","index":0}]]},"Change Events to Items":{"main":[[{"node":"Filter Changed Events","type":"main","index":0}]]},"First Run? Click Here!":{"main":[[{"node":"Register Notification Channel","type":"main","index":0}]]},"Stop Any Existing Notifications":{"main":[[{"node":"Get StartPageToken","type":"main","index":0}]]},"Get Registration From WorkflowStaticData":{"main":[[{"node":"Is Valid Request?","type":"main","index":0}]]}}} \ No newline at end of file