Guide
Save Email Attachments to OneDrive
Invoices, signed contracts, and design files land in your inbox, and your team expects them in OneDrive. The Nylas CLI lists and downloads each attachment across providers; the Microsoft Graph drive content endpoint stores it with one PUT. This guide builds an email-to-OneDrive pipeline that pulls attachments by message, uploads small files directly, and switches to a resumable session for anything over 4 MB.
Written by Pouya Sanooei Software Engineer
Reviewed by Qasim Muhammad
Command references used in this guide: nylas email search, nylas email attachments list, and nylas email attachments download.
How do I save an email attachment to OneDrive?
Save an email attachment to OneDrive by downloading it locally with the Nylas CLI, then uploading it to OneDrive with one HTTP PUT to the Microsoft Graph drive content endpoint. The CLI handles the mailbox side across six providers; Graph handles the storage side. A file under 4 MB uploads in a single request, so most invoices and PDFs need exactly one PUT each.
Two pieces of state come first: a Graph access token with the Files.ReadWrite scope, and the attachment bytes on disk. The CLI fetches the bytes; Microsoft's identity platform issues the token. The simple upload contract is documented in the Graph driveItem PUT content reference, which caps single-request uploads at 250 MB but recommends a resumable session above 4 MB. Each PUT names the destination path under the user's drive root.
# Find recent messages that actually carry attachments
nylas email search "*" --has-attachment --after 2026-06-01 --json --limit 50 > messages.jsonHow do I list and download the attachments from a message?
List the files on a message with nylas email attachments list <message-id>, then pull each one with nylas email attachments download <attachment-id> <message-id>. The list command returns an attachment ID, filename, and size per file; the download command writes the bytes to disk and accepts -o to set the output path. A single message can carry 20 or more attachments, so iterate over the list rather than assuming one file.
Add --json to the list command so the attachment IDs and filenames are machine-readable. Pipe the message IDs from the search step into a loop, list each message's files, and download every attachment into a staging folder. The download writes the original filename by default, which keeps the OneDrive path readable on the upload side.
mkdir -p staging
jq -r '.[].id' messages.json | while read -r msg_id; do
nylas email attachments list "$msg_id" --json | jq -r '.[] | "\(.id) \(.filename)"' | \
while read -r att_id filename; do
nylas email attachments download "$att_id" "$msg_id" -o "staging/$filename"
done
doneWhy does OneDrive need a Microsoft Graph token?
OneDrive uploads run through Microsoft Graph, and Graph rejects every request that lacks a valid OAuth 2.0 bearer token. The token proves the caller holds delegated Files.ReadWrite permission on the signed-in user's drive. Microsoft's identity platform issues these tokens through the authorization-code flow, and they expire after roughly 3,600 seconds, so a long-running sync must refresh before each batch.
The token your pipeline uses for Graph is entirely separate from the mailbox grant the Nylas CLI manages, because they authenticate to different services. Register an app in Entra ID, grant it the Files.ReadWrite delegated scope, and complete the user sign-in once to mint a refresh token. The OAuth 2.0 framework this builds on is defined in RFC 6749, and Graph's specific flow is in the Graph delegated-access docs. Export the resulting token as an environment variable so the upload step can read it.
# A delegated access token with Files.ReadWrite, obtained via the auth-code flow
export GRAPH_TOKEN="eyJ0eXAiOiJKV1Qi..."How do I upload each file to OneDrive with a PUT?
Upload each staged file to OneDrive by PUTting its bytes to the Graph content endpoint, addressing the destination by path under the drive root. The endpoint is /me/drive/root:/Inbox-Attachments/<filename>:/content, and Graph creates or overwrites the item in one call. A successful upload returns HTTP 201 for a new file or 200 for a replacement, along with the new item's ID and webUrl in the JSON body.
Loop over the staging folder and send one PUT per file with curl --data-binary so the raw bytes go up unmodified. Set Content-Type: application/octet-stream for arbitrary binaries; Graph infers the real type from the filename extension. The simple PUT path works for any file up to 250 MB, but Microsoft recommends an upload session above 4 MB to survive a dropped connection.
for path in staging/*; do
filename=$(basename "$path")
curl -s -X PUT \
"https://graph.microsoft.com/v1.0/me/drive/root:/Inbox-Attachments/$filename:/content" \
-H "Authorization: Bearer $GRAPH_TOKEN" \
-H "Content-Type: application/octet-stream" \
--data-binary "@$path"
doneHow do I handle files larger than 4 MB?
Handle files over 4 MB by creating an upload session, then sending the bytes in chunks rather than a single PUT. A POST to /me/drive/root:/<path>:/createUploadSession returns an uploadUrl that accepts ranged PUTs. Microsoft requires each chunk except the last to be a multiple of 320 KiB, and recommends a chunk size between 5 MiB and 10 MiB for reliability over flaky links.
Use the upload session for video, design files, and large PDFs, since a single 100 MB PUT that fails at 90% has to start over while a session resumes from the last confirmed byte. The session URL stays valid for about 15 minutes after the last successful chunk. The full ranged-upload contract, including the Content-Range header format, is in the createUploadSession reference. Graph also throttles aggressive callers, so respect the Retry-After header described in the Graph throttling guidance.
# Create a resumable session, then PUT chunks to the returned uploadUrl
curl -s -X POST \
"https://graph.microsoft.com/v1.0/me/drive/root:/Inbox-Attachments/big-file.zip:/createUploadSession" \
-H "Authorization: Bearer $GRAPH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"item":{"@microsoft.graph.conflictBehavior":"replace"}}'Next steps
- Save email attachments to Google Drive — the same pipeline into Drive
- Save email attachments to Dropbox — the Dropbox content API variant
- Parse email attachments — inspect files before you upload them
- Sync email to S3 — archive attachments to object storage
- Load email into Postgres — index metadata alongside the stored files
- Full command reference — every flag and subcommand documented