Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

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#

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
curl -X PUT https://scaisend.scailabs.ai/api/admin/tenants/tnt_acme/tracking \
  -H "Authorization: Bearer $SCAISEND_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "open_tracking_enabled": true,
    "click_tracking_enabled": true,
    "subscription_tracking_enabled": true,
    "subscription_tracking_html": "<p>Preferences? <a href=\"<% %>\">Manage here</a>.</p>",
    "image_embed_mode": "proxy"
  }'
python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import os, httpx

httpx.put(
    f"https://scaisend.scailabs.ai/api/admin/tenants/{tenant_id}/tracking",
    headers={"Authorization": f"Bearer {os.environ['SCAISEND_JWT']}"},
    json={
        "open_tracking_enabled": True,
        "click_tracking_enabled": True,
        "subscription_tracking_enabled": True,
        "subscription_tracking_html": '<p>Preferences? <a href="<% %>">Manage here</a>.</p>',
        "image_embed_mode": "proxy",
    },
)
typescript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
await fetch(`https://scaisend.scailabs.ai/api/admin/tenants/${tenantId}/tracking`, {
  method: "PUT",
  headers: {
    "Authorization": `Bearer ${process.env.SCAISEND_JWT}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    open_tracking_enabled: true,
    click_tracking_enabled: true,
    subscription_tracking_enabled: true,
    subscription_tracking_html: '<p>Preferences? <a href="<% %>">Manage here</a>.</p>',
    image_embed_mode: "proxy",
  }),
});

PUT is additive — fields you don't send keep their current value.

Per-request override#

json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
  "personalizations": [{"to": [{"email": "user@example.com"}]}],
  "from": {"email": "hello@mail.example.com"},
  "subject": "Newsletter #42",
  "content": [{"type": "text/html", "value": "<html>..."}],
  "tracking_settings": {
    "click_tracking": {"enable": true, "enable_text": false},
    "open_tracking": {"enable": true, "substitution_tag": "%open_pixel%"},
    "subscription_tracking": {"enable": true, "substitution_tag": "%unsubscribe_url%"},
    "ganalytics": {
      "enable": true,
      "utm_source": "newsletter",
      "utm_medium": "email",
      "utm_campaign": "april_2026"
    }
  }
}

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:

  1. Injects an unsubscribe footer at the bottom of the HTML (and text) body. Default text is minimal; override with subscription_tracking_html / subscription_tracking_text on the tenant setting. Use <% %> as the placeholder for the unsubscribe URL. If substitution_tag is 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.
  2. Adds a List-Unsubscribe header containing the unsubscribe URL. Email clients that honor it (Gmail, Apple Mail, Outlook) show a native "Unsubscribe" button in the UI.
  3. Adds a List-Unsubscribe-Post header (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 unsubscribe event.
  • Fans out the unsubscribe webhook.
  • Future sends to this address skip it (unless bypass_list_management or bypass_unsubscribe_management is 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:

bash
1
2
3
4
curl -X PUT https://scaisend.scailabs.ai/api/admin/tenants/tnt_acme/tracking \
  -H "Authorization: Bearer $SCAISEND_JWT" \
  -H "Content-Type: application/json" \
  -d '{"image_embed_mode": "proxy"}'

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:

bash
1
TRACKING_BASE_URL=https://scaisend.example.com

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#

Updated 2026-05-17 01:33:26 View source (.md) rev 1