Consent & privacy
Consent & privacy
Pivolio implements Google Consent Mode v2. Consent isn’t a single on/off switch — each category removes a specific part of the event, and the effects are independent.
The four categories
consent on an event carries ad_storage, analytics_storage, ad_user_data
and ad_personalization.
Consent is enforced in two different places, and conflating them causes a lot of confusion. The browser SDK trims what it collects; the server decides whether a destination may be sent to at all.
In the browser SDK
These run client-side, before the event leaves the page. An ad_storage denial
means there is no delivery to inspect. The other two produce an event that still
arrives, just with less signal — and correspondingly lower
match quality.
On the server
The backend does not strip fields. It uses consent for one decision only: the per-destination gate described below. With enforcement on, a denied required category means the destination is skipped entirely rather than sent a reduced payload.
So an ad_user_data denial reaching a destination that requires it — the
default — produces a skipped delivery, not a delivery with the PII removed.
Unset is treated as not-denied. Only an explicit denied restricts
anything. If you never call setConsent(), events flow with full signal — which
is the right default for regions without a consent requirement, and the wrong
one if you need opt-in. Set consent explicitly before the first event where
consent is required.
Denying ad_storage through setConsent() also immediately discards pending
events from the buffer. Events already sent are gone; anything still queued
locally is not sent.
Three independent kill switches
Three separate mechanisms stop tracking, and they don’t substitute for one another. Knowing which one is active saves a lot of confused debugging.
Init-time config. When true and the browser sends Do Not Track, tracking is
disabled entirely. Evaluated once, at init.
Durable and persisted. Survives reloads and new sessions. An opted-out
visitor is fully inert on later page loads — no identity call, no automatic
page view, no events at all. Reverse it with optIn(); check it with
hasOptedOut().
Runtime, and not persisted. It applies to the current page’s tracker and is gone on the next load unless you set it again — so a consent banner must restore its stored decision on every page.
optOut() is the one to use for a durable user request. Consent state is for
per-page-load regulatory gating, and forgetting that it isn’t persisted is the
most common consent bug.
PII hashing
No raw PII reaches an ad platform. Traits are normalized and SHA-256 hashed to hex before delivery. Normalization is what makes the digest match the platform’s — the same person hashed two different ways is two different people.
Three rules cover everything:
strip()thenlower()— email, first name, last name, city, state, zip, country.- Digits and a leading
+only (E.164-ish), not lowercased — phone. - Byte-for-byte, no normalization at all — Meta’s
external_idonly, per Meta’s spec. Your own ID must arrive exactly as you store it.
The empty check runs on the normalized result, not the input. A phone
recorded as "N/A" normalizes to an empty string — hashing that would produce a
real-looking digest matching nobody, and every such record would look identical.
Values that normalize to empty hash to nothing instead and are simply omitted.
That guard is why placeholder junk in your CRM degrades gracefully rather than poisoning match rates with a phantom shared identity.
Per-destination enforcement
Consent enforcement is configured per destination, so you can hold a
platform to stricter requirements than another. When enforcement is on and a
required category is denied, the send is skipped — the event still records, with
a skipped delivery status.
The default required categories are ad_user_data and ad_storage.
A skipped-for-consent event is not a failure and is never retried. Retrying wouldn’t help: the visitor’s answer isn’t going to change because you asked the API again.
People whose consent state blocks delivery are labelled with the
consent_denied segment — see Identity resolution.