「問い合わせ受付」ワークフローに、既存の顧客かどうかで分ける分岐と、問い合わせの種類で送り先を変える分岐を足し、途中で1本に合流させます。あわせて、スプレッドシートにたまった過去の問い合わせを100件ずつkintoneへ移すループを作ります。読み終わると、If・Switch・Filter・Merge・Loop Over Itemsのどれをどこに置くかを自分で決められます。前提は「n8nの使い方入門:最初のワークフローを作って動かす」で作った「問い合わせ受付」が動いていることです。版は2026年10月時点の安定版2.44.7です。
この記事の内容
先に決めること
ループを書く前に、要るかどうかを見る
n8nのノードは、届いたitemを1件ずつ処理します。シートから30行読めば、後ろのSlackは何も設定しなくても30回動きます。「Loop Over Items」の設定欄にも「You may not need this node — n8n nodes automatically run once for each input item.」(ノードはitemごとに自動で1回ずつ動くので、このノードは要らないかもしれません)と出ます。
自動で繰り返さない例外で当たりやすいのは次の3つです。
| ノード | 動き方 | どうするか |
|---|---|---|
| Code(Run Once for All Items)、Execute Workflow(Run once with all items) | 全itemをまとめて1回 | コードの中で回す。サブワークフローは「Run once for each item」にする |
| RSS Read | 1回で1つのURLしか読まない | Loop Over Itemsで1件ずつ渡す |
| HTTP Requestのページ送り | 1回で1ページ分 | 「Pagination」の設定で続きを取る |
ページ送りは「n8nで外部サービスとつなぐ:Webhook・HTTP Request・API」に、Codeノードは「n8nのCodeノード:JavaScriptとPythonで足りないところを書く」に書いています。
分岐するか、式で済ませるか
分岐を足すと、後ろのノードは道の数だけ増えます。本番で動かしていると、送り先のチャンネルだけが違うSlackノードが6つ並び、文面を1行直すたびに6か所直すことになります。分かれた先でやることが同じで値だけ違うなら式で済ませ、やることそのものが違う(片側だけ顧客管理に登録する、など)なら分岐します。式の書き方は「n8nの式(Expression)とデータの扱い:$json・ノードの参照・日付・JSON」にあります。
分けたあと、合流させるか
分かれた道の後ろに共通の処理(シートに1行足す、など)があるなら、「Merge」で1本に戻してから共通の処理を1つだけ置きます。合流させないと同じノードを道の数だけ複製することになり、列を1つ足すたびに全部を直します。
Execution Logicを確かめる
分岐の動く順番は、ワークフロー名の右の三点メニューから開く「Settings」の「Execution Logic」で決まります。1.0以降に作ったワークフローは「v1 (recommended)」で、分岐を上から1本ずつ最後まで動かします。高さが同じなら左が先です。古いワークフローやテンプレートは「v0 (legacy)」のことがあり、Ifの後ろにMergeを置くと、データが流れていない側の道まで動くことがあります。この記事はv1で書いています。

分岐とループのノード一覧
| ノード | すること | 出口 | 問い合わせ受付での使いどころ |
|---|---|---|---|
| If | 条件に合うかで2つに分ける | true / false | 既存顧客か新規か |
| Switch | ルールか式で3つ以上に分ける | ルールの数+どれにも当たらないもの | 種類で送り先を変える |
| Filter | 条件に合うものだけ通し、ほかは捨てる | 1つ | 採用の問い合わせを除く |
| Merge | 2つ以上の流れを1つにする | 1つ | 合流、顧客一覧との突き合わせ |
| Loop Over Items | 決めた件数ずつ区切って繰り返す | done / loop | 100件ずつkintoneに登録 |
| Aggregate | 複数のitemを1つにまとめる | 1つ | 100件を1回のリクエストにする |
| Remove Duplicates | 重複したitemを取り除く | 1つ | 二重送信を1件にする |
| Wait | 時間か合図が来るまで止める | 1つ | ループの間隔、承認待ち |
If・Switch・Filterの使い分け
3つとも条件の書き方は同じで、違うのは出口の数と、合わなかったitemの行き先です。
| やりたいこと | 使うノード | 理由 |
|---|---|---|
| 条件に合わないものを捨てるだけ | Filter | 出口が1つで線が増えない |
| 2つに分けて、両方で別の処理をする | If | trueとfalseの2本 |
| 3つ以上に分ける | Switch(Rules) | ルールごとに出口ができ、どれにも当たらないものの出口も作れる |
| 値から出口の番号を計算する | Switch(Expression) | 「Output Index」に番号を返す式を書く |
| 1件を複数の出口に同時に流す | Switch(Send data to all matching outputs) | Ifはどちらか1本にしか流さない |
| 「かつ」と「または」を混ぜる | Ifを2つ重ねる | 1つの条件の束の中ではANDとORを混ぜられない |
条件は「Add condition」で足し、左に調べる値、真ん中で型と比べ方、右に比べる値を入れます。Stringなら「is equal to」「contains」「is empty」、Numberなら「is greater than」、Date & Timeなら「is after」のように、型ごとに比べ方が出ます。型が合わないと「Wrong type: ‘5’ is a string but was expecting a number」のように止まります。フォームから届く値はたいてい文字列なので、Numberで比べると起きます。IfとSwitchの「Convert types where required」をオンにすると、比べ方に合わせて変換されます。英字の大文字と小文字を区別しないなら「Add option」から「Ignore Case」を足します。
手順1:既存顧客かどうかで分けて、合流させる(If・Merge)
| 順番 | ノード名 | ノード | 役割 |
|---|---|---|---|
| 1 | 問い合わせフォーム | n8n Form Trigger | 入門の記事で作ったもの |
| 2 | 顧客一覧を読む | Google Sheets(Get row(s) in sheet) | 顧客一覧を全部読む |
| 3 | 顧客と突き合わせ | Merge(Combine) | メールアドレスが一致したら担当を足す |
| 4 | 既存顧客か | If | 担当が入っているかで分ける |
| 5 | 既存顧客の印 / 新規の印 | Edit Fields (Set) | 「区分」を付ける |
| 6 | 合流 | Merge(Append) | 2本を1本に戻す |
| 7 | 問い合わせ一覧に追加 | Google Sheets(Append Row) | 「区分」「担当」も書く |
| 8 | 種類で振り分け | Switch | 手順2で作る |


準備:顧客一覧と列を足す
- スプレッドシート「問い合わせ一覧」に新しいシートを足し、名前を「顧客一覧」にする
- 1行目に「会社名」「メールアドレス」「担当」を入れ、2行目から取引中の顧客を入れる(例: 株式会社サンプル / yamada@example.com / 佐藤)
- 「シート1」の見出しの右に「区分」「担当」の2列を足す
Mergeで顧客一覧と突き合わせる
- 「問い合わせフォーム」の右の + を押し、Google Sheetsの「Get row(s) in sheet」を選ぶ
- 「Document」は「問い合わせ一覧」、「Sheet」は「顧客一覧」を選び、「Filters」は空のままにする
- ノード名を「顧客一覧を読む」に変える
- キャンバスに「Merge」を置き、ノード名を「顧客と突き合わせ」に変える
- 「問い合わせフォーム」の出口から、Mergeの1つ目の入口(Input 1)へ線を引く
- 「顧客一覧を読む」の出口を、2つ目の入口(Input 2)へつなぐ
- 「Mode」を「Combine」、「Combine By」を「Matching Fields」にする
- 「Fields To Match Have Different Names」をオンにし、「Input 1 Field」に
email、「Input 2 Field」にメールアドレスを入れる - 「Output Type」を「Enrich Input 1」にする
「Enrich Input 1」は、Input 1の問い合わせを必ず残し、一致する顧客がいればその行の「担当」などを足します。ここを「Keep Matches」(一致したものだけ)にすると、新規の問い合わせはここで0件になり、後ろが何も動きません。

Mergeのモードは次のとおりです。合流はAppend、突き合わせはCombineのMatching Fields、と覚えれば足ります。
| Mode | 細かい指定 | 何が出るか | 使う場面 |
|---|---|---|---|
| Append | ー | 各入口のitemを順に並べたもの | 分岐の合流 |
| Combine | Matching Fields・Keep Matches | 一致したものだけ、両方の列を合わせて | 両方にあるものだけ欲しい |
| Combine | Matching Fields・Keep Non-Matches | 一致しなかったもの。初期値は両方の入口の分。「Output Data From」を「Input 1」にすると問い合わせ側だけ | 顧客一覧に無い問い合わせだけ |
| Combine | Matching Fields・Enrich Input 1 | Input 1全部に、一致したInput 2の列を足す | 問い合わせに担当を足す |
| Combine | Position | 1件目同士、2件目同士を合わせる | 件数と順番が必ず同じ2つ |
| SQL Query | ー | input1・input2 をSQLで結合した結果 | 3つ以上を突き合わせる |
| Choose Branch | ー | 指定した入口のデータだけ | 両方終わるのを待ってから進む |
列の名前がぶつかると初期値ではInput 2の値が勝ち、「Add option」の「Clash Handling」で変えられます。
Ifで既存顧客か新規かに分ける
- 「顧客と突き合わせ」の右の + から「If」を足し、ノード名を「既存顧客か」に変える
- 「Conditions」の左の欄に
{{ $json['担当'] }}を入れる - 比べ方で「String」の「is not empty」を選ぶ
- 「Execute step」を押し、yamada@example.com の問い合わせがtrue側に出ることを確かめる
日本語の列名は $json['担当'] の形で書くと、空白や記号が入った列名でも同じ書き方で取れます。出口は上がtrue、下がfalseです。片側に何もつながないと、そちらに流れたitemはエラーも出さずに消えます。
![Ifの設定。$json['担当'] が is not empty の条件](https://nexly.co.jp/n8n/wp-content/uploads/2026/10/n8n-27-04.png)
印を付けて合流させる
- trueの出口に「Edit Fields (Set)」を足し、ノード名を「既存顧客の印」に変える
- 「Add Field」を押して名前を「区分」、値を「既存顧客」にし、「Include Other Input Fields」をオンにする
- falseの出口にも「Edit Fields (Set)」を足し、ノード名を「新規の印」に変える
- 「区分」を「新規」、「担当」を「未定」にし、「Include Other Input Fields」をオンにする
- 「Merge」を足してノード名を「合流」に変え、「Mode」は「Append」、「Number of Inputs」は「2」のままにする
- 「既存顧客の印」をInput 1に、「新規の印」をInput 2につなぐ
- 「合流」の後ろに「問い合わせ一覧に追加」をつなぎ直し、「区分」の列に
{{ $json['区分'] }}、「担当」の列に{{ $json['担当'] }}を入れる
「Include Other Input Fields」をオフにすると「区分」だけのitemになり、シートの会社名が空になります。
Appendは、動いた入口のitemを順に並べて出します。v1では片方の道に何も流れなくても、動いた側のitemだけで先へ進みます。合流の後ろは、どちらの道から来ても同じ $json で値が取れる形にしておきます。$('既存顧客の印') のように片方の道のノードを名指しすると、もう片方から来たitemでは値が取れません。

実は、この程度の差ならIfもMergeも使わず、「区分」の列に {{ $json['担当'] ? '既存顧客' : '新規' }} と書くだけで済みます。分岐と合流が役に立つのは、片側にだけノードが増えるとき(新規のときだけSalesforceにリードを登録する、など)です。顧客管理への登録は「n8nとkintone・Salesforce・HubSpot連携:問い合わせを顧客管理に入れる」に書いています。
顧客一覧が大きいときは、0件に備える
顧客一覧が数千行あるなら、全部読まずに「顧客一覧を読む」の「Filters」で「Column」に「メールアドレス」、「Value」に {{ $json.email }} を入れて1行だけ探し、直接Ifへつなぎます。
ここに落とし穴があります。一致する行が無いとGoogle Sheetsは0件を返し、n8nは0件を出したノードから先を動かしません。出力欄にも「n8n stops executing the workflow when a node has no output data.」と出ます。新規の問い合わせだけがどこにも届かず、実行は「Success」なのでエラー通知も飛びません。運用しているn8nで、これに1週間気づかなかったことがあります。
直し方は、Google Sheetsノードの「Settings」で「Always Output Data」をオンにすることです。0件のときに空のitemを1つ出すので、Ifのfalse側に流れます。このときIfより後ろの $json にフォームの値は無いので、$('問い合わせフォーム').item.json.email のようにノード名で取ります。同じメールアドレスの行が2つあると通知が2通飛ぶので、「Add option」から「Return only First Matching Row」も足します。「Always Output Data」をIf自体に付けるのはやめます。公式も無限ループの恐れがあると注意しています。

手順2:問い合わせの種類で振り分ける(Switch)
フォームの「種類」(サービスについて・お見積もり・採用・その他)と、手順1の「区分」で送り先を変えます。
| 出口の名前 | 条件 | 送り先 |
|---|---|---|
| 既存顧客 | 区分が「既存顧客」 | #問い合わせ(本文に担当の名前) |
| お見積もり | 種類が「お見積もり」 | #問い合わせ-営業 |
| 採用 | 種類が「採用」 | #採用 |
| その他 | どのルールにも当たらない | #問い合わせ |
- 「問い合わせ一覧に追加」の右の + から「Switch」を足し、ノード名を「種類で振り分け」に変える
- 「Mode」は「Rules」のまま、1つ目の「Routing Rule」の左に
{{ $('合流').item.json['区分'] }}、「String」の「is equal to」、右に「既存顧客」と入れる - そのルールの「Rename Output」をオンにし、「Output Name」に「既存顧客」と入れる
- 「Add Routing Rule」で2つ目を足し、左に
{{ $('合流').item.json['種類'] }}、右と出口の名前に「お見積もり」と入れる - 同じように3つ目を「採用」で作る
- 「Add option」から「Fallback Output」を足して「Extra Output」を選び、「Rename Fallback Output」で「その他」と名付ける
- 4つの出口にSlackの「Send a message」をつなぎ、送り先を表のとおりにする。本文の式は入門の記事の
$('問い合わせフォーム')を$('合流')に置き換えて書く - キャンバス下の「Execute」で4通りの問い合わせを送り、それぞれの出口に1件ずつ出ることを確かめる
Switchの直前はシートのノードで $json はその結果を指すので、$('合流') で名指ししています。
ルールは上から順に調べ、最初に当たった出口にだけ流れます。既存顧客からの見積もり依頼は「既存顧客」に行き、営業には行きません。両方に出すなら「Add option」の「Send data to all matching outputs」をオンにします。
「Fallback Output」の初期値は「None (default)」で、どのルールにも当たらないitemは捨てられます。選択肢を1つ足した日から、その種類だけ誰にも届かなくなる事故がこれで起きます。Switchには必ず「Extra Output」を作ります。

本文からAIに種類を判定させるなら、Switchの代わりに「Text Classifier」を置きます。組み方は「n8nでAIエージェントを作る:ChatGPT・Claude・Geminiをつなぐ」にあります。
式で出口を決める(Expressionモード)
「Mode」を「Expression」にすると、「Number of Outputs」で出口の数を決め、「Output Index」に出口の番号を返す式を書きます。番号は0から数えます。上の表を出口4つで書くと次のとおりです。
{{ $('合流').item.json['区分'] === '既存顧客' ? 0 : ({ 'お見積もり': 1, '採用': 2 }[$('合流').item.json['種類']] ?? 3) }}
種類が多いと短く済みますが、画面で条件が読めなくなるので、運用ではRulesを基本にしています。番号が出口の数を超えると「The ouput 4 is not allowed」(原文のつづりのまま)で止まります。
手順3:過去の問い合わせを100件ずつkintoneへ移す(Loop Over Items)
Loop Over Itemsが要る場面
| 場面 | 例 | 要るか |
|---|---|---|
| 1件ずつ処理するだけ | 30行をSlackに1通ずつ | 要らない |
| 1回のリクエストに件数の上限がある | kintoneの一括登録は1回100件まで | 要る |
| 相手に間隔を空けて送る | Slackは1チャンネルに1秒1通が目安 | 要る(Waitと組む)。HTTP Requestなら「Batching」で済む |
| 1件ごとに複数のノードを順に通す | 登録して、結果をシートに書き戻す | 要る |
ここでは、シートにたまった過去の問い合わせ(例: 800行)をkintoneへ移します。kintoneの「複数のレコードを登録する」APIは1回100件までなので、100件ずつ区切ってまとめます。Credentialは「n8nで外部サービスとつなぐ:Webhook・HTTP Request・API」で作ったHeader Authを使います。
手順
- 新しいワークフロー「過去の問い合わせをkintoneへ移す」を作り、「Add first step」から「Trigger manually」を選ぶ
- + からGoogle Sheetsの「Get row(s) in sheet」を足し、「問い合わせ一覧」の「シート1」を選ぶ
- + から「Filter」を足し、
{{ $json['種類'] }}が「is not equal to」「採用」の条件を入れて、ノード名を「採用以外に絞る」に変える - + から「Loop Over Items」を足し、「Batch Size」を「100」にして、ノード名を「100件ずつ」に変える
- 「loop」の出口に「Aggregate」を足し、「Aggregate」を「All Item Data (Into a Single List)」にする(「Put Output in Field」は「data」のまま)
- その後ろにHTTP Requestを足し、「Method」を「POST」、「URL」を
https://example.cybozu.com/k/v1/records.json、「Authentication」をkintone用のHeader Authにする - 「Send Body」をオンにし、「Body Content Type」を「JSON」、「Specify Body」を「Using JSON」にして、下のJSONを入れる
- その後ろに「Wait」を足し、「After Time Interval」で「Wait Amount」を「1」、「Wait Unit」を「Seconds」にする
- Waitの出口から「100件ずつ」の入口へ線を引いて戻す
- 「done」の出口にSlackを足し、#問い合わせ に
移行が終わりました({{ $('採用以外に絞る').all().length }}件)と送る設定にして、「Settings」で「Execute Once」をオンにする - まずFilterの条件で5件に絞ってキャンバス下の「Execute」で通し、kintoneに5件入るのを確かめてから全件を流す
{
"app": 12,
"records": {{ JSON.stringify($json.data.map(r => ({
"会社名": { "value": r['会社名'] },
"氏名": { "value": r['お名前'] },
"メール": { "value": r['メールアドレス'] },
"種類": { "value": r['種類'] },
"問い合わせ内容": { "value": r['内容'] }
}))) }}
}
app はアプリID、records の中のキーはkintoneのフィールドコードです。Aggregateが100行を data という配列1つにまとめているので、それを map でkintoneの形に組み替えています。

ループの中で何が起きているか
Loop Over Itemsは受け取った800件を覚えておき、「loop」側に最初の100件を出します。後ろのノードが終わって入口に戻ってくると、次の100件を出します。全部出し切ると、戻ってきたitemをまとめて「done」側に出して止まります。止める条件をIfで書く必要はありません。
「done」に出るのはWaitから戻ってきたitem(リクエスト1回につき1件、計8件)で、元の800行ではありません。そのため通知に「Execute Once」を付け、件数は元のノードから数えています。今何回目かは {{ $('100件ずつ').context['currentRunIndex'] }} で取れます。

失敗に備える
kintoneの一括登録は、100件のうち1件でもエラーがあると、その100件がすべて取り消されます。HTTP Requestの「Settings」で「On Error」を「Continue (using error output)」にし、エラーの出口にSlackへの通知をつなぎ、その先を同じWaitにつないでループに戻します。戻さないと、失敗した回でループが止まります。止まるまでの回は登録済みなので、最初から流し直さず、Filterで残りに絞ってから流します。
応用
フォームの二重送信を1件にする
送信ボタンを2回押されると同じ問い合わせが2件届きます。フォームの直後に「Remove Duplicates」を置き、「Operation」を「Remove Items Processed in Previous Executions」、「Keep Items Where」を「Value Is New」、「Value to Dedupe On」を {{ $json.email }}{{ $json.message }} にすると、前の実行で見た組み合わせはそこで落ちます。覚える件数は「History Size」で、初期値は10,000件です。シートの重複の防ぎ方は「n8nとGoogleスプレッドシート連携:読み書き・更新・重複を防ぐ」にあります。
承認を待ってから進める
Waitの「Resume」を「On Form Submitted」にすると、フォームが送られるまで止まります。見積もり依頼は上長が承認してから返信する、という流れが作れます。承認用のURLは $execution.resumeFormUrl で取れるので、手前のSlackで送ります。「Limit Wait Time」をオンにすれば、決めた時間で待つのをやめて先へ進みます。
間隔を空けて送るより、まとめて1通
毎朝「未対応」を1件ずつSlackに送り直すなら、Loop Over Itemsの「Batch Size」を1にし、Waitを1秒置いて戻します。ただ実際に運用していると、20通に分けるより、Aggregateで1つにまとめて「未対応は20件です」と1通で送るほうが読まれます。毎朝動かす設定と、長い分岐をサブワークフローに切り出す方法は「n8nの定期実行とサブワークフロー:Schedule Triggerと部品化」に書いています。
できないことと、その代わり
| できないこと | 代わりの手 |
|---|---|
| 1つの条件の束でANDとORを混ぜる | Ifを2つ重ねる。または式で真偽を作り「is true」で比べる |
| Mergeの「Combine」で3つ以上を突き合わせる | 「SQL Query」にするか、Mergeを2段にする |
| 分かれた道を同時に並行して動かす | v1は上から1本ずつ動く。重い処理はサブワークフローに切り出す |
| ループの中のループを、1枚のキャンバスで追いやすく組む | 入れ子にすると戻る先が分かりにくく、実行の追跡もしにくい。運用では内側をサブワークフローにしている |
| 実行をまたいだ重複をIfで見分ける | Remove Duplicatesを使う |
| Waitの時刻をワークフローのTimezoneで決める | Waitは常にサーバーの時刻で動く。GENERIC_TIMEZONE と TZ を Asia/Tokyo にしておく |
| n8n Cloudで長時間のループを1回で流す | 1回の実行時間に上限がある(体験中は180秒)。件数を区切って何回かに分ける |
よく詰まるところ
| 症状 | 原因 | 直し方 |
|---|---|---|
| 新規の問い合わせだけ届かない。実行は「Success」 | 検索したノードが0件を出し、そこで止まった | そのノードで「Always Output Data」をオンにするか、Mergeの「Enrich Input 1」で突き合わせる |
| Ifのfalse側に流れたものが消える | false側の出口に何もつながっていない | 出口にノードをつなぐ。捨てるならFilterにする |
| 新しい種類の問い合わせだけ誰にも届かない | Switchの「Fallback Output」が「None (default)」 | 「Extra Output」にして通知をつなぐ |
| 同じ通知が2通来る | 顧客一覧に同じメールアドレスの行が2つある | 「Return only First Matching Row」を足す。Mergeなら「Multiple Matches」を「Include First Match Only」に |
| Mergeから先が動かない | 「Choose Branch」は両方の入口が揃うまで動かない。「Keep Matches」は片方が空だと0件 | 合流は「Append」、突き合わせは「Enrich Input 1」にする |
| 一致するはずの顧客が新規になる | 大文字と小文字、前後の空白が違う | 突き合わせる前に {{ $json.email.trim().toLowerCase() }} で揃える |
| 古いワークフローでIfの両方の道が動く | Execution Logicが「v0 (legacy)」 | 「v1 (recommended)」に替えて試し直す |
| 「Wrong type: ‘5’ is a string…」で止まる | 文字列をNumberで比べている | 「Convert types where required」をオンにする |
| ループが最初の100件で終わる | 最後のノードを入口に戻していない、または処理を「done」側につないだ | 処理は「loop」側につなぎ、最後から入口へ線を戻す |
| ループが途中で止まる | ループの中のIfやFilterで、ある回が0件になった。またはエラーの出口を戻していない | 0件になりうるノードはループの前へ移す。エラーの出口も戻す |
| ループが終わらない | 「Reset」がオン、またはIfで作ったループの終わりの条件が成り立たない | 「Reset」をオフにする。自作のループは回数の上限を条件に足す |
詰まったら、Executionsタブで該当の実行を開き、ノードに付いた件数を上から追います。件数が0になったノードか、急に増えたノードが原因です。

次にやること
- n8nの式(Expression)とデータの扱い:$json・ノードの参照・日付・JSON
- n8nのCodeノード:JavaScriptとPythonで足りないところを書く
- n8nの使い方入門:最初のワークフローを作って動かす
- n8nでAIエージェントを作る:ChatGPT・Claude・Geminiをつなぐ