Tracking
mailery tracks opens, clicks, deliveries, bounces, and complaints. All five converge on mailer_sends documents.
Open tracking
A 1×1 transparent PNG is appended to every send with trackOpens: true:
<img src="https://yourdomain.com/m/open/<sendId>.png" width="1" height="1" alt="" style="display:block" />When the recipient's mail client fetches it, mailery records openedAt + increments openCount on the Send row.
Why opens are noisy
Apple Mail Privacy Protection (iOS 15+, 2021) prefetches all images on inbox arrival — every email to an Apple Mail recipient shows as "opened" within seconds of delivery. Corporate firewalls + spam filters also prefetch. Treat opens as a deliverability signal, not engagement.
For branching flows, use real product events (Used Feature X) or aggregated predicates (openedAtLeastN: { count: 3, withinDays: 30 }).
Click tracking
At send time, every <a href="X"> becomes <a href="https://yourdomain.com/m/click/<sendId>/<linkId>"> where linkId is a short hash of the URL within the send. The original-URL map is stored on mailer_sends.links for lookup.
When the recipient clicks, mailery records firstClickAt + appends to clickedLinks + 302-redirects to the original URL.
Links NOT rewritten
mailto:andtel:links- Anchors (
#section) - The send's own unsubscribe URL (passed to
applyTrackingaspreserveUrls) - Any
<a data-mailer-notrack="true" href="...">link
Provider webhooks
Your provider POSTs delivery / open / click / bounce events to:
POST /m/webhooks/:provider(e.g. /m/webhooks/sendgrid). mailery:
- Verifies the provider's signature (HMAC for SendGrid).
- Deduplicates against
mailer_webhook_eventsby(provider, providerEventId). - Returns 200 fast — INVARIANT 5.
- Asynchronously applies each event to the matching
mailer_sendsrow.
Cascade effects
delivered→status: 'delivered',deliveredAt: timestampopen→openedAt: first-seen,openCount: ++click→firstClickAt: first-seen,clickCount: ++bounce(hard) →status: 'bounced', suppress contact (scopeall), subscription →bouncedbounce(soft) →status: 'bounced', no suppression (provider retries internally)complaint→ suppress contact (scopeall), subscription →complainedunsubscribe→ suppress contact (scopemarketing), subscription →unsubscribed
SendGrid setup
In SendGrid dashboard:
- Authenticate sender domain — SPF, DKIM, DMARC. Single biggest deliverability lever.
- Configure event webhook — POST to
https://yourdomain.com/m/webhooks/sendgrid. - Enable event types — delivered, open, click, bounce, dropped, spamreport, unsubscribe.
- Generate Signed Event Webhook public key — store as
SENDGRID_WEBHOOK_KEYenv var.
mailery disables SendGrid's own click + open tracking in favor of doing it itself — letting both rewrite produces double-rewriting and breaks counts.
Per-template control
{
trackOpens: false, // disable for transactional receipts
trackClicks: false,
}Or globally:
await Mailer.init({
// ...
trackOpens: false,
trackClicks: false,
})IPs
Mailery does not store recipient IPs by default. Only User-Agent and timestamp. To opt in (for compliance investigations):
await Mailer.init({
// ...
storeTrackingIp: true,
})