SaaS & data

From anonymous visit to useful attribution

A first-party measurement flow for visits, forms and bookings, with explicit consent states and a separate path for reporting data.

LaravelPostgreSQLClickHouseQueuesMulti-tenancyREST APIs

The situation

The platform connects website visits to forms, meetings, leads, reservations and orders. Campaign data such as UTM parameters and Google Ads click identifiers must survive even when a conversion arrives much later.

Where it got stuck

The tracker runs on websites with different forms and consent managers. Without consent it cannot simply build an identifiable visitor profile, yet storing nothing makes later attribution impossible. Browser events, webhooks and external API data also arrive in different orders.

The technical question

The same session had to move from anonymous to identifiable without creating duplicate visitors or visits. Each company also needed isolated data, while expensive attribution calculations could not delay the tracking request.

My approach

I built an explicit consent state machine with a limited session-only mode and an idempotent session upgrade once consent is granted. Laravel queues process attribution and integrations. Transactional data stays in PostgreSQL; reporting events are also written to ClickHouse through the same repository contracts.

What changed

A consent change now flows through the measurement chain without a page reload. New form and booking sources can be added as adapters, while logging shows whether each event was received, matched and processed.

Under the hood

Decisions that made the difference

01

Consent as state

Not a single cookie check, but a transition from session-only to cross-session tracking while retaining the existing session.

02

Idempotent activation

A repeated upgrade request must never create a second visitor, visit or conversion.

03

Two storage workloads

PostgreSQL handles relations and current state; ClickHouse handles large time series and reporting queries.

04

Work outside the request

Attribution, imports and external synchronisation run through queues so the tracker can respond quickly.

Let’s talk

Facing something similar?

Send me the context. I can usually tell you quickly whether and how I can help.

Tell me what is going on