Передача событий из системы клиенту (Callback, WebHook)¶
Клиент может получать от системы уведомления о события открытии сообщения (если формат выпуска поддерживает такое событие), перехода по ссылке (если формат выпуска поддерживает такое событие), отписке, заполнения формы, завершения работы трекера.
События, на типы которых настроена передача, передаются клиенту по мере появления, но не сразу, а с интервалом накопления порядка минуты.
Размер передаваемый данных сильно зависит от частоты возникновения событий и их типа. Если это клики или чтения, то за минуту их, даже при интенсивной реакции на ваши письма, будет вряд ли больше сотни. А вот событий генерирующихся доставкой сообщений, может быть очень много.
В данный момент, при сборе событий для передачи в один вызов подбирается максимум 2500-2600 событий.
Что даст запрос размером, примерно, 2-5-10 мегабайт в зависимости от событий и настроенной подробности передаваемых данных.
Так же, надо учитывать, что вы можете получать одновременно несколько вызовов передачи сообщений - каждый компонент распределённой внутренней системы работает независимо и независимо сообщает о накопившихся событиях.
Порядок накопления и передачи сохраняющий порядок происхождения событий не гарантирован. Если для какого-то объекта может быть несколько последовательных событий (например выпуск и изменения его статуса), то близко произошедшие события могут быть переданы как в хронологическом порядке, так и нет. У событий есть собственная временная отметка когда они произошли, и, в примере с выпуском, полученное событие трекера (имеет status.dt) должно вами проверяться не произошло ли оно в прошлом относительно уже ранее полученных и обработанных событий для данного выпуска.
Так же, формально, массив/поток ни каким образом не упорядочен по дате и времени события, хотя, в целом, более ранние события обычно находятся в начале массива/потока при первой попытке передачи.
События передаются в одном из двух форматов. Используйте вызов sys.settings.get/set с параметром callback.format для выбора наиболее подходящего формата.
Формат "json" - по умолчанию - как элемент events в виде JSON-массива содержащего по одному элементу на событие.
Метод передачи POST application/json, кодировка utf8.
Формат "json-stream" - JSON-объекты описывающие события следующие друг за другом без json-разделителя (запятой), возможно разделённые пробельными символами (пробелы,переводы строк). Формат хорошо подходит для поточного разбора.
Метод передачи POST application/x-ndjson, кодировка utf8.
Разные типы событий могут быть настроены для передачи по разным адресам. Если на один адрес настроены несколько типов событий, то внутри массива их порядок не определён.
Для настройки адресов передачи используйте sys.settings.set ccallback.url.*
Так же, возможна настройка индивидуальных адресов для конкретного выпуска - через настройки в его dkim-ключе - смотрите issue.dkim.*
В адресе можно указать меткуSS_EVENT_TYPEи она заменится на тип события как он передаётся в параметре event события.
При наличии у события event.dt так же есть и event.dttz содержащий тоже время, но с тайм-зоной и в формате ISO8601.
Тайм-аут вызова - 15 секунд. Вы должны просто принять данные и сохранить их в свою очередь для дальнейшей обработки, а не пытаться производить её сразу по приходу вызова.
В случае тайм-аута или получения ответа с кодом не 200 и не 204 и не 406 передача событий будет повторяться до 10 раз с возрастающим интервалом между попытками.
В данный момент интервалы следующие
| После попытки | Минимальная пауза минут до следующей |
|---|---|
| 1 | 5 |
| 2 | 5 |
| 3 | 5 |
| 4 | 10 |
| 5 | 15 |
| 6 | 25 |
| 7 | 45 |
| 8 | 60 |
| 9 | 60 |
| 10 | 90 |
Если вызовы упорно заканчиваются тайм-аутом или занимают сравнимое с ним вермя, то, во избежании торможения очереди колбеков тем клиентам, которые в состоянии их обрабатывать вовремя, система может новые события сразу откладывать в более медленную очередь повторов или вообще отключить колбеки, до выяснения ситуации со Службой Поддрержки.
Сохранение в Отчёты¶
Для сохранения событий в Отчёты используйте ссылку вида
Формат событий в файле всегда json-stream
Лимит количества событий в файле в 100 раз больше чем при передаче по http
Файл сжимается zip.
Файл всегда попадает в Отчёты даже если хост в ссылке не report.
Если в ссылке указан путь начинающийся с /callback, то используется именно такой путь как название папки куда поместить файл.
Если в ссылке путь не начинается с /callback, то к нему автоматически добавляется в начале /callback и результат используется как название папки куда поместить файл.
Срок хранения файлов - 48 часов.
Рекомендуется самостоятельно удалять забранные файлы.
Передача событий в Google Big Data¶
Для передачи событий в Google Big Data настройте внешнюю аутентификация для схемы gbd:// (описано в вызовах authext.*) и используйте ссылку вида
где
AUTHEXT_ID - код внешней аутентификации
DATASET_ID - код набора данных
TABLE_ID - код (название) таблицы или view в первую колонку (она должна быть типа STRING) который будут вставлены json-закодированные строки с объектами событий. Одно событие - одна строка.
В дальнейшем с этими данными в таблице можно работать через функции работы с json в запросах Google Big Query.
Расширенная информация о выпуске¶
Включается отдельно через Службу поддержки так как данная информация не нужна большинству получателей.
Добавляется информация:
"issue.dkim.id" : номер дкима (int64)(null)
"issue.name" : "название выпуска"
"issue.label" : [...] -- метки выпуска (null)
"issue.from_name" : "имя отправителя"
"issue.from_email" : "адрес отправителя"
"issue.reply_name" : "имя для ответов"
"issue.reply_email" : "адрес для ответов"
"issue.login" : "логин аккаунта запустивший выпуск" (null)
Событие открытия сообщения¶
{
"event" : "read"
,"event.dt" : "дата-время события (Ys)"
,"account" : "код аккаунта"
,"gate.name" : "код шлюза выпуска письма" -- api smtp tranz
,"gate.uniq" : "уникальный идентификатор сообщения в шлюзе"
,"customer.id" : "идентификатор сообщения назначенный клиентом" (null)
,"issue.id" : "номер выпуска" (int64)
,"issue.draft.id" : "номер черновика выпуска" (int64)(null)
,"letter.id" : "номер сообщения" (int64)
,"letter.custid" : { -- значения пользовательских меток назначенных сообщению при выпуске
-- отсутствует если их не было или если не известны одновременно
-- issue.id и letter.id (так, например, бывает при отписке)
"метка1" : "значение1"
,"метка2" : "значение2"
........
}
,"email" : "адрес получателя"
,"email.id" : "номер адреса" (int64)
,"ip" : "ip-адрес"
,"http.user-agent" : "заголовок User-Agent от браузера подписчика"
,"duration" : "длительность чтения" - целое число секунд. смысл null описан в stat.uni read.duration (int64)(null)
-- информация аналогичная stat.uni
,"gadget.gender" : "тип устройства подписчика" (null)
,"gadget.os" : "операционная система устройства" (null)
,"gadget.browser" : "используемый браузер" (null)
,"gadget.browmajor" : "старший номер версии браузера" (int64)(null)
,"geo.id" : "числовой код географии" (int64)(null)
,"geo.name" : "название географии" (null)
}
Событие перехода по ссылке¶
{
"event" : "click"
,"event.dt" : "дата-время события (Ys)"
,"account" : "код аккаунта"
,"gate.name" : "код шлюза выпуска сообщения" -- api smtp tranz
,"gate.uniq" : "уникальный идентификатор сообщения в шлюзе"
,"customer.id" : "идентификатор сообщения назначенный клиентом" (null)
,"issue.id" : "номер выпуска" (int64)
,"issue.draft.id" : "номер черновика выпуска" (int64)(null)
,"letter.id" : "номер сообщения" (int64)
,"letter.custid" : { ... пользовательские метки ... }
,"email" : "адрес получателя"
,"email.id" : "номер адреса" (int64)
,"ip" : "ip-адрес"
,"http.user-agent" : "заголовок User-Agent от браузера подписчика"
,"url" : "ссылка по которой был направлен подписчик"
-- информация аналогичная stat.uni
,"gadget.gender" : "тип устройства подписчика" (null)
,"gadget.os" : "операционная система устройства" (null)
,"gadget.browser" : "используемый браузер" (null)
,"gadget.browmajor" : "старший номер версии браузера" (int64)(null)
,"geo.id" : "числовой код географии" (null)
,"geo.name" : "название географии" (null)
}
Событие отписки¶
{
"event" : "unsub"
,"event.dt" : "дата-время события (Ys)"
,"account" : "код аккаунта"
,"gate.name" : "код шлюза выпуска сообщения" (null) -- api smtp tranz
,"gate.uniq" : "уникальный идентификатор сообщения в шлюзе" (null)
,"customer.id" : "идентификатор сообщения назначеный клиентом" (null)
,"issue.id" : "номер выпуска" (int64)(null)
,"issue.draft.id" : "номер черновика выпуска" (int64)(null)
,"letter.id" : "номер сообщения" (int64)(null)
,"letter.custid" : { ... пользовательские метки ... }
,"email" : "адрес получателя"
,"email.id" : "номер адреса" (int64)
,"ip" : "ip-адрес" (null)
,"http.user-agent" : "заголовок User-Agent от браузера подписчика" (null)
,"event.type" : "способ отписки" -- unsub - по ссылке отписки
-- topic - по ссылке тематической отписки
-- listunsub - нажато "Отписаться" в веб-почте
-- fbl - нажато "Это спам" в веб-почте
-- admin - служба поддержки
,"unsub.list" : "код группы для отписки при тематической отписке" (null)
,"sender.email" : "адрес отправителя" -- при отписки при активном стоп-листе по отправителю (null)
,"comment" : "сообщение от подписчика о причине отписки. если указал" (null)
,"label" : "метка отписки" - до 64 байт, указывается в параметре label ссылки отписки [% param.url_unsub %]?label=campaign23 (null)
}
Событие отмены отписки¶
{
"event" : "unsubcancel"
,"event.dt" : "дата-время события (Ys)"
,"account" : "код аккаунта"
,"gate.name" : "код шлюза выпуска сообщения" (null) -- api smtp tranz
,"gate.uniq" : "уникальный идентификатор сообщения в шлюзе" (null)
,"customer.id" : "идентификатор сообщения назначеный клиентом" (null)
,"issue.id" : "номер выпуска" (int64)(null)
,"issue.draft.id" : "номер черновика выпуска" (int64)(null)
,"letter.id" : "номер сообщения" (int64)(null)
,"letter.custid" : { ... пользовательские метки ... } (null)
,"email" : "адрес получателя"
,"email.id" : "номер адреса" (int64)
,"ip" : "ip-адрес" (null)
,"http.user-agent" : "заголовок User-Agent от браузера подписчика" (null)
т
,"event.type" : "способ отписки" -- unsub - отмена общей отписки
-- admin - отмена общей отписки службой поддержки
-- topic - отмена тематической отписки
,"unsub.list" : "код группы для отписки при тематической отписке" (null)
,"sender.email" : "адрес отправителя" -- при отписки при активно стоп-листе по отправителю (null)
,"comment" : "сообщение от подписчика о причине отписки. если указал" (null)
,"label" : "метка отписки" - до 64 байт, указывается в параметре label ссылки отписки [% param.url_unsub %]?label=campaign23 (null)
}
Событие подтверждение регистрации¶
Адрес находившийся в состоянии "Требуется подтверждение регистрации" был подтверждён.
Не важно каким путём - по ссылке подтверждения, апи-вызов, заполнение формы.
{
"event" : "member.confirm"
,"event.dt" : "дата-время события (Ys)"
,"account" : "код аккаунта"
,"email" : "адрес получателя"
,"email.id" : "номер адреса" (int64)
,"ip" : "ip-адрес" (null)
,"http.user-agent" : "заголовок User-Agent от браузера подписчика" (null)
}
Событие нового подписчика¶
В базе появился новый подписчик
{
"event" : "member"
,"event.type" : "new"
,"event.dt" : "дата-время события (Ys)"
,"account" : "код аккаунта"
,"obj" : "как obj вызова issue.member.get c datakey *"
}
Событие доставки¶
Для email, некоторые почтовые системы присылают письма о недоставке в формате позволяющим понять только про какой аккаунт и почтовый адрес идёт речь, но не определить номер выпуска и номер письма.
В таком случае в событии передаётся номер выпуска и номер письма как 0.
Для email, для одного сообщения может быть отправлено несколько событий доставки. Сначала письмо принимается (положительное событие доставки), а через некоторое время на технический обратный адрес приходит автоматическое письмо что доставка не состоялась - это вызывает ещё одно событие доставки, на этот раз отрицательное.
Например, на принявшем почтовом сервере настроена пересылка на другой адрес и она не получилась.
Для исключённых из выпуска (статус доставки < -100000) могут быть null в зависимости от причины исключения - letter.id, gate.unuq, deliv.str, email.id
{
"event" : "deliv"
,"event.dt" : "дата-время события (Ys)"
,"account" : "код аккаунта"
,"gate.name" : "код шлюза выпуска сообщения" -- api smtp tranz
,"gate.uniq" : "уникальный идентификатор сообщения шлюзе" (null)
,"customer.id" : "идентификатор сообщения назначенный клиентом" (null)
,"issue.id" : "номер выпуска" (int64)
,"issue.draft.id" : "номер черновика выпуска" (int64)(null)
,"letter.id" : "номер сообщения" (int64)(null)
,"letter.custid" : { ... пользовательские метки ... }
,"email" : "адрес получателя"
,"email.id" : "номер адреса" (int64)(null)
,"deliv.status" : "число-код результата (описано в stat.uni)" (int64)
,"deliv.str" : "строка ответа smtp-сервера" (null)
}
Событие промокода¶
Выдача при выпуске, отзыв при ошибках доставки или отмене
{
"event" : "promocode"
,"event.type": "emited" | "revoked"
,"event.dt" : "дата-время события (Ys)"
,"account" : "код аккаунта"
,"issue.id" : "номер выпуска" (int64)
,"letter.id" : "номер письма" (int64)(null)
,"email" : "адрес получателя"
,"email.id" : "номер адреса" (int64)(null)
,"promocode" : [ промокода ]
}
Событие формы¶
Если форма заполнена не из сообщения, то связанные с ним параметры известны не будут.
{
"event" : "form"
,"event.dt" : "дата-время события (Ys)"
,"account" : "код аккаунта"
,"gate.name" : "код шлюза выпуска сообщения" (null) -- api smtp tranz
,"gate.uniq" : "уникальный идентификатор сообщения в шлюзе" (null)
,"customer.id" : "идентификатор сообщения назначенный клиентом" (null)
,"issue.id" : "номер выпуска" (int64)(null)
,"issue.draft.id" : "номер использованного черновика" (int64)(null)
,"letter.id" : "номер сообщения" (int64)(null)
,"letter.custid" : { ... пользовательские метки ... }
,"email" : "адрес указанный при заполнении"
,"email.id" : "номер адреса" (int64)
,"event.type" : "fill - заполнена, confirm - подтверждена"
,"form.id" : "номер формы" (int64)
,"answer" : {
"имя поля в форме" : "выбранное значение"
,"имя поля в форме" : "выбранное значение"
,"имя поля в форме" : "выбранное значение"
}
,"origin.id" : "номер источника" (int64)(null)
}
Событие трекера¶
В данный момент событие отправляется для
- завершение выпуска не транзакционной рассылки
- завершение импорта
- завершение асинхронного member.list
- завершение асинхронного member.list.count
- завершение асинхронного stat.uni
Для транзакционных рассылок - включается отдельно через Службу Поддержки.
Отправляется при получении трекером окончательного статуса (< 0)
"Окончательность" статуса означает, что он не изменится без явных дополнительных действий пользователя или Службы поддержки. Например выпуск попавший на модерацию так и останется пока Служба поддержки явно не рассмотрит его. А приостановленный выпуск может быть запущен пользователем заново.
В случае выпуска рассылки, так же, при назначении выпуску номера выпуска (статус при этом 2) (для длинных выпусков события связанные с выпуском (например доставки, клики) могут начать приходить ранее завершится формирование выпуска и, следовательно, придёт событие завершения с номером выпуска - получится, что есть события от выпуска про который ещё не известно. а так номер выпуска становится известен сразу)
Таким образом, для одного и того же действия событие может прийти более одного раза. Выпуск попал на модерацию (+событие), Служба поддержки его пропустила, при повторном выпуске он был приостановлен пользователем (+событие), потом им возобновлён и закончился успешно (+событие).
{
"event" : "tracker"
,"event.dt" : "дата-время события (Ys)"
,"track.id" : "id трекера" (int64)
,"api.request.id" : "id api-запроса" (null)
,"account" : "код аккаунта"
,"obj" : {
... аналогично obj в ответе track.get
}
-- для выпусков рассылок
,"issue.id" : "номер выпуска" (int64)(null)
< расширенная информация о выпуске как описано выше > -- в данном случае включается всегда, вне зависимости от настроек
-- для personal. в случае ошибки выпуска может отсутствовать полностью или частично
,"personal.email" : "адрес получателя" (null) -- по идее дублирует obj
,"personal.letter" : "номер письма" (int64)(null) -- по идее дублирует obj
,"personal.custid" : { ... пользовательские метки ... } (null)
}
Событие изменения черновика¶
{
"event" : "draft"
,"event.type" : "create|change|delete"
,"event.dt" : "дата-время события (Ys)"
,"account" : "код аккаунта"
,"obj" : "как obj вызова issue.draft.get"
,"variables" : "как variables вызова issue.draft.get" (null)
}
Ответ на письмо рассылки¶
Обратитесь в Службу Поддержки для настройки.
Если домен отправителя делегирован Сервису, то возможны приём и передача в виде callback ответа на письмо рассылки.
Данные события передаются по одному за вызов, без агрегации.
{
"event" : "emailreply"
,"event.dt" : Ys
,"account": "..."
,"obj" : {
"mailfrom" : "xxx@mail.ru", -- MAIL FROM протокола SMTP
"rcpto" : "yyyy@client.tld", -- RCPTO FROM протокола SMTP
"header" : [ -- unfolded, но не декодированные заголовки по порядку сверху вниз
[
"Return-Path",
"<xxxx@mail.ru>"
],
[
"Received",
"by e.mail.ru with HTTP; Wed, 03 May 2023 14:44:29 +0300"
],
.......
]
"date" : "Wed, 03 May 4065 14:44:29 +0300", -- unfolded, но не декодированный заголовок Date: письма
"message-id" : "1c9d44b12d50955e5dc8649b8c77fa6a@client.tld" -- unfolded, но не декодированный заголовок Message-Id: письма
"in-reply-to" : "<20200422101022.0.20200422101022_eux_@863148.1155769260.2867413>", -- unfolded, но не декодированный заголовок In-Reply-To: письма
"delivered-to" : "yyyy@client.tld" -- unfolded, но не декодированный заголовок Delivered-To: письма
-- разобранный заголовок To: письма
"to_email" : "yyyy@client.tld",
"to_name" : "Имя получателя",
"to" : [ [ "yyyy@client.tld", "Имя получателя" ] .... ] -- формально, заголовок может содержать несколько адресов
-- разобранный заголовок From: письма
"from_email" : "xxxx@mail.ru",
"from_name" : "Имя отправителя",
"from" : [ [ "xxxx@mail.ru", "Имя отправителя" ] .... ] -- формально, заголовок может содержать несколько адресов
"subject" : "Запрос выписки", -- декодированный заголовок Subject: письма
"html" : "html-версия письма если есть"
"amp" : "amp-версия письма если есть"
"text" : "текстовая версия если есть"
"stripped-text" : "текстовая версия из которой по возможности вырезаны цитаты и подпись"
"att" : [ -- атачи письма
{
"name" : "название" -- декодировано
,"content-type" : "тип атача как было описано в его заголовке"
,"content" : "содержимое атача в base64"
},
.....
]
}
}