# Open tracking that tells people from machines

Switch open and click tracking on per domain or per send. Every pixel load and link hit reaches your webhook, and the ones made by Apple's privacy proxy, a security scanner or a script arrive with bot: true and the reason.

[Get started](/signup)
[Documentation](https://docs.aiinbx.com/webhooks/events/email-opened)

Example: one invoice email delivered at 09:14:00 collects seven hits. Apple Mail Privacy Protection fetches the pixel two seconds after delivery (privacy\_proxy); Outlook's link check, a desktop Chrome three seconds after delivery, python-requests and a Chrome 96 visit the links in the first two minutes (known\_bot, too\_fast, script, stale\_browser). Two hours later the recipient reads it in Outlook on Windows and clicks the invoice link in Safari on an iPhone, and only those two hits are marked as a person: 1 read, 1 click, 5 automatic. The first hit reaches your webhook as email.opened with user\_agent Mozilla/5.0, bot: true and bot\_reason: privacy\_proxy.

Privacy proxies

## Apple's prefetch, named as one

With Mail Privacy Protection on, Apple fetches every image the moment a message arrives, read or not. That fetch comes marked privacy\_proxy and stays out of the console's open rate. A later read on the same phone usually sends nothing at all.

[Privacy proxies](https://docs.aiinbx.com/webhooks/events/email-opened#bot-detection)

Scanners

## Every link clicked, and nobody reading

Company mail gateways follow links before the inbox shows the message. Hits inside five seconds of delivery, from HTTP libraries, from scanners that name themselves or from long-retired browsers come flagged; the recipient's own click stays bot: false.

[Scanners](https://docs.aiinbx.com/webhooks/events/email-clicked)

Per send

## Tracked where you want it, left alone where you don't

Opens and clicks are a setting on the sending domain, off until you turn them on. One email can override either half, so a password reset keeps its link exactly as you wrote it on a domain that measures its reports.

[Per send](https://docs.aiinbx.com/guides/sending#tracking)

Replies

## The signal no scanner sends

An open is an image fetch and a click can be a gateway, but an answer is a person. Replies land on the same thread as email.received, with out-of-office replies and bounces labelled, so nothing automatic passes for interest.

[Replies](/features/threading)

bot\_reason

## What gives a machine away

Each open and click is judged when it is recorded, from its user-agent and its timing against delivery. The verdict is a flag on the hit, never a reason to drop it, so your own rule can read the same rows differently. More reasons may be added; treat one you do not know as a machine.

| Reason | What it is | How it is told |
| --- | --- | --- |
| `privacy_proxy` | Apple Mail Privacy Protection loading images as the message lands | A user-agent of Mozilla/5.0 and nothing else |
| `script` | An HTTP library fetching the pixel or the link | python-requests, curl, Go-http-client, axios, Java and the like |
| `known_bot` | A security scanner, link expander or sync daemon | It names itself, or Outlook checks a link before opening it |
| `stale_browser` | A scanner dressed as a browser | Desktop Chrome about eighteen months out of date, or Internet Explorer or the retired Edge outside Outlook |
| `too_fast` | Anything that arrives before a person could | Within five seconds of delivery |

### What open tracking cannot tell you

The detection is conservative on purpose: when in doubt a hit counts as a person, and some reads never arrive at all. Opens are worth reading across a campaign, never as proof one person read one email.

-   An Apple Mail reader with Mail Privacy Protection on shows the prefetch and usually nothing after it, so their real read is not seen.
-   A reader who blocks images loads no pixel and records no open, though their clicks still count. One who reads the plain-text part records neither: only the HTML body is tracked.
-   Gmail and Yahoo load images through their own proxies, usually when the message is opened. Those count as a person unless they land within five seconds of delivery; the device behind them is hidden.
-   A scanner that looks like a current browser and waits more than five seconds counts as a person. So does a user-agent nobody has seen, or none at all.
-   A hit does not say which recipient it came from. On a send to several people, it belongs to the email.
-   Mail sent through a connected Gmail or Outlook mailbox leaves as written and is never tracked.

Set up

## Tracking, in three calls

Step 1, domains.update: both start off.

```
await aiinbx.domains.update("dom_...", {
  track_opens: true,
  track_clicks: true,
})
```

Step 2, emails.send: one email, its own say.

```
await aiinbx.emails.send({
  from: "security@mail.yourapp.com",
  to: "tom@northwind.io",
  subject: "Reset your password",
  html: resetHtml,
  tracking: { opens: false, clicks: false },
})
```

Step 3, your handler: count people, act on clicks.

```
const event = await verifyWebhookRequest(request, secret)

if (event.type === "email.clicked" && !event.data.bot) {
  await attribution.record(event.data.email_id, event.data.url)
}
```

## Questions

### Are email open rates still accurate after Apple Mail Privacy Protection?

Not as a raw count. Apple loads the images of every message as it arrives for anyone with the feature on, so each of those emails looks opened whether or not it was read. AI Inbx marks those fetches bot\_reason privacy\_proxy and leaves them out of the open rate the console shows, which makes the number lower and closer to the truth, but still a floor: an Apple Mail reader's real read usually sends nothing.

### How can I tell an Apple Mail Privacy Protection open from a real one?

The prefetch comes from Apple's proxy with a user-agent of just Mozilla/5.0. Every email.opened event carries bot and bot\_reason, so filter on bot: false, or on bot\_reason privacy\_proxy to count the prefetches on their own.

### Why were all the links in my email clicked seconds after it was sent?

A security gateway checked them. Microsoft, Barracuda and other filters follow links before the recipient sees the message. Those clicks still arrive as email.clicked, flagged bot: true as known\_bot, script, stale\_browser or too\_fast, so attribute only the ones where bot is false.

### Do opens through Gmail's image proxy count?

Usually. Gmail and Yahoo fetch images through their own proxies, mostly when the reader opens the message, so those opens count as a person. A fetch within five seconds of delivery is flagged too\_fast instead. What the proxy hides is the device and location behind it.

### Why did someone click my email without opening it?

An open is only recorded when the tracking pixel loads. A reader who blocks remote images can follow a link without the pixel ever loading, and a security scanner may follow links without loading it either, so check bot on the click. That is one reason clicks are the better signal of the two, once the machines are set aside.

### Should I track opens and clicks on password resets and receipts?

Often not. Click tracking rewrites links through a tracking host, which is what the recipient sees when they hover a link. Pass tracking: { opens: false, clicks: false } on those sends and they go out exactly as written, while the rest of the domain stays tracked.

### How do I track email opens and clicks with an API?

Turn on track\_opens and track\_clicks on the sending domain, or pass tracking on a single send. Opens add a 1×1 pixel to the HTML body and clicks rewrite its links. Each hit is posted to your webhook as email.opened or email.clicked, and GET /emails/{email\_id} lists them under engagements.

Keep reading

## More on building with email

-   [Send email APISend email from your domain or your users' own Gmail and Outlook with one API call. Opt-outs and pacing are checked first, and replies land on the thread.ThreadingMailboxesPacingSuppressions](/solutions/send-email-api)
-   [Auto-reply detectionTell an out-of-office, a ticket acknowledgement or a bounce from a real reply. Every inbound email arrives with a category, decided from headers and content.MailboxesSuppressions](/solutions/auto-reply-detection)
-   [Scheduled email APISchedule an email up to 30 days ahead, then move or cancel it by id until its time comes. It is composed then, so suppressions and threads stay current.ThreadingSending hoursSuppressions](/solutions/scheduled-email-api)

-   [ThreadingOne conversation, however it comes back.](/features/threading)

[All solutions](/solutions)

## Give your product an inbox. The hard parts are already handled.

[Get started](/signup)
