ShiftAIゆるっと作業会
ShiftAIゆるっと作業会 / Googleフォーム × GAS × LINE Messaging API

セミナー受付を
運営LINEグループに自動通知する

開催日: 2026-07-25

架空のセミナー受付を題材に、Googleフォームの回答をスプレッドシートで受け取り、GASから運営用LINEグループへ来場通知を送ります。 参加者本人へのLINE通知は行いません。

完成形フォーム送信後、運営LINEグループに来場通知が届く
所要時間約60分。コードはコピーして貼り付けるだけ
使うものGoogleフォーム、スプレッドシート、GAS、LINE公式アカウント
Googleフォーム受付からLINE運営グループ通知までの全体構成図
フォーム送信からLINE通知までの流れ

全体像

0:00-0:05

受付担当がフォームに来場者情報を入力すると、スプレッドシートへの回答追加をきっかけにGASが動き、LINE Messaging API経由で運営グループに通知します。

1受付フォーム来場者情報を入力
2スプレッドシート回答を自動保存
3GASトリガー回答追加時に実行
4Messaging APILINEへPush送信
5運営LINEグループ来場通知を受信
LINE Notifyは2025年3月31日に終了済みです。この手順では、現在使えるLINE Messaging APIを使います。

このハンズオンで大事にすること

  • フォーム回答を「見に行く」のではなく、いつも見るLINEへ「届く」状態にします。
  • AIに聞くと古いLINE Notifyのコードが出ることがあります。今回はMessaging APIだけを使います。
  • LINE設定が一番の山場です。GASコードはコピーして、設定確認に時間を使います。

参考にした内容

参考

参考ウェビナーから反映したポイント

参考: 問い合わせもトラブル報告も即キャッチ!GoogleフォームをLINE通知で対応遅れゼロに 基礎編

上記ウェビナーの流れをもとに、当日のハンズオンでつまずきやすい箇所を先回りして補強しています。

  • Googleフォームは手作業で作ってOK。GASでフォーム自体を作るのは発展編に回します。
  • LINE Developersは、個人LINEとは分けたビジネスアカウントで作るのがおすすめです。
  • チャネルアクセストークンと送信先IDは秘密情報です。画面共有や公開ページに出さないようにします。
  • トリガー設定で詰まりやすいので、自動設定と手動設定の両方を用意します。

事前準備

0:05-0:10

参加者が用意するもの

  • Googleアカウント
  • LINEアカウント
  • LINE Developersにログインできる状態
  • LINE Developers用のビジネスアカウント。個人LINEとは分けると安心です
  • 通知先にする運営用LINEグループ

今日のハンズオンで作る項目

項目内容
お名前来場者名山田 太郎
会社名・所属所属情報株式会社サンプル
参加枠セミナー枠午前の部
メモ受付時の補足資料1部追加

Googleフォームを作る

0:10-0:20
Step 1

フォームを新規作成する

作業場所Googleフォーム
  1. forms.new を開きます。
  2. タイトルを セミナー来場受付フォーム にします。
  3. 下の項目を追加します。
項目フォーム項目リスト
お名前
会社名・所属
参加枠
メモ
Step 2

回答先のスプレッドシートを作る

作業場所Googleフォームの「回答」タブ → Googleスプレッドシート
  1. フォーム上部の「回答」タブを開きます。
  2. スプレッドシートアイコンを押します。
  3. 新しいスプレッドシートを作成します。
Googleフォームで新しいスプレッドシートを作成する画面
「新しいスプレッドシートを作成」を選び、「作成」を押します。
このスプレッドシートが、GASを置く場所になります。

LINE公式アカウントを準備する

0:20-0:35
LINE Developers設定で押さえるポイントの図
LINE設定で必要なもの: ビジネスアカウント、Messaging API、Webhook、groupId
Step 3

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

作業場所LINE公式アカウント作成画面 / LINE Official Account Manager / LINE Developers Console
  1. Messaging APIを始めよう の「LINE公式アカウントを作成する」を開きます。
  2. ページ内の「LINE公式アカウントを作成する」から、LINE公式アカウントを作成します。
  3. LINE Official Account Managerで対象アカウントを開きます。
  4. 「設定」→「Messaging API」から、Messaging APIの利用を有効にします。
  5. 有効化後、LINE Developers Consoleで対象チャネルを開き、「チャネルアクセストークン(長期)」を発行します。
現在の注意: LINE Developers ConsoleからMessaging APIチャネルを直接作成することはできなくなっています。公式ドキュメントの「LINE公式アカウントを作成する」手順に沿ってLINE公式アカウントを作成してから、LINE Official Account Manager上でMessaging APIを有効にします。
Messaging APIチャネルをLINE Developersコンソールから直接作成できなくなった案内画面
現在は「LINE公式アカウントを作成する」から進めます。
LINE Developers Consoleのトップ画面
LINE Developers Console側では、プロバイダーや有効化後のチャネル情報を確認します。
今回はメールアドレスで新規登録する流れです。 LINEヤフー Business ID画面では「ビジネスアカウント」を選び、メールアドレスに届いたリンクから登録を進めます。
LINE公式アカウント作成時のログイン画面
0. LINE公式アカウント作成に進むと、ログインまたはアカウント作成を求められます。
LINEヤフー Business IDでビジネスアカウントを選ぶ画面
1. ログイン方法は「ビジネスアカウント」を選びます。
LINEヤフー Business IDでメールアドレスを入力して登録リンクを送信する画面
2. メールアドレスを入力し、登録用リンクを送信します。
LINEヤフー Business ID登録メールから登録画面に進む画面
3. 届いたメールの「登録画面に進む」を押します。
LINEヤフー Business IDで名前とパスワードを入力して登録する画面
4. 名前とパスワードを入力して登録します。
LINE公式アカウント作成時にSMS認証を求める確認画面
5. LINE公式アカウント作成時にSMS認証を求められたら進みます。
LINEヤフー Business IDで電話番号を入力してSMSを送信する画面
6. 電話番号を入力してSMSを送信します。
LINEヤフー Business IDでSMS認証番号を入力する画面
7. SMSで届いた6桁の認証番号を入力します。
LINE公式アカウントの会社・店舗情報を登録する画面上部
8. LINE公式アカウント名、メールアドレス、会社・事業者情報、業種を入力します。
LINE公式アカウント作成フォーム下部で主な使い方と確認ボタンを選ぶ画面
9. 主な使い方は「チャット・LINEコール用」を選び、規約確認後に「確認」を押します。
LINE公式アカウントの情報利用に関する同意画面
10. 情報利用に関する同意内容を確認し、「同意」を押します。
LINE公式アカウント運用開始前の友だち追加確認モーダル
11. 運用開始前の案内が出たら、必要に応じて次へ進みます。
LINE Official Account Managerのホーム画面
12. LINE Official Account Managerのホーム画面を開きます。
LINE Official Account ManagerでMessaging APIを利用するボタンを押す画面
13. 左メニューの「設定」→「Messaging API」から「Messaging APIを利用する」を押します。
Messaging API有効化時にプロバイダーを選択する画面
14. プロバイダー選択画面では、作成済みのプロバイダーを選びます。
プロバイダーのプライバシーポリシーと利用規約を任意で登録する画面
15. プライバシーポリシーと利用規約は任意です。今回のハンズオンでは空欄のまま「OK」で進めます。
LINE Developers Consoleでプロバイダーが作成された画面
16. 有効化後、LINE Developers Consoleでプロバイダーとチャネル情報を確認します。
LINE Official Account ManagerのMessaging API設定画面でChannel ID、Channel secret、Webhook URL欄が表示されている画面
17. Messaging APIが「利用中」になったことを確認します。Webhook URLはGASをWebアプリとしてデプロイした後に貼り付けます。
LINE Developers ConsoleのMessaging API設定タブ
18. LINE Developers Console側の「Messaging API設定」タブを開きます。
LINE Developers Consoleでチャネルアクセストークン長期を発行する画面
19. 下部の「チャネルアクセストークン(長期)」で「発行」を押し、ここで発行したトークンをGASのサイドバーに貼り付けます。
チャネルアクセストークンは、LINE Developers Consoleの「Messaging API設定」にある「チャネルアクセストークン(長期)」を使います。Channel IDやChannel secret、別チャネルのトークンを貼るとLINE送信に失敗します。トークンはパスワードのようなものなので、公開ページやGitHubには貼らず、GASのスクリプトプロパティに保存します。
LINE公式アカウント管理画面とLINE Developers Consoleを行き来します。ここが一番迷いやすいので、画面名を確認しながら進めます。
Step 4

グループ参加とWebhookを有効にする

作業場所LINE Official Account Manager / 手元のLINEグループ
  1. LINE Official Account Managerの「設定」→「アカウント設定」を開き、「トークへの参加」で「グループ・複数人トークへの参加を許可」を選びます。ここがオフだと、招待してもLINE公式アカウントがすぐ退会します。
  2. 応答メッセージはオフにします。オンのままだと、接続テスト時に定型の自動返信も一緒に届きます。
  3. Webhookの利用をオンにします。
  4. 設定を保存してから、LINE公式アカウントを運営用LINEグループに招待し直します。
LINE Official Account Managerのアカウント設定でグループ複数人トークへの参加を許可する画面
トークへの参加は、Messaging API画面ではなく「アカウント設定」にあります。「グループ・複数人トークへの参加を許可」を選びます。
LINE Official Account Managerの応答設定でWebhookがオフになっている画面
この画面の「Webhook」をオンにします。Webhook URLを保存しただけでは通知botにメッセージが届かないので、トグルも必ずオンにします。
Webhook URLは、次のGASデプロイ後に発行されるWebアプリURLを入れます。LINE公式ドキュメントでは、このグループ参加設定はデフォルトでオフです。同じグループに複数のLINE公式アカウントは同時参加できないため、別の公式アカウントが入っている場合は外してから招待します。

GASを貼り付ける

0:35-0:50
Step 5

Apps Scriptを開く

作業場所Googleスプレッドシート / Apps Script
  1. 回答用スプレッドシートを開きます。
  2. メニューの「拡張機能」から「Apps Script」を開きます。
  3. 既存コードを消して、下のコードを貼り付けます。
  4. コード内にチャネルアクセストークンは貼り付けません。保存後、スプレッドシート側のサイドバーから設定します。
  5. 貼り付けたら、上部の保存アイコンを押します。
Googleスプレッドシートの拡張機能メニューからApps Scriptを開く画面
回答用スプレッドシートから「拡張機能」→「Apps Script」を開きます。
Apps Scriptエディタ上部の保存アイコン
コードを貼り付けたら、上部の保存アイコンを押します。画面内のコードが古く見えないよう、保存ボタン部分だけを切り出しています。
GASコピーして貼り付けるコード
const PROP_TOKEN = 'LINE_CHANNEL_ACCESS_TOKEN';
const PROP_GROUP_ID = 'LINE_GROUP_ID';
const PROP_TIMEZONE = 'TIMEZONE';
const DEFAULT_TIMEZONE = 'Asia/Tokyo';

function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu('LINE通知設定')
    .addItem('設定サイドバーを開く', 'showLineSettingsSidebar')
    .addSeparator()
    .addItem('テスト送信', 'testSend')
    .addItem('フォーム送信トリガーを設定', 'installTrigger')
    .addItem('設定状態をログに表示', 'checkSettings')
    .addToUi();
}

function showLineSettingsSidebar() {
  const html = HtmlService
    .createHtmlOutput(getLineSettingsSidebarHtml_())
    .setTitle('LINE通知設定');

  SpreadsheetApp.getUi().showSidebar(html);
}

function getLineSettings() {
  const props = PropertiesService.getScriptProperties();

  return {
    hasToken: Boolean(props.getProperty(PROP_TOKEN)),
    groupId: props.getProperty(PROP_GROUP_ID) || '',
    timezone: props.getProperty(PROP_TIMEZONE) || DEFAULT_TIMEZONE
  };
}

function saveLineSettingsFromSidebar(settings) {
  const props = PropertiesService.getScriptProperties();
  const currentToken = props.getProperty(PROP_TOKEN);
  const token = String(settings.token || '').trim();
  const groupId = String(settings.groupId || '').trim();
  const timezone = String(settings.timezone || DEFAULT_TIMEZONE).trim() || DEFAULT_TIMEZONE;

  if (!token && !currentToken) {
    throw new Error('チャネルアクセストークン(長期)を入力してください。');
  }

  const values = {
    [PROP_GROUP_ID]: groupId,
    [PROP_TIMEZONE]: timezone
  };

  if (token) {
    values[PROP_TOKEN] = token;
  }

  props.setProperties(values, false);

  return {
    message: '設定を保存しました。',
    hasToken: true,
    groupId: groupId,
    timezone: timezone
  };
}

function doPost(e) {
  const body = e.postData && e.postData.contents ? e.postData.contents : '{}';
  const data = JSON.parse(body);
  const events = data.events || [];
  const props = PropertiesService.getScriptProperties();

  Logger.log('LINE webhook body: ' + body);

  events.forEach(function(event) {
    const source = event.source || {};
    const targetId = source.groupId || source.roomId;

    Logger.log('LINE webhook source: ' + JSON.stringify(source));

    if (!targetId) {
      Logger.log('groupId/roomIdがないイベントです。type=' + (source.type || 'unknown'));
      return;
    }

    props.setProperty(PROP_GROUP_ID, targetId);
    Logger.log('LINE_GROUP_IDを保存しました: ' + targetId);

    const text = event.message && event.message.type === 'text'
      ? event.message.text
      : '';

    if (event.replyToken && /接続テスト|groupid|グループID/i.test(text)) {
      const idType = source.groupId ? 'groupId' : 'roomId';
      replyText_(event.replyToken, '接続できました。' + idType + 'をLINE_GROUP_IDとしてGASに保存しました。');
    }
  });

  return ContentService.createTextOutput('OK');
}

function installTrigger() {
  ScriptApp.getProjectTriggers().forEach(function(trigger) {
    if (trigger.getHandlerFunction() === 'onFormSubmit') {
      ScriptApp.deleteTrigger(trigger);
    }
  });

  ScriptApp.newTrigger('onFormSubmit')
    .forSpreadsheet(SpreadsheetApp.getActive())
    .onFormSubmit()
    .create();

  Logger.log('フォーム送信時トリガーを設定しました。');
}

function onFormSubmit(e) {
  const groupId = getRequiredProperty_(PROP_GROUP_ID);
  const message = buildAttendanceMessage_(e);
  pushText_(groupId, message);
}

function testSend() {
  const groupId = getRequiredProperty_(PROP_GROUP_ID);
  pushText_(groupId, 'GASからLINE運営グループへのテスト通知です。');
}

function checkSettings() {
  const props = PropertiesService.getScriptProperties();
  Logger.log('TOKEN: ' + (props.getProperty(PROP_TOKEN) ? '保存済み' : '未保存'));
  Logger.log('LINE_GROUP_ID: ' + (props.getProperty(PROP_GROUP_ID) || '未取得'));
  Logger.log('TIMEZONE: ' + (props.getProperty(PROP_TIMEZONE) || DEFAULT_TIMEZONE));
}

function resetGroupId() {
  PropertiesService.getScriptProperties().deleteProperty(PROP_GROUP_ID);
  Logger.log('LINE_GROUP_IDを削除しました。もう一度グループで接続テストを送ってください。');
}

function buildAttendanceMessage_(e) {
  const namedValues = e && e.namedValues ? e.namedValues : {};

  const name = pick_(namedValues, ['お名前', '名前', '氏名', '参加者名']) || '参加者';
  const company = pick_(namedValues, ['会社名・所属', '会社名', '所属', '団体名']);
  const session = pick_(namedValues, ['参加枠', '参加回', 'セミナー枠']);
  const memo = pick_(namedValues, ['メモ', '備考', '受付メモ']);
  const timestamp = pick_(namedValues, ['タイムスタンプ', '受付時刻']);

  const lines = [
    '【来場受付】',
    name + 'さんが来場しました。',
    '',
    '受付時刻: ' + formatDate_(timestamp),
    '会社名・所属: ' + (company || '-'),
    '参加枠: ' + (session || '-')
  ];

  if (memo) {
    lines.push('メモ: ' + memo);
  }

  return lines.join('\n');
}

function pushText_(to, text) {
  const token = getRequiredProperty_(PROP_TOKEN);
  const url = 'https://api.line.me/v2/bot/message/push';
  const payload = {
    to: to,
    messages: [{ type: 'text', text: text }]
  };

  requestLine_(url, payload, token);
}

function replyText_(replyToken, text) {
  const token = getRequiredProperty_(PROP_TOKEN);
  const url = 'https://api.line.me/v2/bot/message/reply';
  const payload = {
    replyToken: replyToken,
    messages: [{ type: 'text', text: text }]
  };

  requestLine_(url, payload, token);
}

function requestLine_(url, payload, token) {
  const response = UrlFetchApp.fetch(url, {
    method: 'post',
    contentType: 'application/json',
    headers: {
      Authorization: 'Bearer ' + token
    },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  });

  const status = response.getResponseCode();
  const body = response.getContentText();

  Logger.log('LINE API status: ' + status);
  Logger.log(body);

  if (status < 200 || status >= 300) {
    throw new Error('LINE API error: ' + status + ' ' + body);
  }
}

function pick_(namedValues, keys) {
  for (let i = 0; i < keys.length; i++) {
    const value = namedValues[keys[i]];
    if (Array.isArray(value) && value[0]) {
      return String(value[0]).trim();
    }
    if (value) {
      return String(value).trim();
    }
  }
  return '';
}

function formatDate_(value) {
  if (!value) {
    return Utilities.formatDate(new Date(), getTimeZone_(), 'yyyy/MM/dd HH:mm');
  }

  const date = value instanceof Date ? value : new Date(value);

  if (isNaN(date.getTime())) {
    return String(value);
  }

  return Utilities.formatDate(date, getTimeZone_(), 'yyyy/MM/dd HH:mm');
}

function getTimeZone_() {
  return PropertiesService.getScriptProperties().getProperty(PROP_TIMEZONE) || DEFAULT_TIMEZONE;
}

function getRequiredProperty_(key) {
  const value = PropertiesService.getScriptProperties().getProperty(key);

  if (!value) {
    throw new Error(key + ' が未設定です。スプレッドシートの「LINE通知設定」メニューから設定してください。');
  }

  return value;
}

function getLineSettingsSidebarHtml_() {
  return `
<!DOCTYPE html>
<html>
<head>
  <base target="_top">
  <style>
    body{font-family:-apple-system,BlinkMacSystemFont,"Noto Sans JP",sans-serif;color:#1f2937;padding:14px;line-height:1.6}
    h2{font-size:18px;margin:0 0 12px}
    label{display:block;font-weight:700;margin-top:14px;font-size:13px}
    input{box-sizing:border-box;width:100%;border:1px solid #d1d5db;border-radius:6px;padding:9px 10px;font-size:13px}
    button{width:100%;border:0;border-radius:999px;padding:10px 12px;margin-top:12px;color:#fff;background:#0f9f9a;font-weight:700;cursor:pointer}
    button.secondary{color:#0f9f9a;background:#eaf7f6}
    .help{color:#64748b;font-size:12px;margin-top:4px}
    .status{background:#f1f5f9;border-radius:6px;padding:9px 10px;font-size:12px;margin:10px 0}
    #message{min-height:20px;margin-top:12px;font-size:12px;color:#166534}
    #message.error{color:#dc2626}
  </style>
</head>
<body>
  <h2>LINE通知設定</h2>
  <div class="status" id="tokenStatus">読み込み中...</div>
  <label for="token">チャネルアクセストークン(長期)</label>
  <input id="token" type="password" autocomplete="off" placeholder="新しく保存する時だけ貼り付け">
  <div class="help">空欄で保存すると、保存済みトークンをそのまま使います。</div>
  <label for="groupId">LINE_GROUP_ID(roomIdも可)</label>
  <input id="groupId" type="text" placeholder="接続テストで自動取得。手入力も可">
  <div class="help">運営LINEグループまたは複数人トークで「接続テスト」と送ると自動保存されます。</div>
  <label for="timezone">タイムゾーン</label>
  <input id="timezone" type="text" value="Asia/Tokyo">
  <button onclick="save()">設定を保存</button>
  <button class="secondary" onclick="testSend()">テスト送信</button>
  <button class="secondary" onclick="installTrigger()">フォーム送信トリガーを設定</button>
  <div id="message"></div>
  <script>
    function setMessage(text, isError) {
      var el = document.getElementById('message');
      el.textContent = text || '';
      el.className = isError ? 'error' : '';
    }
    function load() {
      google.script.run.withSuccessHandler(function(settings) {
        document.getElementById('tokenStatus').textContent = settings.hasToken ? 'TOKEN: 保存済み' : 'TOKEN: 未保存';
        document.getElementById('groupId').value = settings.groupId || '';
        document.getElementById('timezone').value = settings.timezone || 'Asia/Tokyo';
      }).withFailureHandler(function(error) {
        setMessage(error.message, true);
      }).getLineSettings();
    }
    function save() {
      setMessage('保存中...');
      google.script.run.withSuccessHandler(function(settings) {
        document.getElementById('token').value = '';
        document.getElementById('tokenStatus').textContent = settings.hasToken ? 'TOKEN: 保存済み' : 'TOKEN: 未保存';
        document.getElementById('groupId').value = settings.groupId || '';
        document.getElementById('timezone').value = settings.timezone || 'Asia/Tokyo';
        setMessage(settings.message || '保存しました。');
      }).withFailureHandler(function(error) {
        setMessage(error.message, true);
      }).saveLineSettingsFromSidebar({
        token: document.getElementById('token').value,
        groupId: document.getElementById('groupId').value,
        timezone: document.getElementById('timezone').value
      });
    }
    function testSend() {
      setMessage('テスト送信中...');
      google.script.run.withSuccessHandler(function() {
        setMessage('テスト送信しました。LINEグループを確認してください。');
      }).withFailureHandler(function(error) {
        setMessage(error.message, true);
      }).testSend();
    }
    function installTrigger() {
      setMessage('トリガー設定中...');
      google.script.run.withSuccessHandler(function() {
        setMessage('フォーム送信トリガーを設定しました。');
      }).withFailureHandler(function(error) {
        setMessage(error.message, true);
      }).installTrigger();
    }
    load();
  </script>
</body>
</html>`;
}
Step 6

サイドバーでLINE設定を保存する

作業場所Googleスプレッドシートの「LINE通知設定」サイドバー / LINE Developers Console
  1. Apps Scriptを保存したら、回答用スプレッドシートを再読み込みします。
  2. メニューの「LINE通知設定」→「設定サイドバーを開く」を押します。
  3. 初回は「認証が必要です」と表示されるので「OK」を押し、Googleアカウントの権限確認を許可します。
  4. LINE Developers Consoleの「チャネルアクセストークン(長期)」を貼り付けます。Channel IDやChannel secretではありません。
  5. TIMEZONE は通常 Asia/Tokyo のままでOKです。
  6. LINE_GROUP_ID は接続テストで自動取得するため、最初は空欄でOKです。
  7. 「設定を保存」を押します。
Googleスプレッドシート上部にLINE通知設定メニューが表示された画面
スプレッドシートを再読み込みすると、上部メニューに「LINE通知設定」が追加されます。
Googleスプレッドシートで認証が必要ですと表示された画面
初回実行時は「認証が必要です」と表示されます。「OK」を押して進みます。
Googleアカウントのアクセス許可画面ですべて選択にチェックを入れる画面
権限確認画面では「すべて選択」にチェックを入れて、画面下の許可まで進みます。
LINE通知設定サイドバーでトークンやLINE_GROUP_IDを設定する画面
サイドバーからチャネルアクセストークン、LINE_GROUP_ID、タイムゾーンをスクリプトプロパティに保存できます。
保存される値は、GASのスクリプトプロパティです。コード内や公開ページにはアクセストークンを残しません。
Step 7

Webアプリとしてデプロイする

作業場所Apps Script / LINE Official Account ManagerのMessaging API画面
  1. Apps Script右上の「デプロイ」から「新しいデプロイ」を選びます。
  2. 種類は「ウェブアプリ」を選びます。
  3. 実行するユーザーは「自分」、アクセスできるユーザーは「全員」にします。
  4. デプロイ後に表示されるWebアプリURLをコピーします。
  5. LINE Official Account ManagerのMessaging API画面にあるWebhook URLへ貼り付け、「保存」を押します。
  6. Webhookの利用をオンにします。
Apps Scriptのデプロイメニューから新しいデプロイを選ぶ画面
Apps Script右上の「デプロイ」から「新しいデプロイ」を選びます。
Apps Scriptの新しいデプロイ画面で種類をウェブアプリにする画面
歯車アイコンから「ウェブアプリ」を選びます。
Apps Scriptのウェブアプリ設定で実行ユーザーを自分、アクセスできるユーザーを全員にする画面
「次のユーザーとして実行」は自分、「アクセスできるユーザー」は全員にします。
LINE Official Account ManagerのMessaging API画面でWebhook URLを貼り付けて保存する画面
コピーしたWebアプリURLをWebhook URLに貼り付け、「保存」を押します。
アクセス権が「自分のみ」や「Googleアカウントが必要」だと、LINEからWebhookを呼び出せません。

接続テスト

0:50-0:58
Step 8

LINEグループIDを自動取得する

作業場所手元のLINEグループ / Googleスプレッドシートのサイドバー
  1. 運営用LINEグループに、作成したLINE公式アカウントを招待します。
  2. グループ内で下の文を送ります。
  3. LINE公式アカウントから「接続できました」と返信が来るか確認します。
  4. サイドバーを開き直し、LINE_GROUP_ID が入っていることを確認します。
LINEグループで送るテスト文
接続テスト
LINEグループで接続テストを送りLINE_GROUP_ID保存成功の返信が届いた画面
このように「接続できました。LINE_GROUP_IDをGASに保存しました。」と返信が来たら、グループIDの取得は成功です。定型の応答メッセージが一緒に出る場合は、Step 4の応答メッセージをオフにします。
LINE_GROUP_IDが入らない場合:
招待直後にLINE公式アカウントが退会する時は、Step 4の「グループ・複数人トークへの参加」がオフです。オンにして保存してから招待し直します。同じグループに別のLINE公式アカウントが入っている場合は、先に外します。「接続できました」の返信が来ない時は、WebhookがGASに届いていません。Step 7のWebアプリURLが /exec のURLか、Webhook URLを保存したか、Webhookの利用がオンか、Webアプリを新しいバージョンで再デプロイしたかを確認します。
Step 9

GASからLINEにテスト送信する

作業場所Googleスプレッドシートのサイドバー / 手元のLINEグループ
  1. サイドバーの「テスト送信」を押します。
  2. 運営LINEグループに「GASからLINE運営グループへのテスト通知です。」と届けば成功です。
TOKEN保存済みのLINE通知設定サイドバーとテスト送信ボタン
TOKENが「保存済み」になっていることを確認してから、サイドバーの「テスト送信」でLINEグループに通知できるか確認します。
LINEグループにGASからのテスト通知が届いた画面
この通知が届けば、GASからLINE運営グループへのプッシュ送信は成功です。
Step 10

フォーム送信時トリガーを設定する

作業場所Googleスプレッドシートのサイドバー / Googleフォーム / 手元のLINEグループ
  1. サイドバーの「フォーム送信トリガーを設定」を押します。
  2. Googleフォームにテスト回答を送信します。
  3. 運営LINEグループに来場通知が届けば完成です。
LINE通知設定サイドバーでフォーム送信トリガー設定完了と表示された画面
「フォーム送信トリガーを設定しました。」と表示されたら、フォーム送信時にGASが動く準備は完了です。
トリガー設定で失敗した場合:
Apps Script左メニューの「トリガー」→「トリガーを追加」から、実行する関数 onFormSubmit、イベントのソース「スプレッドシートから」、イベントの種類「フォーム送信時」を手動で選びます。
Apps Scriptのフォーム送信時トリガー設定図
手動でトリガーを追加する場合の設定
Googleフォーム送信後にLINEグループへ来場受付通知が届いた画面
Googleフォームを送信して、このように来場受付通知が届けば完成です。
完成時に届く通知
【来場受付】
山田 太郎さんが来場しました。

受付時刻: 2026/06/28 13:05
会社名・所属: 株式会社サンプル
参加枠: 午前の部
メモ: 資料1部追加

この手順書をCloudflare Pagesで公開する

講師向け

このページは静的HTMLなので、Cloudflare Pagesにそのまま公開できます。GitHub連携なら更新も楽です。

Cloudflare Pagesの設定例

Framework presetNone
Build command空欄
Build output directory/ または、このHTMLを置いたディレクトリ
公開URLhttps://プロジェクト名.pages.dev/
CLIWranglerで直接アップロードする場合
npx wrangler pages deploy . --project-name shiftai-yurutto-line-gas
受講者向けの手順書にはLINEアクセストークンなどの秘密情報を載せません。受講者はLINE公式アカウントでMessaging APIを有効化したあと、自分のLINE Developers画面で発行したトークンを、自分のGASにだけ貼り付けます。

困った時の確認リスト

0:58-1:00
LINEに届かない: checkSettings でTOKENとLINE_GROUP_IDを確認します。
テスト送信でLINE APIエラーになる: LINE Developers Consoleの「チャネルアクセストークン(長期)」を貼っているか確認します。Channel ID、Channel secret、別チャネルのトークンは使えません。
招待したLINE公式アカウントがすぐ退会する: LINE Official Account Managerの「アカウント設定」→「トークへの参加」で、グループ・複数人トークへの参加を許可します。別のLINE公式アカウントが同じグループにいる場合は外します。
LINE_GROUP_IDが取れない: グループで「接続テスト」と送って自動返信が来るか確認します。返信がなければWebhook URL、Webhookオン、グループ・複数人トーク参加許可、Webアプリの再デプロイを確認します。
GASを直したのに動きが変わらない: Webアプリは保存だけでは反映されません。デプロイを編集して新しいバージョンに更新します。
Messaging APIチャネルが作れない: LINE Developersから直接作るのではなく、LINE公式アカウント作成後にOfficial Account ManagerでMessaging APIを有効化します。
AIに聞いたコードが動かない: LINE Notify向けの古いコードになっていないか確認します。
GASの権限エラー: 「LINE通知設定」サイドバーの保存や installTrigger 実行時に、Googleの権限確認を許可します。
フォーム送信で動かない: 単純トリガーではなく、インストール型トリガーを使います。自動設定が失敗したら手動で追加します。
401エラー: チャネルアクセストークンの貼り間違い、古いトークン、余計な空白を確認します。
403/404エラー: Webアプリの公開範囲が「全員」になっているか確認します。
通知数が多い: LINE公式アカウントには送信数の上限があります。大量通知の前に管理画面で現在の上限を確認します。
Teams/Slackにも送りたい: 仕組みは可能ですが別設計です。TeamsはPower Automate、SlackはSlackアプリやWebhookを使う発展編にします。
ここまでできたら、Googleフォーム受付からLINE運営グループ通知までの小さな業務自動化が完成です。
コピーしました