// Gmail · Apps Script

Send an email with an attachment in Gmail.

How to attach a Google Drive file to an outbound email using GmailApp.sendEmail() in Apps Script, with the correct Blob pattern that actually makes the attachment appear.

I want to send an email from Apps Script that includes a file from Google Drive as an attachment, but the attachment never shows up in the message.

The script

copy · paste · trigger
sendWithAttachment.gs
Apps Script
// Send a Drive file as an email attachment
// Replace FILE_ID with your Google Drive file ID
function sendWithAttachment() {
  var fileId = 'FILE_ID';
  var file = DriveApp.getFileById(fileId);
  var blob = file.getBlob();

  var recipient = 'someone@example.com';
  var subject = 'Your requested file';
  var body = 'Please find the attached file. Let me know if you have questions.';

  GmailApp.sendEmail(recipient, subject, body, {
    attachments: [blob],
    name: 'Your Name'
  });

  Logger.log('Email sent with attachment: ' + file.getName());
}

Need a variant? Gnaw writes a custom version from one sentence — fields, triggers, edge cases handled.

Walkthrough

Why the attachment option needs a Blob, not a URL

The attachments key inside GmailApp.sendEmail()'s options object expects an array of Blob objects. A Blob (Binary Large Object) is Apps Script's in-memory representation of file bytes plus a MIME type. It is not a URL, not a Drive link, and not a file ID string.

The first time I hit this, I passed a Drive sharing URL as the attachment value and the email arrived clean — body only, no file. No error was thrown. GmailApp silently drops values it cannot serialize as RFC 2822 MIME parts, which makes this failure invisible unless you check the sent message.

The correct path: DriveApp.getFileById(fileId) returns a File object, and calling .getBlob() on that File object fetches the bytes and wraps them with the file's detected MIME type. That Blob is what you put in the array.

Renaming the attachment and setting MIME type explicitly

By default the Blob carries the Drive file's original name. If you want the attachment to arrive with a different filename, call blob.setName('report-june.pdf') before passing it to sendEmail(). The MIME type is preserved from the original file.

For files that DriveApp misdetects (uncommon, but happens with older exports), you can call blob.setContentType('application/pdf') to override. Google Workspace native files — Docs, Sheets, Slides — do not have a downloadable byte stream directly; you need to export them first with file.getAs('application/pdf'), which returns a Blob already converted to the target format.

The getAs() approach is worth knowing because a raw Google Doc has the MIME type application/vnd.google-apps.document, which mail clients cannot open. Exporting to PDF or DOCX before attaching avoids that problem entirely.

Quota limits you will hit before anything else does

GmailApp.sendEmail() counts against Gmail's daily sending quota: 100 emails per day for personal Google accounts, 1,500 per day for Google Workspace accounts. Attachments do not have a separate quota line, but the total message size cannot exceed 25 MB — the same limit as manual Gmail sends.

If you are iterating over a list and sending one email per row, add a check for the total attachment size before calling sendEmail(). blob.getBytes().length gives you the byte count. I have watched this bite people who bulk-exported Sheets as XLSX inside a loop — the 25 MB ceiling arrives faster than expected when each file is 2-3 MB.

If you hit QuotaExceeded, Apps Script throws an exception with that exact string in the message. Catching it and logging the recipient lets you resume the run from where it failed rather than re-sending to everyone who already received the message.

Want a custom version?

Describe your sheet and the rule you want. Gnaw writes the Apps Script — fields, triggers, edge cases — in one shot.

FAQ

4 questions
Can I attach a file that is not in Google Drive?
Yes. Any Blob works, not just ones from DriveApp. If you have raw bytes (from a UrlFetchApp response, for example), use Utilities.newBlob(bytes, mimeType, filename) to construct a Blob directly and pass that to the attachments array.
How do I attach a Google Sheet as an Excel file?
Call DriveApp.getFileById(sheetFileId).getAs('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'), then optionally call .setName('report.xlsx') on the returned Blob before passing it to sendEmail(). The getAs() call exports and converts in one step.
Can I send multiple attachments in one email?
Yes. The attachments value is an array, so pass multiple Blobs: attachments: [blob1, blob2, blob3]. Each Blob becomes a separate attachment in the received message. The 25 MB total size limit applies to the combined encoded payload.
What is the difference between GmailApp.sendEmail() and MailApp.sendEmail()?
Both accept the same attachments option and produce the same result for plain sends. The difference is quota and sender identity: MailApp uses a separate 100/1500 emails-per-day quota and always sends from the script owner's address. GmailApp shares quota with the user's actual Gmail account and lets you set a replyTo address. For attachment sends specifically, either works.