bamboohr-webhooks-events
BambooHR のウェブフック エンドポイントを実装し、HMAC署名検証と従業員変更イベント処理に対応します。グローバルウェブフックと権限付きウェブフックの両方に対応しており、リアルタイム従業員通知の設定、同期トリガーの実装、BambooHR ウェブフック ペイロードの処理が可能です。「bamboohr webhook」「bamboohr events」「bamboohr real-time sync」「bamboohr notifications」「bamboohr employee changes」といったフレーズで実行できます。
description の原文を見る
Implement BambooHR webhook endpoints with HMAC signature validation and employee change event handling. Covers global and permissioned webhooks. Use when setting up real-time employee notifications, implementing sync triggers, or handling BambooHR webhook payloads. Trigger with phrases like "bamboohr webhook", "bamboohr events", "bamboohr real-time sync", "bamboohr notifications", "bamboohr employee changes".
SKILL.md 本文
BambooHR ウェブフック & イベント
概要
BambooHRは2種類のウェブフックをサポートしています: グローバルウェブフック(BambooHR管理UIで設定、フィールドのサブセット)とパーミッションウェブフック(APIで作成、APIキーユーザーがアクセスできるすべてのフィールド)。このスキルは、両方の種類の作成、検証、処理をカバーしています。
前提条件
- ウェブフック管理権限付きのBambooHR APIキー
- インターネットからアクセス可能なHTTPSエンドポイント
- HMAC-SHA256署名検証用のウェブフックシークレット
手順
ステップ1: ウェブフックタイプを理解する
| 機能 | グローバルウェブフック | パーミッションウェブフック |
|---|---|---|
| セットアップ | BambooHR管理UI | API (POST /webhooks/) |
| フィールドアクセス | 標準フィールドのサブセット | ユーザーがアクセスできるすべてのフィールド |
| 認証 | 共有シークレット | ウェブフックごとのシークレット |
| 署名 | SHA-256 HMAC | SHA-256 HMAC |
| アクション | Created、Updated、Deleted | Created、Updated、Deleted |
ステップ2: API経由でパーミッションウェブフックを作成する
// POST /webhooks/ — 新しいウェブフックを登録
const webhook = await client.request<{
id: number;
name: string;
privateKey: string; // HMAC検証に使用するため保存してください
}>('POST', '/webhooks/', {
name: 'Employee Sync Webhook',
monitorFields: [
'firstName', 'lastName', 'jobTitle', 'department',
'division', 'location', 'workEmail', 'status',
'supervisor', 'hireDate', 'terminationDate',
],
postFields: {
firstName: 'firstName',
lastName: 'lastName',
jobTitle: 'jobTitle',
department: 'department',
status: 'status',
workEmail: 'workEmail',
},
url: 'https://your-app.example.com/webhooks/bamboohr',
format: 'json',
frequency: { every: 0 }, // 0 = 即座、またはN = N分ごとにバッチ処理
limit: { enabled: false },
});
console.log(`Webhook ID: ${webhook.id}`);
console.log(`Private Key: ${webhook.privateKey}`);
// 重要: privateKeyは安全に保管してください — これがHMACシークレットです
ステップ3: ウェブフックをリストして管理する
// GET /webhooks/ — このAPIキーのすべてのウェブフックをリスト
const webhooks = await client.request<any[]>('GET', '/webhooks/');
for (const wh of webhooks) {
console.log(`${wh.id}: ${wh.name} -> ${wh.url} (${wh.status})`);
}
// GET /webhooks/{id}/ — ウェブフックの詳細を取得
const detail = await client.request<any>('GET', `/webhooks/${webhook.id}/`);
// GET /webhooks/{id}/log — ウェブフック配信ログを取得
const logs = await client.request<any[]>('GET', `/webhooks/${webhook.id}/log`);
for (const log of logs) {
console.log(`${log.timestamp}: ${log.statusCode} (${log.employeeId})`);
}
// DELETE /webhooks/{id}/ — ウェブフックを削除
await client.request('DELETE', `/webhooks/${webhook.id}/`);
// GET /webhooks/monitor_fields — 監視可能なフィールドを確認
const fields = await client.request<any>('GET', '/webhooks/monitor_fields');
ステップ4: 署名検証
BambooHRはX-BambooHR-Signature(HMAC-SHA256 16進ダイジェスト)とX-BambooHR-Timestampの2つのヘッダーを送信します。
import crypto from 'crypto';
function verifyBambooHRWebhook(
rawBody: Buffer | string,
signature: string,
timestamp: string,
secret: string,
): boolean {
// 1. 5分以上前のタイムスタンプを拒否(リプレイ攻撃対策)
const age = Math.abs(Date.now() - parseInt(timestamp, 10) * 1000);
if (age > 300_000) {
console.error(`Webhook timestamp too old: ${age}ms`);
return false;
}
// 2. 予想されるHMACを計算
const payload = `${timestamp}.${rawBody.toString()}`;
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
// 3. タイミング安全な比較
try {
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expected, 'hex'),
);
} catch {
return false;
}
}
ステップ5: ウェブフックハンドラ(Express.js)
import express from 'express';
const app = express();
app.post('/webhooks/bamboohr',
express.raw({ type: 'application/json' }),
async (req, res) => {
const sig = req.headers['x-bamboohr-signature'] as string;
const ts = req.headers['x-bamboohr-timestamp'] as string;
if (!sig || !ts || !verifyBambooHRWebhook(req.body, sig, ts, process.env.BAMBOOHR_WEBHOOK_SECRET!)) {
console.error('Webhook signature verification failed');
return res.status(401).json({ error: 'Invalid signature' });
}
// ウェブフックペイロードをパース
const payload = JSON.parse(req.body.toString());
// すぐに応答 — 非同期で処理
res.status(200).json({ received: true });
// ペイロード内の各従業員を処理
await processWebhookPayload(payload);
},
);
ステップ6: ウェブフックペイロードを処理する
BambooHRウェブフックペイロードには、アクションタイプ別にグループ化された従業員データが含まれています。
interface BambooHRWebhookPayload {
employees: {
id: string;
action: 'Created' | 'Updated' | 'Deleted';
changedFields: string[]; // この通知をトリガーしたフィールド
fields: Record<string, string>; // 現在のフィールド値(postFields設定から)
}[];
}
async function processWebhookPayload(payload: BambooHRWebhookPayload): Promise<void> {
for (const employee of payload.employees) {
const { id, action, changedFields, fields } = employee;
switch (action) {
case 'Created':
console.log(`New employee: ${fields.firstName} ${fields.lastName} (ID: ${id})`);
await onEmployeeCreated(id, fields);
break;
case 'Updated':
console.log(`Employee ${id} updated: ${changedFields.join(', ')}`);
// 何が変更されたかに基づいて特定のハンドラにルーティング
if (changedFields.includes('department') || changedFields.includes('jobTitle')) {
await onPositionChanged(id, fields);
}
if (changedFields.includes('status')) {
if (fields.status === 'Inactive') {
await onEmployeeTerminated(id, fields);
}
}
if (changedFields.includes('supervisor')) {
await onManagerChanged(id, fields);
}
break;
case 'Deleted':
console.log(`Employee ${id} deleted`);
await onEmployeeDeleted(id);
break;
}
}
}
// 例: ハンドラ
async function onEmployeeCreated(id: string, fields: Record<string, string>) {
// 外部システムでアカウントをプロビジョニング
// 例: Slackアカウント作成、メール設定、トレーニング割り当て
}
async function onEmployeeTerminated(id: string, fields: Record<string, string>) {
// デプロビジョニング: アカウント無効化、アクセス取り消し、データアーカイブ
}
async function onPositionChanged(id: string, fields: Record<string, string>) {
// 組織図、Slackチャンネル、アクセスグループを更新
}
async function onManagerChanged(id: string, fields: Record<string, string>) {
// ダウンストリームシステムのレポーティング階層を更新
}
async function onEmployeeDeleted(id: string) {
// 外部システムから削除
}
ステップ7: 冪等性(重複処理の防止)
import { Redis } from 'ioredis';
const redis = new Redis(process.env.REDIS_URL);
async function deduplicateWebhook(
employeeId: string,
action: string,
changedFields: string[],
): Promise<boolean> {
// この特定の変更のための一意のキーを作成
const changeKey = `bamboohr:webhook:${employeeId}:${action}:${changedFields.sort().join(',')}`;
const wasSet = await redis.set(changeKey, '1', 'EX', 3600, 'NX'); // 1時間のTTL
return wasSet === 'OK'; // true = 初回、false = 重複
}
ステップ8: ウェブフックをローカルでテストする
# 1. ngrokでローカルサーバーを公開
ngrok http 3000
# https:// URLを注意してください
# 2. ngrok URLを指すテストウェブフックを作成
# APIを使用してngrok URLでウェブフックを作成
# 3. またはテストペイロードを手動で送信
curl -X POST http://localhost:3000/webhooks/bamboohr \
-H "Content-Type: application/json" \
-H "X-BambooHR-Timestamp: $(date +%s)" \
-H "X-BambooHR-Signature: test" \
-d '{"employees": [{"id":"1","action":"Updated","changedFields":["department"],"fields":{"firstName":"Jane","department":"Engineering"}}]}'
出力
- 監視対象フィールドを持つBambooHR API経由でウェブフックが登録されました
- すべての受信ウェブフックに対するHMAC-SHA256署名検証
- アクションタイプ別のイベントルーティング(Created、Updated、Deleted)
- フィールド固有の変更ハンドラ(職位、ステータス、マネージャー)
- Redis経由の重複排除
- ngrokを使用したローカルテストワークフロー
エラーハンドリング
| 問題 | 原因 | 解決方法 |
|---|---|---|
| 無効な署名 | 間違ったウェブフックシークレット | ウェブフック作成時のprivateKeyを確認してください |
空のchangedFields | Created/Deletedアクション | 通常 — UpdatedアクションのみがchangedFieldsを含みます |
| ペイロードで見つからないフィールド | postFields設定に含まれていない | ウェブフックpostFields設定を更新してください |
| ウェブフックが発火しない | ウェブフック無効またはURL到達不可 | APIを通じてウェブフックのステータスとログを確認してください |
エンタープライズに関する考慮事項
- HTTPS必須: BambooHRはHTTPS URLへのみ投稿します
- 再試行動作: BambooHRは失敗した配信を再試行します。冪等性を実装してください
- カスタムフィールド: パーミッションウェブフックはカスタムフィールドを監視できます(
/meta/fields/からフィールドIDを使用) - バッチ頻度:
frequency.everyを0より大きく設定して、複数の変更をより少ない配信にバッチ処理します
リソース
- BambooHR Webhooks Guide
- BambooHR Global Webhooks
- BambooHR Permissioned Webhooks
- BambooHR Webhook API Reference
次のステップ
パフォーマンス最適化については、bamboohr-performance-tuningを参照してください。
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- ComeOnOliver
- ライセンス
- MIT
- 最終更新
- 2026/5/11
Source: https://github.com/ComeOnOliver/skillshub / ライセンス: MIT