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
t→ucca-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:
Links to replace in the email receipt¶
| 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:
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-receiptredirects tokeys.ucca.online/verify/TEST - Click is logged to
link_eventsin 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-qrseparate froms=email-receipt - Verify page: footer links replaced with tracking URLs
- Ops console:
/api/contact/:hashreturns contact + timeline - No tracking URL exposed to recipient (they see
t.ucca.onlinebriefly then destination) - Invalid or missing
lparam falls back tohttps://ucca.online— never errors
Notes¶
t.ucca.onlineis 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-forgetpattern (.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-onlineetc. — no schema changes needed
Brief authored: 12 MAR 2026 · UCCA Build Loop