Skip to content
Skip to main content

How to send emails with attachments

Most apps that send email end up needing to attach something: a receipt, a generated PDF, a screenshot, a CSV export, a contract for signature. This recipe covers how to do it through the Nylas API.

The same endpoints work whether the mailbox belongs to a regular user grant or an Agent Account. The choice you do need to make is which encoding to use — JSON base64 for small files, multipart for everything else.

Nylas accepts attachments two ways. Pick based on total request size:

Total request sizeUseWhy
Under 3MBapplication/json with base64Simplest. Everything in one JSON body.
3MB to 25MBmultipart/form-dataAvoids base64 inflation (~33%) and provider request limits.

The 3MB threshold is the total request body, not just the file — subject, body HTML, and other fields count against it too. If you’re close to the limit, switch to multipart.

You’ll need:

  • A Nylas application with a valid API key.
  • A grant for the mailbox you’re sending from. This can be a normal user grant or an Agent Account grant.
  • Send scope on the provider. Google needs gmail.send. Microsoft needs Mail.Send. Agent Accounts have what they need by default.

Encode the file as base64 and include it in the attachments array on the send request.

Each entry in attachments needs filename, content_type, and content (the base64 string). You can attach multiple files in the same array.

Option 2: multipart/form-data (up to 25MB)

Section titled “Option 2: multipart/form-data (up to 25MB)”

For larger files, switch to multipart/form-data. The message JSON goes in the message form part; each attachment is a separate form part. The Nylas SDKs ship a FileUtils / file_utils helper that reads the file, sets the correct MIME type, and lets the SDK handle the multipart encoding — you almost never need to assemble the boundary yourself.

Multipart avoids the base64 size inflation entirely and lets you push files up to 25MB without hitting either Nylas or provider request limits.

Inline images are attachments that render inside the message body — a logo, an inline screenshot, a chart — referenced from the HTML by their content_id via <img src="cid:...">.

To send an inline image:

  1. Decide on a content_id value. It must be alphanumeric and unique within the message (e.g. logo-2025).
  2. Reference it from the body using <img src="cid:logo-2025">.
  3. Attach the file with content_id set to the same value.

When content_id is set on an attachment, Nylas treats it as inline and most mail clients render it in place. If the receiving client can’t render inline images, the file falls back to being shown as a regular attachment.

When you reply in-thread, attach files the same way. Combine the steps with reply_to_message_id:

For the full thread-reply workflow, see Reply to an email thread.

For Agent Accounts, the endpoints and request shape are identical. Substitute the agent’s grant ID and the same JSON or multipart code works. A few practical notes specific to agents:

  • Agents often send attachments from generated content, not files on disk. A common pattern is: agent calls an external service to generate a PDF (a contract, a report, an extracted invoice), receives the bytes in memory, encodes them as base64, and sends. The file never touches the filesystem.
  • tracking_options works on outbound from agent grants too. If your agent wants to know whether the recipient opened the email or clicked a link in it, set tracking_options on the send request. See Email tracking.
  • One outbound per logical reply. Agents that get retried — webhook redelivery, queue retries, manual replays — can easily send the same attachment twice. Dedupe at the agent level before calling send. See Prevent duplicate agent replies.
  • Replacing attachments on a draft is destructive. If you’re updating an existing draft and pass attachments, the array replaces the existing list completely. To keep an existing file and add a new one, include both in the new array.
  • Filename can include the original path on some clients but Nylas strips it. Send the bare filename (invoice.pdf), not a path (/tmp/uploads/invoice.pdf). Most providers ignore paths, but a few mail clients display them awkwardly.
  • Special characters in filenames cause provider rejections. Stick to ASCII, hyphens, underscores, and dots. If the file is from user input, sanitize before sending.
  • content_type matters for rendering. PDFs sent as application/octet-stream show up as generic downloads instead of previewing inline in Gmail. Set the right MIME type so recipients see proper previews.
  • Some providers cap individual attachments below 25MB. Gmail allows 25MB per message total but routes very large attachments through Drive links. Outlook limits vary by mailbox tier. If you regularly hit these limits, check the large attachments flow for Microsoft.
  • Watch for accidental forwarding of inline images. When users forward an email, inline images sometimes detach and become regular attachments. Don’t rely on is_inline staying stable across forwards.