← SaaS Workflowsの一覧へ
SaaSCirclebackWebhook業務自動化

Circlebackの会議アクションをタスクへ渡す:Webhookの署名・担当者・重複処理を検証

11 分で読める

このトピックを深掘りする

SaaSの導入・連携

AIに仕事の続きを渡すために、会議の原文、未完了の宿題、確認できない項目をどう残すか。導入判断と連携実装を掘り下げる。

全3本中 2 本目。

このトピックの記事一覧

シリーズ全体の流れを見ながら、次に読む記事へ進めます。 初めての方はホームへ →

広告:この記事にはCirclebackのアフィリエイトリンクが含まれます。契約により紹介料を受け取る場合があります。検証条件と未確認の範囲は本文に記載しています。

会議のアクション項目をタスク管理へ渡すとき、担当者が空欄の項目と、同じデータの再送をどう扱うかで連携の設計が変わる。今回はCirclebackの公式Webhook仕様から変換コードを作り、署名検証、担当者なし、完了済み、不正入力をローカルで試した。会議IDとアクションIDを保存先のキーに使い、担当者と期限が確定しない項目は未設定のまま残す構成にした。

検証日は2026年9月30日。入力は自分で作った試験用データで、実際の会議記録や顧客情報は使っていない。Circlebackからの実配信、外部タスク管理への書き込み、日本語音声の認識精度は未検証だ。以下で示す結果は、受信後の処理を再現した結果である。

10月2日にはSQLiteへの保存も試した。同じ項目を2プロセスから繰り返し保存し、3件だけ残ることを確認した。古い再送で完了状態が戻る結果も、後半に載せている。

Slack通知とhuddleの設定、毎晩22時の議事録同期という運用全体は、CirclebackをSlackとAIへつなぐ設定6か所にまとめた。この記事は、その先でToDoをタスク管理へ渡す処理に絞る。

会議後にどの作業が残るか

今回の対象は、会議から出た作業を、担当者が確認できる一覧へ移すことだ。要約を読むだけなら、そのまま会議画面を使う選択肢がある。別のタスク管理へ移すのは、期限・完了状態・担当者をそこで管理する運用がある場合に限る。移したあとに誰も更新しない一覧を増やしても、作業の進み具合は確認できない。

連携の前に、出力する項目を決めた。タイトル、説明、担当者のメールアドレス、完了状態、元の会議へのリンクを残す。担当者の名前だけで社内アカウントを推定したり、説明文から期限を補ったりする処理は入れなかった。別人への割り当てや、合意していない期限の登録を避けるためだ。

CirclebackのAutomationsは条件とアクションを組み合わせる。公式資料ではタグや参加者などで対象を絞れ、条件を設定しない場合はすべての会議が対象になる。最初の接続確認では専用のテスト対象だけに限定する構成を勧める。Automationsの公式説明

Webhookのフィールドと保存先の項目

公式仕様の actionItems にある id、title、description、assignee、status を使う。assignee はnullになり得る。状態は PENDING または DONE で、今回参照したアクション項目の仕様には期限フィールドがない。Webhookの公式仕様

保存先には、次の対応で変換する。右列はこの実験で決めた処理であり、Circlebackが提供する機能の説明ではない。

受信データ この実験の出力
会議IDとアクションID 両方を含む固定キー
タイトルと説明(title / description) タイトルと説明。タイトルの前後空白を除去
担当者メール(assignee.email) メールがあれば保存、なければnullと担当者確認フラグ
完了状態(status) PENDING / DONE を保持
期限の指定なし dueDate: null
会議の id 元の会議を開くURL

この対応を決めると、AIの出力が曖昧な場合にも処理を確定できる。名前があってメールがない項目は「担当者確認が必要」として残す。完了済みの項目も削除せず、完了状態を更新できる形で出力する。

AIに仕事の続きを渡すなら、未確定の項目も、そのまま引き継げる必要がある。ここでの null は、担当者の確認が残っているという情報だ。元の会議へのリンクと一緒に残せば、担当者が決まった根拠まで戻れる。

生の本文で署名を確認する

受信したJSONを整形してから署名を比較すると、正しいリクエストでも不一致になる。空白や改行を含む元の本文と、JSON.stringify で作り直した本文はバイト列が違うためだ。公式の x-signature とHMAC-SHA256の方式に合わせ、JSONとして読む前の本文で検証する。署名検証の説明

実験ではWeb Cryptoを使った。本文と共有シークレットから署名を検証し、形式が違う署名、空のシークレット、別のシークレットを拒否する。実際のシークレットはこのコードや記事には含めていない。

import { verifyCirclebackSignature, buildActionRows } from './circleback-webhook.mjs'

// requestは、運用側で用意するWebhook受信リクエスト。
const rawBody = await request.text()
const valid = await verifyCirclebackSignature(
  rawBody,
  request.headers.get('x-signature'),
  signingSecret,
)
if (!valid) throw new Error('Reject request before parsing')
const rows = buildActionRows(JSON.parse(rawBody))

上のコードは受信処理に組み込む例であり、HTTPサーバーを起動するコードではない。ローカルテストでは、空白を含む本文の署名は通り、その本文をJSONとして読み直して再生成すると検証に失敗した。受信フレームワークが本文を先に変換する設定になっていないかを、接続時に確認する必要がある。

再送と完了状態の更新

一つの会議に複数の項目があるため、会議IDだけをキーにすると一項目しか保存できない。今回のコードは JSON.stringify([meetingId, actionId]) を使う。IDを単純に文字列連結する方法と比べ、区切り文字がIDに含まれていても組み合わせを区別できる。

同じ入力を2回変換すると、キーは一致した。タイトルと状態を変更しても、IDを保った項目のキーは同じだった。保存先がそのキーでupsertすれば、変更された内容を同じタスクへ反映できる。

二重登録を防ぐ処理は保存先にも必要だ。キーを作るだけでは、同時に届いた2件のリクエストがそれぞれ新しいタスクを作る可能性がある。保存先の一意制約と原子的なupsertを使うか、外部タスクのIDを保存する仕組みを用意する。9月30日に試したのはキーの安定性まで。10月2日は、次のSQLiteの例で保存まで確かめた。

SQLiteで2プロセスから保存すると3件だけ残る

同じToDoの再送を、手元のデータベースで試した。使ったのはBun 1.3.11の内蔵SQLite。署名を確認したあとのデータを保存する例で、Webhookの受信サーバーや外部サービスは動かしていない。

保存するテーブルでは、会議IDとアクションIDから作ったキーを task_key TEXT NOT NULL PRIMARY KEY にする。同じキーが来たら、SQLiteのupsertの ON CONFLICT(task_key) DO UPDATE で、その行のタイトル・担当者・状態などを更新する。保存前に「同じキーはあるか」と調べてから追加する処理は使わない。

会議の3項目は、一つのトランザクションで書く。コードは db.transaction(...).immediate(rows) で書き込みを始め、途中でSQLが失敗したら、その会議の保存を取り消す。2項目目で失敗する試験では、1項目目も残らず0件になった。SQLの値はパラメータで渡すため、タイトルに引用符やSQL文のような文字列が入っても本文として保存された。

次の4ファイルを同じフォルダへ保存し、Bunで実行できる。今回の保存デモはBun専用だ。前からある署名・変換デモは、引き続きNode.jsでも動く。

bun task-store-demo.mjs

デモは一時フォルダにSQLiteファイルを作る。2つの書き込みプロセスが準備を終えてから、両方へ開始の合図を送る。各プロセスが同じ3項目を20回保存するので、計40回、120項目分のupsertになる。終了時には、自分で作った一時フォルダを消す。

{
  "writers": 2,
  "batches": 40,
  "rowUpserts": 120,
  "storedTasks": 3,
  "pending": 2,
  "pendingOwnerReview": 1,
  "afterUpdateStatus": "DONE",
  "afterOldReplayStatus": "PENDING"
}

保存後は3件だけ残り、未完了2件、うち担当者確認待ち1件になった。担当者なしのメールと期限はnullのままだった。別の会議に同じアクションIDを入れる試験では6件になり、会議をまたいで項目をまとめてしまうこともなかった。

二重登録がなくても、古い再送でDONEが戻る

出力の最後の2行は、このコードの限界を示す。101番の項目をDONEへ更新すると afterUpdateStatus はDONEになった。そのあとで元のPENDINGを再送すると、同じ行がPENDINGへ戻った。主キーとupsertは二重登録を防ぐが、どの入力が新しいかまでは判断しない。

このサンプルでは、最後に保存した入力を採用する。運用へつなぐ前に、保存先で人が更新した状態を会議データで上書きするかを決めておく必要がある。元サービスの最新状態を取り直す方法や、確認できる更新版を比較する方法は、その後の実装になる。今回の変換項目には更新順序を確定する情報がなく、その処理はまだ作っていない。

7つのテストで、再送、変更の反映、別会議との区別、途中失敗の取り消し、不正入力、古い再送の結果、2プロセスからの保存を確かめた。この件数は合成データとローカルSQLiteの結果だ。実際のCircleback配信や外部タスク管理、保存処理の速度を測った数字ではない。

試験用の3項目で確認した結果

入力には、担当者のある未完了項目、担当者なしの未完了項目、完了済み項目を一つずつ置いた。担当者のメールは owner@example.com という試験用の値だ。タスク変換と紹介URLの生成をまとめたデモを実行すると、次の値が得られる。

{
  "rows": 3,
  "pending": 2,
  "pendingOwnerReview": 1,
  "stableKeys": true
}

未完了2件のうち1件が、担当者の確認待ちになる。完了済み1件は出力に残るが、未完了数には含まれない。この結果から確認できるのは、こちらで書いた変換処理が入力の状態を保ったことまでだ。Circlebackが実会議から正しい担当者や作業を抽出するかは、別の試験が必要になる。

不正入力も試した。アクション配列がない、IDが文字列、タイトルが空、状態が未知、担当者が不正なオブジェクト、同じIDが配列内に重複する入力では、変換を止めた。空の配列は正常な「項目0件」として受け入れる。項目のない会議からタスクを作る必要はない。

コードを手元で再現する

以下の3ファイルを同じフォルダに保存し、BunまたはNode.js 22以降で実行できる。ライブラリの追加や製品アカウントの接続は不要だ。

bun demo.mjs
# Node.jsの場合
node demo.mjs

デモにはHTTP待受や外部サービスへの書き込みがない。記事を読んだ時点で、合成データの変換結果を手元で確認できる。ソースを変更して、担当者のメールをnullにする、完了状態を変える、といった入力の差も試せる。

実会議へ接続する前の判断

接続するなら、Circlebackで会議を限定する条件を作り、Webhookで送る項目をアクション項目中心に選ぶ。今回の変換は録音や全文の文字起こしを使わないため、それらを送る必要はない。配信先、送る項目、会議参加者への説明を運用側で確認してから、有効にする。設定画面の手順はWebhookのSetupに記載されている。

導入可否は、合成データのテストだけでは決められない。少数の承認済み会議で、作業の抽出漏れ、担当者の一致、DONEへの変更、受信の失敗を確認する。通知は人が内容を確認したあとに送る運用から始めれば、誤った割り当てをそのまま関係者へ送ることを避けられる。

すでに一つのタスク管理で会議後の作業を扱っていて、転記が負担になっているなら、試す対象になる。会議の件数が少なく、手作業で問題なく追えているなら、Webhookの受信・保存・保守を増やす理由は弱い。この判断を先に済ませると、接続そのものを目的にせずに済む。

紹介リンクの計測はDubと日英UTMの検証で扱った。記事の言語別クリックと、契約に対する紹介料の帰属は分けて確認する。

よくある質問

Q. 実際の会議を録音して精度を比較した記事ですか?
今回は公式仕様に基づくローカル連携実験です。合成データで変換・署名検証を確認しました。日本語の認識精度、実会議の要約、有料プランの効果は測定していません。
Q. 同じWebhookが届いてもタスクは増えませんか?
SQLiteの例では、会議IDとアクションIDのキーを主キーにしてupsertします。2プロセスで同じ3項目を計40回保存しても3件だけ残りました。ただし、古いPENDINGの再送でDONEが戻るため、重複防止とは別に更新順序の確認が必要です。外部タスク管理での動作は未検証です。