Tracking & Analytics
Gordon CRM includes a first-party tracking engine that measures email engagement — opens and clicks — using
your own branded domain. Links and tracking pixels are served from links.yourdomain.com, not a third-party
tracking domain, which improves deliverability and eliminates domain mismatch signals.
Tracking Responsibility Split
Gordon CRM and Resend each own specific email lifecycle events:
| Event | Owned By | Mechanism |
|---|---|---|
| Click | Gordon CRM | Link wrapper + redirect endpoint (/api/c/[id]) |
| Open | Gordon CRM | Pixel injection + pixel endpoint (/api/o/[id]) |
| Delivered | Resend | SMTP 250 OK → webhook email.delivered |
| Bounced | Resend | SMTP rejection → webhook email.bounced |
| Complained | Resend | ISP feedback loop → webhook email.complained |
Resend's built-in click and open tracking is disabled on all domains. Gordon CRM handles these events natively to keep tracking artifacts on your own domain.
Tracking Precedence Matrix
All tracking decisions flow through a single precedence check in the sweepers:
| Category | disable_tracking | Link Wrapping | Open Pixel | Unsubscribe Header | is_subscribed Gate |
|---|---|---|---|---|---|
| Marketing | false (default) | ✅ | ✅ | ✅ Required | ✅ Enforced |
| Marketing | true | ❌ | ❌ | ✅ Required | ✅ Enforced |
| Transactional | N/A | ❌ | ❌ | ❌ Omitted | ❌ Bypassed |
Decision logic:
- If
category = 'transactional'→ tracking is always OFF (category overrides everything) - If
category = 'marketing'anddisable_tracking = true→ tracking is OFF (user override for maximum deliverability) - If
category = 'marketing'anddisable_tracking = false→ tracking is ON (default — links wrapped, pixel injected)
See Campaigns and Broadcasts for details on the category system.
Link Wrapping
When tracking is enabled for an email, Gordon CRM scans the HTML body for <a> tags and wraps eligible
links through the tracking infrastructure.
How It Works
For each email:
- Parse — Scan all
<a href="...">tags in the HTML body - Filter — Skip links that should not be wrapped (see below)
- Insert — Create a
tracked_linksrow in the database for each unique URL - Replace — Swap the
hrefwithhttps://links.yourdomain.com/api/c/{tracked_link_id} - Inject pixel — Append a 1x1 transparent open tracking pixel before
</body>
Skipped Links
The link wrapper intentionally skips certain link types:
| Link Type | Example | Reason |
|---|---|---|
| Unsubscribe URLs | /unsubscribe/... or /api/unsubscribe | Must remain unwrapped for compliance |
mailto: links | mailto:hello@example.com | Not HTTP — cannot redirect |
tel: links | tel:+15551234567 | Not HTTP — cannot redirect |
| Anchor links | #section-2 | In-page navigation, not a URL |
| Naked URL links | <a href="https://calendly.com/user">https://calendly.com/user</a> | See below |
Naked URL Detection
A "naked URL link" is an <a> tag where the visible text looks like a URL — for example,
<a href="https://calendly.com/user">calendly.com/user</a>. Wrapping these would change the href to
links.brand.com/api/c/... while the visible text still shows the original URL. This text-to-href mismatch
is a signal that Gmail and Outlook use to flag emails as phishing.
The link wrapper detects these cases and leaves them untouched, even if the same URL appears elsewhere in
the email with descriptive text (e.g., <a href="https://calendly.com/user">Book a call</a> would be
wrapped).
Graceful Failure
If the database insertion for tracked links fails, the email is sent with only the open pixel injected — no link wrapping. The email is never blocked due to a tracking failure.
Open Tracking Pixel
When tracking is enabled, a 1x1 transparent GIF is injected before </body>:
<img src="https://links.yourdomain.com/api/o/{email_send_id}"
width="1" height="1" style="display:none" alt="" />When the recipient's email client loads this image:
- The pixel endpoint (
/api/o/[id]) looks up theemail_sendsrow - Sets
opened_atto the current timestamp (if not already set) - Advances the email status to
opened(if precedence allows) - Returns the transparent GIF regardless of whether tracking succeeded — the endpoint never reveals tracking state to the email client
Note: Open tracking is inherently unreliable. Many email clients block image loading by default, and privacy proxies (like Apple Mail Privacy Protection) may pre-load images without the recipient actually opening the email. Open data should be treated as a lower-confidence signal than click data.
Click Redirect Server
The click redirect endpoint (/api/c/[id]) handles tracked link clicks:
- Lookup — Finds the
tracked_linksrow by ID - Record — Increments
click_count, setsfirst_clicked_at(if first click), updateslast_clicked_at - Advance — Updates
email_sends.clicked_atand advances the email status toclicked(if precedence allows) - Redirect — Returns a
302 Redirectto the original URL
The redirect is sub-100ms. No authentication is required — the UUID in the URL is unguessable and serves as the access token.
Status Precedence
Email status can only advance forward through the lifecycle:
pending → sent → delivered → opened → clickedNegative events (bounced, complained) override any status. The same precedence model is used by both
Gordon CRM's tracking endpoints and the Resend webhook handler, ensuring consistent status regardless of which
system records the event first.
| Status | Precedence |
|---|---|
pending | -1 |
sent | 0 |
delivered | 1 |
opened | 2 |
clicked | 3 |
bounced | 99 |
complained | 99 |
The tracked_links Table
Each tracked link represents a unique (email_send, url) pair. An email containing 3 distinct links produces
3 tracked_links rows.
| Column | Type | Description |
|---|---|---|
id | uuid | Primary key (used in the redirect URL) |
email_send_id | uuid | FK → email_sends |
workspace_id | uuid | Tenant isolation |
contact_id | uuid | FK → contacts |
original_url | text | The original link destination |
click_count | integer | Number of times this link was clicked (default 0) |
first_clicked_at | timestamptz | Timestamp of the first click |
last_clicked_at | timestamptz | Timestamp of the most recent click |
created_at | timestamptz | When the tracking row was created |
The table has RLS enabled — workspace members can read tracked links for their own workspace.
Tracking Domain Setup
Each workspace's tracking domain is a subdomain of their sending domain:
Sending domain: mail.yourdomain.com
Tracking domain: links.yourdomain.comThe tracking subdomain is automatically provisioned when a workspace adds their sending domain. The system:
- Derives
links.yourdomain.comfrom the root domain - Registers it with Vercel as a custom domain via the Vercel API
- Stores the
tracking_subdomainon thesender_identitiesrow
DNS Configuration
The tracking subdomain requires a CNAME DNS record. The CNAME target is dynamically fetched from the Vercel API — it is a project-specific value unique to each domain, not a static address.
When you view your DNS records in Settings → Sender Identities → DNS Records, the system queries the Vercel API for the recommended CNAME target and displays it alongside your sending domain records.
If the Vercel API is temporarily unavailable, the system falls back to the generic
cname.vercel-dns.comas the CNAME target. Vercel handles SSL certificate provisioning automatically once the CNAME record propagates.
Fallback Tracking Domain
Workspaces that use the fallback sender (no custom
domain) have no tracking_subdomain in the database. For these workspaces, tracking uses the
FALLBACK_TRACKING_DOMAIN environment variable (e.g., track.gordoncrm.com).
The tracking precedence matrix applies identically to fallback senders — a fallback marketing email with tracking enabled will wrap links through the fallback tracking domain, while transactional fallback emails will have no tracking artifacts.
If FALLBACK_TRACKING_DOMAIN is not configured, tracking is silently disabled for fallback senders
(emails still send normally without tracking).