SwiftTracker Guide

Email tracking framework - setup, destinations, and integration guide.

Overview

SwiftTracker is the receiving end of email tracking. Your SMTP provider sends the email; SwiftTracker handles what happens after the recipient interacts with it.

SwiftTracker does not send emails. You keep your existing SMTP provider. This system only listens.

URL structure changed. Tracking links now use short path segments like /g/slug/email instead of long query strings. This is far less likely to be red-flagged by spam filters.

Installation

1. Install Wrangler

npm install
npx wrangler --version

2. Log in to Cloudflare

npx wrangler login

3. Create the D1 database

npx wrangler d1 create swift_tracker_db

Copy the database_id from the output into wrangler.toml.

4. Initialize the schema

npx wrangler d1 execute swift_tracker_db --file=schema.sql --remote

5. Set secrets

echo -n "YourPassword" | shasum -a 256
npx wrangler pages secret put ADMIN_PASSWORD_HASH
npx wrangler pages secret put TELEGRAM_BOT_TOKEN
npx wrangler pages secret put TELEGRAM_CHAT_ID

Secrets bind at deploy time. After changing any secret, redeploy:

npx wrangler pages deploy public --project-name swift-tracker-for-campaign

6. Deploy

npx wrangler pages deploy public --project-name swift-tracker-for-campaign

7. Attach custom subdomains

In the Cloudflare dashboard, add tracking subdomains to your Pages project:

Never use the raw *.pages.dev domain for tracking links. It's on spam blocklists due to phishing abuse. Always use your custom subdomain.

8. Verify _routes.json

This file tells Cloudflare which paths invoke functions. It must contain all four route prefixes:

{
  "version": 1,
  "include": ["/api/*", "/g/*", "/o/*", "/u/*"],
  "exclude": []
}

If /g/, /o/, or /u/ are missing, your tracking links will return the homepage instead of redirecting.

9. Seed the domain pool

INSERT OR IGNORE INTO tracking_domains (domain, label, priority, active, status)
VALUES ('https://track1.yourdomain.com', 'Primary', 10, 1, 'healthy');

INSERT OR IGNORE INTO tracking_domains (domain, label, priority, active, status)
VALUES ('https://track2.yourdomain.com', 'Backup', 20, 0, 'healthy');

10. Log in

Visit https://yourdomain.com/login.html and enter the password whose hash you set in step 5.

Destinations

A destination is a slug you register once that maps to a real URL. Every email link uses the slug - if you ever need to change where the link points, you change the URL in the Destinations page and every email using that slug redirects to the new URL instantly.

Why this matters

Old pattern (spam-filter bait):

https://track1.yourdomain.com/api/track/click?email=user@example.com&url=https://yoursite.com/offer

New pattern (clean, trusted):

https://track1.yourdomain.com/g/offer2025/user@example.com

No query parameters. No visible destination. No redirector signature. Filters see a normal content URL.

Creating a destination

  1. Open Destinations from the top navigation
  2. Click + New Destination
  3. Fill in:
    • Slug: offer2025 (lowercase, alphanumeric, dash, underscore)
    • URL: https://yoursite.com/offer
    • Label: a human-readable name
  4. Click Save
  5. Click Copy next to the new row - you now have the ready-to-paste link template

Changing a destination URL later

Click Edit on any row, update the URL field, click Save. All existing emails that used that slug now redirect to the new URL. No re-sending needed.

Deactivating vs deleting

ActionEffect
Set to InactiveLinks using this slug return 404. Slug remains for record-keeping.
DeleteSlug is removed. Any future email using it will fail. Use for retired campaigns.

Email Integration

Click tracking

<a href="https://track1.yourdomain.com/g/YOUR-SLUG/[~EMail~]">
  View your offer
</a>

Replace YOUR-SLUG with the slug you created in Destinations.

Open pixel

<img src="https://track1.yourdomain.com/o/YOUR-SLUG/[~EMail~]"
     width="1" height="1" alt=""
     style="display:block;width:1px;height:1px;border:0;" />

Unsubscribe

<a href="https://track1.yourdomain.com/u/YOUR-SLUG/[~EMail~]">
  Unsubscribe
</a>

Gmail one-click header (optional but recommended)

List-Unsubscribe: <https://track1.yourdomain.com/u/YOUR-SLUG/[~EMail~]>
List-Unsubscribe-Post: List-Unsubscribe=One-Click

The [~EMail~] placeholder is substituted by your sender at send time. Do not URL-encode it. SwiftTracker decodes it on receipt.

Domain Rotation

SwiftTracker rotates tracking domains automatically when one gets reported as spam. This is D1-driven, so rotation is instant - no redeploy needed.

How it works

  1. All tracking subdomains point to the same Pages project
  2. The active domain lives in tracking_domains (one row has active = 1)
  3. When spam is detected, the flag flips to the next healthy domain
  4. New emails automatically use the new domain - templates don't change

Getting the active domain (for your sender)

GET https://yourdomain.com/api/domain/current
Response: { "domain": "https://track2.yourdomain.com", "domainId": 2 }

Rotation triggers

TriggerHow
Spam reportPOST to /api/spam/report - exceeds threshold
Health checkActive domain reports down or spam
ManualClick Force Rotate on the dashboard

API Reference

Tracking routes (public)

MethodPathPurpose
GET/g/SLUG/EMAILLog click, 302 to destination
GET/o/SLUG/EMAILReturn 1x1 GIF, log open
GET/u/SLUG/EMAILShow unsubscribe page
POST/u/SLUG/EMAILOne-click unsubscribe (RFC 8058)

Admin routes (session cookie)

MethodPathPurpose
POST/api/auth/loginLog in
POST/api/auth/logoutLog out
GET/api/auth/sessionCurrent session info
GET/api/dashboard/statsAggregate metrics
GET/api/dashboard/eventsPaginated events
GET/api/dashboard/geopointsCountry data for the globe
GET/api/destinations/listAll destinations
POST/api/destinations/saveCreate or update
POST/api/destinations/deleteRemove destination
GET/api/domain/listAll tracking domains
POST/api/domain/rotateForce rotation
POST/api/health/checkPing all domains
GET/api/settings/getRead settings
POST/api/settings/saveWrite settings

Troubleshooting

Click route returns the homepage instead of redirecting

Most common cause: _routes.json doesn't include /g/*.

cat public/_routes.json

Should include all four: /api/*, /g/*, /o/*, /u/*. If missing, add them and redeploy.

Click route returns 404 "Link not found"

Function ran, but the slug doesn't exist in the destinations table. Create it via the Destinations page.

Click route returns 400 "Invalid link"

The URL structure is wrong. Expected format: /g/slug/email@example.com. Check that the email portion has a valid format and the slug isn't empty.

"Unknown mode" error when saving a destination

Older versions of save.js accepted create and update but not edit. Update functions/api/destinations/save.js to include:

if (mode === 'edit') mode = 'update';

Login fails with "Invalid password"

Dashboard shows HTML instead of JSON

Console shows Unexpected token '<'. The function isn't loading, so Pages serves index.html as fallback.

Instant logout after login

Custom domain not showing in preview

The Destinations editor preview uses location.origin. Log into the dashboard via your custom domain, not pages.dev.

FAQ

Do I need my own SMTP?
No. SwiftTracker only receives tracking events.

Why not use a pages.dev domain?
Shared domains are on spam blocklists. Use a custom subdomain of a domain you own.

How many tracking subdomains do I need?
At least 2, ideally 3-5. More domains = more rotation runway.

What happens when a domain is flagged as spam?
It's marked spam, skipped during rotation, and returns to healthy after the recovery window (default 24h).

Can I change a destination URL after sending?
Yes. Update it in Destinations and all emails using that slug redirect to the new URL.

Does it work with Gmail bulk requirements?
Yes - RFC 8058 one-click is implemented at /u/SLUG/EMAIL.

How do I back up data?
npx wrangler d1 export swift_tracker_db --output backup.sql --remote