Pełna Referencja JavaScript API

Kompletny kontrakt window.CookieZen. Podstawowe snippety znajdziesz w Integracji JavaScript API - szybki start. Wszystkie przepisy integracyjne (React, AbortController, Multi-tab, GTM, iframe) opisuje plik cookiezen-docs-api.md.

Korzystasz z Agentów AI typu Cursor, Claude Code lub Codex? Pobierz paczkę cookiezen-docs-api.md, która zawiera kompletną referencję API w jednym miejscu. Plik został dostosowany w celu przyspieszenia pracy z narzędziami LLM.

Kategorie zgód

KategoriaOpisDomyślnie
essentialNiezbędne do działania stronyZawsze true
preferencesCzat, personalizacja, systemy opiniifalse
analyticsGoogle Analytics, Hotjar, Clarityfalse
marketingFacebook Pixel, Google Ads, TikTokfalse

require() przyjmuje tylko trzy kategorie nieobowiązkowe (wszystko poza essential). essential jest read-only.

Metody window.CookieZen

MetodaSygnaturaOpis
ready()() => Promise<api>Rozwiązuje się po załadowaniu banera (bot/failsafe: natychmiast).
getConsent()() => SnapshotZawsze zwraca obiekt. Sprawdzaj .finalized.
hasConsent(cat)(cat) => booleanSynchroniczne sprawdzenie kategorii. hasConsent('essential') zawsze zwraca true (w każdym trybie).
hasResponse()() => booleantrue po decyzji użytkownika (accept/reject/customize/withdraw).
require(cat, opts?)(cat, {timeout?, signal?}) => Promise<true>Czeka na zgodę. Odrzuca z kodem błędu.
on(event, cb)(evt, cb) => apiSubskrypcja (łańcuchowa). 8 eventów (poniżej).
off(event, cb)(evt, cb) => apiAnuluje subskrypcję. Zawsze wywołuj przy czyszczeniu w SPA / React.
once(event, cb)(evt, cb) => apiSubskrypcja jednorazowa z automatycznym wyłączeniem.
accept()() => Promise<Snapshot>Akceptacja wszystkiego z poziomu kodu (action='accept'). Zamyka otwarty baner pierwszej wizyty.
decline()() => Promise<Snapshot>Odmowa z poziomu kodu (action='reject' lub 'withdraw'). Zamyka otwarty baner pierwszej wizyty.
submitConsent(partial)(Partial<Cats>) => Promise<Snapshot>Częściowe scalenie, action='customize'. Zamyka otwarty baner pierwszej wizyty.
show(opts?)({view?: 'main'|'details'}) => voidOtwiera centrum zgód.
showSettings()() => voidAlias show({view:'details'}). Dla integracji bez własnego JS użyj deep linku #cookiezen-settings (sekcja niżej).
onMarketingConsent(cb)skrótAlias on('marketing', cb).
onAnalyticsConsent(cb)skrótAlias on('analytics', cb).
onPreferencesConsent(cb)skrótAlias on('preferences', cb).
debug()() => DebugSnapshotDiagnostyka: listy zablokowanych skryptów.

Rekomendowane dla każdego klienta, który nie chce / nie może wkleić własnego JavaScriptu na stronie (CMS-y bez własnego JS: Shoper, Wix, prosty WordPress przez edytor treści).

Wystarczy odnośnik z hashem #cookiezen-settings. Baner sam wykryje go i otworzy widok szczegółów (kategorie + przełączniki + ID zgody, jeśli już zapisana).

Kod
<!-- Najprostsze: względny hash, działa na każdej podstronie -->
<a href="#cookiezen-settings">Ustawienia cookies</a>

<!-- Pełny URL, wklej w pole "URL" przycisku w CMS (Shoper menu, Wix link, itp.) -->
https://twoja-domena.pl/#cookiezen-settings

Jak to działa (routing zależy od stanu zgody użytkownika):

Stan użytkownikaCo się otwieraPo co
Powracający (zgoda już zapisana)Pływający popup: DOKŁADNIE to samo co kliknięcie pływającego przycisku (podsumowanie kategorii + „Pokaż szczegóły" z ID zgody i datą + przyciski „Anulowanie zgody" / „Zmień swoją zgodę")Deep link zastępuje pływający przycisk gdy klient go ukryje. UX musi być 1:1, żeby użytkownik dalej miał dostęp do ID i opcji zmiany/wycofania.
Pierwsza wizyta (brak zgody)Standardowy baner pierwszej wizyty (accept / reject / customize). Hash jest cicho konsumowany, więc odświeżenie po wyrażeniu zgody nie wyświetla niespodziewanego popupa.Główny baner ma już wszystkie przełączniki potrzebne nowemu użytkownikowi; otwieranie dedykowanego centrum byłoby duplikatem.

Mechanika:

  • Wejście na stronę z hashem + zapisana zgoda → pływający popup od razu.
  • Wejście z hashem + brak zgody → główny baner pierwszej wizyty, hash skonsumowany.
  • Kliknięcie w link w trakcie sesji uruchamia trzy ścieżki (bez przeładowania):

- hashchange: klasyczna nawigacja hashem,

- capture click na <a href*="#cookiezen-settings">: fallback dla SPA (Shoper Storefront itp.), z preventDefault w tej samej karcie,

- popstate: wstecz/naprzód z hashem w URL.

  • Krótkie okno deduplikacji (~100 ms) chroni przed podwójnym otwarciem gdy click i hashchange odpalą się razem (inaczej popup by się zamknął przez przełączenie).
  • target="_blank": baner nie przechwytuje kliknięcia; nowa karta otwiera się z hashem i obsługuje go przy załadowaniu.
  • Na wielostronicowych sklepach preferuj względny hash #cookiezen-settings zamiast absolutnego URL do strony głównej.
  • Hash jest konsumowany po obsłużeniu (baner usuwa go przez history.replaceState), więc odświeżenie nie zostawia śmieci w pasku adresu.
  • Skanery i boty: pominięte (baner w tych trybach w ogóle się nie pokazuje).

Kiedy preferować showSettings() w JS:

  • Reaktywny UI we własnym SPA (React/Vue): onClick={() => window.CookieZen.showSettings()}.
  • Centrum klienta z własną logiką (np. otwierane modale).

Gotowe snippety (link absolutny dla Twojej domeny + HTML + względny hash) znajdziesz w panelu CookieZen: Wygląd baneru → Pozycja przycisku ustawień cookies → Ukryty.

Snapshot

getConsent() zwraca obiekt o tym kształcie:

Kod
type Snapshot = {
  categories: { essential: true; preferences: boolean; analytics: boolean; marketing: boolean; };
  consentId: string | null;        // UUID po finalizacji
  timestamp: number | null;        // ms epoch
  policyVersion: string | null;
  action: 'accept' | 'reject' | 'customize' | 'withdraw' | null;
  finalized: boolean;              // false gdy użytkownik nie zatwierdził decyzji
};

getConsent() nigdy nie zwraca null. Przed finalizacją → finalized: false i null w metadanych. Sprawdzaj if (CookieZen.getConsent().finalized).

policyVersion

Wersja polityki skonfigurowana w panelu CMP (np. "v1.2"). Podbicie wersji unieważnia zapisaną zgodę przy następnym pełnym załadowaniu strony: cookie zgody i powiązana pamięć przeglądarki są czyszczone, finalized wraca do false, a wiszące require() nie rozwiążą się ze starej decyzji.

Reset następuje przy załadowaniu strony: w SPA bez przeładowania podbicie wersji na serwerze nie wymusi ponownego pytania o zgodę. Użyj location.reload() lub show(), jeśli potrzebujesz ponownego pytania w tej samej sesji.

Automatyczne usuwanie artefaktów first-party

Po decline(), wycofaniu (withdraw) lub submitConsent() z odmówioną kategorią, a także przy starcie strony dla powracającego użytkownika z odmową, CookieZen automatycznie usuwa first-party cookies, localStorage, sessionStorage i IndexedDB sklasyfikowane przez skaner w tej kategorii.

  • Sterowane klasyfikacją skanera; chronione są własne artefakty zgody CMP i krytyczne nazwy (koszyk, sesja).
  • W innych kartach: synchronizacja przez storage czyści też sessionStorage danej karty dla kategorii odmówionych.
  • Brak opcji wyłączenia, to część kontraktu zgody. Ręczne czyszczenie w unloadSdk() to opcjonalne zabezpieczenie dla zewnętrznych SDK.

Integracja przez iframe (placeholder)

Przy ręcznym blokowaniu zablokowany <iframe> (np. widget opinii Trustmate) jest ukryty i zastąpiony placeholderem (Shadow DOM) z przyciskiem zgody:

  1. Iframe nie ładuje się do czasu przyznania wymaganej kategorii.
  2. Klik w placeholder nadaje całą wymaganą kategorię i zamyka otwarty baner pierwszej wizyty (jeśli był widoczny).
  3. Po zgodzie placeholder znika, iframe się pokazuje; usunięcie iframe z DOM czyści placeholder.
  4. Współgra z on('change') i nie wymaga własnego kodu HTML od integratora.

Szczegóły i snippet: cookiezen-docs-api.md §10.10a.

Eventy

8 typów eventów w JS API. 5 z nich (ready, consent, change, accept, decline) ma odpowiadający DOM CustomEvent na window (zob. niżej); eventy kategorii (marketing/analytics/preferences) istnieją wyłącznie w JS API.

EventOd razu przy on()Przy kolejnych zmianach
consentTak, z aktualnym SnapshotTak (każda zmiana, informacyjny)
readyTak, natychmiastbrak
changeNieTak (gdy zmieniła się co najmniej jedna kategoria)
acceptNieTak (przejście do stanu sfinalizowanego, min. jedna kategoria przyznana)
declineNieTak (przejście do stanu sfinalizowanego, wszystkie kategorie odmówione)
marketing / analytics / preferencesTak, jeśli przyznanaTak (przy zmianie false → true)

Doprecyzowanie semantyki accept / decline (przejście = zmiana co najmniej jednej kategorii LUB pierwsza finalizacja decyzji):

  • Pierwsza akceptacja lub customize z min. jedną przyznaną kategorią → accept.
  • "Odrzuć wszystko" jako pierwsza decyzja → decline. Emituje się, mimo że kategorie się nie zmieniły (stan domyślny to już wszystkie odmówione; przejściem jest sama finalizacja). Uwaga: change w tym przypadku NIE odpala (brak zmiany kategorii).
  • customize obniżające zgodę, ale z min. jedną kategorią nadal przyznaną (np. cofnięcie analytics przy zachowanym marketingu) → accept, nie decline. Logikę per kategoria buduj na payload.categories lub na evencie change.
  • Wycofanie kończące się odmową wszystkich kategorii → decline.
  • Druga decline() z rzędu (wszystkie kategorie już odmówione) → brak eventu (nie zaszło żadne przejście).

Argument callbacka ready: callback dostaje instancję API (window.CookieZen), nie Snapshot.

Kolejność emisji przy zmianie stanu: changeaccept lub decline → eventy kategorii → consent.

Powracający użytkownik (bez zbędnych eventów): przy przeładowaniu strony consent odpala się (informacyjny), ale change/accept/decline NIE, bo w bieżącej sesji stan się nie zmienił. Rozwiązuje klasyczny problem CMP-ów (Cookiebot OnAccept wywołuje się przy każdym załadowaniu strony).

Payload change

Kod
type ChangePayload = Snapshot & {
  previousCategories: ConsentCategories;
  changedCategories: ('marketing' | 'analytics' | 'preferences')[];
};

DOM Events

Pięć eventów ma odpowiadający CustomEvent na window (dla wzorca addEventListener). Eventy kategorii nie mają odpowiedników DOM - nie istnieje np. cookiezen:marketing; użyj CookieZen.on('marketing', cb).

Eventevent.detail
cookiezen:ready{categories, finalized}, emitowany przy każdym załadowaniu strony po inicjalizacji
cookiezen:consentChangePayload, informacyjny
cookiezen:changeChangePayload, realna zmiana stanu
cookiezen:acceptSnapshot
cookiezen:declineSnapshot
Kod
window.addEventListener('cookiezen:accept', function(e) {
  if (e.detail.categories.marketing) initPixel();
});

Kody błędów: kontrakt odrzucenia require()

require() rzuca Error z polem code. Stabilny kontrakt:

KodKiedyerr.category
CONSENT_DECLINEDhasResponse() && !hasConsent(cat); dodatkowo natychmiast w trybie bot/failsafeTak
CONSENT_TIMEOUTMinął opts.timeout msTak
CONSENT_ABORTEDopts.signal.abort()Tak
INVALID_CATEGORYcat ∉ {marketing, analytics, preferences}Nie

CONSENT_DECLINED obejmuje dwie sytuacje: (1) tryb normalny - użytkownik sfinalizował decyzję i kategoria jest odmówiona; (2) tryb bot/failsafe - baner nigdy się nie załaduje, więc zgoda nigdy nie zostanie przyznana i require() odrzuca natychmiast, mimo finalized: false. Traktuj ten kod jako "zgoda jest i będzie niedostępna", a komunikaty dla użytkownika formułuj neutralnie (np. "włącz cookies marketingowe, aby zobaczyć tę treść"), a nie oskarżycielsko ("odrzuciłeś zgodę").

Zasady timeoutu

  • Bez opts.timeoutconsole.warn + twardy limit 30 minut (fail-safe).
  • opts.timeout: 30000 → odrzucenie po 30 sekundach.
  • opts.timeout: 0 lub Infinity → świadomie bez limitu, bez ostrzeżenia w konsoli.

Uwaga: timeout: 0 oznacza tu "bez limitu", a NIE "odrzuć natychmiast". To odstępstwo od konwencji znanej z innych API - nie przenoś tamtego przyzwyczajenia.

Wzorce inicjalizacji

Kolejka poleceń (Twój skrypt po naszym loaderze)

API jest dostępne jako stub od razu po załadowaniu loadera, a wywołania są kolejkowane i opróżniane po inicjalizacji banera. Wszystkie metody Promise (require, accept, decline, submitConsent, ready) obsługują Promise już w stubie.

Metod synchronicznych nie da się zakolejkować, więc stub odpowiada na nie zdefiniowanym stanem sprzed inicjalizacji (stabilny kontrakt):

  • getConsent() zwraca pełny Snapshot z finalized: false, wszystkimi kategoriami nieobowiązkowymi na false i null w metadanych.
  • hasConsent(cat) zwraca true tylko dla 'essential', false dla pozostałych - nawet gdy użytkownik ma zapisaną zgodę (stub nie czyta pamięci przeglądarki).
  • hasResponse() zwraca false.

Nie traktuj hasConsent('marketing') === false odczytanego ze stubu jako decyzji użytkownika. Do decyzji służą require() / on('change'), które rozstrzygają się na realnym stanie po inicjalizacji banera.

Kod
<script src="https://cz-cdn.com/api/cmp/loader?site_key=TWOJ_KEY"></script>
<script>
  CookieZen.on('change', function ({ categories }) {
    console.log('marketing:', categories.marketing);
  });
</script>

CookieZenCallback_OnReady (Twój skrypt może załadować się PRZED naszym loaderem)

Na platformach z asynchronicznym ładowaniem (Shoper Storefront, IdoSell, Magento) nie ma gwarancji, że nasz loader wykona się jako pierwszy. Wtedy zdefiniuj globalną funkcję, a CookieZen wywoła ją po inicjalizacji niezależnie od kolejności. Wzorzec analogiczny do CookiebotCallback_OnLoad z Cookiebot.

Kod
<script>
window.CookieZenCallback_OnReady = function (CookieZen) {
  CookieZen.require('marketing', { timeout: 30000 }).then(initWidget).catch(function () {});
  CookieZen.on('change', function (p) {
    if (p.categories.marketing) initWidget();
    else destroyWidget();
  });
};
</script>

Późna rejestracja: jeśli zarejestrujesz CookieZenCallback_OnReady PO inicjalizacji CookieZen, nie odpali się. Wtedy użyj CookieZen.on('ready', cb); ready odpala się od razu przy subskrypcji.

Kilka wtyczek na jednym sklepie: CookieZenCallback_OnReady to pojedyncza globalna funkcja - ostatnie przypisanie wygrywa i po cichu wyłącza poprzednie (to samo ograniczenie ma Cookiebot). Jeśli Twoja wtyczka może współistnieć z innymi, zamiast nadpisywać - dowiąż się do poprzedniej wartości:

Kod
(function () {
  var prev = window.CookieZenCallback_OnReady;
  window.CookieZenCallback_OnReady = function (CookieZen) {
    if (typeof prev === 'function') { try { prev(CookieZen); } catch (e) {} }
    // ... Twoja integracja ...
  };
})();

Bot / failsafe

Gdy baner nie załaduje się (wykrycie bota lub failsafe timeout 7s), window.CookieZen jest podmieniany na mini-API:

  • require() odrzuca natychmiast z CONSENT_DECLINED, mimo finalized: false (szczegóły w sekcji o kodach błędów).
  • hasConsent(cat) zwraca true tylko dla 'essential'; hasResponse() zwraca false.
  • getConsent() zwraca Snapshot z finalized: false i wszystkimi kategoriami nieobowiązkowymi na false.
  • accept() / decline() / submitConsent() zwracają Snapshot sprzed finalizacji (no-op).
  • on() / once() przyjmują subskrypcje, ale odpalają tylko consent i ready (natychmiast, przez fire-on-subscribe). Pozostałe eventy nigdy nie wystąpią.
  • DOM event cookiezen:ready emitowany.

Kod z try/catch na require() działa identycznie w trybie normalnym i bot/failsafe.

Cookie cmp_consent_<siteKey> (np. cmp_consent_site_abc123) na domenie głównej, SameSite=Lax. Nazwa zawiera Twój site_key, dzięki czemu zgoda jest izolowana między stronami współdzielącymi tę samą domenę nadrzędną:

Kod
{
  "finalized": true,
  "consentId": "c_xyz...",
  "categories": {"essential": true, "preferences": false, "analytics": true, "marketing": false},
  "policyVersion": "v1.2",
  "timestamp": 1715000000000,
  "action": "customize"
}
Kod
// Zastąp <siteKey> swoim site_key (lub przeskanuj cookies po prefiksie cmp_consent_).
$raw = $_COOKIE['cmp_consent_<siteKey>'] ?? null;
if ($raw) {
    $c = json_decode(urldecode($raw), true);
    if ($c && !empty($c['finalized'])) {
        $marketing = !empty($c['categories']['marketing']);
        $action = $c['action'] ?? null;
    }
}
Kod
const raw = req.cookies?.['cmp_consent_<siteKey>'];
if (raw) {
  const c = JSON.parse(decodeURIComponent(raw));
  if (c?.finalized) {
    const marketing = c.categories?.marketing === true;
  }
}

Stare cookies bez actionaction: null, reszta pól wczytuje się normalnie.

Typy TypeScript

Kod
interface ConsentCategories {
  essential: true;
  preferences: boolean;
  analytics: boolean;
  marketing: boolean;
}

interface Snapshot {
  categories: ConsentCategories;
  consentId: string | null;
  timestamp: number | null;
  policyVersion: string | null;
  action: 'accept' | 'reject' | 'customize' | 'withdraw' | null;
  finalized: boolean;
}

interface ChangePayload extends Snapshot {
  previousCategories: ConsentCategories;
  changedCategories: ('marketing' | 'analytics' | 'preferences')[];
}

interface RequireOptions {
  timeout?: number;       // ms; 0/Infinity = bez limitu
  signal?: AbortSignal;
}

type ConsentEvent =
  | 'consent' | 'ready' | 'change' | 'accept' | 'decline'
  | 'marketing' | 'analytics' | 'preferences';

interface ConsentError extends Error {
  code: 'CONSENT_DECLINED' | 'CONSENT_TIMEOUT' | 'CONSENT_ABORTED' | 'INVALID_CATEGORY';
  category?: 'marketing' | 'analytics' | 'preferences';
}

interface CookieZenAPI {
  ready(): Promise<CookieZenAPI>;
  getConsent(): Snapshot;
  hasConsent(category: string): boolean;
  hasResponse(): boolean;
  require(category: 'marketing' | 'analytics' | 'preferences', opts?: RequireOptions): Promise<true>;
  // Payload callbacka zależnie od eventu: 'change' -> ChangePayload;
  // 'ready' -> instancja API (CookieZenAPI, nie Snapshot); pozostałe -> Snapshot.
  on(event: ConsentEvent, cb: (payload: Snapshot | ChangePayload | CookieZenAPI) => void): CookieZenAPI;
  off(event: ConsentEvent, cb: Function): CookieZenAPI;
  once(event: ConsentEvent, cb: (payload: Snapshot | ChangePayload | CookieZenAPI) => void): CookieZenAPI;
  accept(): Promise<Snapshot>;
  decline(): Promise<Snapshot>;
  submitConsent(partial: Partial<{ marketing: boolean; analytics: boolean; preferences: boolean; }>): Promise<Snapshot>;
  show(opts?: { view?: 'main' | 'details' }): void;
  showSettings(): void;
  onMarketingConsent(cb: (snap: Snapshot) => void): CookieZenAPI;
  onAnalyticsConsent(cb: (snap: Snapshot) => void): CookieZenAPI;
  onPreferencesConsent(cb: (snap: Snapshot) => void): CookieZenAPI;
  debug(): unknown;
}

declare global {
  interface Window {
    CookieZen: CookieZenAPI;
    CookieZenCallback_OnReady?: (api: CookieZenAPI) => void;
  }
  interface WindowEventMap {
    'cookiezen:ready':   CustomEvent<{ categories: ConsentCategories; finalized: boolean }>;
    'cookiezen:consent': CustomEvent<ChangePayload>;
    'cookiezen:change':  CustomEvent<ChangePayload>;
    'cookiezen:accept':  CustomEvent<Snapshot>;
    'cookiezen:decline': CustomEvent<Snapshot>;
  }
}

Gdy Google Consent Mode jest włączony dla strony (ustawienie domyślne; można je wyłączyć w panelu CookieZen), CookieZen emituje standardowe sygnały Google Consent Mode v2 do window.dataLayer (gtag('consent', 'update', ...)) po każdej decyzji użytkownika. Dzięki temu narzędzia Google (GTM, GA4, Google Ads) oraz każda integracja oparta na Consent Mode automatycznie respektują zgodę bez dodatkowego kodu.

Jeśli Twoja integracja nasłuchuje Consent Mode, poniżej mapowanie kluczy na kategorie CookieZen:

Klucz Consent ModeKategoria CookieZen
ad_storagemarketing
ad_user_datamarketing
ad_personalizationmarketing
analytics_storageanalytics
functionality_storagepreferences
personalization_storagepreferences

Kształt debug() (DebugSnapshot)

debug() zwraca migawkę stanu wyłącznie do diagnostyki. Nie buduj na niej kodu produkcyjnego: kształt nie jest częścią stabilnego kontraktu i służy tylko do ręcznej diagnostyki.

Kod
type DebugSnapshot = {
  version: number;            // wersja formatu snapshotu (obecnie 2)
  siteKey: string;
  url: string;               // window.location.href w chwili wywołania
  uptimeMs: number;          // ms od startu banera (diagnostyka czasowa)
  consent: {
    categories: ConsentCategories;
    method: string;          // 'none' | 'unknown' | zapisana metoda zgody (np. 'accept')
    loaderHadConsent: boolean;
  };
  // Rejestr zablokowanych zasobów (skrypty + iframe) - do diagnostyki automatycznego/ręcznego blokowania.
  blocked: Array<{
    src: string;             // URL lub '(inline)'
    kind: 'inline' | 'external' | 'iframe';
    categories: string[];    // kategorie wymagane do odblokowania
    reason: string;          // 'manual-data-attr' | 'auto-detect' | 'default-preferences' | ...
    by: 'loader' | 'banner'; // kto zablokował
    t: number;               // znacznik czasu (ms)
  }>;
  armed: unknown[];          // skrypty uzbrojone (armed) - rejestr diagnostyczny
  unknown: unknown[];        // zasoby o nierozpoznanej kategorii
  stats: {
    blockedTotal: number;
    blockedByLoader: number;
    blockedByBanner: number;
    armedTotal: number;
    unknownTotal: number;
    pendingBlockedScripts: number;  // <script type="text/plain" data-cmp-category> czekające na zgodę
    pendingBlockedFrames: number;   // <iframe> czekające na zgodę
  };
  print: () => void;         // czytelny wydruk snapshotu do konsoli narzędzi deweloperskich
};

Przepisy integracyjne

Gotowe wzorce (React z czyszczeniem, once('accept'), AbortController, accept / decline z poziomu kodu, kontekstowa zgoda, GTM Custom HTML, synchronizacja iframe, synchronizacja wielu kart, integracja SDK plugin z load + unload + graceful fallback, cross-CMP przez Consent Mode) znajdziesz w cookiezen-docs-api.md, sekcja §10 Recipes.