Privacy & Data Handling
The Hood Web SDK is built with privacy by design.
This page explains what data the SDK stores, how it behaves with or without consent, and what developers need to configure in their Consent Management Provider (CMP).
Our goal is to keep integration simple while remaining fully compliant with GDPR and other privacy standards — without unnecessary legal complexity.
Overview
The SDK keeps its data in browser storage rather than cookies. There is one exception: when site-wide push subscription is used, a single first-party cookie _ocpsh is set with the constant value 1. It records that this browser is already subscribed so that sibling subdomains do not re-subscribe the same visitor. It carries no identifier, lasts 180 days, and is set as SameSite=Lax (plus Secure over HTTPS).
Enabling push_enable_subdomain_subscription replaces this cookie with a per-origin localStorage flag, in which case the SDK sets no cookies at all.
Apart from that cookie, all data is stored directly in the browser using:
sessionStorage– temporary data automatically removed at the end of a browser sessionlocalStorage– persistent data such as consent preferences and technical keys that last between sessions
All SDK data is kept under a single storage key called _opc (lowercase).
Inside it, values are namespaced by your configuration tag, so several configurations on one page never overwrite each other.
Example structure:
{
"_opc": {
"YOUR_TAG_ID": {
"uid": "1736412000.4821.0",
"userConsent": { "analytics": true, "advertising": false, "functional": true, "consent": true }
}
}
}
_opc in browser storage, set the storeKey configuration option to move the SDK to a different root key.Storage Overview
- Technical storage (session ID, A/B variants, dismissals) is necessary for service operation and does not require consent.
- Processing that links data to a person (profile association via
identity, campaign attribution) runs only with explicit consent. The SDK recognizes exactly four consent categories:analytics,advertising,functionalandconsent. - This page is developer guidance, not legal advice.
| Storage Type | Used For | Lifetime |
|---|---|---|
sessionStorage | Temporary session tracking | Until the tab/browser is closed |
localStorage | Persistent SDK data stored under _opc | Up to 365 days (or until manually cleared) |
Session Data (Without Consent)
When no consent is given, the SDK will:
- create a temporary session ID (random and non-persistent),
- store it as
uidinsidesessionStorage, - use it to group analytics events within the same browser session.
This ID is deleted automatically when the session ends.
It cannot identify a user and does not require consent.
Example session data:
{
"_opc": {
"YOUR_TAG_ID": {
"uid": "1736412000.4821.0"
}
}
}
This allows the SDK to measure simple things like page views or event counts — fully anonymous.
Persistent Data (With Consent)
If the user gives consent via your CMP, the SDK can:
- keep the consent state,
- track campaign parameters (
utm_*) and push permission data, - associate events to a server-side profile when your app calls
identity(optional).
Example flow:
Hood('consent', { analytics: true });
Hood('identity', 'user_12345');
Hood('trackEvent', 'purchase', { value: 25 });
If consent is later withdrawn, the SDK will stop using identifiable data and continue with anonymous session tracking only.
identitypayloads are never stored in the browser.- Send
identityonly when you have consent (for example,analytics: true) and do it consistently on login/auth so events can be linked across sessions. - We generate and manage the internal profile ID server-side; your external ID is attached to that profile as an attribute.
What Data Is Stored
All data is stored under the single key _opc, namespaced by configuration tag. The subkeys below are relative to _opc.<tag>.
| Subkey | Storage | Purpose | Purpose Key | Lifetime | Consent Required |
|---|---|---|---|---|---|
uid | sessionStorage | Temporary anonymous visitor ID | technical | Until session ends | ❌ |
uid + ttl | localStorage | Persistent visitor ID and its expiry | analytics | Up to 1 year | ✅ |
userConsent | localStorage | Consent states per category | technical | Until changed | ❌ |
utm_* | localStorage | Campaign and attribution parameters | analytics | Until overwritten | ✅ |
push | localStorage | Push subscription flag, only when push_enable_subdomain_subscription is enabled (otherwise the _ocpsh cookie is used) | technical | Until overwritten | ❌ |
m_<modalId>.* | localStorage | Per-modal view counters, timestamps and A/B variant | technical | Until overwritten | ❌ |
webpush_dismissals | localStorage | Count of dismissed push prompts | technical | Until overwritten | ❌ |
How the SDK Handles Consent
The SDK includes a simple consent interface that should be synced with your CMP.
Setting Consent
Pass consent states directly from your CMP, using the SDK’s four category names:
Hood('consent', {
analytics: true,
advertising: false,
functional: true,
consent: true
});
Passing a boolean sets all four categories at once, which is a convenient shorthand for an accept-all or reject-all button:
Hood('consent', true);
analytics, advertising, functional and consent are recognized. Keys such as marketing, personalization, push or enabled are dropped without warning, so a CMP mapping that uses them grants nothing.Updating Consent
If the user updates consent:
Hood('consent', newConsentState);
Only categories whose value actually changes trigger a consent event, so re-sending the same state is harmless.
Clearing Data
For “right to be forgotten” or user data removal:
Hood('identity', null);
Hood('consent', { analytics: false, advertising: false, functional: false, consent: false });
Revoking analytics clears the persistent visitor ID and stored campaign parameters from localStorage.
CMP Configuration Guide
In your Consent Management Provider (CMP), define the following categories and map them to the SDK purpose keys:
| Purpose | Purpose Key | When Active | Description |
|---|---|---|---|
| Analytics | analytics | analytics: true | Analytics beacons, the persistent visitor ID, and campaign attribution (UTM storage) |
| Advertising | advertising | advertising: true | Advertising-related processing |
| Functional | functional | functional: true | Functional storage beyond what is strictly necessary |
| Overall | consent | consent: true | The visitor’s overall consent decision |
analytics category, not a separate marketing category. If your CMP models marketing separately, map that choice onto analytics (or advertising) when calling Hood('consent', …).Hood('consent', …) call is not required. The TC string is included as proof alongside the consent event.Best Practices
- Send consent to the SDK immediately after your CMP loads
- Linking events to a person happens server-side via
identityand requires consent (no user IDs are stored locally) - No data passed via
identity()/setUserProperties()is saved locally - Use unique key names if your app also writes to localStorage
- Keep your SDK up to date — privacy rules evolve
Quick Reference
| Use Case | Requires Consent | Storage |
|---|---|---|
| Counting sessions | ❌ | sessionStorage (uid) |
| Measuring page views | ✅ | analytics beacons are suppressed without analytics consent |
| Linking events to a user | ✅ | server-side only (no local storage) |
| Remembering a push subscription | ❌ | _ocpsh cookie, or localStorage (push) in per-subdomain mode |
| A/B testing modals | ❌ | localStorage (m_<modalId>.ab) |
| Tracking campaigns (UTM) | ✅ | localStorage (utm_*) |
Summary
- All SDK data is stored under a single key
_opcin browser storage, namespaced by configuration tag. - The only cookie is
_ocpsh, a constant non-identifying push subscription flag that can be turned off withpush_enable_subdomain_subscription. - Persistent data (the visitor ID and UTM parameters) is stored only with
analyticsconsent. - The push subscription flag is a functional check — never a user identifier.
- The SDK recognizes four consent categories:
analytics,advertising,functional,consent.
Need more technical details?
Check our Integration Guide or contact our support team.