← Все статьи
Руководство

Синхронизация имён контактов 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 не оставит имя обновлённым наполовину. Неизвестные поля не копируются в представление. Если политика хранения позволяет, сохраняйте проверенное событие отдельно для последующего анализа изменений схемы.

Стройте обработку на состоянии отдельных полей

  1. Настройте подписку осознанно. Добавьте contact.updated, не удаляя другие нужные события. Руководство по фильтрам событий объясняет явные списки и wildcard. Сохраните действующую настройку подписи.
  2. Проверьте и сохраните. Следуйте контракту доставки webhook: проверяйте HMAC-SHA256 от X-Device-Timestamp, точки и исходных байтов тела запроса с помощью signing_secret. Проверяйте свежесть временной метки. Надёжно сохраняйте аутентифицированное событие до ответа 2xx. Если подпись верна, но структура некорректна, изолируйте событие для проверки, а не применяйте его молча.
  3. Разрешите идентичность. Маршрутизируйте по типу события и provider. Ограничьте data.contact.id рабочей областью, provider и account_id. Сохраняйте явно переданный conversation_id; не создавайте ID диалога из телефонного номера или ID контакта. Отсутствие соответствия не даёт оснований объединять посторонние записи.
  4. Обрабатывайте повторы и порядок. Для обычных событий устраняйте дубли по ID события внутри workspace. Порядок доставки не гарантируется. Рекомендуем хранить последний применённый occurred_at и ID события для разрешения равных временных меток для каждого поля имени, а не только для контакта целиком. Более старое событие может содержать поле, которого более новое частичное обновление не касалось. Это локальная стратегия приложения, а не дополнительное поле API или гарантия точного причинного порядка на стороне источника.
  5. Учитывайте источник отображаемого имени. Например, сначала используйте локальный псевдоним, затем полное имя адресной книги. После очистки пересчитайте отображаемое имя из остальных разрешённых источников; не восстанавливайте очищенное значение из устаревшего кеша.

Устранение дублей, проверку версии поля и изменение представления выполняйте транзакционно или в последовательном worker. Если поставить отметку «обработано» до записи в базу, сбой может привести к потере обновления.

Проверки и ограничения

Протестируйте обновление только полного имени, только имени, очистку пустой строкой, пропущенное поле, недопустимый null, повторную доставку, частичные изменения в обратном порядке и одинаковый ID контакта в разных аккаунтах. Убедитесь, что локальные псевдонимы и профиль аккаунта не меняются. Это предлагаемые проверки, а не результаты испытаний в production.

UnifyPort — неофициальный интерфейс. Событие не является полным снимком адресной книги, гарантированным воспроизведением пропущенных изменений или сигналом удаления контакта. Не удаляйте контакт из-за очистки имени. Корректная подписка не гарантирует поступление каждого изменения источника: честно показывайте устаревшее или неопределённое состояние и проверяйте поведение на подключённом аккаунте до внедрения.

FAQ

Отсутствие full_name означает, что имя удалено?

Нет. Сохраняйте прежнее значение. Поле очищается только при явно переданной пустой строке.

Можно ли использовать contact.id как адресата ответа?

Не считайте его автоматически равным ID диалога. Это разные ресурсы. Для операций чата используйте документированное соответствие диалогу.

Будут ли так же синхронизироваться имена LINE и Zalo?

Такая поддержка для этого события не документирована. Общий формат события не означает одинаковых возможностей каналов.

Следующий шаг и источники

Изучите контракт стандартных событий, затем проверьте правила объединения на тестовом контакте WhatsApp до включения обновлений общего ящика.

Источники проверены 2026-10-02:

UnifyPort API

Превратите интеграцию сообщений в стабильный продуктовый pipeline.

Начните с отправки через единый API, затем возвращайте входящие сообщения в бизнес-систему стандартными событиями.