Sledování otevření a kliknutí
GET /tracking: zda byla zpráva přečtena a na které odkazy se kliklo.
Spustí kterékoli z 6 volání na této stránce proti vašemu pracovnímu prostoru, s vaším vlastním klíčem.
Co se zaznamenává
Dva nezávislé přepínače, oba zapnuté, pokud nebyly vypnuté pro adresu, ze které se zpráva odesílá, nebo pro Všechny adresy. opens přidává obrázek 1×1; clicks přepisuje odkazy v nové části těla. Citovaná historie pod odpovědí je zpráva někoho jiného a zůstává beze změny. Odeslání uvede tracking: { opens, clicks } a rozhodne tak pro jednu zprávu (v obou směrech, takže false je způsob, jak program odmítne to, co má adresa nastaveno), a pole, které vynecháte, spadne zpět na nastavení adresy, ze které se odesílá, pak na Všechny adresy – ne na výchozí hodnotu, kterou by si toto API zvolilo za workspace.
{ "from": "Acme Billing <[email protected]>", "to": ["[email protected]"], "subject": "Your September invoice", "html": "<p>Invoice attached.</p>", "tracking": { "opens": true, "clicks": true } }Přepíše se nejvýše 100 cílů na zprávu, každý jednou. Tatáž URL odkazovaná z obrázku v hlavičce, z tlačítka a z patičky je jeden řádek, protože je to jedna otázka položená třikrát. Nad limitem zůstanou zbývající odkazy přesně tak, jak byly napsány: nesledovaný odkaz pořád funguje a zpráva, která tiše přijde o posledních dvě stě odkazů, je mnohem horší selhání než neúplný report.
Přepsané odkazy a pixel míří ve výchozím stavu na hostitele OpenEmail API. Pokud má odesílající doména vlastní trackovací doménu, jejíž tracking.status je active, používá nová pošta z té domény místo toho https://<tracking host>/t/...; nastavíte ji přes PATCH /domains/{id}.
Všechno tohle vyžaduje emails:read a žádný scope pro tracking neexistuje. Ten scope už znamená „číst odeslané zprávy a jejich stav doručení“ a to, zda někdo zprávu otevřel, je ten nejdoslovnější možný stav doručení.
Endpointy
| Volání | Vrací |
|---|---|
| `GET /tracking` | Sledované zprávy, od nejnovější. opened, clicked, days (1–365, výchozí 30), limit (max 200). |
| `GET /tracking/stats` | Míry za dané okno. days (výchozí 30) a offsetMinutes, aby se dny lámaly tam, kde končí den čtenáře. |
| `GET /tracking/{id}` | Jeden report. Přijímá trackovací id tmsg_ nebo id msg_, které vrátilo odeslání. |
| `GET /tracking/{id}/opens` | Jednotlivá načtení. includeMachine, limit (max 200). |
| `GET /tracking/{id}/clicks` | Totéž, s linkId a url na každém řádku. |
| `GET /emails/{id}/tracking` | Tentýž report, podle id odeslání, které už držíte. |
Booleovské hodnoty se v query stringu píšou slovy: true, false, 1 nebo 0, cokoli jiného je odmítnuto. Boolean("false") je true, takže zkonvertované ?opened=false by vrátilo přesný opak toho, na co jste se ptali.
Je to samostatný zdroj, a ne pár polí na /emails, kvůli pokrytí: ten výpis obsahuje záznamy o odeslání a editor zprávy, nástroje MCP i asistent odesílají, aniž by nějaký zapsali. Report postavený na něm by byl reportem o vašem provozu na API, a ne o schránce.
Report
{ "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 a clicks říkají, co bylo na zprávu UPLATNĚNO; opened a clicked říkají, co se stalo. openCount počítá čtení a openCountRaw počítá načtení. Ten rozdíl, tady čtyři, jsou skenery a privacy proxy; drží se proto, aby mezera mezi logem a součtem byla prozkoumatelná, a ne nevysvětlená. attributable je pole, které si přečtěte dřív, než někoho jmenujete: false znamená, že čtení dopadlo na kopii, která šla celému seznamu, a každá věta o konkrétním příjemci je od té chvíle dohad.
source pojmenovává plochu, která zprávu odeslala: api pro odeslání přes toto API, composer pro všechno, co odeslala samotná aplikace. sendId je u druhého druhu null, a právě proto trackovací id existuje.
Řádek s null email a attributed: false je místo, kam dopadne čtení, které nešlo připnout ke konkrétní osobě, a report ho ukáže jen tehdy, když nějaké čtení skutečně dopadlo. Zpráva s jediným příjemcem žádný takový nemá, protože jedno tělo a jedna adresa jsou totéž tvrzení. Zpráva s více příjemci ho má za sebou od chvíle, kdy odešla, protože přenos není hotový dřív než při odeslání, a zůstává mimo report, dokud na něj něco nedorazí: trvalé „někdo: neotevřeno“ vedle jmenovaných příjemců je řádek, který se dá jen špatně pochopit. Tam, kde PŘÍTOMEN je, jsou jmenované řádky ty, které stojí na nule, a attributable je false. Čtení je skutečné, čtenář je jedna z osob na zprávě a „někdo na této zprávě“ je jediné vykreslení, které data unesou. Nikdy jméno nedoplňujte ze seznamu příjemců.
Míry za časové okno
{ "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 }] }Míry jsou procenta ze SLEDOVANÝCH zpráv, ne ze vší odeslané pošty: workspace, který sleduje jednu zprávu z deseti, má míru otevření pro těch deset, a dělení vším, co kdy odeslal, by klesalo pokaždé, když někdo pošle nesledovanou odpověď. Zpráva otevřená pětkrát je JEDNA otevřená zpráva. Míry počítají zprávy a součty počítají zásahy; zaměňování obojího je způsob, jak se publikují míry otevření nad 100 %.
byDay je řídké: den, kdy se nic nesledovalo, chybí, místo aby byl nula, takže před vykreslením grafu mezery doplňte. Dny se řadí do košů offsetMinutes východně od UTC (−840 až 840), aby se lámaly tam, kde končí den čtenáře. medianTimeToOpenSeconds je medián, ne průměr, protože jediná zpráva otevřená se třítýdenním zpožděním odtáhne průměr někam, kde ve skutečnosti žádná zpráva není.
Jednotlivé zásahy
{ "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 je human, proxy nebo machine a counted říká, zda se to promítlo do čísel. Strojové zásahy jsou vyloučené, dokud nepošlete includeMachine=true, což je poctivé výchozí nastavení: zaznamenávají se proto, že jejich vypuštění by nechalo nevysvětlitelnou mezeru, ne proto, že by šlo o zájem čtenáře.
Lokalita je hrubá, protože nic jiného není. K žádnému zásahu se neukládá IP adresa. Země, region a město jsou to, co edge už věděl, a jediný další uchovávaný identifikátor je hash, jehož sůl se denně mění, takže během jednoho dne dokáže odlišit dvě načtení a další den je netečný.
Co čísla říct nemohou
- Apple Mail Privacy Protection načítá při doručení každý obrázek v každé zprávě bez ohledu na to, jestli se na ni někdo podívá. Klasifikuje se podle User-Agent a podle sítě a zaznamenává se jako
machine, stejně jako cokoli, co dorazí do deseti sekund od odeslání, protože nic, co dělá člověk, není tak rychlé. - Obrazová proxy Gmailu je
proxy, nemachine: někdo zprávu zobrazil, takže otevření je skutečné, zatímco zařízení, klient a lokalita zjistit nejdou. Proxy navíc kešuje, takže druhé čtení se k nám nemusí vůbec dostat. Počty přes Gmail jsou spodní hranice, nikdy ne celek. - Dvě načtení téže kopie do třiceti sekund jsou jedno čtení. Překreslení náhledového panelu nebo zpráva odrolovaná zpět do zobrazení obrázek načte znovu; opravdová druhá návštěva o hodinu později se počítá dál.
- Pojmenování příjemce vyžaduje zprávu dost malou na to, aby se dala sestavit zvlášť pro každého: odhadovaná velikost krát počet příjemců se musí vejít pod 8 MB. Nad tím jde jedno tělo všem a každý zásah na něm je nepřiřazený.
- Zpráva s kliknutími a bez otevření byla zcela jistě přečtena: obrázky se blokují mnohem častěji, než se nekliká na odkazy. Čtěte oba čítače zvlášť, místo abyste je sčítali.
- Vyžádat sledování kliknutí u těla bez odkazů nezaznamená vůbec nic: bajty, které odešly, jsou totožné s nesledovaným odesláním a řádek tvrdící opak by se nedal s ničím srovnat. Totéž platí pro zprávu, která nemá tělo k přepsání.
- OpenEmail vystřihává obrázky 1×1 z pošty, kterou čtou jeho vlastní uživatelé, včetně pixelu, který sám posílá, a otevření zaznamená sám, když se zpráva zobrazí se zapnutými obrázky. Takový zásah je
humans klientemOpenEmail. Se skrytými obrázky se nezaznamená nic.
GET /tracking/{id} a GET /emails/{id}/tracking odpovídají 404 na zprávu, která nikdy nebyla sledovaná, místo prázdného reportu. Věty „nic jsme nezaznamenali“ a „nikdo ji neotevřel“ jsou různé odpovědi a nesmí sdílet jednu odpověď. Výpisový endpoint obsahuje jen sledované zprávy, takže nesledovaná v něm prostě chybí, místo aby v něm byla s nulami.
Nechte si to oznámit, místo abyste se ptali
Započítané otevření vystřelí email.opened a započítané kliknutí vystřelí email.clicked na každý přihlášený endpoint a obojí se zapíše do vlastní stopy událostí zprávy, pokud prošla tímto API. Ani jedno nevystřelí pro skener nebo privacy proxy. Posílat je by zaplnilo log příjemce přesně tím provozem, který má klasifikátor z čísel držet venku.
Soubor, který odešel jako odkaz ke stažení, se reportuje stejně. Započítané stažení vystřelí email.downloaded a přistane na téže stopě a tentýž klasifikátor z něj drží skenery a náhledovače odkazů, takže počet jsou lidé. Payload pojmenovává soubor (shareId, fileId, filename, mimeType, sizeBytes, url) s downloadCount, first a downloadedAt vedle polí o klientovi a lokalitě, která nese kliknutí. recipient je vždy null a attributed vždy false: odkaz ke stažení je jedna URL pro všechny příjemce zprávy, takže stažení nelze připnout na jednoho z nich.
Z SDK
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(),})Každé zdejší volání je prosté čtení a klient každé z nich opakuje sám. get vyhodí OpenEmailApiError, jehož isNotFound je true pro zprávu, která nikdy nebyla sledovaná – a to je rozdíl, který se vyplatí zachovat v čemkoli, do čeho to pouštíte.