Skip to content

Consent

Owner: whoever owns the privacy notice, with engineering.

Stitch honours an opt-out. It does not collect one.

That distinction is the whole design. The banner, the preference centre, the per-jurisdiction rule packs and the wording of the notice belong to a consent management platform — OneTrust, Osano, Cookiebot, Termly, Ketch, or something hand-rolled — whose entire job is keeping current with law that changes every session of every legislature. Building a worse one inside an attribution tool would be a bad trade twice over.

What Stitch does is the half a CMP cannot do for you: read the signal, record it against the person, and change what happens.


Ten IAB purposes would be theatre here. Stitch does two things that consent bears on, so it stores two answers.

FlagWhat it governsWho can refuse it
analyticsWhether we record this visit at allA CMP only
advertisingWhether we hand it to an ad platformGPC, or a CMP

advertising is the one with teeth. Stitch sends a SHA-256 of a borrower’s email to Google, Meta, TikTok and LinkedIn so those platforms can bid on funded loans. Under the CPRA that is sharing for cross-context behavioural advertising, and it is exactly what a Californian opt-out is an opt-out of.

GPC is an opt-out of sale and sharing. It is not an opt-out of analytics.

Implementations get this wrong in both directions. Treat it as a blanket refusal and you delete first-party measurement nobody asked you to stop — along with the denominator every coverage number is measured against. Treat it as advisory and you keep sharing after a refusal that California has been enforcing since the Sephora settlement in 2022.

So it sets one flag, and it is read in two places:

  • Sec-GPC: 1 on the request, at the edge, on every zone (spec 1.15). This works on a domain that never loaded the SDK and on a page whose JavaScript failed — neither an edge case for a signal whose whole audience is people who care about tracking.
  • navigator.globalPrivacyControl, forwarded by the SDK, in case a proxy or CDN in front of the origin strips the header.

Nothing on the page can override it. A banner answer of “yes to everything” arriving with GPC still set resolves to advertising: false, because the collector applies GPC last.

One call, in the banner’s change callback:

stitch.consent({ analytics: true, advertising: false });

It flushes immediately rather than waiting for the next interval — a refusal that waits five seconds is a refusal that can lose a race with the page being closed.

Every CMP has this hook. In OneTrust it is OptanonWrapper or the OneTrustGroupsUpdated event; in Osano it is osano.cm.addEventListener; in Cookiebot it is CookiebotOnAccept / CookiebotOnDecline. Map your categories onto the two flags — usually performance/analytics onto analytics and targeting/advertising onto advertising — and be deliberate about which of your categories means “share with ad platforms”, because that is the one the law is about.

California requires a link when you share for cross-context behavioural advertising. The link and the page are yours. What it posts to is:

POST https://t.makeabeeline.com/v1/consent
{ "consent": { "advertising": false } }

It returns what was recorded, so the page can confirm rather than promise:

{ "analytics": true, "advertising": false, "source": "request" }

A post is a deliberate act, so it replaces the record — which is what makes a preference centre able to opt someone back in. Everything else merges, and a refusal survives. That asymmetry matters: a GPC header goes missing for reasons that have nothing to do with anyone changing their mind — a proxy strips it, a browser profile lacks the extension — and if an absent signal could overwrite a present one, every opt-out would have a half-life.

Analytics refused. The visit is still counted; no touchpoint is created, so it can earn no credit anywhere (spec 1090). Deleting the event instead would quietly shrink the denominator the coverage screen measures against, and refusals would make our numbers look better.

Sharing refused. No conversion leaves the building for that person — on any platform, from any device. Checked when the conversion is built and again immediately before the request goes out, because the gap between those is five minutes on the cron and hours on a retry, and an opt-out landing inside it has to stop a send already queued.

Withheld conversions are logged as withheld, and show on the export health card beside sent, failed and pending. A conversion that vanishes without trace is indistinguishable from a broken export.

Click IDs are still stored. A gclid arrives as first-party data and keeping it for internal attribution is not sharing; sending it back is. The line is drawn at the export, which is where it is drawn legally too.

Consent is captured per browser, because a banner is answered on the first visit and the person is not known until they raise a lead weeks later. When the identity graph joins two browsers into one person, the most restrictive answer wins.

A phone that sent GPC and a laptop with no record at all is one person who has opted out. Any other rule would make identity resolution — the thing this whole system does — a way to launder a refusal by finding a device that never saw the banner.

The default, and the one thing to get right per domain

Section titled “The default, and the one thing to get right per domain”
UPDATE domains SET consent_default = 'deny' WHERE hostname = 'eu.example.com';

allow is the default and is correct for California: the CCPA and CPRA are opt-out regimes, so collection is lawful until refused. deny gives opt-in behaviour and is what an EU-facing zone needs.

A domain added without thinking about this inherits the US answer silently. That is the known sharp edge of this design. It is per-domain config rather than a constant so the fix is a row rather than a release, but the decision still has to be made by someone each time a zone is added.

What this does not do, and what needs a lawyer

Section titled “What this does not do, and what needs a lawyer”

Not built, deliberately: IAB TCF and GPP string parsing, a preference centre, a banner, per-jurisdiction rule packs, data subject access or deletion workflows. All of those are a CMP’s job or a privacy program’s.

Not settled by this document:

  • The GLBA overlay. Beeline is a financial institution, and non-public personal information collected in a lending relationship is largely exempt from CCPA/CPRA — but marketing-site browsing by a prospect who is not yet a customer generally is not, and the boundary between the two is exactly where this system operates. Counsel, not engineering.
  • Whether hashing changes the analysis. It does not make data non-personal. Treat a SHA-256 of an email as personal information.
  • Cross-brand sharing. Spec 436 notes the brands appear to be affiliates under common ownership, which is materially easier than nonaffiliate sharing — but both privacy notices have to cover the shared identity graph, and that should be confirmed before it goes live, not after.
  • Retention. Nothing here expires a consent record or the events under it.

Not legal advice, and written by an engineer. The certainty tiers, the IP handling and the retention policy need the same review (spec 1282).