Messages: send (with HTML, attachments and large-message upload), reply and reply-all in thread, forward, list and search, get, label changes (one or many), trash and untrash, and attachment download.

Sending. send builds the MIME message with ::google::gmail::mime/build-message (header-injection safe). Up to 5 MiB it is sent as the JSON raw field; larger messages (up to Gmail's 35 MB limit) go through the media-upload endpoint (/upload/gmail/v1/users/me/messages/send?uploadType=multipart). The From address is the authenticated user unless from names one of their send-as aliases.

Scopes. Sending uses gmail.send; reading, listing and attachments gmail.readonly; label changes, trash and untrash gmail.modify. reply, reply-all and forward read the original first, so they need gmail.readonly (or gmail.modify) as well as gmail.send. Defaults only matter for service accounts; override with google.scopes or opts.scopes.

Values

BATCH_LIMIT

BATCH_LIMIT: Int 1000

MAX_MESSAGE_BYTES

MAX_MESSAGE_BYTES: Int 36700160

MAX_PAGE_SIZE

MAX_PAGE_SIZE: Int 500

REPLY_HEADERS

REPLY_HEADERS: Vec [
  "Subject",
  "From",
  "Reply-To",
  "To",
  "Cc",
  "Message-ID",
  "References"
]

SEND_LOCAL_KEYS

SEND_LOCAL_KEYS: Vec [
  "thread-id",
  "threadId",
  "upload",
  "self-email",
  "boundary"
]

UPLOAD_THRESHOLD

UPLOAD_THRESHOLD: Int 5242880

Functions

archive-message

fn (id: Str): Map
fn (id: Str, opts: Map): Map

Remove the INBOX label (Gmail's Archive). Scope: gmail.modify.

attachment-bytes

fn (attachment: Map): Bytes
fn (attachment: Map, opts: Map): Bytes

The bytes of an Attachment (from ::google::gmail::parse/attachments or Email.attachments): its inline data, or a get-attachment call. Scope: gmail.readonly.

email ::google::gmail::messages/get-email(id)
pdf first(filter(email.attachments, (a) { eq(a.mime-type, "application/pdf") }))
bytes ::google::gmail::messages/attachment-bytes(pdf)

batch-modify

fn (ids: Vec, add-label-ids: Vec, remove-label-ids: Vec): Bool
fn (ids: Vec, add-label-ids: Vec, remove-label-ids: Vec, opts: Map): Bool

POST /users/me/messages/batchModify - change labels on many messages, 1000 ids per call (larger Vecs are split). Returns true. Scope: gmail.modify.

forward

fn (message-id: Str, message: Map): Map
fn (message-id: Str, message: Map, opts: Map): Map

Forward a message: the original is attached whole (as message/rfc822, with its attachments) to a new message built from message (at least to; text/html for a note). The subject defaults to Fwd: <original subject>. Scopes: gmail.readonly and gmail.send.

::google::gmail::messages/forward(email.id, {to: "legal@example.com", text: "FYI, see below."})

forward-subject

fn (subject: Str?): Str

"Fwd: <subject>", unless it already starts with Fwd: or Fw:.

get-attachment

fn (message-id: Str, attachment-id: Str): Bytes
fn (message-id: Str, attachment-id: Str, opts: Map): Bytes

GET /users/me/messages/{messageId}/attachments/{id} - an attachment's bytes (decoded from Gmail's base64url). Get ids from ::google::gmail::parse/attachments. Scope: gmail.readonly.

get-email

fn (id: Str): ::google::gmail::parse/Email
fn (id: Str, opts: Map): ::google::gmail::parse/Email

get-message (full format) flattened with ::google::gmail::parse/to-email. Scope: gmail.readonly.

get-message

fn (id: Str): Map
fn (id: Str, opts: Map): Map

GET /users/me/messages/{id} - a message. Options: format (full, the default: headers and decoded-able parts; metadata with metadataHeaders as a Vec; minimal; raw: the whole RFC 5322 message base64url encoded). Read it with ::google::gmail::parse. Scope: gmail.readonly (gmail.metadata allows metadata and minimal only).

label-changes

fn (add-label-ids: Vec?, remove-label-ids: Vec?): Map

The {addLabelIds, removeLabelIds} body (pure), omitting an empty or null side.

list-all-messages

fn (opts: Any): Vec
fn (opts: Any, limit: Int?): Vec

Follow nextPageToken and return every matching {id, threadId}, or at most limit. Takes the same opts as list-messages (a Str is a search). Uses maxResults: 500 (or limit when smaller). Scope: gmail.readonly.

list-messages

fn (): Map
fn (query: Str): Map
fn (opts: Map): Map

GET /users/me/messages - one page of message ids: {messages: [{id, threadId}], nextPageToken?, resultSizeEstimate}. A Str argument is a Gmail search ("from:boss is:unread newer_than:2d"). Follow nextPageToken with {pageToken} or use list-all-messages. Scope: gmail.readonly.

list-params

fn (opts: Map): Map

The query parameters list-messages sends (pure): your Gmail parameters (q, maxResults up to 500, pageToken, labelIds - a Vec, sent repeated - and includeSpamTrash) with nulls and credential keys dropped.

mark-read

fn (id: Str): Map
fn (id: Str, opts: Map): Map

Remove the UNREAD label. Scope: gmail.modify.

mark-unread

fn (id: Str): Map
fn (id: Str, opts: Map): Map

Add the UNREAD label. Scope: gmail.modify.

modify-message

fn (id: Str, add-label-ids: Vec, remove-label-ids: Vec): Map
fn (id: Str, add-label-ids: Vec, remove-label-ids: Vec, opts: Map): Map

POST /users/me/messages/{id}/modify - add and remove label ids: modify-message(id, ["STARRED"], ["UNREAD"]) stars and marks read. Returns the message. Scope: gmail.modify.

page-size

fn (limit: Int?): Int

prepare-reply

fn (message-id: Str, reply: Map, all: Bool, opts: Map): Map

Fetch message-id's headers and return the reply message Map (reply-fields merged under your reply fields) with its thread-id. Used by reply, reply-all and ::google::gmail::drafts/create-reply-draft. Scope: gmail.readonly.

reply

fn (message-id: Str, reply: Map): Map
fn (message-id: Str, reply: Map, opts: Map): Map

Reply to a message, in its thread: fetches the original's headers, addresses the reply to its sender (Reply-To first), sets Re:, In-Reply-To and References, and sends reply (a message Map with at least text or html; any field you set wins). Options: self-email (skips a profile lookup), credentials. Scopes: gmail.readonly and gmail.send.

::google::gmail::messages/reply(email.id, {text: "Thanks, merged."})

reply-all

fn (message-id: Str, reply: Map): Map
fn (message-id: Str, reply: Map, opts: Map): Map

Like reply, but also copies the original To and Cc (minus you) onto cc. Scopes: gmail.readonly and gmail.send.

reply-fields

fn (original: Map, self-email: Str?, all: Bool): Map

The addressing and threading fields for replying to original (a message resource with headers), as a message Map (pure): to, cc, subject, in-reply-to, references and thread-id.

  • Replies go to Reply-To, else From; replying to a message you sent goes to its original recipients.
  • With all, the original To and Cc are copied to cc, without duplicates and without self-email (your own address).
  • In-Reply-To is the original Message-ID; References is the original References plus its Message-ID, so Gmail and other clients thread the reply.

reply-subject

fn (subject: Str?): Str

"Re: <subject>", unless the subject already starts with Re: (any case).

search-emails

fn (query: Str): Vec
fn (query: Str, limit: Int): Vec
fn (query: Str, limit: Int, opts: Map): Vec

Search with Gmail syntax and return up to limit (default 25) matching messages as Email records, newest first. Makes one call per message after the search. Scope: gmail.readonly.

invoices ::google::gmail::messages/search-emails("from:billing@vendor.com has:attachment newer_than:30d", 10)
map(invoices, (e) { {subject: e.subject, files: map(e.attachments, (a) { a.filename })} })

self-email

fn (opts: Map): Str?

send

fn (message: Map): Map
fn (message: Map, opts: Map): Map

Build and send an email from a message Map: to, cc, bcc, from, reply-to, subject, text, html, attachments, headers, in-reply-to, references (see ::google::gmail::mime). Returns the sent message {id, threadId, labelIds}, or an Err for an invalid address, header or attachment. Options: thread-id, upload, plus credentials. Scope: gmail.send.

::google::gmail::messages/send({
  to: ["Ada <ada@example.com>"],
  cc: "ops@example.com",
  subject: "Deploy finished",
  text: "v2.3 is live.",
  html: "<p><b>v2.3</b> is live.</p>",
  attachments: [{filename: "changelog.md", content: "- faster builds\n", mime-type: "text/markdown"}]
})

send-body

fn (raw: Bytes, thread-id: Str?): Map

The JSON body for messages.send / drafts.create (pure): {raw, threadId?} with raw base64url encoded.

send-message

fn (to: Any, subject: Str, text: Str): Map
fn (to: Any, subject: Str, text: Str, opts: Map): Map

Send a plain-text email: send-message("ada@example.com", "Hi", "Hello from Hot!"). to may be a list ("a@x.io, b@x.io" or a Vec). Scope: gmail.send.

send-raw

fn (raw: Any): Map
fn (raw: Any, opts: Map): Map

POST /users/me/messages/send - send a complete RFC 5322 message (Bytes or Str, e.g. from ::google::gmail::mime/build-message). Options: thread-id (add it to a thread), upload: true (force the upload endpoint). Returns the sent message {id, threadId, labelIds}. Scope: gmail.send.

thread-id-of

fn (opts: Map): Str?

trash-message

fn (id: Str): Map
fn (id: Str, opts: Map): Map

POST /users/me/messages/{id}/trash - move a message to the trash (Gmail deletes it after 30 days; untrash-message restores it). Permanent deletion needs the full https://mail.google.com/ scope and is not wrapped. Scope: gmail.modify.

untrash-message

fn (id: Str): Map
fn (id: Str, opts: Map): Map

POST /users/me/messages/{id}/untrash - restore a message from the trash. Scope: gmail.modify.

use-upload

fn (size: Int, opts: Map): Bool

True when a raw message of size bytes should use the media-upload endpoint: over 5 MiB, or opts.upload is true.