Açılma ve tıklama izleme
GET /tracking: bir iletinin okunup okunmadığı ve hangi bağlantıların izlendiği.
Bu sayfadaki 6 çağrının herhangi birini kendi anahtarınızla çalışma alanınıza karşı çalıştırır.
Neler kaydedilir
Birbirinden bağımsız iki anahtar; iletinin gönderildiği adres ya da All addresses için kapatılmadıkça ikisi de açıktır. opens 1×1 bir görsel ekler; clicks gövdenin yeni kısmındaki bağlantıları yeniden yazar. Bir yanıtın altındaki alıntılanmış geçmiş başkasının iletisidir ve ona dokunulmaz. Bir gönderim, tek bir ileti için karar vermek üzere tracking: { opens, clicks } belirtir (her iki yönde de; yani false, bir programın adrese ayarlanmış davranışı reddetme yoludur) ve göndermediğiniz bir alan, bu API'nin bir çalışma alanı adına seçtiği bir varsayılana değil, iletinin gönderildiği adresin ayarına, ardından All addresses ayarına döner.
{ "from": "Acme Billing <[email protected]>", "to": ["[email protected]"], "subject": "Your September invoice", "html": "<p>Invoice attached.</p>", "tracking": { "opens": true, "clicks": true } }İleti başına en çok 100 hedef, her biri bir kez olmak üzere yeniden yazılır. Bir başlık görselinden, bir düğmeden ve bir alt bilgiden bağlanan aynı URL tek bir satırdır, çünkü üç kez sorulmuş tek bir sorudur. Sınırın ötesinde kalan bağlantılar tam olarak yazıldıkları gibi bırakılır: izlenmeyen bir bağlantı yine de çalışır ve son iki yüz bağlantısını sessizce yitiren bir ileti, eksik bir rapordan çok daha kötü bir başarısızlıktır.
Yeniden yazılan bağlantılar ve piksel varsayılan olarak OpenEmail API sunucusunu gösterir. Gönderen alan adının, tracking.status değeri active olan bir özel izleme alan adı varsa, o alan adından gönderilen yeni postalar bunun yerine https://<tracking host>/t/... adresini kullanır; böyle bir ad PATCH /domains/{id} ile ayarlanır.
Bütün bunlar emails:read gerektirir; ayrı bir izleme kapsamı yoktur. O kapsam zaten "gönderilmiş iletileri ve teslim durumlarını oku" demektir ve birinin bir iletiyi açıp açmadığı, olabilecek en düz anlamıyla teslim durumudur.
Uç noktalar
| Çağrı | Döndürdüğü |
|---|---|
| `GET /tracking` | İzlenen iletiler, en yeniden eskiye. opened, clicked, days (1–365, varsayılan 30), limit (en çok 200). |
| `GET /tracking/stats` | Bir zaman penceresindeki oranlar. days (varsayılan 30) ve offsetMinutes; böylece günler okuyucunun günü nerede bitiyorsa orada biter. |
| `GET /tracking/{id}` | Tek bir rapor. tmsg_ ile başlayan bir izleme id'si ya da bir gönderimin döndürdüğü msg_ id'sini alır. |
| `GET /tracking/{id}/opens` | Tek tek getirmeler. includeMachine, limit (en çok 200). |
| `GET /tracking/{id}/clicks` | Aynısı; her satırda linkId ve url ile. |
| `GET /emails/{id}/tracking` | Aynı rapor, elinizde bulunan gönderim id'siyle. |
Sorgu dizesinde boole değerleri açıkça yazılır: true, false, 1 ya da 0; başka her şey reddedilir. Boolean("false") true'dur; bu yüzden tür dönüşümüne uğramış bir ?opened=false, istenenin tam tersini döndürürdü.
Bu, /emails üzerinde birkaç alan olmak yerine kendi başına bir kaynaktır; nedeni kapsama: o liste gönderim kayıtlarını tutar, oysa yazma penceresi, MCP araçları ve asistan bir kayıt yazmadan gönderim yapar. Onun üzerine kurulu bir rapor, posta kutusu hakkında değil API trafiğiniz hakkında bir rapor olurdu.
Rapor
{ "object": "tracking", "id": "tmsg_9c1f7b2e4a5d40b8a3e61d2f", "sendId": "msg_c5f21cc6bfec4e848caf905b", "threadId": "thread_2f9b…", "messageId": "<2598…@acme.com>", "subject": "Your September invoice", "from": "[email protected]", "source": "api", "sentAt": "2026-08-29T08:19:08.000Z", "opens": true, "clicks": true, "opened": true, "clicked": true, "attributable": true, "openCount": 3, "openCountRaw": 7, "clickCount": 1, "clickCountRaw": 2, "firstOpenAt": "2026-08-29T09:04:11.000Z", "lastOpenAt": "2026-08-30T07:42:55.000Z", "firstClickAt": "2026-08-29T09:05:02.000Z", "lastClickAt": "2026-08-29T09:05:02.000Z", "recipients": [ { "email": "[email protected]", "kind": "to", "attributed": true, "openCount": 3, "clickCount": 1, "firstOpenAt": "2026-08-29T09:04:11.000Z", "lastOpenAt": "2026-08-30T07:42:55.000Z", "firstClickAt": "2026-08-29T09:05:02.000Z", "lastClickAt": "2026-08-29T09:05:02.000Z" } ], "links": [ { "id": "lnk_4f0a1c8d29b74e6fa3c05d17", "url": "https://acme.com/invoices/42", "label": "View invoice", "clickCount": 1, "clickCountRaw": 2 } ] }opens ve clicks iletiye UYGULANMIŞ olanlardır; opened ve clicked ise gerçekleşenler. openCount okumaları, openCountRaw getirmeleri sayar. Aradaki fark, burada dört, tarayıcılar ve gizlilik proxy'leridir; günlükle toplam arasındaki boşluk açıklanmadan kalmasın, incelenebilsin diye tutulur. Birini adlandırmadan önce okunacak alan attributable: false olması, bir okumanın listenin tamamına giden bir kopyaya düştüğü anlamına gelir ve bundan sonra belirli bir alıcı hakkında kurulan her cümle tahmindir.
source, iletiyi gönderen yüzeyi adlandırır: bu API üzerinden yapılan gönderimler için api, uygulamanın kendisinin gönderdiği her şey için composer. İkincisinde sendId null'dır; izleme id'sinin var olma nedeni de budur.
email alanı null ve attributed: false olan bir satır, bir okumanın bir kişiye bağlanamadığı yerdir ve rapor böyle bir satırı yalnızca gerçekten bir okuma gerçekleştiğinde gösterir. Tek alıcısı olan bir iletide böyle bir satır hiç yoktur, çünkü tek gövde ve tek muhatap aynı ifadedir. Birden çok alıcısı olan bir iletide ise gittiği andan itibaren arkada böyle bir satır vardır, çünkü taşıyıcı gönderim anına kadar kesinleşmez; üzerine bir şey düşene kadar da rapora girmez: adlandırılmış alıcıların yanında kalıcı bir "biri: açmadı" satırı, yalnızca yanlış okunabilecek bir satırdır. Böyle bir satır VARSA, adlandırılmış satırlar sıfırda duranlardır ve attributable false olur. Okuma gerçektir, okuyan kişi iletideki insanlardan biridir ve "bu iletideki biri" verinin desteklediği tek ifadedir. Adı asla alıcı listesinden doldurmayın.
Bir pencere boyunca oranlar
{ "object": "tracking_stats", "tracked": 128, "trackedForOpens": 128, "trackedForClicks": 47, "opened": 91, "clicked": 34, "openRate": 71.1, "clickRate": 72.3, "totalOpens": 240, "totalClicks": 52, "machineOpens": 173, "medianTimeToOpenSeconds": 2714, "byDay": [{ "day": "2026-08-27", "sent": 12, "opened": 9, "clicked": 3 }], "topLinks": [{ "url": "https://acme.com/pricing", "label": "See pricing", "clickCount": 18 }], "clients": [{ "client": "Gmail", "count": 96 }], "countries": [{ "country": "GB", "count": 71 }] }Oranlar, gönderilen bütün posta üzerinden değil İZLENEN iletiler üzerinden yüzdelerdir: on iletiden birini izleyen bir çalışma alanının o on ileti için bir açılma oranı vardır ve bunu bugüne dek gönderdiği her şeye bölmek, biri izlenmeyen bir yanıt gönderdiğinde her seferinde oranı düşürürdü. Beş kez açılan bir ileti TEK bir açılmış iletidir. Oranlar iletileri, toplamlar vuruşları sayar; ikisini birbirine karıştırmak, %100'ün üzerinde açılma oranlarının yayımlanmasına yol açar.
byDay seyrektir: hiçbir şeyin izlenmediği bir gün sıfır olarak değil, hiç yer almayarak görünür; bu yüzden grafiğe dökmeden önce boşlukları doldurun. Günler UTC'nin offsetMinutes kadar doğusunda (−840 ile 840 arası) kovalara ayrılır; böylece okuyucunun günü nerede bitiyorsa orada biterler. medianTimeToOpenSeconds ortalama değil medyandır, çünkü üç hafta sonra açılan tek bir ileti ortalamayı hiçbir iletinin bulunmadığı bir yere sürükler.
Tek tek vuruşlar
{ "object": "list", "data": [ { "object": "open", "id": "opn_1a7c…", "trackedMessageId": "tmsg_9c1f7b2e4a5d40b8a3e61d2f", "recipient": "[email protected]", "kind": "machine", "counted": false, "client": "Apple Mail Privacy Protection", "device": "unknown", "os": "macOS", "country": "GB", "region": "England", "city": "London", "createdAt": "2026-08-29T08:19:11.000Z" } ] }kind değeri human, proxy ya da machine olur ve counted, vuruşun sayıları değiştirip değiştirmediğini söyler. includeMachine=true göndermedikçe makine vuruşları dışarıda bırakılır; dürüst varsayılan budur: bunlar etkileşim oldukları için değil, atılmaları açıklanamaz bir boşluk bırakacağı için kaydedilir.
Konum kabadır, çünkü elde olan bu kadardır. Hiçbir vuruş için IP adresi saklanmaz. Ülke, bölge ve şehir, uç sunucunun zaten bildikleridir; tutulan tek diğer tanımlayıcı ise tuzu her gün değişen bir karmadır, yani aynı gün içinde iki getirmeyi birbirinden ayırabilir, ertesi gün ise işlevsizdir.
Sayıların söyleyemedikleri
- Apple Mail Privacy Protection, kimse bakmasa da teslimat sırasında her iletideki her görseli getirir. User-Agent ve ağ üzerinden sınıflandırılır ve
machineolarak kaydedilir; gönderimden sonraki on saniye içinde gelen her şey de öyle, çünkü bir insanın yaptığı hiçbir şey o kadar hızlı olmaz. - Gmail'in görsel proxy'si
machinedeğilproxydir: biri iletiyi görüntülemiştir, yani açılma gerçektir; ama cihaz, istemci ve konum bilinemez. Proxy ayrıca önbelleğe alır, dolayısıyla ikinci bir okuma bize hiç ulaşmayabilir. Gmail üzerinden gelen sayılar bir alt sınırdır, asla bir toplam değil. - Aynı kopyanın otuz saniye içindeki iki getirmesi tek bir okumadır. Yeniden çizilen bir önizleme bölmesi ya da kaydırılarak yeniden görünür olan bir ileti görseli yeniden getirir; bir saat sonraki gerçek ikinci ziyaret ise yine sayılır.
- Alıcıyı adlandırmak, kişi başına yeniden oluşturulabilecek kadar küçük bir ileti gerektirir: tahmini boyut ile alıcı sayısının çarpımı 8MB'ın altında kalmalıdır. Bunun üzerinde herkese tek bir gövde gider ve üzerindeki her vuruş bir kişiye bağlanamaz.
- Tıklaması olup açılması olmayan bir ileti kesinlikle okunmuştur: görseller, bağlantıların tıklanmadan kalmasından çok daha sık engellenir. İki sayacı toplamak yerine ayrı ayrı okuyun.
- Bağlantısı olmayan bir gövdede tıklama istemek hiçbir şey kaydetmez: giden baytlar izlenmeyen bir gönderiminkiyle birebir aynıdır ve aksini iddia eden bir satır hiçbir şeyle bağdaştırılamazdı. Yeniden yazılacak gövdesi olmayan bir ileti için de aynısı geçerlidir.
- OpenEmail, kendi kullanıcılarının okuduğu postalardan 1×1 görselleri (kendi gönderdiği piksel dahil) ayıklar ve bir ileti görseller görünür hâldeyken gösterildiğinde açılmayı kendisi kaydeder. Bu vuruş, istemcisi
OpenEmailolan birhumanvuruşudur. Görseller gizliyken hiçbir şey kaydedilmez.
GET /tracking/{id} ve GET /emails/{id}/tracking, hiç izlenmemiş bir ileti için boş bir rapor yerine 404 yanıtı verir. "Hiçbir şey kaydetmedik" ile "kimse açmadı" farklı yanıtlardır ve aynı yanıtı paylaşmamalıdır. Liste uç noktası yalnızca izlenen iletileri tutar; izlenmeyen bir ileti orada sıfırlarla yer almak yerine hiç bulunmaz.
Sormak yerine haber almak
Sayılan bir açılma, abone olan her uç noktada email.opened, sayılan bir tıklama ise email.clicked olayını tetikler ve ileti bu API üzerinden gittiyse ikisi de iletinin kendi olay izine yazılır. Bir tarayıcı ya da gizlilik proxy'si için hiçbiri tetiklenmez. Onları da göndermek, alıcının günlüğünü, sınıflandırıcının sayıların dışında tutmak için var olduğu trafikle doldururdu.
İndirme bağlantısı olarak giden bir dosya da aynı şekilde raporlanır. Sayılan bir indirme email.downloaded olayını tetikler ve aynı ize düşer; aynı sınıflandırıcı tarayıcıları ve bağlantı önizleyicilerini dışarıda tutar, yani sayı insanları gösterir. Yük, dosyayı adlandırır (shareId, fileId, filename, mimeType, sizeBytes, url) ve yanında downloadCount, first ve downloadedAt ile bir tıklamanın taşıdığı istemci ve konum alanları bulunur. recipient her zaman null, attributed her zaman false'tur: bir indirme bağlantısı iletinin her alıcısı için tek bir URL'dir, dolayısıyla bir indirme onlardan birine bağlanamaz.
SDK'dan
const report = await openemail.tracking.get('msg_c5f21cc6bfec…')const cold = await openemail.tracking.list({ days: 30, opened: false })const stats = await openemail.tracking.getStats({ days: 30, offsetMinutes: -new Date().getTimezoneOffset(),})Buradaki her çağrı düz bir okumadır ve istemci her birini kendi başına yeniden dener. get, hiç izlenmemiş bir ileti için isNotFound değeri true olan bir OpenEmailApiError fırlatır; bunu hangi yapıya aktarırsanız aktarın korunmaya değer ayrım budur.