A Complete Guide to Setting Up Mail Tracking in ColdEmailingJet

Mail tracking is a vital component of cold emailing, providing valuable insights into recipient engagement. ColdEmailingJet gives you the tools to embed tracking information into your emails, but you are responsible for setting up your own tracking infrastructure. This guide walks you through the entire process.


How Mail Tracking Works

ColdEmailingJet does not host tracking infrastructure. Instead, it embeds tracking data (like recipient email addresses) into your email links. You set up your own tracking server (Cloudflare Worker + D1 database) to capture and log recipient interactions.

The Complete Flow

1. You set up Cloudflare Worker + D1 Database
2. ColdEmailingJet inserts {email.email} tag into your tracking links
3. Email sent → Recipient clicks link → Request hits YOUR Worker
4. Worker logs data (email, IP, country, user agent) to D1 database
5. Worker serves age verification page (bot detection)
6. Human passes verification → Redirected to final offer page

Part 1: Setting Up Cloudflare Worker & D1 Database

Step 1: Create a D1 Database

  1. Log into your Cloudflare Dashboard
  2. Navigate to Workers & PagesD1
  3. Click Create Database
  4. Name it (e.g., tracking_db)
  5. Click Create

Step 2: Create the Database Table

Run this SQL in your D1 console:

CREATE TABLE email_events (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    email TEXT,
    ip TEXT,
    country TEXT,
    user_agent TEXT,
    language TEXT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

Step 3: Create a Cloudflare Worker

  1. Go to Workers & PagesCreate ApplicationCreate Worker
  2. Name your worker (e.g., tracking-worker)
  3. Click Deploy then Edit Code

Step 4: Worker Code

Here’s a complete worker that:

  • Captures email from URL parameters
  • Logs IP, country, user agent, and language
  • Serves an age verification page
  • Detects bots before redirecting
export default {
  async fetch(request, env, ctx) {
    try {
      const url = new URL(request.url);
      const pathname = url.pathname;

      if (request.method !== "GET") {
        return new Response("Method not allowed", { status: 405 });
      }

      // Extract email from URL parameters
      const email = url.searchParams.get("email") || 
                    url.searchParams.get("addr") || 
                    url.searchParams.get("mail") || 
                    "no_email";

      // Tracking endpoint - logs data and redirects
      if (pathname === "/track") {
        if (isValidEmail(email)) {
          const ip = request.headers.get("CF-Connecting-IP") ||
                     request.headers.get("x-forwarded-for") || "unknown";
          const userAgent = request.headers.get("user-agent") || "unknown";
          const country = request.headers.get("CF-IPCountry") || "unknown";
          const language = request.headers.get("Accept-Language")?.split(',')[0] || "unknown";

          // Save to D1 database (async, don't block redirect)
          ctx.waitUntil(
            env.DB.prepare(
              `INSERT INTO email_events (email, ip, country, user_agent, language)
               VALUES (?, ?, ?, ?, ?)`
            ).bind(email, ip, country, userAgent, language).run()
          );
        }

        // Redirect to final destination
        return Response.redirect("https://your-final-offer-page.com", 302);
      }

      // Age verification landing page
      const object = await env.R2html.get("landing_page.html");
      if (!object) {
        return new Response("Page not found", { status: 404 });
      }

      let html = await object.text();
      html = html.replace(/{{EMAIL}}/g, escapeHtml(email));

      return new Response(html, {
        headers: { "Content-Type": "text/html; charset=utf-8" }
      });

    } catch (err) {
      return new Response("Error: " + err.message, { status: 500 });
    }
  }
};

function isValidEmail(email) {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

function escapeHtml(str) {
  return str.replace(/[&<>]/g, function(m) {
    if (m === '&') return '&amp;';
    if (m === '<') return '&lt;';
    if (m === '>') return '&gt;';
    return m;
  });
}

Step 5: Bind D1 Database to Worker

  1. Go to your Worker → SettingsVariables
  2. Under D1 Database Bindings, click Add binding
  3. Variable name: DB
  4. Select your D1 database
  5. Click Save

Step 6: Upload HTML Landing Page to R2

  1. Create an R2 bucket (e.g., tracking-assets)
  2. Upload your landing_page.html file
  3. Bind R2 to your Worker as R2html

Part 2: Bot Detection on Landing Page

Your HTML landing page should verify the user is human before redirecting. Here’s the key logic:

Bot Detection Techniques

TechniqueHow It Works
Mouse Movement DetectionButton only enables after mouse/touch interaction
Countdown TimerForces a 3-5 second wait (bots rarely wait)
Hidden Trap LinkBot that crawls all links gets caught
Shadow DOMHides verification logic from simple scrapers

Sample Landing Page Structure

<!DOCTYPE html>
<html>
<head>
    <title>Age Verification</title>
</head>
<body>
    <img id="previewImage">
    <h1>You must be 18+ to enter</h1>
    <div id="buttonContainer"></div>
    <a href="/trap" style="display:none">Hidden trap for bots</a>

    <script>
        const email = "{{EMAIL}}";
        let humanDetected = false;
        let countdownFinished = false;

        // Detect human interaction
        window.addEventListener("mousemove", () => markHuman(), { once: true });
        window.addEventListener("touchstart", () => markHuman(), { once: true });
        window.addEventListener("keydown", () => markHuman(), { once: true });

        function markHuman() {
            humanDetected = true;
            enableButtonIfReady();
        }

        // Countdown timer
        let countdown = 3;
        const interval = setInterval(() => {
            countdown--;
            if (countdown <= 0) {
                clearInterval(interval);
                countdownFinished = true;
                enableButtonIfReady();
            }
            button.textContent = `Wait ${countdown}s`;
        }, 1000);

        function enableButtonIfReady() {
            if (humanDetected && countdownFinished) {
                button.disabled = false;
                button.textContent = "ENTER";
                button.onclick = () => {
                    window.location.href = `/track?email=${encodeURIComponent(email)}`;
                };
            }
        }
    </script>
</body>
</html>

Part 3: Configuring Tracking Links in ColdEmailingJet

Step 1: Create Your Tracking Link Template

In your email content, insert tracking links using the {email.email} tag:

https://your-worker.yourdomain.com/track?email={email.email}

Step 2: Using Different URL Parameters

ColdEmailingJet supports multiple parameter names:

ParameterUsage
?email=Standard email parameter
?addr=Alternative parameter name
?mail=Another alternative

Example:

https://your-worker.com/track?addr={email.email}

Step 3: Adding Custom Fields

You can also include custom fields from your contact list:

https://your-worker.com/track?email={email.email}&name={email.FirstName}&campaign=camp_123

Step 4: Where to Put the Tracking Link

Place the tracking link in your email content:

HTML Email Example:

<a href="https://your-worker.com/track?email={email.email}">
    Click Here to Claim Your Offer
</a>

Text Email Example:

Claim your offer: https://your-worker.com/track?email={email.email}

Part 4: Testing Your Setup

Test the Complete Flow

  1. Send a test email to yourself
  2. Click the tracking link in the email
  3. Verify the database record appears in your D1 database:
SELECT * FROM email_events ORDER BY created_at DESC LIMIT 10;

Expected Results

StepExpected Behavior
Link clickWorker logs IP, email, country to D1
Landing pageShows age verification with countdown
Human verificationButton enables after mouse move + timer
Final clickRedirects to your offer page

Summary Checklist

TaskDone
Create Cloudflare D1 database
Create database table
Create Cloudflare Worker
Deploy worker code
Bind D1 to worker
Upload landing page to R2
Bind R2 to worker
Add bot detection to landing page
Insert {email.email} tag in ColdEmailingJet links
Test with real email

Key Takeaways

  • ColdEmailingJet only embeds tracking data into links – it does NOT host tracking
  • You must set up Cloudflare Worker + D1 database to capture clicks
  • Bot detection requires mouse/touch events and countdown timers on your landing page
  • Use {email.email} tag to insert recipient addresses into your tracking links

With this setup, you have complete control over your tracking data while ColdEmailingJet handles the email delivery and personalization.