Tracking
ScaiSend tracks four things: opens, clicks, unsubscribes, and (optionally) Google Analytics UTM parameters. Each one has a tenant-level default and a per-request override. This page covers what's tracked, how it's instrumented, and how to turn it on or off.
The four tracking features#
| Feature | What it tracks | How it's instrumented |
|---|---|---|
| Open tracking | Whether an email was opened | 1×1 transparent GIF appended before </body> |
| Click tracking | Which links were clicked | HTML links rewritten to redirect via /t/c/{token} |
| Subscription tracking | Unsubscribes | Footer link + List-Unsubscribe / List-Unsubscribe-Post headers |
| Google Analytics | Campaign attribution | UTM parameters appended to links |
All four are off-the-shelf; you enable them with configuration, not code.
Configuration levels#
Two layers, with request overriding tenant.
| Layer | Scope | How to configure |
|---|---|---|
| Tenant default | Every message from this tenant unless overridden | PUT /api/admin/tenants/{id}/tracking |
| Request override | This single send only | tracking_settings in the /v3/mail/send body |
The request-level settings are deep-merged over the tenant defaults. A request with tracking_settings: {click_tracking: {enable: false}} disables click tracking for that send only; open tracking, subscription tracking, etc. still use the tenant default.
Defaults#
| Feature | Default |
|---|---|
open_tracking_enabled |
true |
click_tracking_enabled |
true |
click_tracking_enable_text |
false (HTML only by default) |
subscription_tracking_enabled |
false |
ganalytics_enabled |
false |
image_embed_mode |
proxy |
Setting tenant defaults#
1 2 3 4 5 6 7 8 9 10 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
PUT is additive — fields you don't send keep their current value.
Per-request override#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
How each feature works#
Open tracking#
If open_tracking.enable is true and the message has an HTML body, ScaiSend injects a 1×1 transparent GIF before the closing </body> tag. When the recipient's email client loads the image, it hits /t/o/{token}.gif on your ScaiSend instance. That request records an open event.
Caveat: images are cached. Repeat opens from the same client may not be detected — most opens log once, then subsequent opens are silent. The image proxy (image_embed_mode: proxy, below) is a backup signal for when the pixel is blocked.
To embed the pixel at a custom spot, use a substitution tag. If substitution_tag is set, ScaiSend replaces occurrences of the tag with the pixel URL instead of appending to </body>. Use this if your template has structure that breaks when things are injected at the end.
Click tracking#
Every <a href="..."> in the HTML body (and, if enable_text is true, every URL in the plain-text body) is rewritten to https://scaisend.scailabs.ai/t/c/{token}?u=<original_url>. When the recipient clicks, ScaiSend records a click event and redirects with 302 to the original URL.
The redirect is fast — measured in tens of milliseconds — but it's not zero. If you have latency-sensitive links (e.g., login magic links with a short TTL), you can bypass click tracking with tracking_settings.click_tracking.enable: false on that send.
Text-mode click tracking is off by default because it tends to produce weird-looking links in plain-text email. Turn it on deliberately.
Subscription tracking#
If subscription_tracking.enable is true, ScaiSend:
- Injects an unsubscribe footer at the bottom of the HTML (and text) body. Default text is minimal; override with
subscription_tracking_html/subscription_tracking_texton the tenant setting. Use<% %>as the placeholder for the unsubscribe URL. Ifsubstitution_tagis set on the request, ScaiSend replaces that tag instead of injecting a footer — useful when you want the unsubscribe link in a specific position in your template. - Adds a
List-Unsubscribeheader containing the unsubscribe URL. Email clients that honor it (Gmail, Apple Mail, Outlook) show a native "Unsubscribe" button in the UI. - Adds a
List-Unsubscribe-Postheader (List-Unsubscribe=One-Click), enabling RFC 8058 one-click unsubscribes. Gmail and Yahoo use this to honor unsubscribes without the recipient visiting your landing page.
When a recipient unsubscribes — clicking the footer link, clicking the native button, or via one-click — ScaiSend:
- Adds the address to your tenant's global unsubscribe list.
- Records an
unsubscribeevent. - Fans out the
unsubscribewebhook. - Future sends to this address skip it (unless
bypass_list_managementorbypass_unsubscribe_managementis set on the send).
Google Analytics#
If ganalytics.enable is true, ScaiSend appends UTM parameters to every tracked link (and, if tenant-configured, un-tracked links too). Specify any of utm_source, utm_medium, utm_campaign, utm_term, utm_content — they become query-string parameters on the final redirect target.
You typically only care about this if your landing page has Google Analytics (or equivalent) configured to report on email campaigns.
Image embedding modes#
ScaiSend offers two modes for images referenced by a template:
| Mode | Where images live | Pros | Cons |
|---|---|---|---|
proxy (default) |
Served via /i/{image_id} |
Smaller email size; image-load tracking as backup open signal | One extra request per image; requires public HTTPS |
cid |
Embedded as Content-ID inline attachments |
Works offline; no outbound request from client | Bigger email; breaks with strict attachment stripping; no image-load tracking |
Configure per tenant:
1 2 3 4 | |
The tracking base URL#
Tracking pixels, click redirects, and unsubscribe URLs all use the TRACKING_BASE_URL environment variable. Set it to a publicly reachable HTTPS URL that terminates at your ScaiSend API service:
1 | |
Pointing this at localhost will break tracking for any recipient outside your network. If you're self-hosting, put a public hostname with valid TLS in front of ScaiSend and set this variable to match.
Public tracking endpoints#
These don't require authentication — they're invoked by recipients' email clients:
| Endpoint | Purpose |
|---|---|
GET /t/o/{token}.gif |
Open tracking pixel. Returns a 1×1 GIF. |
GET /t/c/{token} |
Click redirect. Records event, redirects to original URL with 302. |
GET /t/u/{token} |
Unsubscribe landing page. Shows a confirmation page. |
POST /t/u/{token} |
Unsubscribe confirmation (from the landing page). |
POST /t/u/{token}/one-click |
RFC 8058 one-click unsubscribe. Returns 200 or 400. |
GET /i/{image_id} |
Image proxy (hides backend URL; records image loads). |
Tokens are long, opaque, and per-message. An attacker can't enumerate.
What events are recorded#
| Event | When |
|---|---|
open |
Tracking pixel loaded |
click |
Click redirect hit (one event per clicked link; duplicate clicks deduplicated by URL per recipient) |
unsubscribe |
Unsubscribe landing POST, or one-click POST, or List-Unsubscribe header honored |
group_unsubscribe |
Unsubscribe specific to an ASM group (see Suppressions) |
group_resubscribe |
Recipient resubscribed to an ASM group |
Each event includes recipient_email, timestamp, and event-specific metadata (URL for clicks, user-agent/IP for opens). See Messages and Events for the timeline view and Webhooks for the push-based delivery.
What's next#
- Suppressions — what happens when a recipient unsubscribes.
- Events and Webhooks — getting events delivered to your service.
- Sending Mail —
tracking_settingson a send.