Webhook
kintone の Webhook 機能を使うと、レコードの追加・更新・削除やプロセス管理のステータス変更をトリガーとして、外部の URL に HTTP リクエストを自動送信できます。JavaScript カスタマイズの kintone.proxy による能動的な通知とは異なり、kintone のサーバー側から自動的にリクエストが送信されるため、ブラウザの JavaScript カスタマイズに依存しない堅牢な連携が実現できます。
この記事では、Webhook の基本的な仕組みから設定方法、受信側の実装例まで解説します。
Webhook と kintone.proxy の違い
| 項目 | Webhook | kintone.proxy |
|---|---|---|
| 送信元 | kintone サーバー | ブラウザ(kintone サーバー経由) |
| トリガー | レコード操作(自動) | JavaScript コード(手動) |
| 実行条件 | API 経由の操作でも発火 | ブラウザ上のイベント時のみ |
| 設定場所 | アプリの設定画面 | JavaScript ファイル |
| カスタマイズ性 | 固定フォーマット | 自由なペイロード |
Webhook は REST API や CSV インポートによるレコード操作でも発火するため、JavaScript カスタマイズでは検知できないデータ変更にも対応できます。
Webhook の設定手順
- アプリの設定画面を開く
- 「設定」タブ → 「Webhook」を選択
- 「+」ボタンをクリックして新しい Webhook を追加
- 以下の項目を設定する
| 設定項目 | 説明 |
|---|---|
| URL | 通知先の URL(HTTPS のみ) |
| 通知を送信する条件 | レコードの追加、更新、削除、ステータスの更新、コメントの書き込み |
- 設定を保存し、アプリの設定を運用環境に反映する
Webhook の送信先 URL は HTTPS のみ対応しています。HTTP(非暗号化)の URL は指定できません。
Webhook で送信されるデータ
Webhook で送信されるリクエストボディには、イベントの種類やレコードの情報が JSON 形式で含まれます。
レコード追加時のペイロード例
{
"hookId": "webhook-hook-id",
"id": 1,
"type": "ADD_RECORD",
"app": {
"id": "123",
"name": "問い合わせ管理"
},
"record": {
"レコード番号": { "type": "RECORD_NUMBER", "value": "1" },
"タイトル": { "type": "SINGLE_LINE_TEXT", "value": "サンプル問い合わせ" },
"カテゴリ": { "type": "DROP_DOWN", "value": "技術サポート" },
"作成日時": { "type": "CREATED_TIME", "value": "2026-02-24T10:00:00Z" }
},
"recordTitle": "サンプル問い合わせ",
"url": "https://your-domain.cybozu.com/k/123/show#record=1"
}
イベントタイプ
type の値 | 説明 |
|---|---|
ADD_RECORD | レコードの追加 |
UPDATE_RECORD | レコードの更新 |
DELETE_RECORD | レコードの削除 |
UPDATE_STATUS | ステータスの更新 |
ADD_COMMENT | コメントの書き込み |
受信側の実装例
Node.js (Express) での受信サーバー
Webhook を受信して処理するシンプルな Node.js サーバーの例です。
const express = require('express');
const app = express();
app.use(express.json());
// kintone Webhook の受信エンドポイント
app.post('/webhook/kintone', (req, res) => {
const { type, app: kintoneApp, record, url } = req.body;
console.log(`[${type}] アプリ: ${kintoneApp.name} (ID: ${kintoneApp.id})`);
switch (type) {
case 'ADD_RECORD':
console.log('レコードが追加されました:', record['タイトル']?.value);
// 新規レコードの処理(例: 外部システムにデータ連携)
break;
case 'UPDATE_RECORD':
console.log('レコードが更新されました:', url);
// 更新時の処理(例: キャッシュの無効化)
break;
case 'DELETE_RECORD':
console.log('レコードが削除されました');
// 削除時の処理(例: 関連データのクリーンアップ)
break;
case 'UPDATE_STATUS':
console.log('ステータスが変更されました');
// ステータス変更時の処理(例: 承認通知の送信)
break;
case 'ADD_COMMENT':
console.log('コメントが追加されました');
break;
}
// kintone は 200 レスポンスを期待する
res.status(200).send('OK');
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Webhook サーバーが起動しました: http://localhost:${PORT}`);
});
Webhook 受信側は 5 秒以内に 200 レスポンスを返す必要があります。処理に時間がかかる場合は、リクエストを受け付けてすぐにレスポンスを返し、実際の処理は非同期で実行する設計にしましょう。
Google Apps Script での受信
Google Apps Script を使って Webhook を受信し、Google スプレッドシートに記録する例です。
/**
* kintone の Webhook を受信してスプレッドシートに書き込む
* @param { Object } e - イベントオブジェクト
* @returns { ContentService.TextOutput } レスポンス
*/
function doPost(e) {
const data = JSON.parse(e.postData.contents);
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Webhook履歴');
sheet.appendRow([
new Date(),
data.type,
data.app.name,
data.record?.['タイトル']?.value || '',
data.url || '',
]);
return ContentService.createTextOutput('OK');
}
Webhook の活用パターン
パターン 1: Slack への自動通知
Webhook と Slack を組み合わせて、レコード操作時に自動で Slack チャンネルに通知する仕組みです。
app.post('/webhook/kintone', async (req, res) => {
res.status(200).send('OK'); // 先にレスポンスを返す
const { type, record, url, app: kintoneApp } = req.body;
if (type === 'ADD_RECORD') {
const slackWebhook = process.env.SLACK_WEBHOOK_URL;
const message = {
text: `📋 ${kintoneApp.name} に新しいレコードが追加されました\nタイトル: ${record['タイトル']?.value}\n${url}`,
};
await fetch(slackWebhook, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(message),
});
}
});
パターン 2: 外部データベースとの同期
kintone のレコードを外部のデータベースにリアルタイムで同期するパターンです。
app.post('/webhook/kintone', async (req, res) => {
res.status(200).send('OK');
const { type, record } = req.body;
switch (type) {
case 'ADD_RECORD':
case 'UPDATE_RECORD':
await upsertExternalDatabase({
id: record['レコード番号'].value,
title: record['タイトル'].value,
status: record['ステータス']?.value,
updatedAt: new Date(),
});
break;
case 'DELETE_RECORD':
await deleteFromExternalDatabase(record['レコード番号'].value);
break;
}
});
セキュリティの考慮点
リクエストの検証
Webhook のリクエストが本当に kintone から送信されたものかを検証する方法を検討する必要があります。
// 送信元 IP アドレスの制限
const ALLOWED_IPS = ['xxx.xxx.xxx.xxx']; // kintone のサーバー IP
app.post('/webhook/kintone', (req, res) => {
const clientIp = req.ip || req.connection.remoteAddress;
if (!ALLOWED_IPS.includes(clientIp)) {
console.warn('不正なIPアドレスからのリクエスト:', clientIp);
return res.status(403).send('Forbidden');
}
// 正常な処理を続行
// ...
});
kintone の Webhook はリクエストに署名検証用のヘッダーを付与しません。送信元の検証には IP 制限や独自のトークン埋め込み(URL パラメータにトークンを含める等)で対応してください。
Webhook URL にトークンを含める方法
https://your-server.com/webhook/kintone?token=your-secret-token
app.post('/webhook/kintone', (req, res) => {
const token = req.query.token;
if (token !== process.env.WEBHOOK_SECRET) {
return res.status(401).send('Unauthorized');
}
// 正常な処理を続行
// ...
});
制限事項
| 制限項目 | 内容 |
|---|---|
| 送信先 URL | HTTPS のみ |
| タイムアウト | 5 秒以内にレスポンスを返す必要がある |
| リトライ | 失敗時のリトライはなし |
| 1 アプリあたりの数 | 最大 10 件 |
| 送信順序 | 送信順序は保証されない |
Webhook の送信は失敗してもリトライされません。確実にデータを処理したい場合は、受信側でキューイングの仕組みを導入するか、定期的にkintone REST API でデータを取得する方式と併用することを検討してください。
まとめ
- Webhook を使うと、レコード操作やステータス変更時に外部 URL へ自動通知を送れる
- REST API 経由の操作でも発火するため、JavaScript カスタマイズだけでは検知できないデータ変更にも対応可能
- 受信側は 5 秒以内にレスポンスを返し、重い処理は非同期で実行する設計にする
- セキュリティ対策として、IP 制限やトークン検証の仕組みを導入する
- リトライ機能はないため、確実な処理が必要な場合はキューイングの併用を検討する