Cookie policy template for sites running the Crossdeck SDK.
Drop-in language you can paste straight into your privacy page. It accurately describes the cookies and browser-storage entries the Crossdeck Web SDK creates on your visitors' devices, and is written to satisfy GDPR (EU), the ePrivacy Directive (EU/UK cookie law), and CCPA/CPRA (California) disclosure requirements. Written from the actual SDK source code, not guessed.
TL;DR
The Crossdeck Web SDK (@cross-deck/web v0.6.0+) writes two
first-party cookies on the domain it's installed on:
crossdeck:anon_id and crossdeck:cdcust_id. They are
functional / analytics cookies — used to keep the same anonymous visitor identity
stable across page loads so dashboards count one human as one human, not as a new
visitor every time the cookie store is cleared.
They never carry advertising data, never reach a third-party ad network, and never
contain raw email addresses or personal information. They contain a randomly-generated
opaque ID and (after a developer-driven login) the Crossdeck customer ID returned to
you by Crossdeck.identify().
Paste the drop-in template below into your cookie policy. Replace the bracketed placeholders with your details.
Exactly what the SDK stores
The SDK uses a dual-store identity model: it writes the same
anonymous-visitor ID to both localStorage and a 1st-party cookie. When one
store is wiped (Safari ITP, "Clear site data," private-browsing limits, privacy
extensions), the other restores the identity so the same human keeps the same
Crossdeck record. This is documented in the SDK source under
identity.ts
and storage.ts.
| Storage entry | Type | Lifetime | Purpose |
|---|---|---|---|
crossdeck:anon_id |
1st-party cookie + localStorage |
2 years (clamped to 7 days on Safari due to ITP) | Anonymous visitor ID. Random opaque string, no PII. Lets us count returning visitors as the same person across sessions. |
crossdeck:cdcust_id |
1st-party cookie + localStorage |
2 years (clamped to 7 days on Safari due to ITP) | Crossdeck customer ID — only set after your code calls Crossdeck.identify(userId). Links the anonymous visitor to your authenticated user. |
Path=/, SameSite=Lax, Secure when the page is
served over HTTPS (omitted on http://localhost for development), and
Max-Age=63072000 seconds. The cookies are NOT marked HttpOnly because
the SDK reads them on the client to honour the redundancy contract — the same
security model used by Stripe.js, Segment, and PostHog for their identity cookies.
Legal basis under GDPR and ePrivacy
For most product analytics, ePrivacy classifies these cookies as "strictly necessary" or "functional" rather than marketing/advertising — they exist to make your product work and to give you the operational metrics you need to run it, not to profile users for ads. That said, "strictly necessary" is a high bar in some jurisdictions and many sites disclose product analytics cookies as requiring consent. The conservative posture is: get consent before initialising the SDK, especially in the EU/UK.
Under GDPR Article 6, the lawful basis for processing the data collected through these cookies is typically:
- Consent (Art. 6(1)(a)) — recommended default for EU/UK visitors. See Wiring up to a consent banner.
- Legitimate interests (Art. 6(1)(f)) — defensible for purely operational metrics where you've performed a balancing test, but consent is safer.
Under CCPA/CPRA, no Crossdeck-supplied cookie qualifies as a "sale" or "share" of personal information by default — Crossdeck never enriches with third-party data, never sells anything, and operates as your data processor. Your privacy notice should still cover the "Right to Know" and "Right to Delete" for data collected via the SDK.
Drop-in template
Copy this block into the "Cookies" or "Tracking technologies" section of your privacy page. Replace bracketed placeholders. Keep the language; the wording has been written to satisfy ePrivacy + GDPR + CCPA disclosure expectations without overpromising or underpromising.
## Analytics & product cookies — Crossdeck This site uses [Crossdeck](https://cross-deck.com) to verify subscription state, count visitors, and surface product analytics. The Crossdeck Web SDK installs two first-party cookies on this domain: | Cookie name | Type | Expires | Purpose | |-----------------------|---------------------|-----------|----------------------------------------------------------------| | `crossdeck:anon_id` | First-party · functional/analytics | 2 years (Safari: 7 days) | Stores a random anonymous visitor ID so we count returning visitors as the same person. Contains no personal data. | | `crossdeck:cdcust_id` | First-party · functional/analytics | 2 years (Safari: 7 days) | Stores your Crossdeck customer record ID after you sign in to [Your Service]. Contains no personal data — it's an opaque internal identifier. | The same values are also stored in your browser's `localStorage` for redundancy. Crossdeck uses these to keep the same visitor identity stable across sessions even if one store is cleared (e.g. by Safari Intelligent Tracking Prevention or "Clear site data"). No other storage entries are created by the Crossdeck SDK. **What the SDK observes about your device.** When the SDK sends an event, it includes operating system family and version, browser family and version, language, time zone, screen size, and viewport size. It also records the originating country (derived server-side from your IP address by Cloudflare's network edge). It does not record your IP address, your precise location, your email address, or any payment details. **Legal basis.** We process this data on the basis of [your legal basis choice — typically `consent` in the EU/UK or `legitimate interests` elsewhere]. Crossdeck acts as a data processor on our behalf under a data processing agreement. **Disabling Crossdeck cookies.** You can opt out of Crossdeck's storage by [link to your cookie banner / consent settings]. When you opt out, [Your Company] instructs the Crossdeck SDK to run in in-memory mode (`persistIdentity: false`) — no cookies, no `localStorage` entries, identity is recreated on each page load. **Retention & data subject rights.** Crossdeck retains analytics events for [your retention window — Crossdeck's default Free-tier retention is 90 days; paid tiers 12 months] and then deletes them permanently. You have the right to request access to, correction of, or deletion of any data Crossdeck holds about you — please contact [your privacy email] and we will forward the request to Crossdeck's data protection contact within [your SLA, e.g. 5 business days]. For Crossdeck's own privacy practices, see [cross-deck.com/legal/cookie-policy](https://cross-deck.com/legal/cookie-policy/).
If your privacy page is structured as a table of cookies, here's the same content as a one-row table you can drop in directly:
<tr> <td><code>crossdeck:anon_id</code></td> <td>First-party · functional/analytics</td> <td>Crossdeck (data processor)</td> <td>2 years (Safari: 7 days)</td> <td>Anonymous visitor ID — keeps the same visitor recognised across sessions for product analytics. No personal data.</td> </tr> <tr> <td><code>crossdeck:cdcust_id</code></td> <td>First-party · functional/analytics</td> <td>Crossdeck (data processor)</td> <td>2 years (Safari: 7 days)</td> <td>Crossdeck customer ID — set after the visitor signs in. Opaque internal identifier, no personal data.</td> </tr>
Disabling persistence with persistIdentity: false
If your visitor declines analytics cookies in your consent banner, initialise the SDK
with persistIdentity: false. The SDK then runs in-memory only — no
cookies, no localStorage writes — and identity is recreated on every
page load. When the visitor later grants consent, re-initialise with
persistIdentity: true (the default).
import { Crossdeck } from "@cross-deck/web";
// Strict-consent flow: skip persistence until the user opts in.
Crossdeck.init({
appId: "app_web_xxx",
publicKey: "cd_pub_live_xxx",
environment: "production",
persistIdentity: false,
});
// Once your consent banner returns "accepted":
function onAnalyticsConsentGranted() {
Crossdeck.init({
appId: "app_web_xxx",
publicKey: "cd_pub_live_xxx",
environment: "production",
persistIdentity: true, // default — explicit for clarity
});
}
persistIdentity: false disables BOTH the cookie and the
localStorage write. The SDK keeps a memory-only anonymousId so the
current page-load's events are still grouped together, but a refresh mints a new ID.
Trade-off: returning visitors look new in your dashboards until they consent.
Crossdeck as your data processor
Crossdeck operates as your data processor under GDPR Article 28. The data Crossdeck
holds about your visitors belongs to you — Crossdeck never resells it, never enriches
it with third-party datasets, and never makes it available to other tenants on the
platform. Tenant isolation is enforced at every query path
(every backend SQL query is scoped by project_id first).
For your records, our infrastructure runs on Google Cloud (us-east4 region by default; EU and ZA regions on request for Scale tier and above). Stripe, Apple, and Google Play credentials live in Google Cloud Secret Manager and are never duplicated into application logs.
A Data Processing Addendum (DPA) is part of the Crossdeck Master Services Agreement, available at cross-deck.com/legal/dpa (or by contacting legal@cross-deck.com).
Wiring up to a consent banner
The SDK has no built-in consent banner — that's a UX decision and most apps already ship one (OneTrust, Cookiebot, Iubenda, or a hand-rolled component). Two patterns work cleanly:
1. Initialise on consent
Don't call Crossdeck.init() until your banner reports "accepted." Pre-
consent visitors land with no SDK active and no events emitted. Simplest pattern;
cleanest legal posture.
2. Initialise immediately, persist only on consent
Init with persistIdentity: false on first paint. The SDK runs in memory
only — no cookies — so your event pipeline starts collecting the current page-load
without writing anything to disk. When your banner reports "accepted," call
Crossdeck.init({...persistIdentity: true}) a second time — the SDK
mints a fresh anonymousId and persists it from that point. We recommend this pattern
for the visibility into pre-consent landing pages it gives you.
FAQ
Are these cookies considered "necessary" or do I need consent?
Functional analytics cookies typically require consent in the EU/UK under the
ePrivacy Directive. The ICO (UK) and most EEA DPAs treat all non-strictly-necessary
cookies as requiring opt-in consent. Outside the EU/UK, consent is usually not
required by law but remains best practice. Use the persistIdentity: false
opt-out to honour declined consent in any jurisdiction.
Can I prefix the cookies with my own brand instead of crossdeck:?
Yes — pass storagePrefix: "yourbrand:" to Crossdeck.init.
The cookie names become yourbrand:anon_id and
yourbrand:cdcust_id. This is purely cosmetic — the data and lifetime
are identical.
Do the cookies travel cross-domain?
No. They are 1st-party cookies set on the domain the SDK is installed on, with
SameSite=Lax. They are never sent to cross-deck.com in a
cookie header — Crossdeck reads identity off the SDK's HTTP request body, not off
cookies. There is no third-party cookie involved.
What about iOS and Android SDKs?
The Crossdeck iOS and Android SDKs (in active development) use the platform's
native key-value storage (UserDefaults on iOS,
SharedPreferences on Android) — not cookies. Mobile cookie disclosure is
normally not required because no browser cookie store is involved, but most
comprehensive privacy notices still mention what's stored on-device.
What happens to the data if I stop using Crossdeck?
All project data is retained for 30 days after subscription cancellation, then hard- deleted. Export tools are available throughout that 30-day window so you can transfer everything before deletion. The DPA codifies this in writing.