n8nとLINE連携:LINE公式アカウントで受け付けて知らせる

公開 書いた人 大林 諒

LINE公式アカウントに届いたメッセージをn8nで受け、Googleスプレッドシート「問い合わせ一覧」に1行足し、Slackの #問い合わせ に知らせ、お客様には受付の返事を返します。「問い合わせ受付」にLINEという入口を1つ足す作業で、LINE側の設定から署名の検証、返信、料金の見積もりまで通します。前提は、n8n 2.44.7(2026年10月時点の安定版)が n8n.example.co.jp のような独自ドメインとHTTPSで動いていることです。WebhookとHTTP Requestの基本は「n8nで外部サービスとつなぐ:Webhook・HTTP Request・API」に任せ、LINE固有の部分に絞ります。

この記事の内容
  1. 先に決めること
  2. 作るものの一覧
  3. LINE側の準備
  4. n8nで受ける
  5. 返信と送信をHTTP Requestで
  6. 問い合わせ受付に組み込む
  7. 応用
  8. できないことと、その代わり
  9. よく詰まるところ
  10. 次にやること

先に決めること

LINE Notifyの代わりをどれにするか

LINE Notifyは2025年3月31日でサービスを終えました。n8nの「Line」ノードはこのLINE Notify用で、2.44.7ではノードの一覧にも出ず、古いワークフローに残っていても何も届きません。LINEで知らせるなら、LINE公式アカウントを作ってMessaging APIで送ります。LINEヤフーもこれを代わりとして案内しています。

ただ、社員への通知に使っていたのなら、移り先をLINEにこだわる必要はありません。Messaging APIのプッシュ送信は通数で料金が決まり、社内向けに使うと無料の枠をすぐ使い切ります。

移り先費用向いている使い方
Slack(#問い合わせ)何件送っても同じ社内への通知の本命。この記事の例
社員のLINEグループへプッシュ1回でグループの人数分の通数Slackを使っていない少人数の会社
当番1人へプッシュ1回1通夜間・休日の当番だけに知らせる
Gmail追加の費用なし通知を記録として残したい

社内はSlack、お客様とはLINE、と分けるのが一番安く済みます。

お客様に返すのは「応答」、こちらから送るのは「プッシュ」

送り方は5種類あり、問い合わせ受付で使うのは2つです。

応答メッセージ(reply)プッシュメッセージ(push)
いつ送るかお客様からメッセージが来た直後いつでも
送り先の指定Webhookで届いた応答トークンユーザーID・グループID
期限受信から1分以内に、1回だけなし
通数(料金)数えない数える
エンドポイントPOST https://api.line.me/v2/bot/message/replyPOST https://api.line.me/v2/bot/message/push

受付の返事は必ず応答で返します。

料金はプッシュの通数で決まる

LINE公式アカウントの料金プランは次の3つです(2026年10月時点、日本のプラン、金額は税別)。

プラン月額固定費無料メッセージ通数(月)追加メッセージ
コミュニケーションプラン0円200通不可
ライトプラン5,000円5,000通不可
スタンダードプラン15,000円30,000通可(従量課金。単価は送信数で変わる)

追加メッセージの単価は2026年10月1日に改定されたので、LINEヤフーの料金ページで確かめます。開設したときはコミュニケーションプランです。通数は「送信対象になった人数」で数え、1回に吹き出しを何個入れても変わりません。公式の例では、4件入れたプッシュを5人いるトークルームに1回送ると5通です。ブロックしている相手に送った分は数えません。

数えるのはプッシュ・マルチキャスト・ブロードキャスト・ナローキャストで、応答メッセージと、LINE Official Account Managerのチャットで担当者が手で返す分は数えません。社員5人のグループに新着を知らせると1件5通なので、200通では月40件で止まります。上限を超えると送信は429で失敗します。コミュニケーションプランとライトプランは追加メッセージを買えないので、超えそうならプランを上げます。

n8nを外から届くURLで動かす

LINEのWebhookは、認証局の証明書を使ったHTTPSのURLにしか送られません。自己署名の証明書は使えず、手元のPCのn8nにも届かないので、n8n CloudかVPSに立てたn8nで組みます。立て方は「n8nをセルフホストで導入する:Docker ComposeでVPSに立て、独自ドメインとHTTPSで使う」に書いています。

作るものの一覧

ノード(名前)役割主な設定
Webhook(LINE受信)LINEからの通知を受けるPOST、Path: line、Immediately、Raw Body
Crypto(署名を計算)署名を計算するHmac、SHA256、BASE64
If(署名が一致)届いた署名と比べるx-line-signature
Split Out(イベントごとに分ける)イベントを1件ずつにするbody.events
Remove Duplicates(再送を捨てる)二重処理を防ぐwebhookEventId
Filter(テキストだけ通す)1対1のテキストだけ残すtype・message.type・source.type
HTTP Request(プロフィールを取る)表示名を取るGET /v2/bot/profile/{userId}
Edit Fields (Set)(LINE用に整える)シートの列に合わせる受付日時・お名前・内容ほか
HTTP Request(受付の返事)応答メッセージを返すPOST /v2/bot/message/reply
Google Sheets「問い合わせ一覧」に1行足すAppend Row
Slack#問い合わせ に知らせるSend a message
完成した「LINE問い合わせ受付」ワークフローの全体
完成した「LINE問い合わせ受付」ワークフローの全体

LINE側の準備

LINE公式アカウントを作り、Messaging APIを有効にする

2024年9月4日から、LINE DevelopersコンソールでMessaging APIのチャネルを直接は作れなくなりました。先にLINE公式アカウントを作り、そのアカウントでMessaging APIを有効にすると、チャネルが自動でできます。

  1. LINEヤフーの「ビジネスID」に登録し、続けて出る作成フォームでLINE公式アカウントを作る。
  2. LINE Official Account Managerで作ったアカウントを開く。
  3. 「設定」の「Messaging API」で「Messaging APIを利用する」を押す。
  4. 開発者情報を求められたら名前とメールアドレスを入れる。
  5. プロバイダーを選ぶか新しく作り、「同意する」、確認画面で「OK」を押す。
  6. 同じアカウントでLINE Developersコンソールに入り、チャネルができていることを確かめる。

プロバイダーは後から変えられません。違うプロバイダーでは同じ人でもユーザーIDが変わるので、会社名のプロバイダー1つにまとめ、制作会社やツール業者のプロバイダーの下には作りません。

チャネルシークレットとチャネルアクセストークン

n8nに渡す鍵は2つです。取り違えると署名が合わないか、送信が401で落ちます。

鍵場所n8nでの使い道入れるCredential
チャネルシークレット「チャネル基本設定」タブ届いた通知の署名を計算するCrypto の「Hmac Secret」
チャネルアクセストークン(長期)「Messaging API設定」タブで発行返信・送信のAPIを呼ぶBearer Auth の「Bearer Token」

トークンには、v2.1(期限を自分で決める・最長30日)、短期(30日)、ステートレス(15分)、長期(期限なし)があります。v2.1は発行にJWTの署名が要り、期限(最長30日)ごとに作り直す仕組みを自分で持つことになります。問い合わせ受付なら長期のトークンで回ります。長期は1チャネルに1つで、再発行すると今のトークンは止まります。漏れると全部の友だちに一斉送信されかねないので、n8nのCredential以外には置きません。Credentialの共有は「n8nの認証情報(Credential):OAuth2・APIキー・共有と権限」に書いています。

応答メッセージとあいさつメッセージを切る

最初から「応答メッセージ」(決まった文の自動返信)と「あいさつメッセージ」(友だち追加のお礼)がオンです。応答メッセージを残すと、n8nの返事と二重に届きます。

  1. 「Messaging API設定」タブで「応答メッセージ」の「編集」を押す。
  2. 開いた「設定」>「応答設定」で、応答メッセージをオフにする。
  3. あいさつメッセージは、出したいなら残す。

2022年12月から、チャットとWebhookは同時に使えます。担当者がチャットで手で返す運用と、n8nでの記録・通知は両立します。

n8nで受ける

Webhookノード

  1. 新しいワークフローを作り、名前を「LINE問い合わせ受付」にする。
  2. 最初のノードに「Webhook」を置き、名前を「LINE受信」にする。
  3. 「HTTP Method」を POST、「Path」を line にする。
  4. 「Authentication」は「None」のままにする。
  5. 「Respond」を「Immediately」(すぐに返事をする)にする。
  6. 「Add Option」から「Raw Body」を足してオンにする。
  7. 「Add Option」から「Only Run If」を足し、{{ $json.body.events.length > 0 }} を入れる。
Webhookノードの設定(Respond: Immediately、Raw Body、Only Run If を足した状態)
Webhookノードの設定(Respond: Immediately、Raw Body、Only Run If を足した状態)

LINEは2秒以内に返事が無いとタイムアウトとして扱うので、後ろを待たずに200を返す「Immediately」にします。LINEは自分で決めたヘッダーを付けられず、送信元IPアドレスも公開していないので、Header Authや「IP(s) Allowlist」を掛けると全部弾かれます。守りは署名の検証が担います。

「Raw Body」は、届いたままの本文をバイナリの data に残すオプションです。署名は届いた文字列そのものから計算する決まりで、読み取ったJSONを文字列に戻すと空白やエスケープが変わって一致しません。

「Only Run If」は、式が true のときだけ動かすオプションです。LINEは疎通確認に "events": [] だけの通知を送ることがあります。この式で、空の通知は200を返すだけで実行履歴にも残りません。

署名を確かめる

届いた通知の x-line-signature ヘッダーには、本文をチャネルシークレットでHMAC-SHA256にかけ、Base64にした値が入っています。同じ計算をして一致したものだけ通します。

  1. 「LINE受信」の後ろに「Crypto」を置き、名前を「署名を計算」にする。
  2. 「Action」を「Hmac」にする。
  3. 「Binary File」をオンにし、「Binary Property Name」を data にする。
  4. 「Type」を「SHA256」にする。
  5. 「Property Name」を signature にする。
  6. 「Encoding」を「BASE64」にする(初期値はHEX)。
  7. Credentialで「Crypto」を新しく作り、「Hmac Secret」にチャネルシークレットを入れる。
  8. 後ろに「If」を置き、名前を「署名が一致」にする。
  9. 条件の左に {{ $json.signature }}、比べ方に「String」の「is equal to」、右に {{ $json.headers['x-line-signature'] }} を入れる。
  10. 「true」の出口だけ次につなぎ、「false」の出口には何もつながない。
Cryptoノードの設定(Hmac、Binary File オン、SHA256、BASE64)
Cryptoノードの設定(Hmac、Binary File オン、SHA256、BASE64)

ヘッダー名はn8nの中では小文字で入るので、X-Line-Signature ではなく小文字で参照します。Codeノードでも書けますが、セルフホストでは組み込みモジュール crypto を許可する設定が要ります。Cryptoノードなら設定なしで動き、シークレットもCredentialに収まります。

先に200を返しているので、署名が合わない通知も送り主からは成功に見えます。偽の通知でシートやSlackが動かなければ足ります。

手元から署名つきで送って試す

スマートフォンの前に、curlで署名つきの通知を送って確かめます。「Listen for test event」を押して120秒以内に送ります。

SECRET='チャネルシークレットをここに'
BODY='{"destination":"U0","events":[{"type":"message","timestamp":1793000000000,"source":{"type":"user","userId":"U1"},"webhookEventId":"TEST001","replyToken":"dummy","message":{"type":"text","text":"料金表を送ってください"}}]}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -binary | openssl base64)
curl -X POST 'https://n8n.example.co.jp/webhook-test/line' \
  -H 'Content-Type: application/json' \
  -H "x-line-signature: $SIG" \
  --data-binary "$BODY"

「署名が一致」の true 側に出れば合っています。SECRET を1文字変えて false 側に落ちることも確かめます。応答トークンが偽物なので「受付の返事」は400で失敗しますが、そこまで来ていれば正常です。2回目は「再送を捨てる」で止まるので、webhookEventId を変えて送ります。

LINE側でWebhook URLを登録して検証する

  1. n8nでワークフローを公開(Publish)する。
  2. LINE Developersコンソールでチャネルを開き、「Messaging API設定」タブを開く。
  3. 「Webhook URL」の「編集」を押し、https://n8n.example.co.jp/webhook/line を入れて「更新」を押す。
  4. 「検証」を押し、「成功」と出ることを確かめる。
  5. 「Webhookの利用」をオンにする。
  6. 「Webhookの再送」をオンにする(注意事項が出るので読んでから)。
  7. 「エラーの統計情報」をオンにする。
  8. 同じタブのQRコードを自分のスマートフォンで読み、友だち追加する。

「検証」で送られるのは events が空の通知で、「Only Run If」で止まるので実行履歴は空のままです。中身はスマートフォンから確かめます。

「Webhookの再送」をオンにすると、n8nが止まっていた間の通知も復旧後に届くことがあります(回数と間隔は非公開で、保証はありません)。再送は deliveryContext.isRedelivery 以外は元と同じで、webhookEventId も変わらないので、これで重複を除けます。「エラーの統計情報」をオンにすると「Webhookエラー」タブに、n8nに届く前の失敗(接続できない、2秒以内に返事が無いなど)が残ります。

イベントを1件ずつに分ける

  1. 「署名が一致」の true の後ろに「Split Out」を置き、名前を「イベントごとに分ける」にする。「Fields To Split Out」は body.events。
  2. 後ろに「Remove Duplicates」を置き、名前を「再送を捨てる」にする。「Operation」は「Remove Items Processed in Previous Executions」、「Keep Items Where」は「Value Is New」、「Value to Dedupe On」は {{ $json.webhookEventId }}。
  3. 後ろに「Filter」を置き、名前を「テキストだけ通す」にする。条件は3つで、すべて「is equal to」。{{ $json.type }} が message、{{ $json.message.type }} が text、{{ $json.source.type }} が user。
Split Outの出力。1件のアイテムに type・replyToken・source・message が並んでいる状態
Split Outの出力。1件のアイテムに type・replyToken・source・message が並んでいる状態

1回の通知に複数のイベントが入ることがあるので分けます。Filterで落ちるのは友だち追加(follow)、ブロック(unfollow)、スタンプや画像、グループでの発言です。画像も記録したいなら、Filterの代わりに「Switch」で message.type ごとに分けます(「n8nの分岐とループ:IF・Switch・Merge・Loop Over Items」)。

返信と送信をHTTP Requestで

HTTP Requestの認証は、どれも「Authentication」を「Generic Credential Type」、「Generic Auth Type」を「Bearer Auth」にし、チャネルアクセストークンのCredentialを選びます。

プロフィールで表示名を取る

  1. 「テキストだけ通す」の後ろに「HTTP Request」を置き、名前を「プロフィールを取る」にする。
  2. 「Method」は GET、「URL」は https://api.line.me/v2/bot/profile/{{ $json.source.userId }}。
  3. 「Settings」の「On Error」を「Continue」にする。

返ってくるのは displayName(表示名)・pictureUrl・statusMessage などで、電話番号やメールアドレスは取れません。友だちでない人やブロックした人などは404になりますが、問い合わせ自体は記録したいので止めずに続けます。

受け取った内容を整える

「プロフィールを取る」の後ろに「Edit Fields (Set)」を置き、名前を「LINE用に整える」にして、シートの列名に合わせた項目を作ります。

項目値
受付日時{{ DateTime.fromMillis($('テキストだけ通す').item.json.timestamp).setZone('Asia/Tokyo').format('yyyy-LL-dd HH:mm') }}
お名前{{ $json.displayName ?? '(表示名を取得できず)' }}
内容{{ $('テキストだけ通す').item.json.message.text }}
経路LINE
LINEユーザーID{{ $('テキストだけ通す').item.json.source.userId }}
replyToken{{ $('テキストだけ通す').item.json.replyToken }}

timestamp はミリ秒のUNIX時間で、再送でも元の発生時刻が入るので、遅れて届いても正しい時刻で残ります。式の書き方は「n8nの式(Expression)とデータの扱い:$json・ノードの参照・日付・JSON」に書いています。

応答メッセージで受付の返事を返す

  1. 「LINE用に整える」の後ろに「HTTP Request」を置き、名前を「受付の返事」にする。
  2. 「Method」は POST、「URL」は https://api.line.me/v2/bot/message/reply。
  3. 「Send Body」をオンにし、「Body Content Type」を「JSON」、「Specify Body」を「Using JSON」にして下を入れる。
  4. 「Settings」の「Retry On Fail」はオフのままにする。
{
  "replyToken": "{{ $json.replyToken }}",
  "messages": [
    {
      "type": "text",
      "text": "お問い合わせありがとうございます。内容を受け付けました。担当者から営業時間内にご連絡します。"
    }
  ]
}
「受付の返事」ノードの設定(URLとJSONの本文)
「受付の返事」ノードの設定(URLとJSONの本文)

応答トークンは1回限りで、受信から1分以内に使う決まりです。なるべく早く使うよう求められているので、返事はシートやSlackより前に置きます。「Retry On Fail」を切るのは、1回目が届いていたときに2回目が「Invalid reply token」で落ちるからです。吹き出しは5つまでです。

プッシュで社員のグループに知らせる(Slackを使わない場合)

  1. 「Messaging API設定」タブの「グループトーク・複数人トークへの参加を許可する」を有効にする(初期値は無効)。
  2. 社員のLINEグループにLINE公式アカウントを招待する。
  3. 招待したときに届く参加イベント(join)の source.groupId を、実行履歴の「イベントごとに分ける」の出力から控える。
  4. HTTP Requestの「URL」を https://api.line.me/v2/bot/message/push にし、本文の to にそのグループIDを入れる。

1つのグループに入れるLINE公式アカウントは1つだけです。二重送信を防ぐ X-Line-Retry-Key は「n8nで外部サービスとつなぐ:Webhook・HTTP Request・API」に書いています。プッシュはブロックした相手にも200が返り、実際には届きません。

問い合わせ受付に組み込む

  1. 「問い合わせ一覧」の右端に「経路」「LINEユーザーID」の列を足す。フォーム側のワークフローは「経路」に フォーム を入れるように直す。
  2. 「受付の返事」の後ろに「Google Sheets」を置き、「Append Row」で「問い合わせ一覧」を選ぶ。
  3. 各列の値は、前のノードではなく「LINE用に整える」から取る。例: 「内容」は {{ $('LINE用に整える').item.json['内容'] }}。「会社名」「メールアドレス」は空のまま、「対応状況」は 未対応。
  4. 後ろに「Slack」の「Send a message」を置き、#問い合わせ を選んで下の本文を入れる。
LINEから問い合わせが届きました
お名前: {{ $('LINE用に整える').item.json['お名前'] }}
内容: {{ $('LINE用に整える').item.json['内容'] }}
返信はLINE Official Account Managerのチャットから

$json で参照しないのは、「受付の返事」を通ると中身がLINEの返事(sentMessages)に置き換わるからです。Slackの通知は「n8nとSlack連携:通知・投稿・ボタンで承認・スラッシュコマンド」に詳しく書いています。

LINEからは会社名もメールアドレスも取れないので、受付の返事でお願いするか、Webフォームへ案内します。2回目以降の返事は、担当者がチャットで手で返すのが通数もかからず確実です。

応用

AIで問い合わせの種類を振り分ける

「受付の返事」と「Google Sheets」の間にAI Agentノードを入れ、内容から「見積もり」「サポート」「その他」を選ばせて「種類」の列に入れます。返事を先に済ませておけば、AIが遅くても応答トークンの1分に引っかかりません。AIの答えをお客様に返すときは、POST https://api.line.me/v2/bot/chat/loading/start で1対1のトークにローディングのアニメーション(5〜60秒)を出せます。組み方は「n8nでAIエージェントを作る:ChatGPT・Claude・Geminiをつなぐ」に書いています。

通数の残りを毎朝見張る

Schedule Triggerで毎朝1回、次の2つをHTTP Requestで取り、使った割合が8割を超えたら #n8n-監視 に知らせます。

取るものエンドポイント返ってくる値
当月の上限目安GET https://api.line.me/v2/bot/message/quotatype(none か limited)と、limited のときだけ value
当月に使った数GET https://api.line.me/v2/bot/message/quota/consumptiontotalUsage

利用状況の totalUsage は管理画面から手で配信した分を含む概算です。超えると月末まで通知が止まります。

できないことと、その代わり

できないこと代わりの手
LINE Notifyや旧「Line」ノードで通知するMessaging APIのプッシュ。社内向けならSlackかGmail
LINE公式アカウントがいないトークのメッセージを受ける友だち追加かグループ招待をしてもらう
お客様の電話番号・メールアドレス・LINE IDを取る返事で聞くか、Webフォームへ案内する
送信元IPアドレスや独自ヘッダーで送り主を絞る署名の検証で確かめる
2秒を超える処理を終えてから返事をするWebhookの「Respond」を「Immediately」にし、処理は後ろで続ける
1分を過ぎてから応答メッセージで返すプッシュで送る(通数を使う)か、チャットで手で返す
無料・ライトプランで通数を超えて送るスタンダードプランに上げる
複数のLINE公式アカウントを1つのPathで受けるシークレットが違うのでPathを分ける

よく詰まるところ

症状原因直し方
「検証」が失敗する未公開、またはURLが webhook-test のまま公開し、URLを /webhook/line にする
「検証」で TLS handshake failure自己署名の証明書か、中間証明書の不足認証局の証明書と中間証明書を入れる
「検証」で403Header AuthかIP(s) Allowlistを掛けている両方外す
「検証」は成功するのに実行履歴が空「Only Run If」で空の通知を止めている正常。スマートフォンから送って確かめる
Webhookノードに出るURLが localhost外向きのURLを設定していないN8N_WEBHOOK_URL=https://n8n.example.co.jp/ を入れて再起動
署名が一致しない「Raw Body」がオフで、読み取ったJSONから計算している「Raw Body」をオンにし、「Binary File」から計算する
署名が一致しない(Raw Bodyはオン)シークレットの取り違え・再発行、「Encoding」がHEXシークレットを入れ直し、BASE64にする
送信が401トークンの再発行、またはシークレットを入れたCredentialを今のトークンに直す
「Invalid reply token」2回使った、1分を過ぎた返事を前に置き、「Retry On Fail」を切る
返事が二重に届く応答メッセージがオンのまま応答設定でオフにする
シートに同じ行が2つ入る再送で同じイベントが2回届いたwebhookEventId で重複を捨てる
429「You have reached your monthly limit.」無料の通数を使い切った社内通知をSlackに移すか、プランを上げる
プッシュは200なのに届かないブロックされている200を届いた証拠にしない
プロフィールが404友だちでない、ブロックされた、取得に同意していない「Continue」で表示名なしで記録する
グループの発言で userId が無いiOS版・Android版以外のLINEから送られたグループIDだけで記録する
Executionsタブで「署名が一致」のfalse側に止まった実行を開いた状態
Executionsタブで「署名が一致」のfalse側に止まった実行を開いた状態

詰まったらExecutionsタブでどのノードまで動いたかを見て、何も無ければLINE側の「Webhookエラー」タブを見ます。

次にやること

あわせて読む