Blog / Technical Guides
Save Gmail Attachments to Google Drive Automatically, by Sender or Job
By Keyona Meeks, Founder, ReRev Labs · Oct 31, 2026 · 9 min read
The short answer: Add a Gmail label called
Save to Driveto a thread. A small Google Apps Script runs every hour. It saves the thread's attachments into a Drive folder, named by date, sender's domain and original file name, then moves the thread to aSaved to Drivelabel. If the thread also has a label likeJobs/2417, the files go into a folder called2417. If not, they go into a folder named after the sender's domain. I mock-tested the script. I did not run it in a live Google account.
By the end of this post, you'll be able to:
- Set up labels that tell the script where files belong.
- Paste in a script that saves attachments with clean names.
- Test it on a few emails before you trust it.
- Know what it does not save, and why.
A pay application arrives as an attachment. So does a submittal, a lien waiver, a photo set and a vendor quote. They sit in an inbox until someone needs one, and then someone searches by sender, by month, by guess. Saving them to the right folder takes a minute each. It is the kind of minute that does not happen on a busy day.
I sell build work, so you should know I benefit if you hire help. This script is free to copy, and a small office can run it without us.
Why not use one of the other ways to save attachments?
There are several. When I searched "save gmail attachments to google drive automatically" on October 30, 2026, page one was mostly automation-vendor tutorials, a few blog posts and a Workspace add-on listing. Titles only. I did not review them and I name none. They are fine options. A tool that connects Gmail to Drive for you is quicker to set up and may cost money per run. A script is free, runs in your own account, and lets you decide the folder and file names. Which you pick depends on how much you want to control.
If you have not used Google Apps Script before, read what Google Apps Script is first. If you are not sure whether this is the filing job to fix first, read what to automate first in a small business.
What does the script do?
You do one thing: put the label Save to Drive on a thread. Optionally, you also put a job label on it, like Jobs/2417. The script does the rest:
- Finds labeled threads. Every hour it looks at threads with
Save to Drive. - Chooses a folder. If a label starts with
Jobs/, the rest of the label is the folder name. Otherwise the folder isBy sender, then the sender's domain. - Saves each attachment. The file name is the message date, then the sender's domain, then the original name, for example
2026-10-01_steelco.com_Pay App 3.pdf. - Skips files already saved. If a file with that name is already in the folder, it is not saved again.
- Moves the thread. It removes
Save to Driveand addsSaved to Drive.
Google's reference describes the pieces it relies on. getAttachments() "Gets all the attachments for this message." createFile(blob) "Creates a file in the current folder from a given Blob of arbitrary data." getFoldersByName(name) "Gets a collection of all folders that are children of the current folder and have the given name." Source: Class GmailMessage and Class Folder, checked October 30, 2026.
How do you set it up?
- In Drive, make a folder for saved files, for example
Saved from Gmail. Open it and copy its folder ID. It is the last part of the folder's web address, after/folders/. I did not find this in Google's docs, so check the address on your own folder. - In Gmail, make the label
Save to Drive. If you want job folders, also make labels likeJobs/2417. The script createsSaved to Driveitself. - Go to script.google.com and start a new project. Delete the placeholder code and paste the script below.
- In
CONFIG, replacePASTE_YOUR_DRIVE_FOLDER_ID_HEREwith your folder ID. - Click Save. In the function list, choose
setupSaveTriggerand click Run. Google asks you to allow the script to read and change your Gmail and your Drive. Read the list. Approve it only if you agree.
// Save Gmail attachments to Drive, by job label or by sender. Paste into script.google.com.
const CONFIG = {
ROOT_FOLDER_ID: 'PASTE_YOUR_DRIVE_FOLDER_ID_HERE', // the Drive folder everything goes into
TRIGGER_LABEL: 'Save to Drive', // you add this label to a thread
DONE_LABEL: 'Saved to Drive', // the script moves the thread here
JOB_LABEL_PREFIX: 'Jobs/', // a label like Jobs/2417 sends files to the folder 2417
SENDER_FOLDER: 'By sender', // threads with no job label go to By sender/<domain>
MAX_THREADS: 30 // threads handled per run
};
function saveAttachments() {
const root = DriveApp.getFolderById(CONFIG.ROOT_FOLDER_ID);
const trigger = GmailApp.getUserLabelByName(CONFIG.TRIGGER_LABEL);
if (!trigger) return;
const done = getOrCreateLabel_(CONFIG.DONE_LABEL);
const threads = trigger.getThreads(0, CONFIG.MAX_THREADS);
threads.forEach(function (thread) {
const job = jobFromLabels_(thread);
const messages = thread.getMessages();
messages.forEach(function (msg) {
const files = msg.getAttachments({ includeInlineImages: false });
if (!files.length) return;
const folder = job
? getOrCreateFolder_(root, job)
: getOrCreateFolder_(getOrCreateFolder_(root, CONFIG.SENDER_FOLDER), domainOf_(msg.getFrom()));
files.forEach(function (att) {
const name = buildName_(msg, att);
if (folder.getFilesByName(name).hasNext()) return; // already saved
folder.createFile(att.copyBlob().setName(name));
});
});
thread.removeLabel(trigger);
thread.addLabel(done);
});
}
function buildName_(msg, att) {
const day = Utilities.formatDate(msg.getDate(), Session.getScriptTimeZone(), 'yyyy-MM-dd');
const clean = function (s) { return String(s).replace(/[\\\/:*?"<>|]/g, '-'); };
return day + '_' + clean(domainOf_(msg.getFrom())) + '_' + clean(att.getName());
}
function jobFromLabels_(thread) {
const labels = thread.getLabels();
for (let i = 0; i < labels.length; i++) {
const n = labels[i].getName();
if (n.indexOf(CONFIG.JOB_LABEL_PREFIX) === 0) return n.substring(CONFIG.JOB_LABEL_PREFIX.length);
}
return '';
}
function domainOf_(from) {
const m = String(from).match(/@([A-Za-z0-9.-]+)/);
return m ? m[1].toLowerCase() : 'unknown';
}
function getOrCreateLabel_(name) {
return GmailApp.getUserLabelByName(name) || GmailApp.createLabel(name);
}
function getOrCreateFolder_(parent, name) {
const it = parent.getFoldersByName(name);
return it.hasNext() ? it.next() : parent.createFolder(name);
}
function setupSaveTrigger() {
ScriptApp.getProjectTriggers().forEach(function (t) {
if (t.getHandlerFunction() === 'saveAttachments') ScriptApp.deleteTrigger(t);
});
ScriptApp.newTrigger('saveAttachments').timeBased().everyHours(1).create();
}
Run setupSaveTrigger once. It schedules saveAttachments every hour and removes only its own earlier trigger.
How do you change the folders or the names?
Edit the CONFIG block at the top:
TRIGGER_LABELandDONE_LABELare the two label names.JOB_LABEL_PREFIXis the start of a job label. Change it toProject/if you prefer.SENDER_FOLDERis the folder name for threads with no job label.MAX_THREADSlimits how many threads one run handles.
To change the file name pattern, edit buildName_. Keep the date first, so a folder sorts in order.
What is a construction example?
Say invoices and submittals come in all day for several jobs. For each job, make a label like Jobs/2417. When an email with an invoice arrives, add Save to Drive and Jobs/2417. An hour later the PDF is in folder 2417, named by date and sender. For a small shop, the habit that matters is one label per job when the email comes in.
Saving files is filing, not record keeping. If your contract or the law tells you how long to keep records, follow that. As one written example, the federal rule on contractor records retention, FAR 4.703, says contractors "shall make available records" and gives a period of "3 years after final payment" as one option, with a longer period possible if a contract clause specifies one. Source: FAR 4.703, Policy, page showing FAC 2026-01, last updated 10/26/2026, checked October 30, 2026. That is a federal rule for federal contracts. It is an example of how a written retention rule can read, not the rule for your job. Check with an attorney if you are unsure what you must keep.
What should you check before you trust it?
I tested the script with made-up threads in a mock harness (10 of 10 checks passed) and a syntax check. I did not run it against a live Gmail account or Drive. Test it on a few low-stakes emails:
- Send yourself two emails with a PDF each. Label one
Save to DriveandJobs/9999. Label the otherSave to Driveonly. - Run
saveAttachmentsby hand. - Check that the first PDF is in a folder
9999and the second is inBy sender, then your own domain. - Check that both threads moved to
Saved to Drive. - Run it again and check that nothing is saved twice.
Step 3 matters most. I could not confirm from Google's reference two things. First, that a nested label like Jobs/2417 returns the name with the slash in it from getName(). If your folders do not come out as 2417, this is why. Second, what the includeInlineImages: false option does. I use it to skip logos and signature images. Look at what lands in your folder after a test. If logos still arrive, change the script.
What else should you know?
- It saves what is attached, not what is linked. If someone sends a Drive link or a large file by link, there is no attachment to save.
- One folder per name. Two jobs with the same label text go to the same folder.
- Same name, same day, same sender. The second file is skipped as a duplicate. Rename one by hand.
- Original messages stay in Gmail. The script copies files. It does not delete anything.
- Private mail. Anything you label gets saved. Do not label threads with files you do not want in Drive.
- Google limits. Scripts have daily limits on runtime and a limit per run. See Google's quotas page for the current numbers.
- Shared drives. I did not test it on a shared drive folder.
Where does an LLM fit in this?
Not in the saving. Moving a file to a folder by label is a rule, and rules are cheaper and easier to check than a model. An LLM can help in one place: reading an attachment with no useful name, such as scan0042.pdf, and suggesting a name or a job from what is inside. A person should check the suggestion before it renames anything. My stance: AI writes the code, plain automation runs it, and an LLM shows up only where the input is messy and a person checks the result.
Where this has limits
- Not run in a live account. Mock-tested only. Test on a copy of a few emails.
- It reads Gmail and writes Drive. Read the permission screen. Use an account you are comfortable giving that to.
- Labels are manual. You label the thread. The script does not guess the job.
- Retention is your call. See the FAR example above and ask an attorney what you must keep.
- One mailbox. It sees threads in the account that installed the trigger.
- Search results change. My look at what ranks is from October 30, 2026.
If filing is the step that slips on your team, see our AI-powered operations work or tell us about it on /letstalk. Bring a list of what arrives and where it should go. Do not send passwords, login details or private files.
Frequently asked questions
Can Gmail save attachments to Drive automatically?
Not by itself in the way this post describes. A Google Apps Script can do it for labeled threads, which is what the script above does. Check Gmail's help for what it offers today.
How do I save attachments by sender?
Label the thread Save to Drive with no job label. The script puts files in By sender, then a folder named after the sender's domain.
How do I save attachments by project or job?
Add a label like Jobs/2417 along with Save to Drive. The files go in folder 2417.
Does it delete the email or the attachment?
No. It copies the attachments to Drive and changes the labels.
Will it save the same file twice?
No. If a file with the same name is already in the folder, it is skipped.
Do I need to know how to code?
No. You paste the script, set one folder ID, run one setup function and approve the permissions. Test it on a few emails first.
Read next: A Drive folder for every new project gives the saved files somewhere organized to land, and Gmail labels by job number sorts the emails the same way.