Перейти к документации
Ruby

Отслеживание открытий и переходов

`emails.get_tracking` и всё пространство имён `tracking`.

Одно сообщение

tracking.rb
report = client.emails.get_tracking("msg_3f9a1c07d2b84e6a9c5b1f20") puts "#{report[:openCount]} opens from #{report[:recipients].size} recipients"report[:links].each { |link| puts "#{link[:url]} #{link[:clickCount]}" }

Для сообщения, которое никогда не отслеживалось, выбрасывается OpenEmail::NotFoundError, у которого not_found? равно true, а не возвращается пустой отчёт. «Мы ничего не записали» и «никто не открыл» являются разными ответами и не должны выглядеть одинаково. Сообщение, отправленное с тестовым ключом, никогда не отслеживается, поэтому для него эта ошибка выбрасывается всегда.

По всему почтовому ящику

tracking_report.rb
client.tracking.list(opened: false, days: 7, limit: 100)client.tracking.get_stats(days: 30, offset_minutes: Time.now.utc_offset / 60)client.tracking.get("msg_3f9a1c07d2b84e6a9c5b1f20")client.tracking.list_opens("msg_3f9a1c07d2b84e6a9c5b1f20", include_machine: true)client.tracking.list_clicks("msg_3f9a1c07d2b84e6a9c5b1f20")

list, list_opens и list_clicks возвращают одну OpenEmail::Page, а list_all, iterate, list_all_opens, iterate_opens, list_all_clicks и iterate_clicks обходят все страницы за вас. get, list_opens и list_clicks принимают либо идентификатор отправки msg_…, либо собственный tmsg_… записи трекинга.

Отдельное пространство имён, а не методы в emails, и причина в охвате: emails перечисляет записи об отправке, которые существуют только для почты, обработанной этим API. Композер, инструменты MCP и ассистент отправляют без таких записей, поэтому отчёт на основе emails был бы отчётом о вашем API-трафике, а не о почтовом ящике.

Как читать цифры честно

ПараЧто означает
opens и clicksЧто было ПРИМЕНЕНО: ушло ли сообщение с пикселем или с переписанными ссылками.
opened и clickedЧто произошло.
openCountЗасчитанные обращения. Сканеры и прокси приватности исключены.
openCountRawВсе обращения. Именно так, выдавая это за вовлечённость, получают процент открытий выше 100%.
attributableМожно ли вообще привязать прочтение к конкретному получателю.

Доли из tracking.get_stats считаются по ОТСЛЕЖИВАЕМЫМ сообщениям, а не по всему отправленному. Иначе почтовый ящик, который отслеживает одно сообщение из десяти, выглядел бы так, будто показатели обрушились. openRate и clickRate являются процентами, округлёнными до одного знака после запятой, например 42.5, а не долями от 0 до 1.

Параметры: tracking.list

openedBoolean
`true` выбирает сообщения хотя бы с одним засчитанным открытием, а `false` выбирает отслеживаемые сообщения без единого. Ни то ни другое не является значением по умолчанию, и `false` никогда не означает неотслеживаемую почту, которой в этом списке нет вовсе.
clickedBoolean
Тот же фильтр для засчитанных кликов, применяемый независимо от `opened`. Можно задать оба, и сообщения должны удовлетворять обоим.
daysInteger
На сколько дней назад от текущего момента смотреть, от 1 до 365, по умолчанию 30, а вне этого диапазона даёт 422. Окно отсчитывается по времени создания записи трекинга, и в список попадают только записи, отправка которых действительно состоялась.
minutesInteger
Окно в минутах вместо дней, от 1 до 527040. Если заданы оба, оно имеет приоритет над `days`. Окну короче суток нужен более мелкий `grain`.
grainString
`minute`, `hour` или `day`, по умолчанию `day`. Он только округляет вниз начало окна, чтобы этот список совпадал с `get_stats`, прочитанным с той же гранулярностью, и ничего не меняет в форме ответа.
limitInteger
Число отчётов на странице, от 1 до 200, по умолчанию 50, сначала новые. Для следующей страницы передайте `next_cursor` страницы обратно как `cursor:` с теми же фильтрами или позвольте `list_all` и `iterate` обойти всё окно.
cursorString
`next_cursor` предыдущей страницы, идентификатор `tmsg_`.
api_keyString
Запрашивает список с этим ключом вместо ключа клиента.

Ответ: отчёт о трекинге

emails.get_tracking и tracking.get возвращают один отчёт как Hash с ключами типа Symbol, а tracking.list возвращает страницу таких отчётов.

objectString
Всегда `tracking` у отчёта, полученного самостоятельно, через `tracking.get`, `tracking.list` или `emails.get_tracking`. Тот же отчёт, вложенный как `tracking` в сообщение из `emails.get`, приходит без этого ключа, потому что там он является частью сообщения, а не отдельно полученным объектом.
idString
Собственный идентификатор записи трекинга, `tmsg_…`. Именно по нему работают `list_opens` и `list_clicks`, а `msg_…`, переданный им, сначала сопоставляется с этим идентификатором.
sendIdString or nil
Отправка `msg_…`, которой соответствует этот отчёт, и nil, если запись об отправке не создавалась. Композер, `sendEmail` из MCP и ассистент отправляют без такой записи. Трекинг охватывает почтовый ящик, а не только API-трафик.
threadIdString or nil
Заполняется после передачи, чтобы интерфейс чтения мог снова найти сообщение, и равно nil, если драйвер ничего не сообщил. Ни на что не влияет: запись, где это поле nil, всё равно учитывается.
messageIdString or nil
Message-ID по RFC 5322, а не наш идентификатор. Тоже заполняется после передачи и равно nil, если транспорт не вернул ничего, чем его заполнить.
subjectString or nil
Тема в том виде, в каком она была при отправке. nil у сообщения, записанного без темы.
fromString
Адрес отправителя, скопированный в запись, а не подтянутый из отправки. Отчёты читают спустя долгое время, и адрес, с тех пор исправленный или удалённый, иначе переписал бы историю.
sourceString
Какая поверхность его отправила: `composer`, `api`, `mcp`, `ai` или `queue`. Может появиться поверхность, которую этот гем пока не называет, поэтому считайте незнакомое значение информацией, а не ошибкой.
sentAtString or nil
Когда сообщение ушло, в виде момента ISO 8601. nil у записи, отправка которой так и не завершилась. `tracking.list` такие записи не показывает, а `get` показывает.
opensBoolean
Был ли к этому письму ПРИМЕНЁН пиксель. Это то, что было сделано, а не то, что говорит настройка аккаунта сейчас.
clicksBoolean
Были ли переписаны ссылки этого сообщения. False, если в теле не было ссылок, потому что тогда ничего не менялось, а запись, утверждающую обратное, нельзя было бы сверить с байтами.
openedBoolean
Было ли зафиксировано хоть одно засчитанное открытие среди копий. Читайте его вместе с `opens`: отсутствие данных из-за того, что их не собирали, говорит совсем не о том, что сообщение никто не прочитал.
clickedBoolean
Был ли зафиксирован хоть один засчитанный клик. Более веское свидетельство, чем открытие, поскольку изображения блокируют гораздо чаще, чем не переходят по ссылкам.
attributableBoolean
Можно ли каждое прочтение здесь привязать к конкретному получателю. False в тот момент, когда непривязанная копия показывает засчитанную активность: это случай нескольких получателей, когда одно тело уходит всему списку под одним токеном, так что проверьте его, прежде чем писать «Боб это не открывал».
openCountInteger
Открытия, которые считаются вызванными человеком, просуммированные по копиям. Машинные обращения исключены, а повторы в пределах тридцати секунд схлопываются в одно, так что именно эту цифру стоит показывать читателю.
clickCountInteger
Засчитанные клики, просуммированные по копиям. Дедуплицируются по ссылке, а не по сообщению, так что две разные ссылки, пройденные с разницей в секунды, дают два клика.
openCountRawInteger
Каждая загрузка пикселя, включая сканеры и прокси конфиденциальности. `openCountRaw` минус `openCount` показывает, сколько обращений было отсеяно (машинные загрузки и повторы в пределах тридцати секунд вместе), и это единственное доступное свидетельство того, что фильтрация вообще происходила.
clickCountRawInteger
Каждое посещение переписанной ссылки, включая машинные обращения и повторы.
firstOpenAtString or nil
Самое раннее учтённое открытие по всем копиям, nil, пока его нет. Машинные обращения никогда его не сдвигают.
lastOpenAtString or nil
Последнее учтённое открытие по всем копиям, nil, пока его нет.
firstClickAtString or nil
Самый ранний учтённый клик по всем копиям, nil, пока его нет.
lastClickAtString or nil
Последний учтённый клик по всем копиям, nil, пока его нет.
recipientsArray<Hash>
По одной записи на каждую отслеживаемую копию: по одной на получателя там, где транспорт позволяет байтам различаться для каждого человека, и одна общая запись там, где не позволяет. Общая запись отбрасывается, если на неё в действительности ничего не попало, поэтому нетронутая строка «кто-то» никогда не стоит рядом с настоящими именами.
linksArray<Hash>
Все ссылки, переписанные в этом письме, в порядке их расположения в теле. Пусто там, где переписывать было нечего: письмо отправлено с выключенным `clicks` или в его теле вообще не было ссылок.

Каждая запись в recipients

emailString or nil
Кому ушла эта копия, в нижнем регистре и в том виде, в каком адрес был при отправке. nil ровно тогда, когда `attributed` равно false.
kindString or nil
`to`, `cc` или `bcc`: в каком заголовке стоял адрес, чтобы отчёт читался так же, как сообщение. nil у общей копии, которая не принадлежит ни одному адресу.
attributedBoolean
Называет ли эта строка конкретного человека. Читайте её раньше `email`: false означает общую копию, которая попадает в список, как только на неё приходит хотя бы одно обращение, и приписать этому обращению имя, даже в письме с единственным получателем, значило бы выдумать тот единственный факт, которого механизм дать не может.
openCountInteger
Засчитанные открытия только по этой копии, с теми же исключениями, что и в итоге по письму: машинные обращения отброшены, а повторы в пределах тридцати секунд схлопнуты в одно.
clickCountInteger
Засчитанные клики только по этой копии, с дедупликацией по ссылке, а не по копии.
firstOpenAtString or nil
Самое раннее учтённое открытие этой копии, nil, пока его нет.
lastOpenAtString or nil
Последнее учтённое открытие этой копии, nil, пока его нет.
firstClickAtString or nil
Самый ранний учтённый клик по этой копии, nil, пока его нет.
lastClickAtString or nil
Последний учтённый клик по этой копии, nil, пока его нет.

Каждая запись в links

idString
Собственный идентификатор ссылки, `lnk_…`. Именно это значение называет `linkId` в строке клика, поэтому обращение из `list_clicks` можно сопоставить с записью здесь.
urlString
Куда на самом деле ведёт ссылка, в том виде, в каком она была в сообщении до переписывания. Редиректор находит по идентификатору этот адрес и отправляет посетителя дальше.
labelString or nil
Текст ссылки в том виде, в каком он был в сообщении, или nil, если его не было, например у изображения или голого URL. Он нужен, чтобы отчёт мог сказать «ссылка на цены», а не цитировать URL с тремя параметрами трекинга, и он никогда не заменяет `url`.
clickCountInteger
Засчитанные переходы по этой ссылке, просуммированные по копиям. То же тридцатисекундное окно на ссылку, что и у `clickCount` письма.
clickCountRawInteger
Все переходы по этой ссылке, включая машинные обращения и повторы.