Синхронизация имён контактов WhatsApp через contact.updated
Чтобы обновлять имена контактов WhatsApp в общем входящем ящике, обрабатывайте contact.updated от UnifyPort как частичное изменение адресной книги, а не замену всей записи. Применяйте переданные строки, сохраняйте пропущенные поля и считайте пустую строку явной очисткой. Значение null недопустимо. Разделяйте идентификаторы контакта и диалога и не перезаписывайте локальный псевдоним, заданный оператором, или профиль подключённого аккаунта для обмена сообщениями.
Главное
- Документация относит событие к
provider: whatsapp, а не кwhatsapp-protocolили всем каналам общего ящика. - Отсутствующее поле имени и пустое имя имеют разный смысл.
- Имена адресной книги, локальные псевдонимы и имя самого аккаунта храните раздельно.
- Сначала проверьте подпись и надёжно сохраните событие, затем обновляйте представление контакта.
Какое именно имя изменилось?
В официальном объявлении WhatsApp об управлении контактами описано управление контактами с подключённых устройств. Это объясняет, почему имя может измениться вне приложения поддержки. Однако объявление не определяет событие UnifyPort и не гарантирует его отправку после каждого редактирования.
Справочник стандартных событий UnifyPort определяет contact.updated как изменение имён в адресной книге WhatsApp. Это не добавление человека и не отправка его реквизитов собеседнику. Для таких операций используйте руководство по добавлению контакта и отправке vCard.
| Данные | Значение | Рекомендуемая граница хранения |
|---|---|---|
data.contact.id | Идентификатор ресурса контакта | Ключ в пределах workspace, provider и аккаунта |
data.contact.conversation_id | Связанный диалог, если поле передано | Явное соответствие, а не значение, вычисленное из ID контакта |
data.contact.address_book | Переданные поля имени | Объединение только присутствующих поддерживаемых полей |
| Локальный псевдоним оператора | Метка вашего приложения | Отдельное хранение без перезаписи этим событием |
account.profile.updated | Публичное имя самого подключённого аккаунта | Отдельный обработчик |
Переименование контакта клиента — не переименование бизнес-аккаунта и не редактирование сообщения.
Читайте payload как частичное обновление
Пример ниже соответствует документированному формату. Идентификаторы и имена приведены для иллюстрации, это не событие реального клиента:
{
"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 — только функция объединения двух документированных полей имени. Она не реализует полноценный 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;
}
Все целевые поля проверяются до применения любого из них: некорректный patch не оставит имя обновлённым наполовину. Неизвестные поля не копируются в представление. Если политика хранения позволяет, сохраняйте проверенное событие отдельно для последующего анализа изменений схемы.
Стройте обработку на состоянии отдельных полей
- Настройте подписку осознанно. Добавьте
contact.updated, не удаляя другие нужные события. Руководство по фильтрам событий объясняет явные списки и wildcard. Сохраните действующую настройку подписи. - Проверьте и сохраните. Следуйте контракту доставки webhook: проверяйте HMAC-SHA256 от
X-Device-Timestamp, точки и исходных байтов тела запроса с помощьюsigning_secret. Проверяйте свежесть временной метки. Надёжно сохраняйте аутентифицированное событие до ответа 2xx. Если подпись верна, но структура некорректна, изолируйте событие для проверки, а не применяйте его молча. - Разрешите идентичность. Маршрутизируйте по типу события и provider. Ограничьте
data.contact.idрабочей областью, provider иaccount_id. Сохраняйте явно переданныйconversation_id; не создавайте ID диалога из телефонного номера или ID контакта. Отсутствие соответствия не даёт оснований объединять посторонние записи. - Обрабатывайте повторы и порядок. Для обычных событий устраняйте дубли по ID события внутри workspace. Порядок доставки не гарантируется. Рекомендуем хранить последний применённый
occurred_atи ID события для разрешения равных временных меток для каждого поля имени, а не только для контакта целиком. Более старое событие может содержать поле, которого более новое частичное обновление не касалось. Это локальная стратегия приложения, а не дополнительное поле API или гарантия точного причинного порядка на стороне источника. - Учитывайте источник отображаемого имени. Например, сначала используйте локальный псевдоним, затем полное имя адресной книги. После очистки пересчитайте отображаемое имя из остальных разрешённых источников; не восстанавливайте очищенное значение из устаревшего кеша.
Устранение дублей, проверку версии поля и изменение представления выполняйте транзакционно или в последовательном worker. Если поставить отметку «обработано» до записи в базу, сбой может привести к потере обновления.
Проверки и ограничения
Протестируйте обновление только полного имени, только имени, очистку пустой строкой, пропущенное поле, недопустимый null, повторную доставку, частичные изменения в обратном порядке и одинаковый ID контакта в разных аккаунтах. Убедитесь, что локальные псевдонимы и профиль аккаунта не меняются. Это предлагаемые проверки, а не результаты испытаний в production.
UnifyPort — неофициальный интерфейс. Событие не является полным снимком адресной книги, гарантированным воспроизведением пропущенных изменений или сигналом удаления контакта. Не удаляйте контакт из-за очистки имени. Корректная подписка не гарантирует поступление каждого изменения источника: честно показывайте устаревшее или неопределённое состояние и проверяйте поведение на подключённом аккаунте до внедрения.
FAQ
Отсутствие full_name означает, что имя удалено?
Нет. Сохраняйте прежнее значение. Поле очищается только при явно переданной пустой строке.
Можно ли использовать contact.id как адресата ответа?
Не считайте его автоматически равным ID диалога. Это разные ресурсы. Для операций чата используйте документированное соответствие диалогу.
Будут ли так же синхронизироваться имена LINE и Zalo?
Такая поддержка для этого события не документирована. Общий формат события не означает одинаковых возможностей каналов.
Следующий шаг и источники
Изучите контракт стандартных событий, затем проверьте правила объединения на тестовом контакте WhatsApp до включения обновлений общего ящика.
Источники проверены 2026-10-02:
Превратите интеграцию сообщений в стабильный продуктовый pipeline.
Начните с отправки через единый API, затем возвращайте входящие сообщения в бизнес-систему стандартными событиями.