Apps Script example · 6 min read

Save Gmail Attachments to a Drive Folder: Copy-Paste Apps Script Pattern

Working save gmail attachments to a drive folder example in Apps Script—copy-paste code, common mistakes, and when to get it built professionally.

GmailDriveAttachments

Invoices, signed contracts, and reports often arrive as Gmail attachments that quietly pile up in an inbox instead of living in a shared Drive folder where a team can find them.

This script searches for matching messages, walks through every attachment on every message, and saves each file into a Drive folder named for the current month.

To avoid saving the same attachment twice on a re-run, the script labels each processed message and skips file types like calendar invites that are rarely worth keeping.

By the end you will have a repeatable pipeline that turns an email attachment habit into an organized, searchable Drive archive with no manual downloading involved.

Need this built? Hire a Google Apps Script developer →

Sheet / project setup

ResourceName / valuePurpose
Gmail labelhas-attachmentsSource thread filter
Drive folderEmail AttachmentsParent folder for saved blobs
Processed markersaved-to-drive labelPrevents re-saving

What It Does

The script searches Gmail for messages matching a label, loops through their attachments, and writes each file into a Drive folder named after the current month, creating that folder if it does not exist yet.

It skips attachments smaller than 1 KB and calendar invite files, since those are almost always inline images or scheduling noise rather than documents worth archiving.

Prerequisites

Create a top-level Email Attachments folder in Drive and note its ID, since the script creates month subfolders underneath that fixed parent rather than scattering folders across your whole Drive.

Apply a Gmail label to the messages you want archived, either manually while testing or through a Gmail filter once you know the automation works as expected.

Walkthrough

Paste the saveAttachmentsToDrive function into the script editor, set PARENT_FOLDER_ID to the folder ID from the Drive URL, and update LABEL_NAME to match your Gmail label.

Run the function once manually and grant the Gmail and Drive scopes, then open Drive and confirm a month-named subfolder appeared containing the expected files.

Add a time-driven trigger every 30 minutes once you are satisfied attachments are landing in the right place with the right names.

Edge Cases

Messages with no attachments at all are labeled as processed too, so the script does not waste time re-scanning them on every subsequent run.

Two attachments with identical filenames from different messages are both kept, since DriveApp allows duplicate names in the same folder and the script does not attempt to merge them.

Testing

Send yourself a test email with two attached files, one above and one below the 1 KB threshold, and confirm only the larger file is saved to Drive.

Re-run the function immediately after and verify no new files are created, which confirms the done-label check is correctly skipping already-processed messages.

Hardening

Wrap each attachment's DriveApp.createFile call in its own try/catch so one corrupted or oversized attachment cannot stop the rest of the batch from being saved.

Track total bytes saved per run in a log sheet so you notice early if attachment volume is trending toward your Drive storage limit.

Variations

Instead of grouping by month, group folders by sender domain when you need an archive organized by vendor rather than by time.

Pair this script with the folder-structure tutorial to file attachments directly into an existing Year/Month/Project hierarchy instead of a flat month folder.

saveAttachmentsToDrive.gs

saveAttachmentsToDrive finds a shared month folder or creates it, then copies every attachment larger than 1 KB and not a calendar invite from matching Gmail threads into that folder.

// Save matching Gmail attachments into a dated Drive folder
function saveAttachmentsToDrive() {
  var LABEL_NAME = 'has-attachments';
  var DONE_LABEL = 'attachments-saved';
  var PARENT_FOLDER_ID = 'REPLACE_WITH_FOLDER_ID';

  var parent = DriveApp.getFolderById(PARENT_FOLDER_ID);
  var monthName = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'yyyy-MM');
  var folders = parent.getFoldersByName(monthName);
  var monthFolder = folders.hasNext() ? folders.next() : parent.createFolder(monthName);

  var doneLabel = GmailApp.getUserLabelByName(DONE_LABEL) || GmailApp.createLabel(DONE_LABEL);
  var threads = GmailApp.search('label:' + LABEL_NAME + ' -label:' + DONE_LABEL, 0, 30);

  for (var i = 0; i < threads.length; i++) {
    var messages = threads[i].getMessages();
    for (var j = 0; j < messages.length; j++) {
      var attachments = messages[j].getAttachments();
      for (var k = 0; k < attachments.length; k++) {
        var file = attachments[k];
        if (file.getSize() > 1024 && file.getContentType() !== 'text/calendar') {
          monthFolder.createFile(file);
        }
      }
    }
    threads[i].addLabel(doneLabel);
  }
}
  1. Line 8: Formatting today's date as yyyy-MM gives every attachment saved this month a shared, sortable folder name.
  2. Line 10: Checking hasNext before creating a folder means the script reuses this month's folder on every run instead of making a new one each time.
  3. Line 18: getAttachments returns every file on a single message, which the inner loop then filters one at a time.
  4. Line 21: Filtering out small files and calendar invites keeps inline signature images and scheduling noise out of the archive.
  5. Line 22: createFile on the month folder is what actually copies the attachment blob into Drive as a standalone file.
  6. Line 26: Labeling the whole thread as saved, even if some of its attachments were filtered out, avoids rescanning it on the next run.

Deploy this example

  1. 01

    Open Apps Script

    In the bound spreadsheet: Extensions → Apps Script. For standalone projects, create one at script.google.com and link your Sheet by ID.

  2. 02

    Paste and save

    Add a .gs file, paste the code below, rename constants at the top (sheet names, column letters, API property keys), then save.

  3. 03

    Authorize once

    Run the main function from the editor. Accept OAuth scopes when prompted — triggers cannot run until authorization succeeds once.

  4. 04

    Add the trigger

    Triggers → Add trigger → choose the handler function and event (time-driven, on edit, or on form submit). Delete test triggers before production.

Before you run: save gmail attachments to drive

  • 1Parent 'Email Attachments' folder created with its Drive ID copied
  • 2Gmail label applied to messages containing relevant attachments
  • 3Size and content-type filters reviewed for your use case
  • 4Function run manually and Gmail/Drive scopes granted
  • 5Month subfolder confirmed to appear correctly
  • 6Re-run tested to confirm no duplicate saves
  • 7Time-driven trigger scheduled

Frequently asked questions

Most signature images are small enough to be filtered out by the 1 KB size threshold, but you can raise or remove that threshold if needed.

They are still labeled as processed so the script does not keep rescanning them, even though nothing is saved to Drive for those messages.

No, DriveApp allows duplicate filenames in the same folder, so both files are kept side by side rather than one overwriting the other.

Swap the month-folder logic for a lookup based on the sender's domain, following the same getOrCreateFolder pattern shown in the folder-structure tutorial.

It only ever handles whatever Gmail already accepted as an attachment, so there is no additional size limit imposed by this script beyond Gmail's own.

DriveApp.getFolderById throws an error immediately, which is a clear signal to double check the ID copied from the folder's URL.

Related examples

Want this wired into your real workflow?

I adapt these patterns to your Sheet structure, APIs, and triggers — deployed in your Google account. Fixed-scope quotes from $500 · free 30-min consult · quote within 24 hours.