LINE Messaging API 複数チャンネルを使い分ける設計と同一プロバイダーでのユーザーID共有

LINE公式アカウントと社内Bot、どちらにもメッセージが届くのに「どのチャンネルで返すべきか」が毎回わからなくなっていませんか?同一プロバイダー内ならユーザーIDは共通なので、チャンネルを正しく使い分けるだけで混乱はゼロになります。

目次

フェーズ1:課題の整理——なぜ複数チャンネル運用は混乱するのか

LINE Messaging APIを使って「LINE公式アカウント(OA)」と「社内Bot用チャンネル」を並行運用している事業者さんや個人開発者さんから、よくこんな声を聞きます。「OAにメッセージが来たのに、なぜかBotチャンネルのトークンで返信しようとしてしまった」「どっちのWebhookで受けたイベントなのか、コードを追わないとわからなくなってきた」——。これ、設計の段階で少し整理するだけで、ほぼ完全に解消できる問題なんです。

チャンネルごとに独立しているもの、共通なもの

まず前提として押さえておきたいのが、LINE Messaging APIにおける「チャンネル」の独立性です。各チャンネルには以下がそれぞれ独立して存在します。

  • Webhook URL:LINEプラットフォームがイベントを送り届ける先のエンドポイント
  • Channel Secret:Webhookリクエストの署名検証に使う秘密鍵
  • Channel Access Token:Messaging APIを呼び出すための認証トークン

つまり、OAチャンネルに届いたWebhookイベントはOAのChannel Secretで署名されており、OAのChannel Access Tokenを使って返信しなければなりません。BotチャンネルはBotチャンネルで完全に別の認証情報を持っています。ここを混在させると、署名検証エラーが発生したり、意図しないチャンネルから返信が飛んだりと、デバッグに時間を取られる羽目になります。

同一プロバイダー内ではユーザーIDが統一される——これが超重要

一方で、複数チャンネルを運用するうえで最大のメリットになるのが「同一プロバイダー配下のチャンネル間ではユーザーIDが統一される」という仕様です。詳細はユーザーIDを取得する | LINE Developersにも記載されていますが、これはつまり、OAチャンネルのWebhookイベントで取得した userId を、そのままBotチャンネルの pushMessage() に渡して通知を送れるということです。

この仕様があるからこそ、「OAでユーザーからリクエストを受けて、社内BotチャンネルでグループにPush通知を飛ばし、承認結果をOAチャンネルでユーザーに返す」という非同期クロスチャンネルフローが成立します。ユーザーIDの変換処理が一切不要なので、実装もシンプルに保てます。

絶対に避けるべき落とし穴:プロバイダーをまたいだ運用

ここで絶対に覚えておいてほしいのが、プロバイダーをまたぐとユーザーIDが異なるという点です。たとえば「プロバイダーA配下のOA」で取得したuserIdと「プロバイダーB配下のBot」で取得したuserIdは、同じLINEユーザーであっても別の値になります。これは仕様上の制限であり、回避策はありません。

複数チャンネルを新規に作成する際は、必ず同一プロバイダーの配下に作成する——これを鉄則として覚えておいてください。既存のチャンネルが別プロバイダーに分散してしまっている場合は、統合を検討する価値があります。

⚠️ チェックポイント

LINE Developersコンソールにログインして「プロバイダー」一覧を確認しましょう。OAチャンネルとBotチャンネルが同じプロバイダーの配下にあることを必ず確認してください。

フェーズ2:解決アプローチの設計——Webhook分離とトークン切り替えの考え方

課題が整理できたところで、具体的な設計アプローチに入りましょう。ポイントは「受信の分離」と「送信トークンの動的切り替え」の2つです。

設計パターン①:Webhookエンドポイントをチャンネルごとに独立させる

最もシンプルかつ堅牢な設計は、チャンネルごとに独立したWebhookエンドポイントを用意することです。

  • /webhook/oa:OAチャンネル専用。OAのChannel Secretで署名検証
  • /webhook/bot:Botチャンネル専用。BotのChannel Secretで署名検証

「1つのエンドポイントに全部集めて、中でifで分岐すればいいんじゃ?」と思う方もいるかもしれません。しかし署名検証はチャンネルごとのChannel Secretを使う必要があるため、エンドポイントを分けてSDKのmiddlewareに検証を任せるほうが、セキュリティ的にも実装のシンプルさ的にも優れています。

設計パターン②:Channel Access Tokenを環境変数で管理して動的に切り替える

返信先チャンネルを動的に切り替えるには、Channel Access Tokenを環境変数(.envファイルなど)で管理し、イベントの種別や送信元チャンネルIDに応じて使用するクライアントを選択するロジックを設計します。

Messaging APIリファレンス | LINE Developersを見ると、replyMessage(返信)と pushMessage(プッシュ通知)では使用するトークンが同じでも、呼び出すAPIエンドポイントが異なります。クライアントオブジェクトをチャンネルごとに生成しておき、用途に応じて使い分けるのがベストプラクティスです。

ユースケース例:承認フローの非同期クロスチャンネル通知

設計の理解を深めるために、具体的なユースケースを見てみましょう。社内の承認フローを想定した例です。

📋 承認フロー設計図

  1. ユーザーがOAチャンネルに「申請:○○」とメッセージを送信
  2. OA Webhookがイベントを受信。replyTokenで「申請を受け付けました」とOAチャンネル経由でユーザーに即時返信
  3. 同一プロバイダー内なので取得済みの userId をそのまま使い、BotチャンネルのPush APIで社内担当者のグループLINEに通知
  4. 担当者がBotチャンネルで「承認」と返信 → Bot Webhookがイベントを受信
  5. 元の申請者の userId(DBやKVSに保存済み)をOAチャンネルのPush APIで引き出し、承認完了通知をユーザーに送信

このフローで重要なのは、ステップ3と5でユーザーIDの変換が一切不要な点です。同一プロバイダー内というだけで、こんなにスムーズにクロスチャンネル通知が実現できます。

フェーズ3:実装手順——Node.jsで2チャンネルを並行運用する

設計が固まったら、実際に手を動かしていきましょう。ここではNode.js(@line/bot-sdk)を使った実装手順を、ゼロから丁寧に解説します。

Step 1:LINE Developersコンソールでチャンネルを準備する

まず、LINE Developersコンソール(https://developers.line.biz/)にログインして、以下を確認・実施します。

  1. プロバイダーの確認:OAチャンネルとBotチャンネルが同一プロバイダー配下にあることを確認。なければ新規プロバイダーを作成し、その配下に両チャンネルを作成します。
  2. OAチャンネルの設定:「Messaging API」タブから「Channel Secret」と「Channel Access Token(長期)」を取得します。
  3. Botチャンネルの作成:「新規チャンネル作成」→「Messaging API」を選択して開発用Botチャンネルを作成。同様にChannel SecretとChannel Access Tokenを取得します。
  4. Webhook設定:各チャンネルの「Messaging API設定」タブで「Webhookの利用」をONにします。Webhook URLは後ほどngrokのURLを設定します。

取得した4つの値(OA用2つ、Bot用2つ)は後ほど .env に設定します。絶対にGitHubなどの公開リポジトリにコミットしないよう注意してください。

Step 2:プロジェクトをセットアップする

以下のコマンドでプロジェクトを初期化し、必要なパッケージをインストールします。

mkdir line-multi-channel && cd line-multi-channel
npm init -y
npm install @line/bot-sdk dotenv express

続いて、以下のディレクトリ構成・ファイルを作成します。

ディレクトリ構成

project/
├── .env
├── index.js
└── package.json

.env

OA_CHANNEL_SECRET=your_oa_channel_secret
OA_CHANNEL_ACCESS_TOKEN=your_oa_channel_access_token

BOT_CHANNEL_SECRET=your_bot_channel_secret
BOT_CHANNEL_ACCESS_TOKEN=your_bot_channel_access_token

PORT=3000

package.json

{
  "name": "line-multi-channel",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "@line/bot-sdk": "^9.3.0",
    "dotenv": "^16.4.5",
    "express": "^4.19.2"
  }
}

index.js — メインエントリポイント

OAチャンネル用・Botチャンネル用それぞれの middlewareClient を個別に生成し、/webhook/oa/webhook/bot の2エンドポイントに割り当てます。署名検証はSDKのmiddlewareが自動で行うため、Channel Secretを混在させないことが重要です。

require("dotenv").config();
const express = require("express");
const line = require("@line/bot-sdk");

const app = express();

// ── OA チャンネル設定 ──────────────────────────────────────
const oaConfig = {
  channelSecret: process.env.OA_CHANNEL_SECRET,
  channelAccessToken: process.env.OA_CHANNEL_ACCESS_TOKEN,
};
const oaClient = new line.messagingApi.MessagingApiClient({
  channelAccessToken: oaConfig.channelAccessToken,
});

// ── Bot チャンネル設定 ─────────────────────────────────────
const botConfig = {
  channelSecret: process.env.BOT_CHANNEL_SECRET,
  channelAccessToken: process.env.BOT_CHANNEL_ACCESS_TOKEN,
};
const botClient = new line.messagingApi.MessagingApiClient({
  channelAccessToken: botConfig.channelAccessToken,
});

// ── OA Webhook (/webhook/oa) ───────────────────────────────
// OA の Channel Secret で署名検証
app.post(
  "/webhook/oa",
  line.middleware({ channelSecret: oaConfig.channelSecret }),
  async (req, res) => {
    res.sendStatus(200);
    const events = req.body.events;
    await Promise.all(events.map(handleOaEvent));
  }
);

async function handleOaEvent(event) {
  if (event.type !== "message" || event.message.type !== "text") return;

  const userId = event.source.userId;
  const userMessage = event.message.text;

  // OA チャンネルで返信
  await oaClient.replyMessage({
    replyToken: event.replyToken,
    messages: [{ type: "text", text: `[OA] 受信: ${userMessage}` }],
  });

  // 同一プロバイダー内なので userId をそのまま Bot チャンネルへ横断通知
  await notifyViaBot(userId, userMessage);
}

// ── Bot Webhook (/webhook/bot) ─────────────────────────────
// Bot の Channel Secret で署名検証
app.post(
  "/webhook/bot",
  line.middleware({ channelSecret: botConfig.channelSecret }),
  async (req, res) => {
    res.sendStatus(200);
    const events = req.body.events;
    await Promise.all(events.map(handleBotEvent));
  }
);

async function handleBotEvent(event) {
  if (event.type !== "message" || event.message.type !== "text") return;

  const userId = event.source.userId;
  const userMessage = event.message.text;

  await botClient.replyMessage({
    replyToken: event.replyToken,
    messages: [{ type: "text", text: `[Bot] 受信: ${userMessage}` }],
  });
}

// ── クロスチャンネル通知 ───────────────────────────────────
// OA で受け取った userId を使い、Bot チャンネルの Access Token で pushMessage
async function notifyViaBot(userId, originalMessage) {
  await botClient.pushMessage({
    to: userId,
    messages: [
      {
        type: "text",
        text: `[Bot 通知] OA 経由のメッセージ:\n"${originalMessage}"`,
      },
    ],
  });
}

// ── サーバー起動 ───────────────────────────────────────────
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
  console.log(`OA  Webhook: http://localhost:${PORT}/webhook/oa`);
  console.log(`Bot Webhook: http://localhost:${PORT}/webhook/bot`);
});

ngrok でローカル公開(開発時)

ngrok を起動して発行されたURLをLINE Developersコンソールの各チャンネルのWebhook URLに設定します。OAチャンネルには /webhook/oa、Botチャンネルには /webhook/bot を指定してください。

# ターミナル1: サーバー起動
node index.js

# ターミナル2: ngrok でポート3000を公開
ngrok http 3000

# 出力例
# Forwarding  https://xxxx-xx-xx-xxx-xx.ngrok-free.app -> http://localhost:3000

ngrokが発行したURLを使って、LINE Developersコンソールで以下のように設定します。

  • OAチャンネルのWebhook URL:https://xxxx-xx-xx-xxx-xx.ngrok-free.app/webhook/oa
  • BotチャンネルのWebhook URL:https://xxxx-xx-xx-xxx-xx.ngrok-free.app/webhook/bot

設定後、コンソール上の「検証」ボタンを押してステータス200が返ることを確認しましょう。

💡 実装のポイント

handleOaEvent 内で res.sendStatus(200) を先に返してからイベント処理を行っている点に注目してください。LINEプラットフォームは一定時間内にレスポンスが返らないとタイムアウトエラーになるため、まず200を返してから非同期処理を実行するのがベストプラクティスです。

フェーズ4:動作確認と応用——3チャンネル構成へのスケールアップ

実装が完成したら、動作確認を丁寧に行いましょう。そのうえで、実務でよくある「もう1チャンネル追加したい」というニーズにも対応できる拡張設計を紹介します。

動作確認:LINE Official Account Managerの設定を忘れずに

2チャンネル並行運用でハマりやすいのが、LINE Official Account Manager(OAM)側の設定です。OAMにはデフォルトで「自動応答メッセージ」機能がONになっていることがあり、これが有効だとWebhookとOAMの両方から返信が飛んで二重応答になります。

以下の手順で設定を確認してください。

  1. LINE Official Account Manager(manager.line.biz)にログイン
  2. 対象のOAを選択 →「設定」→「応答設定」を開く
  3. 「Webhook」をONに設定
  4. 「自動応答メッセージ」をOFFに設定(または「Webhookで応答」を選択)
  5. 「あいさつメッセージ」も必要に応じてOFF

この設定が完了したら、実際にOAにメッセージを送ってみましょう。以下の2点が確認できれば成功です。

  • OAチャンネルから「[OA] 受信: ○○」という返信が届く
  • Botチャンネルから「[Bot 通知] OA 経由のメッセージ: “○○”」というPush通知が届く

応用パターン①:モジュールチャンネルを追加して3チャンネル構成にする

LINEのエコシステムをフルに活用したい場合、モジュールチャネルからMessaging APIを利用する | LINE Developersで紹介されているモジュールチャンネル(LINE Front-end Framework連携など)を追加することで、3チャンネル構成に拡張できます。

3チャンネル構成の場合、index.js への追加は以下のパターンで対応できます。

// ── モジュールチャンネル設定(追加分)─────────────────────
const moduleConfig = {
  channelSecret: process.env.MODULE_CHANNEL_SECRET,
  channelAccessToken: process.env.MODULE_CHANNEL_ACCESS_TOKEN,
};
const moduleClient = new line.messagingApi.MessagingApiClient({
  channelAccessToken: moduleConfig.channelAccessToken,
});

// ── モジュール Webhook (/webhook/module) ──────────────────
app.post(
  "/webhook/module",
  line.middleware({ channelSecret: moduleConfig.channelSecret }),
  async (req, res) => {
    res.sendStatus(200);
    const events = req.body.events;
    await Promise.all(events.map(handleModuleEvent));
  }
);

async function handleModuleEvent(event) {
  // モジュールチャンネル固有の処理をここに記述
}

このパターンを踏襲すれば、チャンネル数が増えても構造は一貫して保てます。チャンネルごとにconfigオブジェクト、clientインスタンス、Webhookエンドポイント、ハンドラー関数の4点セットを用意するのがルールです。

応用パターン②:チャンネルアクセストークンをRedis/KVSで一元管理する

複数サーバーでスケールアウトする構成や、チャンネル数が5つ以上になってきた場合は、Channel Access Tokenを環境変数だけで管理するのが辛くなってきます。そこで活用したいのが、RedisやAWS DynamoDB、Cloudflare KVといったKVS(Key-Value Store)による一元管理です。

設計のポイントは以下の通りです。

  • キー設計channel:{channelId}:accessToken のような形式で、チャンネルIDをキーにトークンを格納
  • 起動時ロード:サーバー起動時にKVSから全チャンネルのトークンを読み込み、Mapオブジェクトでメモリキャッシュ
  • 動的更新:トークンをローテーションする際もKVSを更新するだけで、サーバー再起動不要
  • クライアントの動的生成:イベント受信時にチャンネルIDをキーにKVSからトークンを取得し、MessagingApiClient を動的に生成して使用

この設計に移行することで、チャンネルの追加・削除・トークン更新がすべてKVSの操作だけで完結するようになり、コードの変更なしに運用できます。本番環境でのスケールアップを見据えるなら、早めに検討しておく価値があります。

ハナの所見

今回の設計を一言で表すなら「責務の分離を徹底する」です。受信はチャンネルごとのエンドポイントで分離、認証情報は環境変数で独立管理、送信はクライアントオブジェクトを使い分ける——この3原則を守るだけで、複数チャンネル運用の混乱は驚くほど少なくなります。

ハナが特に強調したいのは、「同一プロバイダー内ではユーザーIDが統一される」という仕様の活用です。この仕様を知っているかどうかで、実装の複雑さが大きく変わります。OAとBotを橋渡しするためのユーザーID変換テーブルをわざわざ作っている方を見かけることがありますが、同一プロバイダー内であれば一切不要です。まず自分のチャンネル構成を確認して、この恩恵を享受できる状態にすることが最初のステップです。

また、今回はNode.jsでの実装例を紹介しましたが、Webhookルーティングや非同期イベント処理の考え方はどの言語・フレームワークでも共通です。設計パターンとして理解しておくと、Python(Flask/FastAPI)やGo(Gin)での実装にもそのまま応用できます。

複数チャンネル設計をさらに深掘りしたい方には、以下のリソースをおすすめします。

📚 Node.jsデザインパターン 第3版

Webhookルーティングや非同期イベント処理など、複数チャンネル設計に直結するNode.js

この記事はハナ編集部が執筆しました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

はじめまして、「白米元気」と申します。

ノースキルで副業をスタートし、2ヶ月で月10万円を達成。
その後も毎日ChatGPTとにらめっこしながら、
「どうやったら仕組みで稼げるのか?」を考え続けてきました。

そんな中出会ったのが「LLM無職」です。
AIと仕組みを作り、AIに仕事をさせる。
副業や働き方そのものを実験していく——そんな挑戦をしています。

このブログでは、わたしのLLM無職への道のりの途中で
AIを活用した具体的な方法や工夫、日々の実践内容を紹介。
ときどき家族の話もまじえながら、
読んでくれた方が「なんかおもしろそう!」と思えるような、
リアルで実験的な情報をお届けしていきます。

コメント

コメントする

目次