Skip to content

BRIEF: Link Intelligence — Contact Tracking via t.ucca.online

SURFACE: t.ucca.online (new Worker: ucca-track) TOUCHES: ucca-ir (email receipt links), ucca-keys (verify page links) DO NOT TOUCH: ucca-site, ucca-ops, rtopacks-site, ucca-engine


What We're Building

Every link on every UCCA surface that has a contact hash in context gets replaced with a tracking redirect URL. When clicked, it logs the event to D1 against the contact's hash, then 302 redirects instantly to the real destination. Invisible to the recipient. Zero latency impact.

The result: every contact has a behavioural timeline in the ops console — who clicked what, from which surface, when.


Part 1 — Database (engine-db)

Run this migration against engine-db (ID: 0efa8970-0053-4623-8436-4e877af10887):

CREATE TABLE IF NOT EXISTS link_events (
  id           INTEGER PRIMARY KEY AUTOINCREMENT,
  clicked_at   TEXT    NOT NULL DEFAULT (datetime('now')),
  hash         TEXT    NOT NULL,
  short_hash   TEXT    NOT NULL,
  link_id      TEXT    NOT NULL,
  surface      TEXT    NOT NULL,
  destination  TEXT    NOT NULL,
  ip           TEXT,
  country      TEXT,
  city         TEXT,
  user_agent   TEXT,
  referrer     TEXT
);

CREATE INDEX IF NOT EXISTS idx_link_events_hash      ON link_events(hash);
CREATE INDEX IF NOT EXISTS idx_link_events_clicked   ON link_events(clicked_at);
CREATE INDEX IF NOT EXISTS idx_link_events_surface   ON link_events(surface);

Part 2 — New Worker: ucca-track

Cloudflare setup

  • Worker name: ucca-track
  • Route: t.ucca.online/*
  • DNS: CNAME tucca-track.workers.dev (orange cloud)
  • D1 binding: DB → engine-db (0efa8970-0053-4623-8436-4e877af10887)

Worker logic

Endpoint: GET /r

Query params: - h — full hash (64 char) of the contact - l — link_id (e.g. keys, privacy, terms, ucca, optout) - s — surface (e.g. email-receipt, verify-page, ir-form)

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname === '/r') {
      const hash      = url.searchParams.get('h') || '';
      const linkId    = url.searchParams.get('l') || '';
      const surface   = url.searchParams.get('s') || '';
      const shortHash = hash.substring(0, 8);

      // Destination map
      const destinations: Record<string, string> = {
        keys:    `https://keys.ucca.online/verify/${hash}`,
        ucca:    'https://ucca.online',
        privacy: 'https://ucca.online/privacy',
        terms:   'https://ucca.online/terms',
        optout:  `https://ucca.online/optout?ref=${shortHash}`,
      };

      const destination = destinations[linkId] || 'https://ucca.online';

      // Log the click (fire and forget — don't block redirect)
      const cf = (request as any).cf || {};
      env.DB.prepare(`
        INSERT INTO link_events
          (hash, short_hash, link_id, surface, destination, ip, country, city, user_agent, referrer)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
      `).bind(
        hash,
        shortHash,
        linkId,
        surface,
        destination,
        request.headers.get('CF-Connecting-IP') || '',
        cf.country || '',
        cf.city    || '',
        request.headers.get('User-Agent') || '',
        request.headers.get('Referer')    || '',
      ).run().catch(() => {}); // swallow errors — never block redirect

      return Response.redirect(destination, 302);
    }

    return new Response('Not found', { status: 404 });
  }
}

interface Env {
  DB: D1Database;
}

wrangler.toml additions

[[d1_databases]]
binding = "DB"
database_name = "engine-db"
database_id = "0efa8970-0053-4623-8436-4e877af10887"

Part 3 — Wire Into Email Receipt (ucca-ir)

In the ucca-ir Worker, find the HTML email template. Every link currently hardcoded in the receipt needs to be replaced with a tracking URL.

The tracking URL pattern is:

https://t.ucca.online/r?h={FULL_HASH}&l={LINK_ID}&s={SURFACE}

Current link Replace with
https://keys.ucca.online/verify/${hash} https://t.ucca.online/r?h=${hash}&l=keys&s=email-receipt
https://ucca.online https://t.ucca.online/r?h=${hash}&l=ucca&s=email-receipt
https://ucca.online/privacy https://t.ucca.online/r?h=${hash}&l=privacy&s=email-receipt
https://ucca.online/terms https://t.ucca.online/r?h=${hash}&l=terms&s=email-receipt
https://ucca.online/optout?ref=${shortHash} https://t.ucca.online/r?h=${hash}&l=optout&s=email-receipt

Also replace the QR code target URL — the QR currently encodes the raw keys.ucca.online/verify/${hash} URL. Change it to encode the tracking URL:

https://t.ucca.online/r?h=${hash}&l=keys&s=email-qr
This means QR scans are tracked separately from link clicks — you'll know if they scanned the QR vs clicked the link in the email.

Surface value for email receipt: email-receipt Surface value for QR scan: email-qr


Part 4 — Wire Into Verify Page (ucca-keys)

On the keys.ucca.online/verify/:hash page, all outbound links in the footer get the same treatment but with s=verify-page:

Link Tracking URL
https://ucca.online https://t.ucca.online/r?h=${hash}&l=ucca&s=verify-page
https://ucca.online/privacy https://t.ucca.online/r?h=${hash}&l=privacy&s=verify-page
https://ucca.online/terms https://t.ucca.online/r?h=${hash}&l=terms&s=verify-page

The hash is already in the URL path (/verify/:hash) so it's available on the page — use it.


Part 5 — Ops Console Contact Timeline

In ucca-ops, add a contact detail view that queries both contacts and link_events and renders a unified timeline for any given hash.

New endpoint: GET /api/contact/:hash

-- Contact record
SELECT * FROM contacts WHERE hash = ?;

-- Click timeline
SELECT clicked_at, link_id, surface, country, city, user_agent
FROM link_events
WHERE hash = ?
ORDER BY clicked_at ASC;

Response shape:

{
  "contact": { ...contacts row... },
  "timeline": [
    {
      "type": "registered",
      "at": "2026-03-12T04:06:49Z",
      "surface": "ir.ucca.online",
      "detail": "TPG Internet · AU · Gold Coast"
    },
    {
      "type": "click",
      "at": "2026-03-12T04:08:12Z",
      "link_id": "keys",
      "surface": "email-receipt",
      "detail": "AU · Gold Coast"
    },
    {
      "type": "click",
      "at": "2026-03-12T04:09:44Z",
      "link_id": "ucca",
      "surface": "verify-page",
      "detail": "AU · Gold Coast"
    }
  ]
}

UI in ops console: Simple timeline list under the contact record. Purple dots for registration events, blue for clicks. Show link_id as a badge (KEYS / UCCA / PRIVACY / TERMS / OPTOUT). Show surface in smaller text below.


Acceptance Criteria

  • t.ucca.online/r?h=TEST&l=keys&s=email-receipt redirects to keys.ucca.online/verify/TEST
  • Click is logged to link_events in engine-db within 500ms
  • Redirect happens in < 100ms (log is fire-and-forget, never blocks)
  • Email receipt: all 5 links replaced with tracking URLs
  • QR code URL updated — s=email-qr separate from s=email-receipt
  • Verify page: footer links replaced with tracking URLs
  • Ops console: /api/contact/:hash returns contact + timeline
  • No tracking URL exposed to recipient (they see t.ucca.online briefly then destination)
  • Invalid or missing l param falls back to https://ucca.online — never errors

Notes

  • t.ucca.online is already in Cloudflare — just needs the Worker route added
  • The full 64-char hash goes in the URL — it's not secret (it's already in the email)
  • fire-and-forget pattern (.catch(() => {})) is intentional — the redirect must never fail because logging failed
  • Link IDs are intentionally short — they're visible in the URL bar briefly
  • Future surfaces (ir-form footer, ucca.online nav, rtopacks.com.au) can be added by just using s=ir-form, s=ucca-online etc. — no schema changes needed

Brief authored: 12 MAR 2026 · UCCA Build Loop