- 動作確認日:2026-10-01
- n8nのバージョン:2.41.3(Docker、セルフホスト)
目次
① 結論:このワークフローでできること
Gmailを検索して、条件に合うメールの「受信日時・件名・本文の冒頭」を、Googleスプレッドシートに1通1行で記録します。
- 検索の条件は、Gmailの検索ボックスと同じ書き方で指定します(今回は件名に
[n8nテスト]を含むメール)。 - メールごとのIDで照合するので、何回実行しても、同じメールは1行にまとまります。
- 送信者や宛先のアドレスは、あえて記録していません。
- 使う認証情報(Credentials)は、GmailとGoogle Sheetsの2つです。
- 動作確認のため、起動にはWebhookを使っています。実際に使うときは、Schedule Triggerで定期的に実行する想定です。
② 完成イメージ

自分宛てに、件名に [n8nテスト] を入れたテストメールを2通送ってから、WebhookのURLにGETでアクセスしました。次の文章が返ってきます(実際の出力です)。
2件のメールをシートに記録しました
記録されたシートは、次のとおりです(messageId の列は省略しています)。
| receivedAt | subject | snippet |
|---|---|---|
| 2026-10-01 1:46 | [n8nテスト] | (空欄) |
| 2026-10-01 1:54 | [n8nテスト] | hello world I like you |
同じワークフローを2回実行したあとも、シートは2行のままでした。
実行したときの件数の流れは、次のとおりです。
| ノード | 出てきた件数 |
|---|---|
| Webhook | 1 |
| Gmail(Search Mail) | 2 |
| Format Row | 2 |
| Google Sheets | 2 |
| Respond to Webhook | 2(返事は1回だけ) |
動かしてみて気づいた点が3つあります。
- 1通目は本文を書かずに送ったので、
snippet(本文の冒頭)は空になりました。 - Gmailノードの「Simplify」をオンにすると、出力は
id、snippet、internalDate、Subject、From、Toなどの形になりました。FromとToにはメールアドレスが入るので、今回はシートに渡していません。 internalDateは、1970年からのミリ秒の数字(文字列)で返ってきました。そのままでは読めないので、日本時間の日時に直しています。
③ 使うノードと必要なもの
必要なもの
- n8n(今回はバージョン2.41.3をDockerで動かしました)
- Googleスプレッドシートと、タブ(今回は
gmail-logという名前のタブ。中身は空で大丈夫です) - 認証情報:Gmail OAuth2 API と Google Sheets OAuth2 API(どちらも、Google Cloudで作った同じOAuthクライアントを使えます)
使うノード
| ノード | 役割 | 設定したこと |
|---|---|---|
| Webhook | 動作確認用の起動ボタン | Path:test-gmail-sheets、Respond:Using 'Respond to Webhook' Node |
| Gmail(Search Mail) | 条件に合うメールを探す | Resource:Message、Operation:Get Many、Limit:10、Simplify:オン、Search:subject:"[n8nテスト]" |
| Edit Fields(Format Row) | シートに書く4つの項目を作る | 下の表のとおり |
| Google Sheets | メールごとに追記、または上書きする | Operation:Append or Update Row、Sheet:By Name gmail-log、Column to match on:messageId |
| Respond to Webhook | 記録した件数を返す | Respond With:Text、本文:{{ $input.all().length }}件のメールをシートに記録しました |

Search Mailの「Search」には、Gmailの検索ボックスと同じ書き方で条件を入れます。たとえば、次のように変えられます。
| 書き方 | 探すメール |
|---|---|
subject:"[n8nテスト]" | 件名に [n8nテスト] を含む |
from:example.com | example.com から届いた |
has:attachment newer_than:7d | 7日以内に届いた、添付ファイルがある |
Limitは 10 にしました。条件に合うメールが多いときは、新しい順に10通までを取ります。

Format Rowでは、次の4つの項目を作っています。
| 項目名 | 値 |
|---|---|
receivedAt | {{ DateTime.fromMillis(Number($json.internalDate)).setZone('Asia/Tokyo').toFormat('yyyy-MM-dd HH:mm') }} |
subject | {{ $json.Subject }} |
snippet | {{ ($json.snippet || '').slice(0, 80) }} |
messageId | {{ $json.id }} |
receivedAt:ミリ秒の数字を、日本時間の日時に直しています。snippet:本文の冒頭を、長すぎないように80文字までにしています。messageId:Gmailがメールごとに付けるIDです。Google Sheetsノードで、同じメールかどうかを見分けるのに使います。
④ JSON
n8nの公開API(POST /api/v1/workflows)でこのJSONからワークフローを作成し、有効化してから、上の結果を確認しました。
使うときは、次の箇所を自分の環境に合わせて書き換えてください。
YOUR_SPREADSHEET_ID:記録先のスプレッドシートのID(URLの/d/と/editの間の文字列)YOUR_CREDENTIAL_ID:n8nに登録したGoogle Sheetsの認証情報のIDYOUR_GMAIL_CREDENTIAL_ID:n8nに登録したGmailの認証情報のID
読み込んだあとに、ノードの画面で認証情報を選び直しても大丈夫です。
{
"name": "test-gmail-sheets",
"nodes": [
{ "parameters": { "path": "test-gmail-sheets", "responseMode": "responseNode", "options": {} },
"id": "a9000000-0001-4000-8000-000000000001", "name": "Webhook", "type": "n8n-nodes-base.webhook", "typeVersion": 2, "position": [0, 0], "webhookId": "a9000000-aaaa-4000-8000-000000000001" },
{ "parameters": { "operation": "getAll", "limit": 10, "filters": { "q": "subject:\"[n8nテスト]\"" } },
"id": "a9000000-0002-4000-8000-000000000002", "name": "Search Mail", "type": "n8n-nodes-base.gmail", "typeVersion": 2.1, "position": [220, 0],
"webhookId": "a9000000-bbbb-4000-8000-000000000002",
"credentials": { "gmailOAuth2": { "id": "YOUR_GMAIL_CREDENTIAL_ID", "name": "Gmail account" } } },
{ "parameters": {
"assignments": { "assignments": [
{ "id": "g1", "name": "receivedAt", "value": "={{ DateTime.fromMillis(Number($json.internalDate)).setZone('Asia/Tokyo').toFormat('yyyy-MM-dd HH:mm') }}", "type": "string" },
{ "id": "g2", "name": "subject", "value": "={{ $json.Subject }}", "type": "string" },
{ "id": "g3", "name": "snippet", "value": "={{ ($json.snippet || '').slice(0, 80) }}", "type": "string" },
{ "id": "g4", "name": "messageId", "value": "={{ $json.id }}", "type": "string" }
] },
"options": {} },
"id": "a9000000-0003-4000-8000-000000000003", "name": "Format Row", "type": "n8n-nodes-base.set", "typeVersion": 3.4, "position": [440, 0] },
{ "parameters": {
"operation": "appendOrUpdate",
"documentId": { "__rl": true, "value": "YOUR_SPREADSHEET_ID", "mode": "id" },
"sheetName": { "__rl": true, "value": "gmail-log", "mode": "name" },
"columns": { "mappingMode": "autoMapInputData", "value": {}, "matchingColumns": ["messageId"], "schema": [] },
"options": {} },
"id": "a9000000-0004-4000-8000-000000000004", "name": "Google Sheets", "type": "n8n-nodes-base.googleSheets", "typeVersion": 4.5, "position": [660, 0],
"credentials": { "googleSheetsOAuth2Api": { "id": "YOUR_CREDENTIAL_ID", "name": "Google Sheets account" } } },
{ "parameters": { "respondWith": "text", "responseBody": "={{ $input.all().length }}件のメールをシートに記録しました", "options": {} },
"id": "a9000000-0005-4000-8000-000000000005", "name": "Respond to Webhook", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.1, "position": [880, 0] }
],
"connections": {
"Webhook": { "main": [[{ "node": "Search Mail", "type": "main", "index": 0 }]] },
"Search Mail": { "main": [[{ "node": "Format Row", "type": "main", "index": 0 }]] },
"Format Row": { "main": [[{ "node": "Google Sheets", "type": "main", "index": 0 }]] },
"Google Sheets": { "main": [[{ "node": "Respond to Webhook", "type": "main", "index": 0 }]] }
},
"settings": { "executionOrder": "v1" }
}
⑤ つまずきポイントと対処
このワークフロー自体は、1回目の実行でエラーなく動きました。ここでは、確認の途中で気づいたことを書きます。
送信者のアドレスがシートに入ってしまう
- 気をつけること:Format Rowを使わずにGmailノードの出力をそのままシートに渡すと、
FromやToの列ができて、メールアドレスが記録されます。 - 対処:Format Rowで、シートに書く項目だけを作ってから渡しています。記録する項目を自分で決められるので、個人情報を残したくないときに便利です。
⑥ 応用アイデア
どれも、まだ試していないアイデアです。
- Webhookを「Schedule Trigger」に替えて、1時間ごとに新しいメールを記録する
- 検索の条件を
from:に変えて、特定の取引先から届いたメールの一覧を作る - 記録したメールのうち、件名に「請求書」を含むものだけをSlackやDiscordに通知する
- シートに「対応済み」の列を足して、メールの対応状況を管理する
