Saves each new Gmail message that matches your search as a row in your spreadsheet, and never saves the same message twice.

UsesGmailGoogle Sheets

Save matching emails to a spreadsheet

Whenever new mail arrives in Gmail, the workflow looks for the messages your search matches, such as everything from a billing address, and writes one row per message into your spreadsheet. Each row records when the message was sent, who sent it, who it went to, the subject, an excerpt of the body, the Gmail message id and a link that opens the message in Gmail.

Nothing is saved twice. Each message is labelled 'Saved to sheet' the moment it is picked up and the search excludes that label, so a message already saved is never fetched again. The label goes on before the row is written on purpose: if the sheet cannot be written to, the label comes off again, the message is retried next run and the run ends in failure so you can see it. Several matching messages landing at once are all saved, and a backlog is worked through a batch at a time.

Before the first run you need a spreadsheet tab with headings in that column order, and a Gmail search that picks out the mail worth saving.

GmailGmailWhen an email arrives.When an email arrives.
ValuesSet emailSearch to blank.Set emailSearch to blank.
ValuesSet spreadsheetUrl to blank.Set spreadsheetUrl to blank.
ValuesSet sheetTabName to Emails.Set sheetTabName to Emails.
ValuesSet savedLabel to Saved to sheet.Set savedLabel to Saved to sheet.
ValuesSet maxEmailsPerRun to 10.Set maxEmailsPerRun to 10.
ValuesSet excerptLength to 300.Set excerptLength to 300.
ValuesSet gmailAccountIndex to 0.Set gmailAccountIndex to 0.
MathSet emailLimit to maxEmailsPerRun multiplied by 1.Set emailLimit to maxEmailsPerRun multiplied by 1.
Decisions & LoopsIf not emailLimit is greater than or equal to 1 and emailLimit is less than or…If not emailLimit is greater than or equal to 1 and emailLimit is less than or…
Yes
Decisions & LoopsStop and report a problem: Set 'maxEmailsPerRun' to a whole number from 1 to 500: it caps how many emails one run saves.Stop and report a problem: Set 'maxEmailsPerRun' to a whole number from 1 to 500: it caps how many emails one run saves.
No
No steps
MathSet excerptLimit to excerptLength multiplied by 1.Set excerptLimit to excerptLength multiplied by 1.
Decisions & LoopsIf not excerptLimit is greater than or equal to 0 and excerptLimit is less than…If not excerptLimit is greater than or equal to 0 and excerptLimit is less than…
Yes
Decisions & LoopsStop and report a problem: Set 'excerptLength' to a whole number from 0 to 50000: it is how many characters of each message body go into the Excerpt column, and 0 leaves the column empty.Stop and report a problem: Set 'excerptLength' to a whole number from 0 to 50000: it is how many characters of each message body go into the Excerpt column, and 0 leaves the column empty.
No
No steps
MathSet accountIndex to gmailAccountIndex multiplied by 1.Set accountIndex to gmailAccountIndex multiplied by 1.
Decisions & LoopsIf not accountIndex is greater than or equal to 0 and accountIndex is less than…If not accountIndex is greater than or equal to 0 and accountIndex is less than…
Yes
ValuesShow in the output: 'gmailAccountIndex' is not a whole number from 0 up, so the links in the sheet will open in the first Google account you are signed in to..Show in the output: 'gmailAccountIndex' is not a whole number from 0 up, so the links in the sheet will open in the first Google account you are signed in to..
ValuesChange accountIndex to 0.Change accountIndex to 0.
No
No steps
ValuesSet searchQuery to emailSearch -label:@savedLabel.Set searchQuery to emailSearch -label:@savedLabel.
GmailGmailList emails matching searchQuery. Save it as found.List emails matching searchQuery. Save it as found.
ValuesSet matchCount to 0.Set matchCount to 0.
Decisions & LoopsIf a step below fails, save the error as listErrorIf a step below fails, save the error as listError
Try
ListsCount the items in found.emails and save it as matchCount.Count the items in found.emails and save it as matchCount.
On error
Decisions & LoopsStop and report a problem: Gmail did not return a readable list of messages for the search '@emailSearch', so nothing was saved this run. Details: listError as text.Stop and report a problem: Gmail did not return a readable list of messages for the search '@emailSearch', so nothing was saved this run. Details: listError as text.
ValuesSet processedCount to 0.Set processedCount to 0.
ValuesSet savedCount to 0.Set savedCount to 0.
ValuesSet failedCount to 0.Set failedCount to 0.
Decisions & LoopsFor each message in found.emails:For each message in found.emails:
Decisions & LoopsIf processedCount is greater than or equal to emailLimitIf processedCount is greater than or equal to emailLimit
Yes
Decisions & LoopsStop repeating.Stop repeating.
No
No steps
Decisions & LoopsIf a step below fails, save the error as messageErrorIf a step below fails, save the error as messageError
Try
TextTrim message.id and save it as messageId.Trim message.id and save it as messageId.
Decisions & LoopsIf messageId is emptyIf messageId is empty
Yes
ValuesAdd 1 to failedCount.Add 1 to failedCount.
ValuesShow in the output: Skipped a message that Gmail returned without an id..Show in the output: Skipped a message that Gmail returned without an id..
No
GmailGmailAdd label savedLabel to email messageId.Add label savedLabel to email messageId.
ValuesAdd 1 to processedCount.Add 1 to processedCount.
ValuesSet subject to (no subject).Set subject to (no subject).
Decisions & LoopsIf a step below fails, save the error as missingFieldIf a step below fails, save the error as missingField
Try
TextTrim message.subject and save it as fieldValue.Trim message.subject and save it as fieldValue.
Decisions & LoopsIf fieldValue isn't emptyIf fieldValue isn't empty
Yes
ValuesChange subject to fieldValue.Change subject to fieldValue.
No
No steps
On error
No steps
ValuesSet sender to (unknown sender).Set sender to (unknown sender).
Decisions & LoopsIf a step below fails, save the error as missingFieldIf a step below fails, save the error as missingField
Try
TextTrim message.from and save it as fieldValue.Trim message.from and save it as fieldValue.
Decisions & LoopsIf fieldValue isn't emptyIf fieldValue isn't empty
Yes
ValuesChange sender to fieldValue.Change sender to fieldValue.
No
No steps
On error
No steps
ValuesSet recipient to blank.Set recipient to blank.
Dates & TimesGet the current date and time and save it as sentAt.Get the current date and time and save it as sentAt.
ValuesSet headers to an empty record.Set headers to an empty record.
Decisions & LoopsIf a step below fails, save the error as missingHeadersIf a step below fails, save the error as missingHeaders
Try
GmailGmailGet the sender, subject and date of email messageId. Save it as headers.Get the sender, subject and date of email messageId. Save it as headers.
On error
ValuesShow in the output: Could not read this message's headers; the row records the time it was saved instead of the time it was sent, and leaves the To column empty..Show in the output: Could not read this message's headers; the row records the time it was saved instead of the time it was sent, and leaves the To column empty..
Decisions & LoopsIf a step below fails, save the error as missingDateIf a step below fails, save the error as missingDate
Try
TextTrim headers.Date and save it as dateHeader.Trim headers.Date and save it as dateHeader.
Decisions & LoopsIf dateHeader isn't emptyIf dateHeader isn't empty
Yes
ValuesChange sentAt to dateHeader.Change sentAt to dateHeader.
No
No steps
On error
No steps
Decisions & LoopsIf a step below fails, save the error as missingRecipientIf a step below fails, save the error as missingRecipient
Try
TextTrim headers.To and save it as toHeader.Trim headers.To and save it as toHeader.
Decisions & LoopsIf toHeader isn't emptyIf toHeader isn't empty
Yes
ValuesChange recipient to toHeader.Change recipient to toHeader.
No
No steps
On error
No steps
ValuesSet excerpt to blank.Set excerpt to blank.
Decisions & LoopsIf a step below fails, save the error as missingTextIf a step below fails, save the error as missingText
Try
TextTrim message.body and save it as sourceText.Trim message.body and save it as sourceText.
TextShorten sourceText to excerptLimit characters and save it as excerpt.Shorten sourceText to excerptLimit characters and save it as excerpt.
On error
No steps
Decisions & LoopsIf excerpt is emptyIf excerpt is empty
Yes
Decisions & LoopsIf a step below fails, save the error as missingTextIf a step below fails, save the error as missingText
Try
TextTrim message.snippet and save it as sourceText.Trim message.snippet and save it as sourceText.
TextShorten sourceText to excerptLimit characters and save it as excerpt.Shorten sourceText to excerptLimit characters and save it as excerpt.
On error
No steps
No
No steps
ValuesSet messageLink to https://mail.google.com/mail/u/@accountIndex/#all/@messageId.Set messageLink to https://mail.google.com/mail/u/@accountIndex/#all/@messageId.
Decisions & LoopsIf a step below fails, save the error as rowErrorIf a step below fails, save the error as rowError
Try
Google SheetsGoogle SheetsAdd a row to sheetTabName in spreadsheetUrl.Add a row to sheetTabName in spreadsheetUrl.
ValuesAdd 1 to savedCount.Add 1 to savedCount.
On error
ValuesAdd 1 to failedCount.Add 1 to failedCount.
Decisions & LoopsIf a step below fails, save the error as unlabelErrorIf a step below fails, save the error as unlabelError
Try
GmailGmailRemove label savedLabel from email messageId.Remove label savedLabel from email messageId.
On error
ValuesShow in the output: Could not take the '@savedLabel' label back off message messageId after its row failed - remove it by hand so the message is saved on the next run.Show in the output: Could not take the '@savedLabel' label back off message messageId after its row failed - remove it by hand so the message is saved on the next run.
ValuesShow in the output: Could not save one email to the sheet; it will be tried again on the next run: rowError as text.Show in the output: Could not save one email to the sheet; it will be tried again on the next run: rowError as text.
On error
ValuesAdd 1 to failedCount.Add 1 to failedCount.
ValuesShow in the output: Could not process one email: messageError as text.Show in the output: Could not process one email: messageError as text.
Decisions & LoopsIf failedCount is greater than 0If failedCount is greater than 0
Yes
Decisions & LoopsStop and report a problem: Saved savedCount email(s) to the sheet, but failedCount could not be saved - the messages above say why. A message that is not saved is tried again on the next run.Stop and report a problem: Saved savedCount email(s) to the sheet, but failedCount could not be saved - the messages above say why. A message that is not saved is tried again on the next run.
No
No steps
Decisions & LoopsIf savedCount is 0If savedCount is 0
Yes
ValuesShow in the output: No new email matching the search to save..Show in the output: No new email matching the search to save..
No
ValuesShow in the output: Saved savedCount email(s) to the sheet.Show in the output: Saved savedCount email(s) to the sheet.