Webhookの宛先は旧オーナーのままだ

Webhookの宛先は旧オーナーのままだ
Webhookの宛先URLは自社のコードベースではなく外部サービスの管理画面に保存されるため、コードを精査しても発見できず移管の棚卸しから構造的に漏れる。宛先が失われても受信側が存在しないためエラーは誰にも届かず、失効ドメインのパーキングページはHTTP 200を返すので配信ログ上は成功に見える。買い手は各SaaSの設定画面と直近の配信ログを実際に開き、成功している宛先が自社管理ドメインかまで突き合わせる必要がある。

買収したサービスが、引き渡しから数ヶ月経っても表向きは何ごともなく動いている。サイトは開くし、決済も通るし、管理画面のグラフもちゃんと伸びている。それなのに、四半期の締めで会計と突き合わせたときに、どうしても数十件分の入金が帳簿に乗っていない。調べていくと、決済事業者からの入金通知が、買い手の誰も知らないURLに向かって飛び続けていた ── こういう事態が、Webサービスの移管の現場で静かに繰り返されています。

厄介なのは、これが「壊れた」という顔をしないことです。サーバーが落ちれば監視が鳴ります。ドメインが切れれば誰かが気づきます。データベースが飛べば画面が真っ白になります。ところがWebhookは、宛先が間違っていても、画面はいつもどおりに表示されるのです。

この記事では、なぜWebhookという設定だけが移管の棚卸しから構造的に漏れるのか、それが漏れたときに事業がどういう壊れ方をするのか、そして買い手と売り手がそれぞれ何を確認すればいいのかを、実務の順序で整理していきます。

Webhookは、あなたのリポジトリに存在しない

Webhookは、あなたのリポジトリに存在しない

まず、なぜこれが「棚卸しから漏れる」のかという話からです。理由は精神論でもうっかりでもなく、完全に構造的なものです。

Webhookというのは、外部サービス側で起きた出来事を、外部サービスが自発的にこちらへHTTPで叩き込んでくる仕組みです。決済が成立した、在庫が更新された、フォームが送信された、テストが通った。そういうイベントが起きるたびに、送信側が指定されたURLへPOSTを投げてきます。

ここで決定的に重要なのは、「どこへ投げるか」という設定が、こちら側のコードベースには一行も書かれていないという事実です。宛先URLが記録されているのは、決済事業者の管理画面であり、GitHubのリポジトリ設定であり、フォームツールのダッシュボードです。つまり他人の家の中です。

これがどれほど厄介かというと、買い手のエンジニアがソースコードを隅から隅までgrepしても、Webhookの宛先は一件も出てこないということです。設計書にも出てきません。インフラのコードにも出てきません。なぜなら、そこには存在しないからです。存在しないものは、レビューできません。

比較すると分かりやすいと思います。よく似た話に見える定期処理の設定は、実は性質がまったく違います。cronはこちら側のサーバーの上に住んでいるので、サーバーを移せば設定ファイルが目に入りますし、動かなければこちらのログにエラーが残ります。能動的に動く処理は、動かなくなったときに自分の家の中で音を立てるのです。

Webhookは逆です。受動的に届く通知は、届かなくなっても自分の家の中では無音です。音が鳴るとすれば、それは送信側の家の中であって、こちらには一切聞こえません。

そしてもうひとつ。Webhookの設定は、たいていサービスを立ち上げた日に一度だけ書かれ、以後は誰も開かないという性質を持っています。一度つないでしまえば動き続けるので、開く理由がない。開く理由がないものは、記憶からも引き継ぎ資料からも消えます。数年経ったころには、そもそもWebhookを設定したという事実自体が組織の誰の頭にも残っていません。

失敗しても、誰のところにもエラーは届かない

失敗しても、誰のところにもエラーは届かない

次に、宛先が旧オーナーのままだった場合、実際に何が起きるのかを追っていきます。ここがWebhook特有の残酷さです。

送信側のサービスは、指定されたURLにPOSTを投げます。宛先が既に存在しなければ、当然エラーになります。多くのサービスはここで律儀にリトライしてくれます。少し待ってもう一度、それでもダメならもう少し待ってまた一度。ただし、永遠にではありません。何度か試して届かなければ、送信側はそのイベントの配信を諦めます。サービスによっては、失敗が続くエンドポイントを自動的に無効化します。

問題は、この一連の流れの中に買い手が登場する場面が一度もないことです。

送信側は「送ったが届かなかった」と自分のログに記録します。そのログを見られるのは、そのサービスの管理画面にログインした人だけです。受信側は存在しないので、当然何も記録しません。エラーを受け取る主体がどこにもいないまま、イベントだけが静かに捨てられていきます。

送信側が親切に「あなたのWebhookが失敗しています」というメールを送ってくれる場合もあります。ただ、その通知先として登録されているメールアドレスが誰のものかを考えてみてください。アカウントに紐づいた連絡先、つまり多くの場合は旧オーナーが登録したアドレスです。壊れたことを知らせるメールが、壊したことに関心のない人のところへ届く。この構図が、事故の発見を何ヶ月も遅らせます。

結果として何が起きるかというと、「動いているように見えるのに、特定のイベントだけが処理されていない」という状態です。サイトは動く、決済も通る、ユーザーも普通に使えている。ただ、入金確認の自動処理だけが半年前から一件も走っていない。この壊れ方は、全部止まってくれるより圧倒的にタチが悪い。全部止まれば1時間で気づきますが、一部だけ止まっても、気づくきっかけが構造的に存在しないのです。

200 OKという、最悪の「緑」

200 OKという、最悪の「緑」

ここまでは「宛先が存在しない」ケースでした。ところが実務でもっと恐ろしいのは、宛先が存在してしまうケースです。

旧オーナーが使っていたドメインが、更新されずに失効したとします。しばらくすると、そのドメインは第三者に再登録され、たいていの場合はパーキングページか広告ページが立ちます。ここで何が起きるか。

そのURLへのPOSTは、HTTP 200を返します

パーキングページは「そんなページはありません」とは言いません。何を投げつけられても、にっこりと200を返して広告を表示します。送信側から見れば、これは完璧な成功です。配信ログには美しい緑のチェックが延々と並びます。誰も何も疑いません。そして、そのPOSTのボディに載っていた顧客の氏名や、メールアドレスや、購入金額や、注文内容は、全部そのドメインを買った誰かのサーバーに届いています。

ここで「署名検証があるから大丈夫では」と思われるかもしれません。残念ながら逆です。署名は受信側を偽の送信者から守る仕組みであって、送信側のデータが間違った宛先へ行くのを止める仕組みではありません。受け取った第三者が署名を検証できないことは、こちらにとって何の慰めにもなりません。彼らは検証しません。ただ受け取って、中身を読むだけです。

署名鍵といえば、Webhookの署名シークレットが旧オーナーの手元やチャットログに残ったままというのも、併せて確認すべき論点です。この種の「設定した日に書かれ、以後誰も触らない秘密情報」が、退職者のローカル環境や古いドキュメントに残り続ける問題は、APIキーが漏洩する経路とまったく同じ構造をしています。

だから、配信ログを見るときは「成功している」ことを確認するのでは足りません。「成功している宛先が、本当に自分たちのものか」まで見る必要があります(緑のチェックほど人を安心させて、かつ何も保証していないUIは他にないと思います)。

宛先は、だいたいこの5つのどれかで事故る

宛先は、だいたいこの5つのどれかで事故る

では具体的に、どういうWebhookが移管で問題を起こすのか。現場で繰り返し出てくるのは、おおむね次の5系統です。

1. 決済の入金通知

最も直接的に金額へ跳ね返るのがこれです。決済事業者は、支払いの成立・失敗・返金・サブスクリプションの更新といったイベントをWebhookで通知します。この宛先が生きていないと、入金はされているのに、自社のDB上では注文が「未確定」のままという状態が生まれます。ユーザーからは「払ったのに商品が届かない」というクレームになり、事業側からは売上の欠落になります。

特にサブスクリプション型では被害が長期化します。毎月の更新イベントを取りこぼすと、解約したはずのユーザーに課金が続いたり、逆に課金が続いているユーザーのアクセスが止まったりします。Stripeのような事業者を基幹に据えているサービスでは、移管時にアカウント側の審査や名義の話に注意が集中しがちですが、その裏でWebhookの宛先という地味な設定が同じくらいの破壊力を持っています。

2. 在庫・受注の同期

ECや予約系では、外部の在庫管理やモール側の受注情報をWebhookで受けている構成が普通にあります。ここが切れると、在庫がゼロなのに売れ続けるという、謝罪と返金しか出口のない事故になります。しかも画面上は正常なので、発覚するのは顧客が怒った時点です。

3. フォーム送信の通知

問い合わせフォームや資料請求フォームが外部ツールで、送信をWebhookで自社側やチャットへ飛ばしている構成。ここが切れると、問い合わせが一件も来なくなります。恐ろしいのは、これを「最近は反響が少ないですね」と季節要因で片付けてしまえることです。来ていないのではなく、届いていない。BtoBのリード獲得が事業の生命線なら、これは静かな心臓発作です。

4. CI/CDのトリガー

リポジトリへのプッシュをフックしてビルドやデプロイを起動する構成。移管でここが切れると、デプロイしたつもりが本番に反映されていないという混乱が起きます。逆に、旧オーナー側の環境へデプロイが飛び続けるという、権限の観点でさらに深刻なパターンもあります。

5. 監視・エラーのアラート

個人的にこれが一番タチが悪いと思っています。監視の通知先Webhookが旧オーナーのSlackを向いたままだと、「アラートが鳴らないから正常だ」という完全に誤った安心が組織に定着します。障害は起きています。検知もされています。ただ、その悲鳴が全部よその会社のチャンネルに流れているだけです。

しかもSlackのIncoming Webhookは、そのURL自体が実質的な鍵として機能します。つまり、そのURLを握っている限り、旧オーナー側のワークスペースにメッセージを投げ込み続けられますし、逆に言えばこちらから止めることができません。無効化できるのは、そのワークスペースの管理者、すなわち旧オーナーだけです。

棚卸しの実務 ── 探す場所と、見るべきログ

棚卸しの実務 ── 探す場所と、見るべきログ

ここからは処方箋です。やるべきことは単純で、難しくもありません。ただし、順序と徹底度が結果を分けます。

探す場所は「コード」ではなく「管理画面」

前述のとおり、Webhookはコードベースにはいません。したがって、棚卸しの捜索範囲は事業が使っている全SaaSの管理画面になります。逆算のアプローチが有効です。請求書とクレジットカードの明細を並べて、課金されているSaaSを全部列挙する。そして一つずつログインして、Webhookの設定画面を開く。地味ですが、これ以上に確実な方法はありません。

捜索対象として最低限押さえるべきなのは、決済、メール配信、フォーム、CRM、在庫・受注、リポジトリ、CI/CD、監視、チャット、そして各種の自動化ツール(ZapierやMakeのような接続系ツールは、Webhookの巣窟です)。

見るべきは「設定」ではなく「配信ログ」

ここが最大のポイントです。設定画面に並んでいるURLの一覧を眺めるだけでは、半分しか分かりません。そのエンドポイントが、直近で実際にどう応答しているかを見る必要があります。

主要なサービスには、ほぼ例外なく配信ログ(Delivery / Event log)の画面があります。ここには、いつ、どのイベントを、どのURLへ投げて、レスポンスが何番だったかが記録されています。この配信ログこそが唯一の真実です。設定は「そう書いてある」だけですが、配信ログは「実際にどうなったか」を語ります。

そして見るべき観点は3つです。

  • 失敗が積み上がっていないか: 4xx・5xxが並んでいるなら、そのイベントは既に取りこぼされています。いつから失敗しているかを遡れば、被害の期間が特定できます
  • 成功している宛先が自分たちのものか: 200が並んでいても安心しないこと。URLのドメインを一つずつ確認し、自社が管理しているホストかを突き合わせます。知らないドメインが混ざっていたら、それは前述のパーキングページか、旧オーナーの生きているサーバーです
  • そもそもログが空でないか: 「最近このエンドポイントに配信された記録がない」場合、設定は残っているが実質的に死んでいる可能性があります。逆に、想定より配信数が多すぎる場合は、宛先が二重に登録されていることを疑います

切り替えは「作ってから消す」

宛先を移す際の順序も決まっています。新しいエンドポイントを追加して、配信ログで正常に受けられていることを確認してから、古いエンドポイントを削除する。この順序を逆にすると、その間に発生したイベントが丸ごと消えます。Webhookのイベントは、一度捨てられると原則として戻ってきません(送信側に再送機能があれば救えることもありますが、あることを前提に設計するものではありません)。

買い手が確認すべきことと、査定への織り込み方

買い手が確認すべきことと、査定への織り込み方

買い手の立場では、Webhookの棚卸しは技術的DDの中でも、優先度を上げて扱う価値のある項目です。理由は、調査コストが極端に低いのに、見逃したときの損失が青天井だからです。管理画面を開いて配信ログを見るだけなので、多くても数時間で終わります。

質問の仕方には注意が必要です。「Webhookは設定されていますか」と聞くと、返ってくる答えはほぼ「はい、動いています」です。売り手に悪意はありません。本当にそう信じているだけです(そして実際、今日までは動いていたのですから、嘘でもありません)。

そうではなく、「使っているサービスの管理画面で、Webhookの設定画面と配信ログを一緒に見せてください」と依頼するのが正解です。存在の有無は質問ではなく、画面で確認する類のものです。

そのうえで、判明した状態は査定に反映させます。目安としては次のような整理になります。

  • 宛先が全て自社管理ドメインで、配信ログも健全: この論点で減点する理由はありません。移管作業の段取りだけの話になります
  • 宛先の一部が旧オーナー・退職者・外注先のドメインを向いている: 移管作業に加えて、切り替え作業とその検証コストを見込みます。切り替えに旧オーナー側の協力が必要な場合(Slackの無効化など)は、その協力義務を契約に落とし込む必要があります
  • 棚卸しそのものができない(誰も全体像を把握していない): これは金額の問題ではなく、引き渡しの前提が整っていない状態です。引き渡し後の事故が起きたときに、原因の特定にすら辿り着けません

契約面では、譲渡対象資産や引き渡し条件の中に「事業運営に使用している全ての外部サービスのアカウントおよび連携設定(Webhookの宛先を含む)の一覧を提供し、移管に協力する」という趣旨の一文を入れておく価値があります。「サイト一式」「システム一式」という書き方では、他人の管理画面の中にある設定が含まれるのかどうかを、後から争う余地が残ります。

誰も見ない設定こそが、事業を静かに壊す

誰も見ない設定こそが、事業を静かに壊す

M&Aの技術的な論点は、どうしても目に見えるものに集まります。コードの品質、インフラの構成、ドメイン、アカウントの名義。どれも大事ですし、これらは少なくとも「見に行けば、そこにある」という性質を持っています。

Webhookが特異なのは、見に行こうと思っても、どこを見ればいいのかが分からないという点です。自社のサーバーにもリポジトリにも存在せず、他人の管理画面の奥に、設定した日のまま何年も静かに座っています。誰も見ないから壊れているのに気づかず、壊れても音を立てず、音を立てないから誰も見に行かない。この閉じた円環が、半年分の売上の欠落を作ります。

この構造は、外部サービスの上に事業を建てている以上、多かれ少なかれ避けられません。他人の家に置かせてもらっている設定は、こちらの努力だけでは守りきれない領域があります。

だからこそ、せめて「どこに何を置いているかを把握している」という状態だけは、自分の手で作れます。売り手にとっては、これは資産の証明です。「うちのWebhookはこの4サービスに合計7本、全部この配信ログで健全性を確認できます」と即座に出せることは、そのまま事業の管理品質の証明になります。技術的な不備そのものより、把握できていないという事実の方が、買い手の疑念を呼び、価格に効きます。

買い手にとっては、これは数時間の作業で防げる、数百万円規模の欠落です。契約書を精読する時間の10分の1で終わります。

設定した日に一度だけ書いて、以後誰も見ない。そういう性質のものが、事業の中には必ずいくつか潜んでいます。Webhookはその代表格です。引き渡しの前に、一度だけでいいので、その管理画面を開いてみてください。何年も前に自分が書いたURLが、まだそこに座って待っています。

この記事の著者

黒沢 大輔

株式会社studio C 代表取締役

これまで4回の事業譲渡を実現。上場企業にてエンジニア、制作ディレクション、SEO事業立ち上げを歴任。副業で始めた複数の掲示板サイトを国内最大規模まで成長させて事業譲渡。日本のM&Aに透明性と精度をもたらすべく、デジタル事業のM&Aプラットフォーム『RIKKA M&A』を立ち上げ。