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

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

`emails.getTracking` и весь ресурс `tracking`.

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

tracking.ts
const report = await openemail.emails.getTracking('msg_…') console.log(report.openCount, 'opens from', report.recipients.length, 'recipients')for (const link of report.links) console.log(link.url, link.clickCount)

Сообщение, которое никогда не отслеживалось, бросает OpenEmailApiError, у которого isNotFound равно true, а не отдаёт пустой отчёт. «Мы ничего не записали» и «никто не открыл» — разные ответы, и они не должны делить один ответ.

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

tracking-report.ts
await openemail.tracking.list({ opened: false, days: 7, limit: 100 })await openemail.tracking.getStats({ days: 30, offsetMinutes: -new Date().getTimezoneOffset() })await openemail.tracking.get('msg_…')await openemail.tracking.listOpens('msg_…', { includeMachine: true })await openemail.tracking.listClicks('msg_…')

list, listOpens и listClicks разрешаются в обычные массивы. get, listOpens и listClicks принимают либо идентификатор отправки msg_…, либо собственный tmsg_… записи трекинга.

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

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

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

Доли из tracking.getStats считаются по ОТСЛЕЖИВАЕМЫМ сообщениям, а не по всему отправленному. Иначе почтовый ящик, отслеживающий одно сообщение из десяти, выглядел бы рухнувшим.

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

openedboolean
`true` выбирает сообщения хотя бы с одним засчитанным открытием, `false` — отслеживаемые сообщения без единого. Ни то ни другое не является значением по умолчанию, и `false` никогда не означает неотслеживаемую почту, которой в этом списке нет вовсе.
clickedboolean
Тот же фильтр для засчитанных кликов, применяемый независимо от `opened`. Можно задать оба, и сообщения должны удовлетворять обоим.
daysnumber
На сколько дней назад от текущего момента смотреть: от 1 до 365, по умолчанию 30; вне этого диапазона — 422. Окно отсчитывается по моменту создания записи трекинга, и перечисляются только записи, чья отправка действительно ушла.
limitnumber
Не более стольких сообщений: от 1 до 200, по умолчанию 50, новые сверху. Курсора нет: это отчёт за окно, а не лента, так что он ограничен `days` и `limit` и читается целиком.

Ответ: TrackingResource

object'tracking'
Всегда `'tracking'` у отчёта, полученного сам по себе — через `tracking.get`, `tracking.list` или `emails.getTracking`. Тот же отчёт, вложенный как `email.tracking` в полученное сообщение, приходит без этого ключа, потому что там он часть того объекта, а не что-то запрошенное отдельно.
idstring
Собственный идентификатор записи трекинга, `tmsg_…`. Именно на нём ключуются вызовы по отдельным обращениям — `listOpens` и `listClicks`; переданный им `msg_…` сперва разрешается в него.
sendIdstring | null
Отправка `msg_…`, с которой это соотносится; null там, где запись об отправке не создавалась. Композер, `sendEmail` в MCP и ассистент отправляют без неё. Отслеживание охватывает почтовый ящик, а не только трафик API.
threadIdstring | null
Заполняется после передачи, чтобы интерфейс чтения мог снова найти письмо; null там, где драйвер ничего не сообщил. Не несёт нагрузки: запись с null здесь всё равно учитывается.
messageIdstring | null
Message-ID по RFC 5322, а не наш идентификатор. Тоже заполняется после передачи; null там, где транспорт не вернул ничего, чем его заполнить.
subjectstring | null
Тема в том виде, в каком она была на момент отправки. Null для письма, записанного без темы.
fromstring
Адрес отправителя, скопированный в запись, а не подтянутый из отправки. Отчёты читают спустя долгое время, и адрес, с тех пор исправленный или удалённый, иначе переписал бы историю.
sourceEmailSource | (string & {})
Какая поверхность отправила письмо: `composer`, `api`, `mcp`, `ai` или `queue`. Тип открытый, поэтому поверхность, которую этот SDK ещё не называет, не является ломающим изменением.
sentAtstring | null
Когда письмо ушло, как момент времени ISO-8601. Null для записи, отправка которой так и не завершилась. `tracking.list` такие исключает, `get` — нет.
opensboolean
Был ли к этому письму ПРИМЕНЁН пиксель. Это то, что было сделано, а не то, что говорит настройка аккаунта сейчас.
clicksboolean
Были ли переписаны ссылки этого сообщения. False, когда в теле не было ссылок, потому что тогда ничего не менялось, а запись, утверждающая обратное, не сошлась бы с байтами.
openedboolean
Было ли зафиксировано хоть одно засчитанное открытие среди копий. Читайте его вместе с `opens`: отсутствие данных из-за того, что их не собирали, — совсем не тот факт, что сообщение никто не прочитал.
clickedboolean
Был ли зафиксирован хоть один засчитанный клик. Более веское свидетельство, чем открытие, поскольку изображения блокируют гораздо чаще, чем не переходят по ссылкам.
attributableboolean
Можно ли каждое прочтение здесь привязать к конкретному получателю. False в тот момент, когда непривязанная копия показывает засчитанную активность, — это случай нескольких получателей, когда одно тело уходит всему списку под одним токеном, так что проверьте его, прежде чем писать «Боб это не открывал».
openCountnumber
Открытия, которые считаются вызванными человеком, просуммированные по копиям. Машинные обращения исключены, а повторы в пределах тридцати секунд схлопываются в одно, так что именно эту цифру стоит показывать читателю.
clickCountnumber
Засчитанные клики, просуммированные по копиям. Дедуплицируются по ссылке, а не по сообщению, так что две разные ссылки, пройденные с разницей в секунды, — это два клика.
openCountRawnumber
Каждая загрузка пикселя, включая сканеры и прокси приватности. `openCountRaw - openCount` — это сколько их отсеял классификатор и единственное доступное свидетельство того, что фильтрация вообще была.
clickCountRawnumber
Каждое посещение переписанной ссылки, включая машинные обращения и повторы.
firstOpenAtstring | null
Самое раннее засчитанное открытие среди копий; null, пока его нет. Машинные обращения его никогда не сдвигают.
lastOpenAtstring | null
Самое последнее засчитанное открытие среди копий; null, пока его нет.
firstClickAtstring | null
Самый ранний засчитанный клик среди копий; null, пока его нет.
lastClickAtstring | null
Самый последний засчитанный клик среди копий; null, пока его нет.
recipientsTrackingRecipientResource[]
По одной записи на каждую отслеживаемую копию: по одной на получателя там, где транспорт позволяет байтам различаться для каждого человека, и одна общая запись там, где не позволяет. Общая запись отбрасывается, если на неё в действительности ничего не попало, поэтому нетронутая строка «кто-то» никогда не стоит рядом с настоящими именами.
recipients[].emailstring | null
Кому ушла эта копия — в нижнем регистре и в том виде, в каком адрес был на момент отправки. Null ровно тогда, когда `attributed` равно false.
recipients[].kind'to' | 'cc' | 'bcc' | null
В каком заголовке стоял адрес, чтобы отчёт читался так же, как читалось письмо. Null для общей копии, которая не принадлежит ни одному адресу.
recipients[].attributedboolean
Называет ли эта строка конкретного человека. Читайте её раньше `email`: false — это общая копия, которая попадает в список, как только на неё приходит хотя бы одно обращение, и приписать этому обращению имя, даже в письме с единственным получателем, значило бы выдумать тот единственный факт, которого механизм дать не может.
recipients[].openCountnumber
Засчитанные открытия только по этой копии, с теми же исключениями, что и в итоге по письму: машинные обращения отброшены, а повторы в пределах тридцати секунд схлопнуты в одно.
recipients[].clickCountnumber
Засчитанные клики только по этой копии, с дедупликацией по ссылке, а не по копии.
recipients[].firstOpenAtstring | null
Самое раннее засчитанное открытие этой копии; null, пока его нет.
recipients[].lastOpenAtstring | null
Самое последнее засчитанное открытие этой копии; null, пока его нет.
recipients[].firstClickAtstring | null
Самый ранний засчитанный клик по этой копии; null, пока его нет.
recipients[].lastClickAtstring | null
Самый последний засчитанный клик по этой копии; null, пока его нет.
linksTrackingLinkResource[]
Все ссылки, переписанные в этом письме, в порядке их расположения в теле. Пусто там, где переписывать было нечего: письмо отправлено с выключенным `clicks` или в его теле вообще не было ссылок.
links[].idstring
Собственный идентификатор ссылки, `lnk_…`. Это то значение, которое называет `linkId` в строке клика, поэтому обращение из `listClicks` можно сопоставить с записью отсюда.
links[].urlstring
Куда ссылка ведёт на самом деле — в том виде, в каком она была в письме до переписывания. Редиректор разворачивает идентификатор обратно в это значение и отправляет посетителя дальше.
links[].labelstring | null
Текст ссылки в том виде, в каком он был в письме, или null там, где текста не было, — например, у изображения или у голого URL. Он нужен, чтобы отчёт мог сказать «ссылка на тарифы», а не цитировать URL с тремя параметрами отслеживания, и он никогда не заменяет `url`.
links[].clickCountnumber
Засчитанные переходы по этой ссылке, просуммированные по копиям. То же тридцатисекундное окно на ссылку, что и у `clickCount` письма.
links[].clickCountRawnumber
Все переходы по этой ссылке, включая машинные обращения и повторы.