第一個 workflow:早上 8:00 把 Google 行事曆推 Slack
前面七章都在講概念、看別人的 workflow。這章第一次從空白 canvas 拉一個「你會真的每天用到」的東西:每天早上 8:00,n8n 自動去讀你今天的 Google 行事曆,把事件標題和時間傳到你的 Slack。看完你不只有一個能跑的 workflow,更重要的是——你會抓到「Trigger → Action → Action」這個 pattern,之後所有 workflow 都是這個骨架加東西。
為什麼第一個 workflow 選這個
你可能已經按過第 6 章教的「改別人的 workflow」,甚至在第 7 章認識了三種節點。但你心裡一定有個疑問:「所以我到底什麼時候能自己拉一個?」 答案是——現在。
選「早上推行事曆到 Slack」當第一個 workflow,是有理由的:
- 你會真的每天用到——不是玩具範例,是喝早上第一杯咖啡的時候瞄一眼就會用到的東西。我自己用了兩年多。
- 三個節點就夠——Schedule → Google Calendar → Slack。剛好把 Trigger、拉資料、送出去這三個 pattern 湊齊。
- 兩個都是公司在用的 SaaS——你不用去申請奇怪的第三方帳號,用你現有的 Google Workspace 跟公司 Slack 就能做。
- 失敗了不會壞事——這 workflow 只讀不寫,最壞就是 Slack 沒收到訊息,不會誤刪任何東西。
如果你還沒設過 Slack 或 Google 的 credential,這章會帶你在流程裡順便設好。想先集中處理 credential 的話,也可以先跳到第 13 章把兩組帳號先綁好再回來。
動手前先想清楚的三件事
做任何 workflow 之前,我都會先在紙上(或腦袋裡)回答三個問題。這是本書最重要的一個習慣,你養成它,之後你想到任何想自動化的事,都能在 30 秒內拆解成 n8n 骨架:
| 問題 | 對應到 n8n 的什麼 | 這次的答案 |
|---|---|---|
| 什麼時候要跑? | Trigger 節點的選擇 | 每天固定時間 → Schedule Trigger,設 08:00 |
| 要拿什麼資料? | 中間的 SaaS 節點 | 今天的行事曆 → Google Calendar 節點的 Get Many(Resource=Event、Operation=Get Many) |
| 要做什麼事? | 最後一個 action 節點 | 推訊息給我自己 → Slack 節點的 Send Message(Resource=Message、Operation=Send Message) |
三題答完就 done。你已經知道要拉哪三個節點、每個節點的 operation 選哪個。剩下的工作只是「照著填」而已。
畫成流程長這樣
用最白話的方式畫出來:
[⏰ 每天 08:00] → [📅 讀今天行事曆] → [💬 傳給 Slack]
Schedule Google Calendar Slack
Trigger Event · Get Many Message · Send Message
三個節點、兩條連線。就這樣。等下你就要一步一步把它做出來。
從零到 workflow 完成——十一個步驟
準備:確認你已經登入 n8n.woowtech.io,並且你的 Google 帳號可以看到自己的 Calendar、Slack 裡有一個「只有你」的頻道(或用你自己的 DM)可以測試。
-
側欄點 Workflows,右上按 Add workflow
登入之後預設就在 Workflows 列表頁。看不到列表的話點左邊側欄的 Workflows。列表頁右上角有一顆 + Add workflow 按鈕,按下去。畫面會切到一張空白的 canvas(灰色格子背景),中央有一顆大大的 + Add first step 按鈕。
圖 8-1空白 Canvas 是你每個 workflow 的起點,點中間的 Add first step 就開始拉第一顆 Trigger 節點。 -
給 workflow 取名字:
每日行事曆推送畫面正上方會有一個預設名 My workflow,點它就能改。取一個之後好找的名字,例如
每日行事曆推送。旁邊有 Save(或 Ctrl+S)先存一次,避免中途瀏覽器崩掉全部白做。提示:Workflow 名字盡量用「動詞+對象+時間」的格式:每日行事曆推送、每小時抓 RSS、Slack 收單自動建 Notion。之後 workflow 一多,光看名字就能秒懂它在幹嘛。 -
按 Add first step,選 Schedule Trigger
點 canvas 中央那顆 + Add first step。右邊會彈出節點清單,最上面幾類是「Trigger」類型。找到 On a schedule(或直接在搜尋框打
schedule)點下去。canvas 中央會出現一個時鐘圖示的節點,這就是你的 Schedule Trigger。 -
設定 Schedule:Every Day,08:00
雙擊剛加的節點打開設定面板:
- Trigger Interval:從下拉選
Days(下拉裡就寫 Days,沒有「Every Day」這個選項,別找)。 - Days Between Triggers:
1(每天)。 - Trigger at Hour:從下拉選
8am(這欄是 dropdown 不是輸入數字,選項寫 Midnight / 1am / 2am / ... / Noon / 1pm ...,直接挑 8am)。 - Trigger at Minute:
0(這欄才是輸入數字,0–59)。
設好點面板左上角 ← Back to canvas 關閉(或按 Esc)。節點下方會出現排程摘要,例如 Every day at 8:00 am。
- Trigger Interval:從下拉選
-
Trigger 節點右邊按 +,找 Google Calendar
把滑鼠移到 Schedule Trigger 節點右邊,會冒出一個小小的 +。點它,右邊再彈節點清單,搜尋框打
google calendar。點選出現的 Google Calendar 節點。面板打開後,Resource 選
Event,Operation 選Get Many(下拉裡的字就是 Get Many,「Events」是 resource 名,n8n UI 不會拼成 Get Many Events)。這個組合的意思是「拿多筆行事曆事件」。 -
綁 Google credential(第一次會跳 OAuth 授權)
面板最上方 Credential to connect with 這欄如果是空的,點下拉 → Create new credential。會跳出 OAuth 視窗,讓你選 Google 帳號並允許 n8n 讀取 Calendar。允許之後視窗會自己關掉,credential 就綁好了。
注意:OAuth 允許畫面會出現 Google 警告「這個 app 沒被 Google 驗證」,那是因為 Woow n8n 是自架的,不是官方 app,屬正常現象。點進階 → 前往 n8n.woowtech.io(不安全)即可。你授權的對象是自家伺服器,跟公開的 app 不同。 -
Calendar 選你自己的、Time Min/Max 填今天範圍
回到 Google Calendar 節點面板,繼續填:
- Calendar:從下拉選你自己的(通常顯示成你的 email 或 Primary)。這欄是 resource locator,可以切 From list(下拉挑)或 By ID(貼 calendar ID)。
- Return All:ON(或關掉改設 Limit,例如 50)。
- After:這欄直接躺在面板上、不在 Options 裡,切成 Expression 模式(欄位右邊有一個小 fx 圖示),填
{{ $now.startOf('day') }}。(欄位預設值是{{ $now }},內部叫timeMin,但 UI 顯示的字就是 After。) - Before:同樣直接在面板上,切 Expression,填
{{ $now.endOf('day') }}。(內部叫timeMax。)
這兩個 expression 的意思是「今天凌晨 00:00 到今天 23:59:59.999」。Expression 是 n8n 的動態表達式,詳細寫法第 14 章會拆。
注意:不少舊教學(包括早期版本的 n8n 官方文件)把這兩欄叫 Time Min / Time Max,那是 Google Calendar API 底層的名字。n8n 目前 UI 顯示的就是 After / Before,找不到「Time Min」是正常的,不要在 Options 裡挖。 -
按 Execute Node,先看看今天到底有沒有行程
Google Calendar 節點面板右上角有一顆 Execute step(有些版本寫 Test step 或 Execute Node)按鈕,按下去。這個按鈕只跑「這一顆節點」,不會執行整個 workflow。
右邊的 OUTPUT 面板會列出每個事件一筆 n8n item(今天 5 個行程 → 5 筆)。每一筆的頂層有
summary(標題)、start.dateTime(開始時間)、end.dateTime(結束時間)等欄位——所以在 Slack 節點裡,$json.summary拿的是「目前這一筆」的標題,不是全部。看到列表就代表資料拉成功了。觀念:這裡的關鍵是「n8n 把每筆事件展成獨立 item」而不是「塞成一個items陣列讓下一顆自己 map」。這個差別很重要,因為下一步 Slack 要「把所有事件拼成一則訊息」,不能寫$json.items(那個欄位不存在),得用$input.all()或加一顆 Aggregate 節點先把 N 筆合成 1 筆。第 9 章會把 items 資料流講透。 -
加 Slack 節點:Send Message
Google Calendar 節點右邊 + → 搜
slack→ 點 Slack。Resource 選Message、Operation 選Send Message(下拉裡就寫 Send Message)。Credential 一樣:第一次要點 Create new credential,跑一次 OAuth 授權你的 Slack workspace。授權完回到節點面板繼續填。
提示:在直接接 Slack 之前,強烈建議在 Google Calendar 和 Slack 之間先插一顆 Aggregate 節點(Fields to Aggregate 填summary),把「N 筆事件」壓成「1 筆 items 陣列」。這樣下面 Slack 就只會發一則訊息,不會每個行程各發一則把 Slack 洗版。下面步驟 10 假設你有加 Aggregate;沒加的話 Slack 會照 n8n 預設「每個 item 各執行一次」機制發 N 則訊息。 -
Channel 選你自己的頻道,Text 用 Expression 拼字串
Slack 節點的欄位:
- Send Message To:
Channel(下拉裡也有 User 可以選,等下 FAQ 會提)。 - Channel:resource locator,可切 From list 從下拉挑,或 By name 打
#daily-me、By ID 貼C0XXXXXXX。挑一個「只有你看得到」的頻道測試,或選 DM 給自己(Send Message To 改 User 再挑自己)。不要一開始就選#general,測試訊息會發到全公司。 - Message Text(有些舊版寫 Text):切 Expression 模式(欄位右邊那顆小 fx),貼下面這段(假設你按前一步的 tip 加了 Aggregate 節點,Fields to Aggregate 填
summary):
早安!今天有 {{ $json.summary.length }} 個行程: {{ $json.summary.join('、') }}意思是「Aggregate 之後
summary已經是一個陣列,直接算長度、直接 join」。沒加 Aggregate 的替代寫法(讓 Slack 節點自己看整批 upstream items):
早安!今天有 {{ $input.all().length }} 個行程: {{ $input.all().map(i => i.json.summary).join('、') }}差別:
$json.xxx只看「目前這一筆」,$input.all()才能拿到全部 upstream items。絕對不要寫$json.items.map(...)——Google Calendar 節點的 output 裡根本沒有items這個欄位,那是 raw Google API 的 shape,n8n 已經把它拆平了。按 Execute step 手動測一次,切到你的 Slack 應該就會看到那則訊息。
- Send Message To:
-
Save,然後把右上 Inactive toggle 切成 Active
整個 workflow 完成了。按 Save(或 Ctrl+S)存檔。然後最重要的一步——切畫面右上角的 Inactive / Active 開關到 Active(就在 Save 按鈕旁邊、workflow 名稱那一條 header 的右邊)。這個開關沒切的話,Schedule Trigger 是「睡眠狀態」,時間到了不會自動觸發,你只能手動按 Execute Workflow 才會跑。
注意:90% 新手第一次踩的坑就是「奇怪我明明設好 8:00 了為什麼隔天沒收到訊息」——十次有九次就是 Inactive 忘了切成 Active。存檔跟啟用是兩件事,開關就在右上角 Save 旁邊,不會躲。
恭喜。你剛做出人生第一個會自己跑的 n8n workflow。明天早上 8:00 它就會自動去讀你的行事曆、傳到你的 Slack,不需要你做任何事。
Slack Message Text 進階:三種常見版型
上面步驟 10 用的是最簡版。訊息想寫得漂亮一點,Slack 支援它自己一套 markdown 語法(叫 mrkdwn),加上 emoji 可以讓早上那則通知看起來像個東西,不像 log。三種常見版型(都假設你前面有加 Aggregate 節點,Fields to Aggregate 填 summary, start;沒加就把 $json.summary 改成 $input.all().map(i => i.json.summary)):
| 版型 | Message Text 欄位寫法 | 什麼時候用 |
|---|---|---|
| 極簡版 | 今天 {{ $json.summary.length }} 個行程 |
只想知道數字、不想看細節;用在很多會的日子避免 Slack 被撐爆 |
| 條列版 | 今天行程:\n{{ $json.summary.map((s, i) => '• ' + s + ' @ ' + $json.start[i].dateTime).join('\n') }} |
要看清楚每一場什麼時間;日常主力版型 |
| Markdown 版 | *今天行程* :calendar:\n> {{ $json.summary.map((s, i) => s + ' (' + $json.start[i].dateTime + ')').join('\n> ') }} |
訊息夠正式、方便長按引用;跟同事分享時看起來比較體面 |
Slack 支援的語法快速版:
*粗體*、_斜體_、~刪除線~`行內 code`、```code block```> 引用(每行前面加>)- emoji 用
:calendar:、:coffee:、:tada:這種 shortcode(Slack 會自動渲染成圖) - 換行是
\n(在 expression 裡是真的兩個字元反斜線+n)
手動測 vs 排程跑——兩顆按鈕差在哪
在 canvas 上你會看到兩顆長得很像但意義完全不一樣的按鈕:
| 按鈕 | 位置 | 做什麼 | 什麼時候用 |
|---|---|---|---|
| Execute Node | 某個節點的面板右上 | 只跑這一顆節點,不會觸發下游 | 剛設好一顆節點想確認資料對不對 |
| Execute Workflow | canvas 下方中央 or 右上 | 從 Trigger 一路跑到最後,但不寫入 execution log(有些版本會寫,看設定) | 開發階段整體測試 |
| Active toggle(ON) | 畫面右上角 | 讓 Schedule Trigger 進入「等時間到自動跑」的正式狀態 | 開發完想上線 |
對新手來說一句話講完:Execute Workflow = 給你自己看的、Active = 交給時間跑的。開發時用左邊,做完把右邊打開。
看執行紀錄——排錯的黃金頁面
workflow 開始自己跑之後,你怎麼知道「昨天早上到底跑了沒?成功還失敗?資料長怎樣?」——答案在 Executions 頁面。這是你之後最常回來看的一頁。
-
兩個入口都能到
方法 A:側欄點 Executions(會看到全帳號所有 workflow 的執行)。方法 B:進到某個 workflow,上方 tab 切 Executions(只看這個 workflow 的)。第一次找的時候,用方法 B 比較不會被別人 workflow 的紀錄干擾。
-
列表看綠 vs 紅
每一筆執行都有狀態:綠色 Success(全通)、紅色 Error(中間某個節點炸掉了)、黃色 Waiting(暫停,Woow 教學範圍不太會用到)。時間欄顯示執行的時間點跟花費時間。
-
點進去看每個節點吐什麼
點某一筆進去,會看到跟編輯 canvas 幾乎一樣的視圖,但每個節點下面會多顯示「這一次跑的時候,這顆節點吃了什麼、吐了什麼」。點 Google Calendar 節點就能看到那天早上 8:00 抓到的事件 JSON。
-
紅色的節點左邊會亮紅框
執行失敗的話,出事的那顆節點會有紅框,點下去下方會顯示錯誤訊息。這是排錯最快的方式,比看 log 快十倍。錯誤訊息看不懂就整段複製丟 Google 或問 IT,通常兩下就找到原因。
學會的 workflow 怎麼變出五種花樣
骨架搞懂了,剩下都是「換零件」。同一個「早上推行事曆」邏輯,改一兩個地方就能生出完全不同的用途。這五種是我實際用過的變形:
| 想達成 | 改哪裡 | 怎麼改 |
|---|---|---|
| 不想每天推、只想上班日 | Schedule Trigger | Trigger Interval 改成 Days of the week,勾 Mon~Fri |
| 下班前也推一次隔天的 | 加第二條 Schedule | 複製整個 workflow、Schedule 改 17:30,Google Calendar 的 Time Min/Max 改 {{ $now.plus({days:1}).startOf('day') }} 到 .endOf('day') |
| 推給整個小組、不只自己 | Slack Channel | Channel 從 #daily-me 改成 #team-daily;但事件內容改成小組共用行事曆會更有意義 |
| 推到 Line 不要 Slack | 換節點 | 刪 Slack 節點、加 Line Notify 節點(或用 HTTP Request 打 Line 的 webhook),詳見第 11 章 |
| 當天沒行程就不要推 | 中間加 IF 節點 | 在 Google Calendar 後面接 IF,條件 {{ $json.items.length }} 不等於 0,True 分支才接 Slack。core node IF 的用法之後專章講 |
你會發現,這些改法都不是「重做一個 workflow」,而是「同一個骨架換零件」。這就是 n8n 最強的地方——你學會的每一個 pattern,之後都是可以複用的積木。
這個 workflow 教會你的 pattern
抽掉細節,這章其實只教了一件事:Trigger → Action → Action。之後你會看到的所有 workflow,不管長多複雜,骨頭都是這個:
[什麼時候跑] → [拿什麼資料] → [做什麼事]
Trigger Action 1 Action 2
(可以 0 到很多顆) (通常最少 1 顆)
後面的章節只是把中間的部分擴充:
- 資料需要判斷分流?中間插一顆 IF(core node)
- 要同時處理多個來源?用 Merge 把它們合起來
- 資料要清一清、算一算?用 Set 或 Code 節點加工
- 要用不同 SaaS?換掉 Google Calendar、換掉 Slack 就好,pattern 不變
Trigger 有哪幾種、什麼時候該選哪種——第 10 章會拆完。SaaS 有哪些常用的、每個怎麼設——第 11 章。這章請把「Trigger → Action → Action」記牢,這是你的第一塊拼圖。
常見卡關
第一次做的人幾乎都會撞到下面五個之一。逐一對照:
-
Google Calendar 節點 OAuth 授權完,Calendar 下拉是空白
通常是授權時沒有勾 Calendar scope,或用錯 Google 帳號。解法:回到 credential 設定頁刪掉那組 credential,重新 Create new credential,OAuth 視窗跳出來時看清楚選的是你有 Calendar 的那個 Google 帳號,同意畫面要看到「查看您日曆的事件」這句才對。
-
Slack 節點紅色 401 / not_authed / invalid_auth
credential 沒設好或 token 過期。回到 credential 設定頁:(1)確認 Slack workspace 是對的那個;(2)重跑一次 OAuth;(3)如果公司 Slack 有安裝限制(app 需要 admin 批准),你可能要請 IT 先批准 n8n app 才能連。
-
Schedule Trigger 隔天早上沒自動跑
90% 的原因:右上 Active toggle 沒切成 Active。回去切就好。其他可能:(1)workflow 存檔前忘了 save,Schedule 沒生效;(2)時區設錯,你以為 08:00 是台北時間,但 self-hosted n8n 預設是
America/New_York(不是 UTC,也不是台北),實際會晚 13 小時才跑。改法:Workflow settings → Timezone 選Asia/Taipei,或改整個實例的GENERIC_TIMEZONE環境變數;(3)Trigger 節點被 deactivate(節點右鍵可以 Deactivate,會變灰色,整條 workflow 都不會跑)。 -
Executions 頁面完全沒紀錄
幾種可能:(1)時間還沒到,Schedule 從來沒被觸發;(2)Trigger 節點 disable 掉了(看看是不是灰色);(3)你手動按 Execute Workflow 但 workflow 的「Save Data」設定關掉了(Workflow settings → Save execution progress 設 On);(4)你在看整帳號的 Executions,但你的 workflow 是私人的,那筆躲在下面翻幾頁才看到。
-
Slack 收到訊息,但內容變成
{{ $json.items.length }}這串字Message Text 欄位沒切成 Expression 模式。欄位右邊有一顆小小的 fx 圖示,切過去才會執行
{{...}},不切的話就會被當成純文字。切完之後{{ $json.summary.length }}應該會即時預覽成數字。 -
訊息推出去了但事件標題顯示
undefinedGoogle Calendar API 有時候會回沒有
summary欄位的事件(例如全天空白事件、或某些日曆的隱私設定)。解法:把 expression 改成{{ $input.all().map(i => i.json.summary || '(無標題事件)').join('、') }},用||給一個 fallback 字串就不會 undefined。(有加 Aggregate 的話寫{{ $json.summary.map(s => s || '(無標題事件)').join('、') }}。)
還是不行的話,複製 Executions 頁面裡那筆的 URL 貼給同事或 IT,比自己截圖形容快多了。錯誤處理更完整的機制(Error workflow、retry)在第 17 章展開。
常見問題
跑失敗會怎樣?我會被通知嗎?
預設情況下,跑失敗會在 Executions 頁面留下紅色的一筆,但不會主動通知你。你不去看就不知道。如果這個 workflow 很重要(例如客戶通知、對帳),可以設定一個 Error Workflow——就是「主 workflow 掛掉的時候自動跑另一個 workflow 傳 Slack 給你」。詳細做法在第 17 章「錯誤處理」整套講。
一天跑一次會不會很浪費資源?
一天一次非常省。這個 workflow 跑一次大概不到 1 秒、資料量幾 KB,可以說完全不佔資源。你就算一天跑 24 次(每小時一次)也還在很省的範圍。Woow n8n 實例扛幾百個這種等級的 workflow 完全沒問題。真正會吃資源的是「每分鐘跑一次 + 一次抓幾萬筆 + 用 Code 節點做重度計算」那種,一般用不到。
我可以同時做很多個這種 workflow 嗎?會不會互相影響?
可以做很多個,彼此不會互相影響(每個 workflow 是獨立執行)。實務上很多同事帳號下都有 10~30 個 active workflow 在跑,都很順。要注意的只有兩件事:(1)credential 是共用的(例如同一組 Google credential 綁在多個 workflow),credential 被刪或 token 過期會一次全部炸;(2)名字取好一點,多了才好找。
Time Min / Time Max 用 $now.startOf('day') 是哪個時區?
用的是該 workflow 設定的時區,沒設就 fallback 到 n8n 實例的時區。self-hosted n8n 預設是 America/New_York(不是 UTC,也不是台北時間),一定要主動改過。兩個地方可以設:Workflow settings → Timezone 選 Asia/Taipei(只影響這個 workflow),或改實例的 GENERIC_TIMEZONE 環境變數(全部 workflow 都套用)。Woow 版通常已經預設 Taipei,但新拉的 workflow 建議還是自己確認一次。時區細節在第 14 章會補充。
我可以推到 Slack 的私訊(DM)而不是頻道嗎?
可以。Slack 節點的 Send Message To 選 User(不選 Channel),然後 User 從下拉選你自己或某個人的 Slack 名字。這樣訊息會出現在 Slackbot 的 DM 或跟那個人的私訊裡。對「只給自己看的每日提醒」這種用途,DM 通常比開一個專屬頻道乾淨。
如果我改了 workflow 但忘了 Save,Active 還是原來那版嗎?
對。Active 執行的是「最後一次 Save 的版本」,沒 Save 的改動不會生效。所以順序是:改動 → Save → 觀察下一次自動觸發。有些版本 canvas 上方會顯示「Unsaved changes」提醒你,看到那個字樣就代表你在編輯還沒存的草稿。
Google Calendar 節點看得到「同事分享給我的 calendar」嗎?
看得到,只要那個 calendar 已經分享給你的 Google 帳號、你在 Google Calendar 網頁版也看得到,n8n 節點的 Calendar 下拉就會出現。選擇對應的 calendar 就能抓那個 calendar 的事件。做「小組每日行程推送」很好用。
下一步該學什麼?
建議路線:第 9 章「items 資料流」把「節點之間到底怎麼傳資料」搞清楚(這是 n8n 最容易混淆的觀念),然後第 10 章「Trigger 全覽」認識除了 Schedule 之外還有哪些觸發方式(Webhook、SaaS Trigger、Manual)。這兩章讀完你就有能力自己設計第二個、第三個 workflow 了。想直接照範例動手做的話,第 11 章有一堆真實情境。