Пакетные уведомления о доставке и прочтении в UnifyPort
Уведомление UnifyPort о доставке или прочтении может относиться сразу к нескольким сообщениям. При наличии пакетного поля обрабатывайте каждый ID из data.message.ids; для одиночного уведомления используйте data.message.id. Храните подтверждение отдельно от содержимого сообщения, чтобы не потерять его, если оно придёт раньше. Приём запроса на отправку, подтверждение от получателя и состояние прочтения в рабочем inbox — разные свидетельства, а не один флаг успеха.
Главное
- Сопоставляйте подтверждения в пределах workspace, провайдера, аккаунта для сообщений и беседы, а не только по ID сообщения.
- Разделяйте время, указанное провайдером, и время получения webhook.
- Позднее уведомление о доставке не должно отменять уже зарегистрированное прочтение.
- Отсутствие подтверждения означает неизвестный результат, а не разрешение отправить сообщение повторно.
Сначала определите состав пакета
В контракте стандартных событий message.delivered означает доставку на устройство получателя, а message.read — подтверждение прочтения получателем. Поле пакетного подтверждения — data.message.ids. Обновление только одиночного id оставляет остальные сообщения без обработки.
| Свидетельство | Что подтверждает | Чего не подтверждает |
|---|---|---|
Ответ отправки с status: accepted | Приём запроса | Доставку или прочтение |
message.delivered | Доставку указанных сообщений | Прочтение |
message.read | Прочтение указанных сообщений | Решение обращения или прочтение всеми участниками группы |
| Локальный статус беседы | Состояние списка чатов подключённого аккаунта | Подтверждение от получателя |
Последняя строка относится к отдельному процессу синхронизации прочитанных и непрочитанных бесед. Не вызывайте действие отметки беседы прочитанной, чтобы исправить отсутствие подтверждения исходящего сообщения.
Ниже сокращённый пример на основе полей из справочника message.read. Это иллюстрация, а не событие реального клиента:
{
"id": "evt_2f9c1a4b7e",
"type": "message.read",
"provider": "whatsapp",
"account_id": "acc_8c21d0",
"occurred_at": "2026-06-08T12:35:10Z",
"data": {
"conversation": { "id": "8613912345678", "type": "user" },
"sender": { "id": "8613912345678", "type": "user" },
"message": {
"id": "wamid.HBgM",
"ids": ["wamid.HBgM", "wamid.HBgN"],
"read_at": "2026-06-08T12:35:10Z"
}
}
}
Подтверждение охватывает оба перечисленных сообщения. Оно не доказывает, что прочитаны все более ранние сообщения. Даже если ID выглядят как числа, не выводите из них непрерывный диапазон.
Храните наблюдения до построения индикаторов
Рекомендуемая схема состоит из двух уровней: долговечной записи проверенного события и представления подтверждений по сообщениям. Это структура вашего приложения, а не дополнительные поля UnifyPort.
- Добавьте оба имени событий в подписки, сохранив остальные нужные события и настройки подписи.
- Следуйте контракту доставки webhook: проверяйте HMAC-SHA256 строки из временной метки, точки и исходных байтов тела с помощью
signing_secret. Проверяйте также свежесть подписанной временной метки. - Сохраняйте проверенное событие до ответа 2xx. Запись события и долговечная задача должны фиксироваться атомарно; другой вариант — обновлять представление и запись дедупликации в одной транзакции.
- Разверните пакет в наблюдения по отдельным сообщениям. Сохраняйте контекст workspace,
provider,account_id,data.conversation.id, ID события и доступные сведения об отправителе. - Раздельно храните
occurred_at, время провайдераread_atилиdelivered_atи собственное время приёма. Время подтверждения также дублируется в одноимённом поле уровняdata. Отсутствующее время оставляйте неизвестным: время приёма не заменяет время провайдера.
Эта функция JavaScript извлекает только состав подтверждения из уже проверенного события. Она не реализует полный приёмник или базу данных:
function receiptMessageIds(event) {
if (!['message.delivered', 'message.read'].includes(event.type)) return [];
const message = event.data?.message;
const ids = message?.ids === undefined ? [message?.id] : message.ids;
if (!Array.isArray(ids) || ids.length === 0 ||
ids.some(id => typeof id !== 'string' || id.length === 0)) {
throw new Error('Receipt scope needs review');
}
return [...new Set(ids)];
}
Не подменяйте некорректный пакет одиночным ID без предупреждения. Сохраните событие для разбора вместо применения предполагаемого набора сообщений.
Объединяйте свидетельства, не создавая их заново
Храните доставку и прочтение независимо. Если прочтение пришло раньше доставки, интерфейс может показать «прочтение подтверждено», не выдумывая delivered_at. Позднее событие доставки добавляет сведения, но не понижает статус до «доставлено». Повторные события не должны создавать повторные уведомления.
Если содержимое сообщения ещё не сохранено, удерживайте подтверждение по составному идентификатору и связывайте его с записью, когда она появится. Не создавайте входящее сообщение и не увеличивайте счётчик inbox только на основании подтверждения. Для детерминированного порядка используйте occurred_at и ID события, но не присваивайте статус по принципу «последний пришедший побеждает».
При отправке сохраняйте возвращённые message_id и provider_ref вместе с локальной операцией. Перед связыванием установите, какой идентификатор соответствует наблюдаемому сообщению провайдера: разные поля не дают универсальной гарантии равенства. Нераспознанное подтверждение направляйте в очередь сверки, а не прикрепляйте к ближайшему по времени сообщению. Сохраняйте доступную идентичность отправителя, не превращая одно наблюдение в «прочитали все».
Руководство по диагностике отправки медиа разбирает источник файла и приём запроса. Подтверждения относятся к другому этапу. Тайм-аут или отсутствие события не должны автоматически запускать новую отправку.
Ограничения и проверки
UnifyPort использует неофициальный интерфейс. Матрица событий провайдеров описывает возможности парсеров, а не гарантирует события от каждого аккаунта или окружения. Проверяйте выбранного провайдера, включая LINE, прежде чем обещать индикаторы доставки. Отсутствие события само по себе не доказывает, что сообщение не прочитано, не доставлено или отправитель заблокирован. Повторная выдача пропущенных данных также не гарантируется.
Рекомендуемые проверки: пакет из двух сообщений, одиночное подтверждение, дубликат, прочтение раньше доставки, подтверждение раньше содержимого, одинаковые ID в разных аккаунтах, некорректный пакет и полное отсутствие подтверждения. Убедитесь, что изменяются только явно перечисленные ID и ни один случай не вызывает повторную отправку. Это план тестирования, а не заявленные результаты.
Частые вопросы
Обрабатывать id или ids?
Используйте корректный непустой пакет ids, если он присутствует. Иначе используйте одиночный id. Некорректный состав требует разбора.
Можно заполнить delivered_at после получения read?
Нет. Покажите свидетельство прочтения, но не создавайте отсутствующую временную метку доставки провайдера.
Отсутствие подтверждения означает ошибку отправки?
Само по себе — нет. Оставьте подтверждение неизвестным и проверьте поддержку событий, подписки, приём и сопоставление идентификаторов.
Следующий шаг и источники
Начните со справочника уведомлений о доставке: реализуйте хранилище наблюдений с точной областью действия до добавления индикаторов в интерфейс.
Документация продукта проверена 2026-11-06:
Превратите интеграцию сообщений в стабильный продуктовый pipeline.
Начните с отправки через единый API, затем возвращайте входящие сообщения в бизнес-систему стандартными событиями.