Googleフォーム回答をGoogle Chatに通知する方法|Apps Script実装レシピ

Googleフォームの回答をGoogle Chatへ通知する方法を、Apps ScriptとWebhookのコード、方式比較、個人情報対策、エラー監視、湯田Q&Aで解説します。

著者: SakuraAI 編集部監修: 湯田英也公開: 最終更新: 9
目次
30秒でわかる結論

Googleフォームの回答は、Apps Scriptのフォーム送信トリガーとGoogle Chatの受信Webhookを組み合わせれば、サーバーを用意せずに通知できます。ただし、回答全文をChatへ流す設計は勧めません。Chatには受付番号、種別、要点、正本リンクだけを送り、個人情報と対応履歴はフォームの回答先で管理するのが実務向けです。

本記事は2026年6月29日時点のGoogle公式情報をもとにしています。Webhookの利用可否、Apps Scriptの割り当て、イベントオブジェクトは変更される可能性があります。実装前に公式ドキュメントと自社のGoogle Workspace設定を確認してください。

Expert Q&A

フォーム通知を作る前に確認すること

湯田英也SakuraAI株式会社 代表 / ソフトウェアエンジニア

Q. フォーム回答は全部Chatへ流した方が便利ですか?

A. 便利に見えますが、個人情報が複製され、通知も読みにくくなります。Chatには初動に必要な情報だけを載せ、詳しい回答は権限を絞った回答先で確認する形がよいです。

Q. フォームとスプレッドシートのどちらにスクリプトを置きますか?

A. 質問単位で回答を扱うならフォーム紐づけ、行追加や受付台帳と一緒に処理するならスプレッドシート紐づけです。どちらでも作れますが、イベントオブジェクトが違うので途中で混ぜません。

Q. Webhookだけで問い合わせ管理は完結しますか?

A. 完結しません。Webhookは知らせるところまでです。担当、状態、期限、対応履歴はスプレッドシートやCRMなど別の正本で管理します。

問い合わせや申込の回答は、届いたことより、誰がいつ対応するかが重要です。Google Chat通知を入れると初動は速くなりますが、回答全文を貼るだけでは、情報漏えいと通知疲れを増やします。

この記事では、フォーム回答の取得方法、通知項目、振り分け、失敗監視までを一つの流れで整理します。Webhookの準備はWebhookの使い方、Apps Scriptの基礎はApps Scriptで通知する方法も参照してください。

立場をはっきりさせておくと、Chatは通知先で、回答の正本はフォームの回答先(スプレッドシートなど)です。この境界を引かずに回答全文をChatへ流すと、個人情報が複製され、通知も読みにくくなります。

まず決める構成

同じフォーム送信トリガーでも、スクリプトを置く場所で回答の受け取り方が変わります。

方式主なイベント値向くケース注意点
フォーム紐づけe.response質問ごとに回答を選んで通知するフォーム送信元のインストール型トリガーを使う
回答先シート紐づけe.namedValues / e.values受付台帳、採番、行更新と一緒に処理する質問名の重複と列順の変更に注意する
外部自動化サービスサービスごとのフォーム連携複数SaaSへノーコードで連携するデータの保存先、料金、実行上限を確認する

このページでは、質問単位で通知項目を選びやすいフォーム紐づけ方式を使います。回答先シートで受付番号を発行したい場合は、シート紐づけ方式に寄せた方が管理しやすくなります。

通知の処理フロー

01

Googleフォーム

回答を受け付ける

標準機能
02

送信トリガー

回答時にApps Scriptを起動

標準機能
03

項目を選別

初動に必要な情報だけ整形

要開発
04

受信Webhook

固定スペースへ一方向送信

要開発
05

Google Chat

担当者へ受付を知らせる

標準機能
06

回答先を確認

詳細と対応履歴は正本で管理

標準機能
標準機能で対応開発・設定が必要

Chatを回答データの保管場所にしないことが、この構成のポイントです。通知を削除しても回答は残り、Chatの閲覧者へ必要以上の情報を見せずに済みます。

通知項目を決める

実装前に、何を送るかを決めます。最初から全項目を通知すると、後から削る判断が難しくなります。

項目Chatへ通知回答先だけに保存理由
受付日時するする初動の遅れを判断できる
受付番号するするChatと正本を結び付ける
問い合わせ種別するする担当を判断できる
要点必要なら短く通知する全文を複製しない
氏名業務上必要な場合だけする閲覧範囲を広げない
メールアドレス・電話番号原則しないする個人情報の複製を避ける
自由記述の全文原則しないする長文通知と機密情報の流出を防ぐ
回答先リンクする-権限を持つ担当者だけ詳細を開く

フォームの回答編集用URLや回答先シートのURLを載せる場合も、リンクを知っていれば誰でも見られる設定にはしません。リンク先のGoogle Workspace権限で閲覧者を制御します。

Webhookを用意する

ブラウザ版Google Chatで通知先スペースを開き、アプリとインテグレーションから受信Webhookを追加します。組織の設定によっては、管理者がWebhookの利用を許可している必要があります。

発行されたURLには秘密のkeytokenが含まれます。コードへ直書きせず、Apps ScriptのスクリプトプロパティにCHAT_WEBHOOK_URLとして保存します。公開リポジトリ、ログ、Chat本文へ貼り付けてはいけません。

Google Chatの設定画面は、公開前に運用環境で手動撮影した実画面へ差し替えます。アカウント名、スペース名、Webhook URLなどの機密情報をマスキングしてから掲載してください。

Apps Scriptを実装する

対象フォームからApps Scriptを開き、次のコードを追加します。通知する質問はCHAT_FIELDSで明示し、フォームの全回答をそのまま送らない構成です。

const CHAT_FIELDS = ['お問い合わせ種別', '会社名', 'お問い合わせ内容'];

function onFormSubmit(e) {
  if (!e || !e.response) {
    throw new Error('フォーム送信トリガーから実行してください。');
  }

  const answers = new Map(
    e.response.getItemResponses().map((itemResponse) => [
      itemResponse.getItem().getTitle(),
      formatAnswer(itemResponse.getResponse()),
    ]),
  );

  const submittedAt = Utilities.formatDate(
    e.response.getTimestamp(),
    Session.getScriptTimeZone(),
    'yyyy-MM-dd HH:mm',
  );

  const lines = [
    '*新しいフォーム回答*',
    `受付日時: ${submittedAt}`,
    ...CHAT_FIELDS
      .filter((title) => answers.has(title))
      .map((title) => `${title}: ${truncate(answers.get(title), 200)}`),
  ];

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

function formatAnswer(value) {
  return Array.isArray(value) ? value.join(', ') : String(value || '未入力');
}

function truncate(value, maxLength) {
  return value.length <= maxLength
    ? value
    : `${value.slice(0, maxLength)}...`;
}

function sendToChat(message) {
  const url = PropertiesService.getScriptProperties()
    .getProperty('CHAT_WEBHOOK_URL');

  if (!url) {
    throw new Error('CHAT_WEBHOOK_URLが設定されていません。');
  }

  const response = UrlFetchApp.fetch(url, {
    method: 'post',
    contentType: 'application/json; charset=UTF-8',
    payload: JSON.stringify({ text: message }),
    muteHttpExceptions: true,
  });

  const status = response.getResponseCode();
  if (status < 200 || status >= 300) {
    throw new Error(`Chat通知に失敗しました。HTTP ${status}`);
  }
}

質問タイトルをキーにしているため、同じタイトルを複数の設問で使わないようにします。質問名を変更した場合はCHAT_FIELDSも更新します。

このサンプルは説明用の最小構成です。自由記述をChatへ送る場合は、文字数制限だけでなく、機密情報、個人情報、プロンプト文字列などが混ざる前提で通知範囲を設計してください。

インストール型トリガーを設定する

onFormSubmitは、フォーム送信元のインストール型トリガーに登録します。

  1. Apps Scriptのトリガー画面を開く
  2. 実行する関数にonFormSubmitを選ぶ
  3. イベントのソースにフォームからを選ぶ
  4. イベントの種類にフォーム送信時を選ぶ
  5. 保存し、必要な権限を承認する

インストール型トリガーは、作成者のアカウント権限で動きます。担当者の退職やアカウント停止で運用が止まらないよう、所有者と引き継ぎ方法を決めておきます。

エディタの実行ボタンからonFormSubmitを直接動かすと、eが渡されず失敗します。フォームからテスト回答を送信して確認してください。

回答先スプレッドシートで処理する場合

回答先シートに紐づける場合は、e.namedValuesまたはe.valuesを使います。Google公式のイベントオブジェクト資料でも、スプレッドシートのフォーム送信イベントにこれらの値が定義されています。

function onFormSubmitFromSheet(e) {
  const values = e.namedValues;
  const category = (values['お問い合わせ種別'] || ['未分類'])[0];
  const company = (values['会社名'] || ['未入力'])[0];

  sendToChat([
    '*新しいフォーム回答*',
    `種別: ${category}`,
    `会社名: ${company}`,
  ].join('\n'));
}

フォーム紐づけのe.responseと、シート紐づけのe.namedValuesを同じコードで扱おうとすると、テストしにくくなります。関数を分け、どのトリガーから呼ばれるかを明示します。

回答内容で通知先を分ける

営業、採用、サポートで通知先を分ける場合は、種別ごとにWebhook URLをスクリプトプロパティへ保存します。

function getWebhookUrl(category) {
  const keyByCategory = {
    営業: 'CHAT_WEBHOOK_SALES',
    採用: 'CHAT_WEBHOOK_RECRUITING',
    サポート: 'CHAT_WEBHOOK_SUPPORT',
  };

  const propertyKey = keyByCategory[category] || 'CHAT_WEBHOOK_DEFAULT';
  const url = PropertiesService.getScriptProperties().getProperty(propertyKey);

  if (!url) {
    throw new Error(`${propertyKey}が設定されていません。`);
  }
  return url;
}

通知先を増やしすぎると、誰も見ないスペースが生まれます。分岐を追加する前に、各スペースの担当者、対応時間、未対応の確認方法を決めます。

テストで確認する項目

確認項目テスト方法合格条件
項目取得選択式、未入力、複数選択、長文を送る欠損や配列表示の崩れがない
通知先各問い合わせ種別で送信する意図したスペースだけに届く
個人情報氏名、連絡先、自由記述を確認する設計した項目以外がChatへ出ない
権限担当外のアカウントでリンクを開く回答先を閲覧できない
失敗テスト用の無効URLで実行する実行履歴に失敗が残る
引き継ぎ別の管理担当が設定を確認する所有者、プロパティ、トリガーを追跡できる

本番運用で見る指標

Apps Scriptの割り当てやGoogle Chatの送信制限は変更されるため、固定値を前提に設計しません。公式の割り当てページを確認し、Apps Scriptの実行履歴で失敗を監視します。

Decision chart

フォーム通知で優先して監視する項目

問い合わせ対応への影響をもとにSakuraAIが整理した優先度です。

通知失敗件数100/100

受付済みなのに担当が気づけない

受付から初回確認までの時間95/100

通知導入の効果を直接測れる

未対応件数94/100

通知だけで放置されていないかを見る

誤ったスペースへの通知92/100

個人情報と対応遅延の両方に影響する

通知文字数75/100

長文化すると重要箇所を読み落としやすい

通知が届いた件数だけでなく、初動と未対応まで追います。

Webhookの送信が失敗した場合、HTTPステータスを記録し、一時的なエラーには指数バックオフを検討します。重要な受付では、Chat通知の成否にかかわらず回答先へ保存される構成を保ちます。

この方式が向かないケース

  • Chat上で承認、差戻し、担当引受まで行いたい
  • 大量の回答を短時間に処理し、厳密な再送キューが必要
  • 複数組織や多数のスペースへ製品として配布したい
  • 通知の配信保証や監査ログを業務要件にしている

この場合は、Webhookだけで広げず、Chatアプリ、Cloud Run、Pub/Sub、チケット管理などを含めて設計します。Webhookは小さく始めるには向きますが、双方向処理や配信保証の仕組みではありません。

運用判断のQ&A

Q. 最初に自動化するフォームはどう選びますか?

A. 回答件数の多さより、見落としたときの損失が大きく、担当と次の行動が明確なフォームを選びます。採用応募や営業問い合わせは有力ですが、通知先の責任者を先に決めておくこと。ここが曖昧だと、通知は飛ぶのに誰も動かない状態になります。

Q. AIで回答を要約してからChatへ送ってもいい?

A. 補助としては使えます。ただし条件があって、氏名や連絡先を必要もなく外部モデルへ送らないこと、要約を正本にしないこと、重要な受付では失敗時に元回答へたどれる経路を残すこと。この3つを守れば実用になります。

Q. 通知を入れた後、何を改善しますか?

A. 通知の数ではなく、初回確認までの時間・未対応件数・誤振り分けを見ます。ここが悪いときは、通知文を増やすより、担当と期限がひと目で分かる形に直すほうが効きます。

関連レシピ

メール起点の通知はGmail自動化、Drive更新の通知はDrive更新をChatに通知する方法、双方向処理を含む全体像はGoogle Chat APIの解説を参照してください。

SakuraAIで支援できること

SakuraAIは、Googleフォームの通知、担当別の振り分け、受付台帳、失敗監視まで設計・実装します。Chatへ通知するだけで終わらせず、個人情報の境界、対応状態の正本、運用担当の引き継ぎまで含めて構築します。

よくある質問

スプレッドシート連携のフォームでも通知できますか?

できます。フォームに紐づくスクリプトではe.response、回答先スプレッドシートに紐づくスクリプトではe.namedValuesやe.valuesを使います。トリガーの作成元とイベントオブジェクトを混同しないことが重要です。

回答の一部だけ通知できますか?

できます。問い合わせ種別や受付番号など、初動に必要な項目だけを抽出します。氏名、メールアドレス、自由記述の全文を毎回Chatへ複製する設計は避けます。

通知先を回答内容で振り分けられますか?

できます。問い合わせ種別などの回答値に応じてWebhookを切り替えます。ただし、URLをコードへ直書きせず、スクリプトプロパティで管理します。

onFormSubmitをエディタから実行するとエラーになるのはなぜですか?

エディタからの手動実行ではイベントオブジェクトeが渡されないためです。フォームからテスト回答を送信して確認します。通知関数だけを試す場合は、テスト用の引数を渡す別関数を用意します。

Webhookだけで担当者の引受や承認までできますか?

できません。WebhookはChatへの一方向通知です。ボタン操作や回答を受け取りたい場合はChatアプリ、対応状態の正本を持ちたい場合はスプレッドシートやチケット管理を組み合わせます。

参考・一次情報

湯田英也

Reviewed by

監修者: 湯田英也

SakuraAI株式会社 代表 / ソフトウェアエンジニア

Google Chatの導入・移行、Google Workspace連携、Google Chat API / Webhook / Bot、Gmail・Meet・Drive・Calendarをまたぐ業務自動化領域を監修しています。

Google公式情報、各プロダクトの仕様、実務での運用・開発観点をもとに、企業が導入判断・運用設計・自動化設計を行える内容になるよう確認しています。

開発相談

Google Chat Bot・Webhook開発を相談する

通知・承認・問い合わせ連携のBotとAPI実装を支援します。

開発を相談する