Email Marketing
Tracking & Analytics

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:

EventOwned ByMechanism
ClickGordon CRMLink wrapper + redirect endpoint (/api/c/[id])
OpenGordon CRMPixel injection + pixel endpoint (/api/o/[id])
DeliveredResendSMTP 250 OK → webhook email.delivered
BouncedResendSMTP rejection → webhook email.bounced
ComplainedResendISP 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:

Categorydisable_trackingLink WrappingOpen PixelUnsubscribe Headeris_subscribed Gate
Marketingfalse (default)✅ Required✅ Enforced
Marketingtrue✅ Required✅ Enforced
TransactionalN/A❌ Omitted❌ Bypassed

Decision logic:

  1. If category = 'transactional' → tracking is always OFF (category overrides everything)
  2. If category = 'marketing' and disable_tracking = true → tracking is OFF (user override for maximum deliverability)
  3. If category = 'marketing' and disable_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:

  1. Parse — Scan all <a href="..."> tags in the HTML body
  2. Filter — Skip links that should not be wrapped (see below)
  3. Insert — Create a tracked_links row in the database for each unique URL
  4. Replace — Swap the href with https://links.yourdomain.com/api/c/{tracked_link_id}
  5. Inject pixel — Append a 1x1 transparent open tracking pixel before </body>

Skipped Links

The link wrapper intentionally skips certain link types:

Link TypeExampleReason
Unsubscribe URLs/unsubscribe/... or /api/unsubscribeMust remain unwrapped for compliance
mailto: linksmailto:hello@example.comNot HTTP — cannot redirect
tel: linkstel:+15551234567Not HTTP — cannot redirect
Anchor links#section-2In-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:

  1. The pixel endpoint (/api/o/[id]) looks up the email_sends row
  2. Sets opened_at to the current timestamp (if not already set)
  3. Advances the email status to opened (if precedence allows)
  4. 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:

  1. Lookup — Finds the tracked_links row by ID
  2. Record — Increments click_count, sets first_clicked_at (if first click), updates last_clicked_at
  3. Advance — Updates email_sends.clicked_at and advances the email status to clicked (if precedence allows)
  4. Redirect — Returns a 302 Redirect to 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 → clicked

Negative 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.

StatusPrecedence
pending-1
sent0
delivered1
opened2
clicked3
bounced99
complained99

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.

ColumnTypeDescription
iduuidPrimary key (used in the redirect URL)
email_send_iduuidFK → email_sends
workspace_iduuidTenant isolation
contact_iduuidFK → contacts
original_urltextThe original link destination
click_countintegerNumber of times this link was clicked (default 0)
first_clicked_attimestamptzTimestamp of the first click
last_clicked_attimestamptzTimestamp of the most recent click
created_attimestamptzWhen 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.com

The tracking subdomain is automatically provisioned when a workspace adds their sending domain. The system:

  1. Derives links.yourdomain.com from the root domain
  2. Registers it with Vercel as a custom domain via the Vercel API
  3. Stores the tracking_subdomain on the sender_identities row

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.com as 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).