Guide
Are Gmail Attachment IDs Stable?
A Gmail attachmentId is scoped to a single message — it is not globally unique, and the same physical file carries a different ID in a different message. This guide explains why, shows the reliable way to list and download attachments, and what to store so links never break.
Written by Prem Keshari Senior SRE
Reviewed by Qasim Muhammad
Are Gmail attachment IDs stable or globally unique?
No. A Gmail attachmentId is scoped to a single message and is not globally unique. It identifies a part inside one message's payload, so it only resolves when paired with that message's ID. The Gmail API attachments.get reference requires both a messageId and an id for exactly this reason.
Because the value is long — often 300+ characters of base64url — it looks like a durable key. It isn't. Treat it as a short-lived pointer you read out of the message you're holding, not an address you can write down and revisit later.
Why does the same file have a different attachmentId in two messages?
The same PDF forwarded to two people produces two messages, and each message gets its own independent attachmentId for that file. Gmail assigns the ID per message part, not per byte content, so identical files never share an ID across messages. There is no content-hash deduplication exposed in the attachment ID.
This trips up systems that key a storage table on attachmentId alone, expecting one row per unique file. You instead get one ID per (message, part) pair. The fix is to always carry the messageId alongside it — the pair is what's addressable.
How do I reliably list and download an attachment?
The reliable path is two steps: list the attachments on a message to get current IDs, then download by ID. The nylas email attachments list command takes a message ID and returns each attachment's ID, filename, content type, and size, so you never hardcode an ID that may have come from a stale fetch.
# 1. List attachments on a message to get fresh IDs and filenames
nylas email attachments list "$MSG_ID" --jsonWith a current attachment ID in hand, download the bytes. The nylas email attachments download command writes the file locally and works the same across all 6 provider types — no per-provider MIME parsing. Pass both the attachment ID and its message ID, since the pair is what resolves.
# 2. Download by attachment ID + the message it belongs to
nylas email attachments download "$ATTACHMENT_ID" "$MSG_ID"How should I store a reference to an attachment?
Store the messageId, the filename, and a content hash you compute yourself — never the raw attachmentId as a long-term key. The message ID is durable for the life of the message; the attachment ID is only valid relative to a fetch. A SHA-256 of the bytes gives you the real cross-message dedup key the API never provides.
This matters at scale. Gmail caps a single message at 25 MB of attachments on send, per Google's attachment size limits, so archives accumulate many small files fast. Re-listing a message to refresh IDs is cheap; rebuilding a table keyed on a dead attachment ID is not.
# Build a durable reference: message id + filename + content hash
nylas email attachments download "$ATTACHMENT_ID" "$MSG_ID" --output /tmp/file.bin
HASH=$(shasum -a 256 /tmp/file.bin | cut -d' ' -f1)
echo "{\"message_id\":\"$MSG_ID\",\"sha256\":\"$HASH\"}"How does the Nylas CLI handle attachment IDs across providers?
The CLI gives every provider one attachment model, so the Gmail quirk doesn't become six different quirks. Microsoft Graph, IMAP, Yahoo, and iCloud each identify attachment parts differently under the hood; the tool normalizes them so attachments list and attachments download behave identically everywhere.
You still follow the same rule — list to refresh, then download — but you write it once. That single workflow replaces provider-specific MIME walking that typically runs 40–80 lines per backend. Pair it with attachment parsing when you need to extract content, not just save the file.
Next steps
- Fix Garbled Email Encoding (Mojibake) — Diagnose mojibake from raw MIME headers
- Parse and download email attachments — extract text and data from attachments across providers
- Gmail API eventual consistency — why a just-fetched message can read differently moments later
- Gmail API error codes — decode the 4xx and 5xx responses you hit while fetching parts
- Sync email and attachments to S3 — archive messages and their files with durable references
- Command reference — every flag, subcommand, and example
- Gmail API: users.messages.attachments.get — the reference that requires both message ID and attachment ID