Real-world reference for integrating the Relo Pixel into a Next.js lead-gen site. Every step, file, and event described here is live in production at quote.miami.
| Component | Tech | Purpose |
|---|---|---|
| Site | Next.js 15 on Cloudflare Pages | Static + SPA navigation, 30+ city landing pages (EN + ES) |
| Pixel | p.relo.mx/r.js | Loaded once in root layout via ReloPixel component |
| Lead capture | Multi-step React form | Fires relo('lead', ...) on successful submit |
| City pages | CityViewTracker component | Fires view_product on each city landing |
| Attribution | t.relo.mx/c/demoquote | Partner click wrapper → Redis → resolved on lead_submit |
| Analytics | Relo admin + partner portal | /c/quote/leads, /p/quote/leads |
Single file that owns pixel lifecycle. Three exports:
<ReloPixel /> — default export, mount once in app/layout.tsx. Loads r.js async + fires page.trackReloLead({...}) — call on successful form submit. Fires lead_submit.trackCityView(city, language) — call from city landing pages (via CityViewTracker).trackLeadStageUpdate(newStage) — optional, for client-side stage changes.ensureReloQueue() shim in ReloPixel.tsx, a CityViewTracker mounted inside a page would try to call window.relo(...) before the root layout's <ReloPixel /> has initialized the runtime. The shim pushes calls to a queue that the async r.js runtime drains once loaded.
'use client'
import { useEffect } from 'react'
import { trackCityView } from './ReloPixel'
export default function CityViewTracker({ city, language = 'en' }) {
useEffect(() => { trackCityView(city, language) }, [city, language])
return null
}
Mount on every city landing page:
<main>
<CityViewTracker city="Aventura" language="es" />
<StructuredData ... />
...
</main>
Fires trackReloLead in two spots:
'raw', passes session + funnel telemetry.'duplicate', helps partners see returning users.trackReloLead({
zip: data.zip,
locale,
sessionId: sessionIdRef.current,
timeOnPageSeconds: timeOnPage,
formStartToSubmitSeconds: formTime,
stepsCompleted: Array.from(stepsCompletedRef.current).sort().join(','),
abVariant,
})
Root layout imports + mounts <ReloPixel /> — this covers every route including SPA navigations (pixel listens for pathname changes and fires page automatically).
| Event | Fires when | Props captured |
|---|---|---|
page_view | Route change (auto) | path, title, utm, ref |
view_product | City landing page mount | product (city), product_category, city, language |
form_start | First form focus (auto) | form_id |
form_submit | Form submit event (auto) | form_id, field count |
form_abandon | pagehide with dirty form (auto) | form_id, fields filled |
lead_submit | Successful API response | product, form_id, stage, lead_value, currency, x_zip, x_locale, x_session_id, x_ab_variant, eh, ph |
lead_stage_update | Client-side stage change (rare) | new_stage, form_id |
Plus ~50 auto-captured UX + bot signals: scroll, click, rage_click, copy, autofill_detected, keystroke_rate, net_quality, battery_state, web_vitals, etc.
-- clients
id=5, name='Quote', slug='quote', tracking_type='pixel'
-- client_pixel_config (client_id=5)
allowed_domains=['quote.miami', 'www.quote.miami']
consent_required=false
jurisdiction='mx'
consent_defaults=15 (bits 0-3: analytics, personalization, dsp, cross-client)
-- partner_clients
partner_id=30 (Quote Demo Partner), client_id=5, status='active'
-- campaigns
id=8, name='Quote Auto Insurance - Demo', client_id=5, tracking_type='pixel'
slug='quote-auto-demo', status='active'
-- Namespace: 0b04030f85724c3e8d118e3a9efbe2ee (SHORT_URLS)
-- Key: demoquote
-- Value:
{
"url": "https://quote.miami?utm_source=relo-demo",
"pid": 30, // partner_id
"cid": 5, // client_id
"aid": 8 // campaign_id
}
Tracking link: https://t.relo.mx/c/demoquote → 302 to quote.miami with _relo_cid cookie set. On lead_submit Go backbone reads the cookie, looks up Redis click cache, populates partner_id + campaign_id server-side.
/c/quote/partnerst.relo.mx/c/XXXXXX link to promote.For lead-gen clients, commission is per-event (not per-revenue). Configure via PartnerLeadRatesModal:
lead_submit, purchase, signup, etc.)monthly_cap to limit payoutSpecificity: stage-specific rules beat generic ones. Older rates remain in DB for historical commission calculations.
| Role | URL | Shows |
|---|---|---|
| Admin | /c/quote/dashboard | Sessions, conversions, funnel, cities, campaigns |
| Admin | /c/quote/leads | All conversions, CSV export, stage + product filters |
| Admin | /c/quote/audit | Live events stream, event type breakdown, heatmap |
| Client user | /client/quote | Sessions, conversions, funnel — no partner/commission info |
| Partner | /p/quote | Per-campaign sessions, leads with hash prefixes, CSV export |
# Confirm client config
curl "$SUPABASE_URL/rest/v1/clients?id=eq.5&select=id,name,slug,tracking_type" \
-H "apikey: $SUPABASE_KEY"
# Confirm pixel events flowing
ssh root@178.156.195.219 "clickhouse-client -d relo -q \\
\"SELECT event_name, count() FROM events \\
WHERE client_id=5 AND event_time >= now() - INTERVAL 1 DAY \\
GROUP BY event_name ORDER BY count() DESC\""
# Confirm click wrapper entry exists
curl "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/storage/kv/namespaces/$KV_ID/values/demoquote" \
-H "Authorization: Bearer $CF_TOKEN"
# Test end-to-end: visit via partner link, submit form, check event landed
curl -I https://t.relo.mx/c/demoquote
# → 302 Location: https://quote.miami?...
ensureReloQueue() shim that queues init synchronously. See section 2.batch.go originally only dumped full props for lead_submit. Extended to all conversion + funnel events (purchase, view_product, add_to_cart, form_submit, signup, subscription, checkout_open, lead_stage_update).lead_submit if the visitor came through t.relo.mx/c/CODE and the _relo_cid cookie survived. Cross-domain navigations break attribution unless cookie is set on .relo.mx with SameSite=Lax.SETNX in Redis with 10min TTL on dedup:lead_submit:{client}:{device}:{form}:{product}. Refreshes + double-submits count once.cd backbone/workers/pixel && npx wrangler deploy
cd ~/seguros && bash scripts/deploy-cloudflare.sh
cd backbone && GOOS=linux GOARCH=amd64 go build -o relo-ingest-new ./cmd/ingest/
scp relo-ingest-new root@178.156.195.219:/opt/relo/relo-ingest-new
ssh root@178.156.195.219 "systemctl stop relo-ingest && \\
cp /opt/relo/relo-ingest-new /opt/relo-ingest/relo-ingest && \\
chmod +x /opt/relo-ingest/relo-ingest && systemctl start relo-ingest"
ssh root@178.156.195.219 "clickhouse-client -d relo -q \\
\"SELECT event_name, event_properties, event_time \\
FROM events WHERE client_id=5 \\
AND event_time >= now() - INTERVAL 5 MINUTE \\
ORDER BY event_time DESC LIMIT 10 FORMAT Vertical\""