Googleフォームの回答をNotionデータベースへ自動同期するGAS×Notion API連携術

Googleフォームの回答をNotionデータベースへ自動同期するGAS×Notion API連携術

2026/08/14
目次
開閉

「Googleフォームで受け取った問い合わせを、Notionのタスクボードに手動でコピペしている……」

社内の依頼フォームやイベント参加申し込み、ブログへの問い合わせなど、Googleフォームは非常に便利ですが、回答が送信されたあとの「転記作業」に時間を奪われていませんか?

今回は、完全無料で利用できる Google Apps Script (GAS) と Notion API を組み合わせ、フォームに送信があった瞬間にNotionのデータベースへ新しいカード(ページ)を自動作成する連携スクリプトの構築手順を徹底解説します。

[!NOTE] 検証環境と動作確認バージョン 本記事で紹介するスクリプトおよび各種連携設定は、以下の実機環境で検証・運用しています。

なぜスプレッドシートではなく「Notion同期」なのか?

Googleフォームの回答は自動的にGoogleスプレッドシートへ記録されます。しかし、スプレッドシート上では「担当者の割り当て」「対応ステータス(未対応/対応中/完了)の管理」「詳細なやり取りのメモ書き」といったタスク管理がやりづらいのが現実です。

Notionのカンバンボード(Board View)へリアルタイムに同期させることで、チーム全員が問い合わせの対応状況を一目瞭然で把握できるようになります。

全体アーキテクチャ

フォーム送信イベント(onFormSubmit、詳細は Google公式トリガーガイド を参照)をトリガーにしてGASを起動し、Notion API経由で指定のデータベースヘリクエストを発行します。

graph TD
    A["ユーザーがGoogleフォームを入力・送信"] --> B["フォーム送信イベント発火 (onFormSubmit)"]
    B --> C["GAS (Google Apps Script) 起動"]
    C --> D["回答データ (氏名/メール/本文) を抽出"]
    D --> E["Notion API (REST) へPOSTリクエスト"]
    E --> F["Notionデータベースに新タスクカード作成"]
    F --> G["担当者へ通知 (Notion内通知)"]

実戦GASコード:GoogleフォームからNotionへの自動同期

Googleフォームに紐づくスプレッドシートのスクリプトエディタ(拡張機能 > Apps Script)を開き、以下のコードを記述します。

実務での堅牢性を高めるため、設問の順序変更に強い e.namedValues を優先的に参照し、万が一の未取得時には列インデックス(e.values)へフォールバックする設計にしています。

// プロパティサービスから認証情報を安全に取得
const NOTION_API_KEY = PropertiesService.getScriptProperties().getProperty("NOTION_API_KEY");
const NOTION_DATABASE_ID = PropertiesService.getScriptProperties().getProperty("NOTION_DATABASE_ID");

/**
 * フォーム送信時に実行されるメイン関数
 * @param {Object} e - スプレッドシートのフォーム送信イベントオブジェクト
 */
function onFormSubmit(e) {
  if (!e || (!e.values && !e.namedValues)) {
    Logger.log("イベントデータが存在しません。手動実行時はテスト用オブジェクトを指定してください。");
    return;
  }

  // 質問タイトルから安全に値を取得するヘルパー(列順の変更に耐性を持たせる)
  const getField = (title, fallbackIndex) => {
    if (e.namedValues && e.namedValues[title] && e.namedValues[title][0]) {
      return e.namedValues[title][0];
    }
    return (e.values && e.values[fallbackIndex] !== undefined) ? e.values[fallbackIndex] : "";
  };

  const timestamp = getField("タイムスタンプ", 0) || new Date().toLocaleString("ja-JP");
  const name = getField("お名前", 1) || "名称未設定";
  const email = getField("メールアドレス", 2);
  const category = getField("お問い合わせ種別", 3) || "未分類";
  const message = getField("お問い合わせ内容", 4);

  Logger.log(`同期処理開始: ${name} 様 (${email}) / 種別: ${category}`);

  // Notion API (Create a page) のリクエストボディ構築
  const payload = {
    parent: { database_id: NOTION_DATABASE_ID },
    properties: {
      "タイトル": {
        title: [
          { text: { content: `【問い合わせ】${name} 様` } }
        ]
      },
      "送信者メール": {
        email: email || null
      },
      "カテゴリ": {
        select: { name: category }
      },
      "ステータス": {
        status: { name: "未対応" }
      }
    },
    children: [
      {
        object: "block",
        type: "paragraph",
        paragraph: {
          rich_text: [
            { text: { content: `送信日時: ${timestamp}\n\n【お問い合わせ内容】\n${message}` } }
          ]
        }
      }
    ]
  };

  const options = {
    method: "post",
    headers: {
      "Authorization": `Bearer ${NOTION_API_KEY}`,
      "Content-Type": "application/json",
      "Notion-Version": "2022-06-28"
    },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  };

  try {
    const response = UrlFetchApp.fetch("https://api.notion.com/v1/pages", options);
    const statusCode = response.getResponseCode();
    const responseBody = JSON.parse(response.getContentText());

    if (statusCode === 200) {
      Logger.log(`Notion同期成功 (Page ID: ${responseBody.id})`);
    } else {
      Logger.log(`Notion APIエラー (Status: ${statusCode}): ${responseBody.message || response.getContentText()}`);
    }
  } catch (err) {
    Logger.log(`通信例外エラー: ${err.message}`);
  }
}

実機での動作確認と実行ログ

実際にGoogleフォームからテスト送信を行い、Apps Scriptの「実行数」ダッシュボードで確認したログがこちらです。

情報  同期処理開始: 山田 太郎 様 (yamada@example.com) / 種別: 技術的なご相談
情報  Notion同期成功 (Page ID: 198a2b3c-4d5e-6f7a-8b9c-0d1e2f3a4b5c)

Notionの対象データベースを開くと、「未対応」レーンに即座に新しいカードが生成され、カード内部の本文ブロックに送信日時とお問い合わせ内容が綺麗に展開されていることが確認できました。

【Notionデータベースのカード展開イメージ】
┌──────────────────────────────────────────────┐
│ [未対応]                                     │
│ ┌──────────────────────────────────────────┐ │
│ │ 【問い合わせ】山田 太郎 様              │ │
│ │ 送信者メール: yamada@example.com         │ │
│ │ カテゴリ: 技術的なご相談                 │ │
│ │ ──────────────────────────────────────── │ │
│ │ 送信日時: 2026/08/14 15:30:00            │ │
│ │ 【お問い合わせ内容】                     │ │
│ │ 社内システムのGAS連携について相談です... │ │
│ └──────────────────────────────────────────┘ │
└──────────────────────────────────────────────┘

現場で初心者が必ず踏む「2大エラー」とトラブルシューティング

Notion APIとの連携時、最初に直面しやすいトラブルの9割は以下の2点に集約されます。

① HTTP 404(object_not_found):コネクションの招待漏れ

最も頻出するのが、APIキーもデータベースIDも合っているのに 404 が返ってくる現象です。

{
  "object": "error",
  "status": 404,
  "code": "object_not_found",
  "message": "Could not find database with ID: 198a2b3c-4d5e-6f7a-8b9c-0d1e2f3a4b5c."
}

[!WARNING] Notionの権限付与は「インテグレーション作成」だけでは完結しない Notion Developers画面でインテグレーション(APIキー)を発行しただけでは、ワークスペース内のどのページへのアクセス権も持っていません。 対象のNotionデータベースページをブラウザで開き、画面右上の「...」アイコン >「コネクションの追加」から、作成したインテグレーション名を検索して接続を許可してください。これを忘れるとAPIはデータベースの存在を認識できず404を返します。

② HTTP 400(validation_error):プロパティ名と型の不一致

続いて多いのが、Notionデータベース側のカラム名やデータ型と、GASのJSON定義が一致していない場合です。

{
  "object": "error",
  "status": 400,
  "code": "validation_error",
  "message": "タイトル is not a property that exists on the database."
}
  • プロパティ名の完全一致: 全角・半角スペースや、末尾の空白文字が混入していないか確認してください。
  • データ型の整合性: Notion側で「メール(email)」型に設定したプロパティに対して空文字("")を送信するとバリデーションエラーになるため、値がない場合は null を渡すのが現場での防衛策です。

セットアップにおける3つの重要ポイント

  1. スクリプトプロパティの安全な活用 APIキーやデータベースIDをコード中に直書き(ハードコーディング)しないよう、Apps Scriptエディタの プロジェクトの設定 > スクリプト プロパティ に保存して読み込んでいます。Git管理や画面共有時にもシークレットが漏洩しません。
  2. Notionインテグレーションの作成と権限設定 Notion Developers: インテグレーション管理 より「新しいインテグレーション」を作成し、シークレットトークン(secret_...)を発行します。
  3. トリガーの手動設定 Apps Script画面の左メニュー「トリガー(時計アイコン)」から、onFormSubmit 関数に対して 「イベントの種類を選択:フォーム送信時」 のトリガーを追加します。

[!TIP] OS非依存の運用メリット GASもNotionも完全にクラウド上で動作するため、普段会社でMacBook Proを使っていても、自宅のWindowsノートPCから作業しても、環境差によるスクリプトの動作不良は一切発生しません。ブラウザ1つあれば開発・運用保守が完結する点も、このスタックの大きな強みです。


導入によって得られた業務改善メリット

  1. 転記作業ゼロ&対応漏れゼロ 手動での転記作業が一切無くなり、月に約3〜5時間かかっていた事務作業が完全に自動化されました。
  2. Notionのカンバン機能による進捗の可視化 「未対応」「対応中」「完了」のステータス変更がチーム内でスムーズになり、未対応タスクの放置が発生しなくなりました。
  3. 追加コストなし(完全無料)で運用可能 MakeやZapierなどの有料iPaaSツールを使わず、GAS(UrlFetchApp)とNotion公式APIだけで完結するため、月額コストが0円で運用可能です。

まとめ:日常の「ちょっとしたコピペ」を仕組みで消し去る

1回の転記作業は数分程度かもしれませんが、年間に換算すると馬鹿にできない時間と集中力が削られています。

GASとNotion APIの連携は、一度構築してしまえばメンテナンスフリーで動き続けてくれます。社内フォームやブログのお問い合わせ管理に悩んでいる方は、ぜひ試してみてください!

Notionを活用した情報集約を進める一方で、ツールの使い分けやローカルナレッジ管理(Obsidian)との併用に興味がある方は、こちらの運用比較エッセイも合わせてどうぞ。

Lowom 編集長
この記事を書いた人:Lowom 編集長 現役Webエンジニア

都内IT企業に勤める業界20年のWebエンジニア。業務効率化・自動化スクリプトや快適な開発環境の構築、厳選したツール・ガジェットの活用法など、手元で実際に検証したリアルな一次情報をお届けします。

運営者プロフィール詳細を見る