GAS×ChatGPT×Notion連携|議事録を自動整理してDBに保存
会議メモをNotionに整理する作業は地味に時間がかかる。形式がバラバラだと後から検索もしにくい。GAS×ChatGPT×Notion APIを連携させれば、生の会議メモを貼り付けるだけで、決定事項とTODOが抽出された議事録がNotionデータベースに自動登録される。
ここでは、その仕組みをゼロから構築する手順を説明する。
目次
この記事でできること
完成すると、以下の流れが自動で走る。
1. スプレッドシートに会議メモを貼り付け
↓
2. ChatGPTが議事録を整形(決定事項・TODO抽出)
↓
3. Notion APIでデータベースに自動登録
↓
4. 整形された議事録がNotionで閲覧可能
活用例:
- 定例会議の議事録を自動整理
- 決定事項とTODOを構造化して保存
- チームのナレッジベースを自動構築
事前準備
必要なもの
| 項目 | 詳細 | 取得方法 |
|---|---|---|
| Googleアカウント | スプレッドシート、GAS用 | お持ちのもの |
| OpenAI APIキー | ChatGPT連携用 | platform.openai.com |
| Notion APIキー | Notion連携用 | 後述 |
| NotionデータベースID | 保存先DB | 後述 |
Notion API設定
1. インテグレーションを作成
- Notion Developers にアクセス
- 新しいインテグレーションをクリック
- 名前を入力(例: 議事録Bot)
- ワークスペースを選択
- 送信をクリック
- 表示されたInternal Integration Tokenをコピー
[画像: Notion インテグレーション作成画面]
2. データベースを作成・共有
- Notionで新規データベースを作成
- 以下のプロパティを設定する:
| プロパティ名 | 種類 | 用途 |
|---|---|---|
| 会議名 | タイトル | 会議の名前 |
| 日付 | 日付 | 会議日 |
| 参加者 | テキスト | 参加者リスト |
| 決定事項 | テキスト | 決定事項 |
| TODO | テキスト | アクションアイテム |
| ステータス | セレクト | 未処理/処理済 |
- データベースページ右上の三点メニュー → コネクトを追加
- 作成したインテグレーションを選択
[画像: Notionデータベース構成]
3. データベースIDを取得
データベースのURLから取得する。
https://www.notion.so/ワークスペース名/[ここがデータベースID]?v=xxx
32文字の英数字部分がデータベースIDにあたる。
実装手順
STEP 1: スプレッドシートを準備
| A列(会議名) | B列(日付) | C列(参加者) | D列(議事メモ) | E列(処理済) |
|---|---|---|---|---|
| 定例MTG | 2026-02-01 | 山田,田中,鈴木 | (生の議事メモ) | FALSE |
STEP 2: GASコードを設置
// ===================================================
// 設定
// ===================================================
const CONFIG = {
OPENAI_API_KEY: PropertiesService.getScriptProperties().getProperty('OPENAI_API_KEY'),
NOTION_API_KEY: PropertiesService.getScriptProperties().getProperty('NOTION_API_KEY'),
NOTION_DATABASE_ID: PropertiesService.getScriptProperties().getProperty('NOTION_DATABASE_ID'),
SHEET_NAME: '議事録',
MODEL: 'gpt-4o-mini'
};
// ===================================================
// メイン処理: 議事録を整理してNotionに登録
// ===================================================
function processMinutesToNotion() {
const sheet = SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName(CONFIG.SHEET_NAME);
const data = sheet.getDataRange().getValues();
for (let i = 1; i < data.length; i++) {
// 未処理の行のみ対象
if (data[i][4] === true || data[i][4] === 'TRUE') continue;
if (!data[i][3]) continue; // 議事メモが空
const meeting = {
name: data[i][0],
date: data[i][1],
participants: data[i][2],
rawMinutes: data[i][3]
};
// 1. ChatGPTで議事録を整理
const structured = structureMinutes(meeting.rawMinutes);
// 2. Notionに登録
createNotionPage(meeting, structured);
// 3. 処理済みフラグを設定
sheet.getRange(i + 1, 5).setValue(true);
console.log(`${meeting.name} の議事録をNotionに登録しました`);
}
}
// ===================================================
// ChatGPTで議事録を構造化
// ===================================================
function structureMinutes(rawMinutes) {
const prompt = `以下の会議メモを整理してください。
【出力形式】
## 議題
(箇条書きで議題を列挙)
## 決定事項
(決定された内容を箇条書き)
## TODO
(誰が・何を・いつまでに、の形式で箇条書き)
## 次回への申し送り
(次回会議で確認すべき事項)
---
会議メモ:
${rawMinutes}`;
return callChatGPT(prompt);
}
// ===================================================
// ChatGPT API呼び出し
// ===================================================
function callChatGPT(prompt) {
const url = 'https://api.openai.com/v1/chat/completions';
const payload = {
model: CONFIG.MODEL,
messages: [
{ role: 'system', content: 'あなたは議事録を整理するアシスタントです。' },
{ role: 'user', content: prompt }
],
max_tokens: 1500,
temperature: 0.3
};
const options = {
method: 'post',
contentType: 'application/json',
headers: { 'Authorization': 'Bearer ' + CONFIG.OPENAI_API_KEY },
payload: JSON.stringify(payload),
muteHttpExceptions: true
};
const response = UrlFetchApp.fetch(url, options);
const json = JSON.parse(response.getContentText());
if (json.error) throw new Error(json.error.message);
return json.choices[0].message.content;
}
// ===================================================
// Notionにページを作成
// ===================================================
function createNotionPage(meeting, structuredMinutes) {
const url = 'https://api.notion.com/v1/pages';
// 決定事項とTODOを抽出
const decisions = extractSection(structuredMinutes, '決定事項');
const todos = extractSection(structuredMinutes, 'TODO');
const payload = {
parent: { database_id: CONFIG.NOTION_DATABASE_ID },
properties: {
'会議名': {
title: [{ text: { content: meeting.name } }]
},
'日付': {
date: { start: formatDate(meeting.date) }
},
'参加者': {
rich_text: [{ text: { content: meeting.participants } }]
},
'決定事項': {
rich_text: [{ text: { content: decisions } }]
},
'TODO': {
rich_text: [{ text: { content: todos } }]
},
'ステータス': {
select: { name: '未処理' }
}
},
children: [
{
object: 'block',
type: 'heading_2',
heading_2: {
rich_text: [{ text: { content: '議事録' } }]
}
},
{
object: 'block',
type: 'paragraph',
paragraph: {
rich_text: [{ text: { content: structuredMinutes } }]
}
}
]
};
const options = {
method: 'post',
contentType: 'application/json',
headers: {
'Authorization': 'Bearer ' + CONFIG.NOTION_API_KEY,
'Notion-Version': '2022-06-28'
},
payload: JSON.stringify(payload),
muteHttpExceptions: true
};
const response = UrlFetchApp.fetch(url, options);
const json = JSON.parse(response.getContentText());
if (json.object === 'error') {
throw new Error(json.message);
}
return json;
}
// ===================================================
// ユーティリティ関数
// ===================================================
function extractSection(text, sectionName) {
const regex = new RegExp(`## ${sectionName}\\n([\\s\\S]*?)(?=## |$)`);
const match = text.match(regex);
return match ? match[1].trim() : '';
}
function formatDate(date) {
if (date instanceof Date) {
return Utilities.formatDate(date, 'Asia/Tokyo', 'yyyy-MM-dd');
}
return date;
}
STEP 3: スクリプトプロパティを設定
以下の3つを登録する:
OPENAI_API_KEYNOTION_API_KEYNOTION_DATABASE_ID
STEP 4: 実行テスト
- スプレッドシートにテストデータを入力
processMinutesToNotion関数を実行- Notionデータベースにページが作成されていれば成功
[画像: Notionに登録された議事録]
カスタマイズ例
TODO担当者を自動抽出
// TODOから担当者を抽出してプロパティに設定
const todoWithAssignee = todos.match(/(\w+)さん/g);
if (todoWithAssignee) {
payload.properties['担当者'] = {
multi_select: todoWithAssignee.map(name => ({ name: name }))
};
}
会議種別で自動分類
if (meeting.name.includes('定例')) {
payload.properties['種別'] = { select: { name: '定例会議' } };
} else if (meeting.name.includes('企画')) {
payload.properties['種別'] = { select: { name: '企画会議' } };
}
トラブルシューティング
| エラー | 原因 | 対処法 |
|---|---|---|
validation_error |
プロパティ名の不一致 | Notionのプロパティ名を確認 |
unauthorized |
API権限エラー | インテグレーションの共有設定を確認 |
object_not_found |
DB IDが無効 | データベースIDを再取得 |
まとめ
GAS×ChatGPT×Notion連携で、議事録の整理と保存が自動化された。会議メモをスプレッドシートに貼り付けるだけで、構造化された議事録がNotionに蓄積されていく。トリガーを設定すれば、定期的なバッチ処理も回せる。
関連記事:
- GAS×ChatGPT入門 で基礎を固める
- GAS×Slack連携 で通知も組み合わせる
最終更新: 2026-02-02
コメント