UPSIDERでエンジニアをしている太田です。 (@Hide55832241)
私たちのチームでは、CS業務などにZoom Phoneを活用しています。
今回はZoom Phoneを用いた業務改善の取り組みについて、経験した反省とそこから得た学びを共有します。
前提
Zoom Phone
Zoom Phoneとは、オンライン会議ツール「Zoom」に組み込まれたクラウド電話サービスです。電話番号を利用した発着信が可能で、多彩な機能を提供し企業の電話業務を効率化します。
コールキュー
Zoom Phoneの機能のひとつで、着信をユーザーグループにルーティングできます。
たとえば、問い合わせ受付用のコールキューに割り当てられた電話番号に着信があった場合、コールキューに所属する複数のメンバーに同時に転送されます。
その中で都合の良い任意のメンバーが応答できるため、顧客を待たせる時間を短縮し迅速に対応することができます。
主な特徴は以下の通りです。
- 電話番号割り当て: コールキューには電話番号が割り当てられる
- 複数メンバーの所属: コールキューには複数のメンバーを登録可能
- 転送ルール: 着信時に所属メンバーへの転送ルールを設定できる
- 応答できなかったら次のメンバーへ着電、メンバー全員に同時に着電など選択可能
課題
私たちのチームでは、CS業務を複数のメンバーで分担しています。
しかし、状況によっては誰も電話に応答できず不在着信が発生する場合があります。
この不在着信が発生した際に以下の点が課題として見えてきました。
- 不在着信をメンバー間で共有しづらい
- 不在着信を取りこぼすことがある
- 不在着信に対する折り返しが複数メンバーで重複してしまう
これらの課題を解決するため、不在着信の通知をSlackで共有できないかと考えました。
プロトタイプ作成
Zoom Phoneを知らなかったこと、Zoom Phoneに関する開発知見がなかったことから、まずは実現可能性を確認する必要がありました。
不在着信イベントをSlack通知する事例の記事を見つけたため、同様の設計で実装を試みました。
また以下の検証を行い、すべて問題なく実行できたため技術的な課題はクリアできたと判断しました。
- Zoom App Marketplaceでアプリケーションの作成
- Zoom Appからリクエストを受けるためのアプリケーションの作成
- Zoom Appの認証
- Webhookイベントの取得
- Slack通知
これらを踏まえ、不在着信イベントを受け取りSlack通知を行うプロトタイプを実装し、運用を開始する準備を進めました。
プロトタイプでの運用を始めてみたら、思わぬ誤算が浮き彫りになりました。
以下に具体的な内容を紹介します。
誤算1: Zoomアカウントの権限問題
開発者のZoomアカウントに管理者権限を付与して開発を進めましたが、運用段階で管理者権限を外した結果、作成したアプリケーションが使用できなくなりました。
管理者権限はZoom組織の重要な操作を実行できるため、開発者へ管理者権限を付与することはリスクが高まります。
他のクラウドサービスなどと同様に、最低限の権限のみを付与する必要がありました。
問題解決の試み
1. 開発者権限の付与
社内で開発者権限を申請しましたが、Zoom APIを利用した経験がないため手続きに時間がかかりました。
2. アプリの譲渡
アプリを管理者に譲渡しようとしましたが、ドキュメント記載の Transfer App Owner
が利用できませんでした。(記載されているTransfer App Ownerというボタンが表示されない)
アプリの種類により譲渡に制限がある可能性があります。
3. 管理者によるアプリ作成の依頼
管理者にアプリを作成してもらう案も検討しましたが、管理者が運用に直接関与しないため最適解ではありませんでした。
解決方法
結果的に時間は要しましたが、開発者権限を付与して問題を解決しました。
反省点
適切な権限を付与して開発を始められたらよかったかもしれません。
Zoom APIを使用した開発の知見がなく様々な検証をする必要があったため、それがはじめからできたかはわかりませんが、すべて実装する前に権限の問題も解消しておくべきだったと思います。
実装完了した後に権限の問題で運用が開始できなくなったら意味がありません。
この問題の解決に1週間以上かかったので、当初考えていた期間内に運用を開始することはできなくなりました。
誤算2: 応答したのに不在着信のWebhookリクエストが発生している
運用開始後、応答したにもかかわらずSlackに不在着信通知が送信される問題が発生しました。
また、1着信に対して複数回の不在着信通知が送信されることがありました。
問題の原因
不在着信のWebhookリクエストは phone.callee_missed
というイベントで受信します。
このイベントはCSチームが応答前にカスタマーが電話を切った際に発生することを想定していました。
しかしこのイベントが応答した際にも発生していることや、1着信に対して複数回発生していることがわかりました。
調査をすすめるとコールキュー内のメンバーに対してそれぞれイベントが発生していることがわかりました。
具体的には以下です。
- カスタマーが電話をかける
- メンバーAへ着電
- メンバーAが出られなかった → イベント発生
- メンバーBへ着電
- メンバーBが出られなかった → イベント発生
- メンバーCへ着電
- メンバーCが応答
解決方法
リクエストのパラメータ handup_result
を使用して判定できました。 (これも後で大きな問題になりますが)
handup_resultは以下のように定義されています
handup_result string
The reason the call was missed. Values: No Answer | Busy | Call Cancel
handup_result
には No Answer
Busy
Call Cancel
のいずれかが入ります。
Call Cancel
は応答前にカスタマーが電話を切った場合、それ以外はコールキュー内の次のメンバーへ着信が移った場合に発生することがわかりました。
- 応答した場合、イベントは以下のような順で発生し
handup_result: Call Cancel
イベントは発生しません- メンバーAが不在 (
handup_result: Busy または Answer
イベントが発生) - メンバーBが応答 (イベントは発生しない)
- メンバーAが不在 (
- 応答しなかった場合、イベントは以下のような順で発生し
handup_result:Call Cancel
イベントが発生します- メンバーAが不在 (
handup_result: Busy または Answer
のイベントが発生) - カスタマーが電話を切った (
handup_result: Call Cancel
イベントが発生)
- メンバーAが不在 (
このことから handup_result: Call Cancel
が発生した場合のみSlackへ通知すれば解決できそうだと考えました。
反省点
実運用環境での検証不足
不在着信の動作確認をWebhookリクエストでしか行っていませんでした。
CSチームが普段使用している環境と同じまたは似たものを作成しておけば運用開始前に把握することができました。
誤算3: Call Cancel
が発生していないけど実際には不在着信
誤算2の解決方法に handup_result
から判定できると書きました。
しかし Call Cancel
が発生していない (Slack通知されない) けど実際には不在着信となっているケースがありました。
理由はわかりませんが、Webhookリクエストと実際の不在着信がずれているのは事実です。
解決方法
1つのWebhookリクエストで通知判定が行えないので頭を悩ませました。
システムが複雑になってしまいますが以下のようにスケジュール通知を行うことにしました。
phone.callee_missed
リクエストが発生した場合、Call Cancel
だったらSlack通知するCall Cancel
以外の場合、保存しておいてスケジュール通知する- 今回はWebhookリクエストを受けるアプリケーションにCloudflare Workersを使用していたため、ストレージはCloudflare Workers KVを使用した
phone.callee_answered
イベント (電話応答した際にリクエストされる) も取得するようにする- 同一のCall ID (着信ごとに一意の値。1つの着信に対するイベントはすべて同一のCall IDとなる。) を持つイベントが発生した場合は、保存済みのリクエストを削除する
- 5分毎にスケジュールイベントを実行し、ストレージにイベントが残っていたら通知して削除 する (直近登録されたイベントはまだ後続イベントが発生するかもしれないので対象外)
- スケジュールイベントはCloudflare WorkersのCron Triggersを使用した
当初考えていたものに対して複雑な設計になってしまいました。
また Call Cancel
が発生しないリクエストはリアルタイム通知できなくなってしまいましたが、なんとかSlack通知が実現できそうな目処が立ちました。
反省点
誤算2と同様、実運用環境で動作検証をしていれば事前に気づけました。
誤算4: 応答したけど phone.callee_answered
が発生していない
誤算3同様、応答したにもかかわらず phone.callee_answered
が発生しないケースがあることがわかりました。
こちらも原因がわかりません。
後述しますが、コールキューの転送ルールの設定を途中で変えたことなどが影響していたかもしれません。
解決方法
ここで開発を仕切り直すことになります。
phone.callee_missed
イベントを受け取ってSlack通知をすることを考えていましたが、他のイベントを使えないか検討することにしました。
ドキュメントを参照また動作確認していくと phone.callee_call_history_completed
とphone.callee_call_log_completed
が使えそうということがわかりました。
どちらでも問題なさそうだったので今回は phone.callee_call_log_completed
を使うことにしました。
反省点
- 誤算2と同様、実運用環境での事前検証が不足していた
phone.callee_answered
イベントが発生していないけど、応答済みということは環境を整えていれば把握できました。
- 初期に検討した
phone.callee_missed
イベントを使用することに固執し、複雑な設計をおこなったこと- スケジュール通知の設計をした際に
phone.callee_missed
以外のイベントを確認しませんでした。 - なんとかしないといけない焦り、当初参考にした記事といかにも不在着信っぽいイベント名に囚われてしまったと感じています。
- スケジュール通知の設計をした際に
最終的な設計
- コールキュー内の特定のメンバー以外へのイベントは無視する
- Webhookリクエストで
phone.callee_call_log_completed
イベントを受け取る- このイベントは通話が終了した際にコールキュー内の全メンバーに対してリクエストされるため、すべて対象とすると1不在着信に対してメンバー数分Slack通知されてしまいます
- リクエストパラメータの
result
がCall Connected
(応答した) またはAnswered by Other Member
(他のメンバーが応答した) 以外の場合、Slack通知する
最終的にとてもシンプルな設計になりました。
あとがき
今回の開発は反省点が多いものとなりましたが、学びも多いものとなりました。
最終的な設計はシンプルになりましたが、まだここにもいくつか課題があります。
コールキューの転送ルールの設定を 同時
にしていないとSlack通知が動きません。
これに関しても反省点です。
開発当初転送ルールの設定は メンバー1 → メンバー2 というように順番につながる設定となっていると聞いていました。
しかし運用していくと不在着信が発生しやすかったため、途中で全メンバーへ同時に着信するよう変更したと開発途中で聞きました。
この設定によりWebhookリクエストのふるまいが変わったため、開発者と運用者のコミュニケーションが必要であったと感じています。
また特定のメンバーに対して常にWebhookイベントが発生する設定にしておく必要があります。
Zoom Phoneはコールキューのメンバーであってもコールキューの着信を受け付けない設定にできます。
実際に他の業務を行っている際は、コールキューを受け付けない設定に変更しているメンバーがいました。
これに関して今回は開発者のZoomアカウントをコールキューに追加し、常時コールキューを受け付ける設定とし解決しました。
しかし前述の転送ルールの設定を変更した場合、この運用は使えません。
このようにまだ課題はありますが、少しずつ電話業務の改善を続けていければと考えています。
We Are Hiring !!
支払い.comをはじめUPSIDERでは現在積極採用をしています。 ぜひお気軽にご応募ください。
カジュアル面談はこちら!
Culture Deckはこちら📣