Отслеживание открытий и переходов
`emails->getTracking` и всё пространство имён `tracking`.
Одно сообщение
$report = $client->emails->getTracking('msg_3f9a1c07d2b84e6a9c5b1f20'); echo $report['openCount'], ' opens from ', count($report['recipients']), ' recipients', PHP_EOL; foreach ($report['links'] as $link) { echo $link['url'], ' ', $link['clickCount'], PHP_EOL;}Для сообщения, которое никогда не отслеживалось, выбрасывается NotFoundException, у которого isNotFound() равно true, а не возвращается пустой отчёт. «Мы ничего не записали» и «никто не открыл» являются разными ответами и не должны выглядеть одинаково. Сообщение, отправленное с тестовым ключом, никогда не отслеживается, поэтому для него это исключение выбрасывается всегда.
По всему почтовому ящику
$unopened = $client->tracking->list(opened: false, days: 7, limit: 100);$stats = $client->tracking->getStats(days: 30, offsetMinutes: intdiv((int) date('Z'), 60));$report = $client->tracking->get('msg_3f9a1c07d2b84e6a9c5b1f20');$opens = $client->tracking->listOpens('msg_3f9a1c07d2b84e6a9c5b1f20', includeMachine: true);$clicks = $client->tracking->listClicks('msg_3f9a1c07d2b84e6a9c5b1f20'); echo count($unopened), ' unopened, ', $stats['openRate'], '% opened', PHP_EOL;echo count($opens), ' opens and ', count($clicks), ' clicks on ', $report['id'], PHP_EOL;list, listOpens и listClicks возвращают одну OpenEmail\Result\Page, а listAll, iterate, listAllOpens, iterateOpens, listAllClicks и iterateClicks обходят все страницы за вас. get, listOpens и listClicks принимают либо идентификатор отправки msg_…, либо собственный tmsg_… записи трекинга.
Отдельное пространство имён, а не методы в emails, и причина в охвате: emails перечисляет записи об отправке, которые существуют только для почты, обработанной этим API. Композер, инструменты MCP и ассистент отправляют без таких записей, поэтому отчёт на основе emails был бы отчётом о вашем API-трафике, а не о почтовом ящике.
Как читать цифры честно
| Пара | Что означает |
|---|---|
| opens и clicks | Что было ПРИМЕНЕНО: ушло ли сообщение с пикселем или с переписанными ссылками. |
| opened и clicked | Что произошло. |
| openCount | Засчитанные обращения. Сканеры и прокси приватности исключены. |
| openCountRaw | Все обращения. Именно так, выдавая это за вовлечённость, получают процент открытий выше 100%. |
| attributable | Можно ли вообще привязать прочтение к конкретному получателю. |
Доли из tracking->getStats считаются по ОТСЛЕЖИВАЕМЫМ сообщениям, а не по всему отправленному. Иначе почтовый ящик, который отслеживает одно сообщение из десяти, выглядел бы так, будто показатели обрушились. openRate и clickRate являются процентами, округлёнными до одного знака после запятой, например 42.5, а не долями от 0 до 1.
Параметры: tracking->list
openedbool- `true` выбирает сообщения хотя бы с одним засчитанным открытием, а `false` выбирает отслеживаемые сообщения без единого. Ни то ни другое не является значением по умолчанию, и `false` никогда не означает неотслеживаемую почту, которой в этом списке нет вовсе.
clickedbool- Тот же фильтр для засчитанных кликов, применяемый независимо от `opened`. Можно задать оба, и сообщения должны удовлетворять обоим.
daysint- На сколько дней назад от текущего момента смотреть, от 1 до 365, по умолчанию 30, а вне этого диапазона даёт 422. Окно отсчитывается по времени создания записи трекинга, и в список попадают только записи, отправка которых действительно состоялась.
minutesint- Окно в минутах вместо дней, от 1 до 527040. Если заданы оба, оно имеет приоритет над `days`. Окну короче суток нужен более мелкий `grain`.
grainstring- `minute`, `hour` или `day`, по умолчанию `day`. Он только округляет вниз начало окна, чтобы этот список совпадал с `getStats`, прочитанным с той же гранулярностью, и ничего не меняет в форме ответа.
limitint- Число отчётов на странице, от 1 до 200, по умолчанию 50, сначала новые. Для следующей страницы передайте `nextCursor` страницы обратно как `cursor:` с теми же фильтрами или позвольте `listAll` и `iterate` обойти всё окно.
cursorstring- `nextCursor` предыдущей страницы, идентификатор `tmsg_`.
apiKeystring- Запрашивает список с этим ключом вместо ключа клиента.
Ответ: отчёт о трекинге
emails->getTracking и tracking->get возвращают один отчёт как массив с ключами в camelCase, а tracking->list возвращает страницу таких отчётов.
objectstring- Всегда `tracking` у отчёта, полученного самостоятельно, через `tracking->get`, `tracking->list` или `emails->getTracking`. Тот же отчёт, вложенный как `tracking` в сообщение из `emails->get`, приходит без этого ключа, потому что там он является частью сообщения, а не отдельно полученным объектом.
idstring- Собственный идентификатор записи трекинга, `tmsg_…`. Именно по нему работают `listOpens` и `listClicks`, а `msg_…`, переданный им, сначала сопоставляется с этим идентификатором.
sendIdstring or null- Отправка `msg_…`, которой соответствует этот отчёт, и null, если запись об отправке не создавалась. Композер, `sendEmail` из MCP и ассистент отправляют без такой записи. Трекинг охватывает почтовый ящик, а не только API-трафик.
threadIdstring or null- Заполняется после передачи, чтобы интерфейс чтения мог снова найти письмо; null там, где драйвер ничего не сообщил. Не несёт нагрузки: запись с null здесь всё равно учитывается.
messageIdstring or null- Message-ID по RFC 5322, а не наш идентификатор. Тоже заполняется после передачи; null там, где транспорт не вернул ничего, чем его заполнить.
subjectstring or null- Тема в том виде, в каком она была при отправке. null у сообщения, записанного без темы.
fromstring- Адрес отправителя, скопированный в запись, а не подтянутый из отправки. Отчёты читают спустя долгое время, и адрес, с тех пор исправленный или удалённый, иначе переписал бы историю.
sourcestring- Какая поверхность его отправила: `composer`, `api`, `mcp`, `ai` или `queue`. Может появиться поверхность, которую этот пакет пока не называет, поэтому считайте незнакомое значение информацией, а не ошибкой.
sentAtstring or null- Когда сообщение ушло, в виде момента ISO 8601. null у записи, отправка которой так и не завершилась. `tracking->list` такие записи не показывает, а `get` показывает.
opensbool- Был ли к этому письму ПРИМЕНЁН пиксель. Это то, что было сделано, а не то, что говорит настройка аккаунта сейчас.
clicksbool- Были ли переписаны ссылки этого сообщения. False, если в теле не было ссылок, потому что тогда ничего не менялось, а запись, утверждающую обратное, нельзя было бы сверить с байтами.
openedbool- Было ли зафиксировано хоть одно засчитанное открытие среди копий. Читайте его вместе с `opens`: отсутствие данных из-за того, что их не собирали, говорит совсем не о том, что сообщение никто не прочитал.
clickedbool- Был ли зафиксирован хоть один засчитанный клик. Более веское свидетельство, чем открытие, поскольку изображения блокируют гораздо чаще, чем не переходят по ссылкам.
attributablebool- Можно ли каждое прочтение здесь привязать к конкретному получателю. False в тот момент, когда непривязанная копия показывает засчитанную активность: это случай нескольких получателей, когда одно тело уходит всему списку под одним токеном, так что проверьте его, прежде чем писать «Боб это не открывал».
openCountint- Открытия, которые считаются вызванными человеком, просуммированные по копиям. Машинные обращения исключены, а повторы в пределах тридцати секунд схлопываются в одно, так что именно эту цифру стоит показывать читателю.
clickCountint- Засчитанные клики, просуммированные по копиям. Дедуплицируются по ссылке, а не по сообщению, так что две разные ссылки, пройденные с разницей в секунды, дают два клика.
openCountRawint- Каждая загрузка пикселя, включая сканеры и прокси конфиденциальности. `openCountRaw` минус `openCount` показывает, сколько обращений было отсеяно (машинные загрузки и повторы в пределах тридцати секунд вместе), и это единственное доступное свидетельство того, что фильтрация вообще происходила.
clickCountRawint- Каждое посещение переписанной ссылки, включая машинные обращения и повторы.
firstOpenAtstring or null- Самое раннее засчитанное открытие среди копий, и null, пока его нет. Машинные обращения его не сдвигают.
lastOpenAtstring or null- Самое последнее засчитанное открытие среди копий; null, пока его нет.
firstClickAtstring or null- Самый ранний засчитанный клик среди копий; null, пока его нет.
lastClickAtstring or null- Самый последний засчитанный клик среди копий; null, пока его нет.
recipientsarray- По одной записи на каждую отслеживаемую копию: по одной на получателя там, где транспорт позволяет байтам различаться для каждого человека, и одна общая запись там, где не позволяет. Общая запись отбрасывается, если на неё в действительности ничего не попало, поэтому нетронутая строка «кто-то» никогда не стоит рядом с настоящими именами.
linksarray- Все ссылки, переписанные в этом письме, в порядке их расположения в теле. Пусто там, где переписывать было нечего: письмо отправлено с выключенным `clicks` или в его теле вообще не было ссылок.
Каждая запись в recipients
emailstring or null- Кому ушла эта копия, в нижнем регистре и в том виде, в каком адрес был при отправке. null ровно тогда, когда `attributed` равно false.
kindstring or null- `to`, `cc` или `bcc`: в каком заголовке стоял адрес, чтобы отчёт читался так же, как сообщение. null у общей копии, которая не принадлежит ни одному адресу.
attributedbool- Называет ли эта строка конкретного человека. Читайте её раньше `email`: false означает общую копию, которая попадает в список, как только на неё приходит хотя бы одно обращение, и приписать этому обращению имя, даже в письме с единственным получателем, значило бы выдумать тот единственный факт, которого механизм дать не может.
openCountint- Засчитанные открытия только по этой копии, с теми же исключениями, что и в итоге по письму: машинные обращения отброшены, а повторы в пределах тридцати секунд схлопнуты в одно.
clickCountint- Засчитанные клики только по этой копии, с дедупликацией по ссылке, а не по копии.
firstOpenAtstring or null- Самое раннее засчитанное открытие этой копии; null, пока его нет.
lastOpenAtstring or null- Самое последнее засчитанное открытие этой копии; null, пока его нет.
firstClickAtstring or null- Самый ранний засчитанный клик по этой копии; null, пока его нет.
lastClickAtstring or null- Самый последний засчитанный клик по этой копии; null, пока его нет.
Каждая запись в links
idstring- Собственный идентификатор ссылки, `lnk_…`. Именно это значение называет `linkId` в строке клика, поэтому обращение из `listClicks` можно сопоставить с записью здесь.
urlstring- Куда на самом деле ведёт ссылка, в том виде, в каком она была в сообщении до переписывания. Редиректор находит по идентификатору этот адрес и отправляет посетителя дальше.
labelstring or null- Текст ссылки в том виде, в каком он был в сообщении, или null, если его не было, например у изображения или голого URL. Он нужен, чтобы отчёт мог сказать «ссылка на цены», а не цитировать URL с тремя параметрами трекинга, и он никогда не заменяет `url`.
clickCountint- Засчитанные переходы по этой ссылке, просуммированные по копиям. То же тридцатисекундное окно на ссылку, что и у `clickCount` письма.
clickCountRawint- Все переходы по этой ссылке, включая машинные обращения и повторы.