contact.updated WebhookでWhatsAppの連絡先名を同期する
共有受信箱のWhatsApp連絡先名を更新するには、UnifyPortの contact.updated を連絡先レコード全体の置換ではなく、アドレス帳の部分更新として扱います。渡された名前の文字列だけを反映し、省略されたフィールドは維持します。空文字は明示的な消去、null は不正な値です。連絡先IDと会話IDを分け、担当者が設定したローカルの別名や、接続中のメッセージングアカウント自身のプロフィールを上書きしないでください。
要点
- このイベントの対象は
provider: whatsappです。whatsapp-protocolや他の全チャネルへの対応を意味しません。 - 名前が省略されている場合と、空になっている場合は別です。
- アドレス帳名、ローカルの別名、アカウント自身の名前は別々に保存します。
- 署名検証と永続化を済ませてから、受信箱の表示用データを更新します。
どの名前が変わったのか
WhatsAppの公式の連絡先管理に関する発表では、リンク済みデバイスから連絡先を管理する取り組みが説明されています。サポート用アプリの外でも名前が変わり得る背景になりますが、この発表はUnifyPortのイベント仕様ではなく、すべての編集でイベントが発生するという保証でもありません。
UnifyPortの標準イベント仕様は、contact.updated をWhatsAppのアドレス帳名の変更として定義しています。連絡先の追加や、第三者への連絡先カード送信とは別の処理です。その操作には連絡先追加とvCard送信の比較ガイドを参照してください。
| データ | 意味 | 保存時の境界 |
|---|---|---|
data.contact.id | 連絡先リソースの識別子 | ワークスペース、provider、メッセージングアカウントでスコープを限定 |
data.contact.conversation_id | 提供されている場合の関連会話ID | 明示的な対応として保存し、連絡先IDから生成しない |
data.contact.address_book | 今回提供された名前フィールド | 対応しているフィールドのうち、実際にあるものだけをマージ |
| 担当者のローカル別名 | アプリ独自の表示ラベル | このイベントとは別に維持 |
account.profile.updated | 接続中アカウント自身の公開名プロフィール | 別のハンドラーへ振り分け |
顧客の連絡先名変更は、事業者アカウントの改名でも、メッセージ編集でもありません。LINEとWhatsAppを同じ画面に表示していても、この境界を保つ必要があります。
ペイロードを部分更新として読む
以下は仕様に沿った例です。識別子と名前は説明用であり、実際の顧客イベントではありません。
{
"id": "0000000000000000000000000000000000000000000000000000000000000191",
"type": "contact.updated",
"provider": "whatsapp",
"account_id": "acc_example",
"occurred_at": "2026-09-20T03:00:00Z",
"data": {
"contact": {
"id": "15550000002@s.whatsapp.net",
"conversation_id": "100000000000002@lid",
"address_book": {
"full_name": "Example customer",
"first_name": "Example"
}
},
"event": {
"kind": "contact_updated",
"source": "address_book",
"changed_fields": ["address_book.full_name", "address_book.first_name"],
"changed_at": "2026-09-20T03:00:00Z"
}
}
}
address_book に実際に存在する値を使います。changed_fields に名前があっても、オブジェクトに値がないフィールドを削除扱いにしたり、"" で補ったりしないでください。
| 入力 | 処理 |
|---|---|
| 空でない文字列 | そのアドレス帳フィールドを設定 |
"" | そのフィールドを消去 |
| 省略 | 以前の値を維持 |
null または文字列以外 | パッチを適用せず、検証対象にする |
次のJavaScriptは、仕様にある2つの名前フィールドだけをマージするヘルパーです。完全なWebhook受信処理、識別子解決、イベント順序制御は含みません。
function mergeAddressBook(current, patch) {
if (!patch || typeof patch !== 'object' || Array.isArray(patch)) {
throw new Error('Invalid address_book object');
}
const fields = ['full_name', 'first_name'];
for (const field of fields) {
if (Object.hasOwn(patch, field) && typeof patch[field] !== 'string') {
throw new Error('Invalid address-book name');
}
}
const next = { ...current };
for (const field of fields) {
if (Object.hasOwn(patch, field)) next[field] = patch[field];
}
return next;
}
すべての対象フィールドを検証してから適用するため、不正なパッチで半分だけ更新された状態になりません。未知のフィールドは表示用データへコピーしません。保存ポリシーが許す場合は、認証済みイベントを別途保持し、後からスキーマ変更を確認できるようにします。
フィールド単位の状態で受信処理を設計する
- 購読を明確にする。 必要な既存イベントを削除せず、
contact.updatedを追加します。イベントフィルターのガイドで明示的リストとワイルドカードの違いを確認し、既存の署名設定を維持してください。 - 検証して永続化する。 Webhook配信仕様に従い、
X-Device-Timestamp、ピリオド、リクエスト本文の生バイトを連結した値のHMAC-SHA256をsigning_secretで検証します。タイムスタンプの鮮度も確認し、認証済みイベントを永続化してから2xxを返します。署名は有効でも構造が不正なイベントは隔離して確認し、黙って適用しないでください。 - 識別子を解決する。 イベント種別とproviderで振り分け、
data.contact.idをワークスペース、provider、account_idの範囲で管理します。明示されたconversation_idだけを対応として保存し、電話番号や連絡先IDから会話IDを作らないでください。対応が不明でも無関係なレコードを統合してはいけません。 - 重複と順序に対応する。 通常イベントはワークスペース内でイベントIDにより重複排除します。配信順序は保証されません。表示用データでは、最後に適用した
occurred_atと同時刻の順序決定に使うイベントIDを、連絡先全体ではなく名前フィールドごとに保持する方針を推奨します。古いイベントにも、新しい部分更新が触れていないフィールドが含まれ得るためです。これはアプリ側の管理方針であり、追加のAPIフィールドでも、上流の因果順序を完全に復元する保証でもありません。 - 表示元を明示する。 例えばローカルの別名を優先し、次にアドレス帳のフルネームを使います。消去後は残った許可済みの情報源から表示名を再計算し、古いキャッシュから消去済みの値を復元しないでください。
重複排除、フィールドの版判定、表示用データの更新はトランザクションまたは直列化したワーカーで行います。DB更新前に処理済みフラグだけを立てると、クラッシュ時に更新を失う可能性があります。
受け入れテストと制限
フルネームのみの更新、名のみの更新、空文字による消去、フィールド省略、不正な null、重複配信、順序が逆の部分更新、異なるメッセージングアカウントに同じ連絡先IDがある場合をテストします。ローカルの別名とアカウントのプロフィールが変わらないことも確認してください。これは推奨テストであり、本番での実施結果ではありません。
UnifyPortは非公式インターフェースです。このイベントは完全なアドレス帳スナップショット、配信の確実な再実行、連絡先削除の通知ではありません。名前の消去を理由に連絡先を削除しないでください。正しい購読設定でも、すべての上流変更の受信は保証されません。古い状態や未確認状態を適切に表示し、実際に接続したアカウントで動作を確認してから利用します。
FAQ
full_nameがない場合、名前を消しますか?
いいえ。既存値を維持します。明示的に空文字が渡された場合だけ、そのフィールドを消去します。
contact.idを返信先にできますか?
会話IDと同じだと仮定しないでください。連絡先と会話は別リソースです。チャット操作には文書化された会話の対応関係を使います。
LINEやZaloの名前も同期できますか?
このイベントでは、その対応は文書化されていません。共通のエンベロープでも、チャネルごとの機能は異なります。
次のステップと出典
標準イベント仕様を確認し、管理下のWhatsApp連絡先でマージ規則をテストしてから受信箱の更新を有効にしてください。
確認日:2026-10-02。
メッセージ連携を安定したプロダクトパイプラインへ。
まずは 1 つの API で送信を始め、標準イベントですべての inbound メッセージを業務システムへ戻しましょう。