ConsentStackDocs

JavaScript API

Complete API reference for the ConsentStack JavaScript SDK.

Once the ConsentStack script loads, the full API is available at window.consentstack. Use it to read consent state, set consent programmatically, listen for changes, and control the banner and preferences UI.

TypeScript types

The key types you will work with:

// Consent state: a map of category ID to granted/denied
type ConsentCategories = Record<string, boolean>
// Example: { essential: true, analytics: false, marketing: false }

// SDK event names
type ConsentEventType =
  | "ready"
  | "consent"
  | "error"
  | "preferences:open"
  | "preferences:close"

// Event payloads
type ConsentEventData = {
  ready: {
    config: ConsentConfig | null
    consent: Record<string, boolean> | null
    effectiveConsent: Record<string, boolean> | null
    hasDecision: boolean
  }
  consent: Record<string, boolean>
  error: Error
  "preferences:open": undefined
  "preferences:close": undefined
}

// Full config object (returned by getConfig)
interface ConsentConfig {
  appearance: Appearance
  content: Content
  categories: ConsentCategory[]
  showReentryButton: boolean
}

These methods let you read and write consent state directly.

getConsent()

Returns the visitor's effective consent: the per-category state the request blocker acts on right now. Precedence is explicit decision, then GPC forcing, then implicit grant by consent model. A fresh visitor in an opt-out region gets { analytics: true, ... } before they touch the banner; the same visitor in an opt-in region gets { analytics: false, ... }.

Returns null only before initialization when no edge-seeded state is available. With the standard two-tag CDN install, consent state is stamped into the first script at the edge, so getConsent() is correct from the moment the script parses.

getConsent(): Record<string, boolean> | null
const consent = window.consentstack.getConsent()

if (consent?.analytics) {
  loadCustomAnalytics()
}

Because getConsent() returns effective consent, it is non-null even before the visitor has chosen. To check whether an explicit decision exists (for example, to decide whether to show a custom banner), use hasDecision(), not getConsent() === null.

hasDecision()

Returns whether an explicit consent decision exists, either stored from a prior visit or made this session. This replaces the old getConsent() === null idiom for custom-banner logic.

hasDecision(): boolean
if (!window.consentstack.hasDecision()) {
  // No explicit choice yet: show your custom banner
  showMyCustomBanner()
}

setConsent()

Sets consent programmatically. This saves the decision to local storage, logs it to the ConsentStack server, activates or blocks scripts accordingly, and hides the banner.

setConsent(categories: Record<string, boolean>): Promise<void>
ParameterTypeDescription
categoriesRecord<string, boolean>A map of category IDs to true (granted) or false (denied).
// Grant analytics, deny marketing
await window.consentstack.setConsent({
  essential: true,
  analytics: true,
  marketing: false,
})

If this is the visitor's first consent decision, it is logged as an initial event. Subsequent calls are logged as update events. If you revoke a previously granted category, the SDK shows a refresh prompt since already-executed scripts cannot be undone.

hasConsent()

Returns whether the category is effectively granted right now. Precedence: GPC-forced denial, then the stored decision, then implicit grant by consent model. In opt-in regions (e.g. GDPR), this is false until the visitor explicitly grants. In opt-out regions, it is true until the visitor explicitly rejects. This is the same state the request blocker and data-cs-category script blocking act on, and it is correct at parse time with the standard two-tag install.

hasConsent(category: string): boolean
if (window.consentstack.hasConsent("analytics")) {
  loadCustomAnalytics()
}

onConsentChange()

Subscribes to effective-consent changes. Your callback fires every time effective consent changes, whether from the banner, preferences panel, a setConsent() call, or init reconciliation (when initialization resolves a state that differs from what was known before). Returns an unsubscribe function.

onConsentChange(
  callback: (consent: Record<string, boolean>) => void
): () => void
const unsubscribe = window.consentstack.onConsentChange((consent) => {
  if (consent.analytics) {
    initAnalytics()
  }
})

// Later, stop listening
unsubscribe()

UI operations

Control the consent banner and preferences panel from your own code.

showBanner()

Displays the consent banner. In the default banner mode, the SDK shows the banner automatically for new visitors. Use this method to re-show it manually, for example, in headless mode where you handle the UI trigger yourself.

showBanner(): void
document.getElementById("manage-cookies").addEventListener("click", () => {
  window.consentstack.showBanner()
})

showPreferences()

Opens the preferences panel where visitors can toggle individual consent categories.

showPreferences(): void
// Open preferences from a footer link
document.getElementById("cookie-settings").addEventListener("click", () => {
  window.consentstack.showPreferences()
})

You can also open preferences by navigating to #cs-preferences. The SDK listens for this hash automatically.

hidePreferences()

Closes the preferences panel programmatically. Emits a preferences:close event and re-shows the re-entry button if the visitor has already made a consent decision.

hidePreferences(): void

isPreferencesOpen()

Returns whether the preferences panel is currently visible.

isPreferencesOpen(): boolean
if (!window.consentstack.isPreferencesOpen()) {
  window.consentstack.showPreferences()
}

setLanguage()

Switches the banner and preferences panel to a different language at runtime. Useful when your site has its own language switcher and you want the consent UI to follow it.

setLanguage(language: string): Promise<void>

Accepts ISO language codes with or without a region subtag ("ja" and "ja-JP" are equivalent). Only languages you have enabled for the site in the dashboard take effect; requests for other languages are ignored. The choice persists across page loads.

// Follow the page language (e.g. after a locale-prefixed navigation)
const pageLang = document.documentElement.lang || "en"
window.consentstack.setLanguage(pageLang)

Because setLanguage() runs after the banner has rendered, switching this way briefly shows the previous language. For localized sites, declare the page language before the SDK loads instead. The banner then renders in the page language from the first paint:

<script src="https://cdn.consentstack.io/consent-core.js?k=YOUR_SITE_KEY" data-lang="auto"></script>

data-lang="auto" follows the page's <html lang> attribute; you can also pass a fixed code like data-lang="ja", or set window.consentstackLang = document.documentElement.lang in an inline script above the consent tags if you cannot modify the script tag attributes.

Events

Subscribe to SDK lifecycle events for fine-grained control over your integration.

on()

Subscribes to a named SDK event. Returns an unsubscribe function.

on<T extends ConsentEventType>(
  event: T,
  callback: (data: ConsentEventData[T]) => void
): () => void
EventPayloadFires when
"ready"{ config, consent, effectiveConsent, hasDecision }SDK has initialized and consent state is known. consent is the stored explicit decision (or null), effectiveConsent is the state the request blocker acts on, hasDecision is whether an explicit decision exists.
"consent"Record<string, boolean>Effective consent changes (same as onConsentChange), including init reconciliation when initialization resolves a state that differs from what was known before.
"error"ErrorAn SDK error occurs, including init aborts (site not configured, domain not registered for the site key).
"preferences:open"undefinedPreferences panel opens.
"preferences:close"undefinedPreferences panel closes.
// Wait for the SDK to be ready before reading state
const unsubscribe = window.consentstack.on("ready", ({ config, effectiveConsent, hasDecision }) => {
  console.log("SDK ready. Region categories:", config?.categories)
  console.log("Effective consent:", effectiveConsent)
  console.log("Explicit decision exists:", hasDecision)
  unsubscribe()
})

// Track when users open preferences
window.consentstack.on("preferences:open", () => {
  analytics.track("consent_preferences_opened")
})

Configuration

getConfig()

Returns the loaded consent configuration object, or null if the SDK has not finished initializing. The config includes appearance settings, content strings, category definitions, and re-entry button settings.

getConfig(): ConsentConfig | null
const config = window.consentstack.getConfig()

if (config) {
  console.log("Categories:", config.categories.map(c => c.name))
  console.log("Layout:", config.appearance.layout)
}

getRegion()

Returns the resolved region string for the current visitor (e.g. "US-CA", "gdpr"), or null if not yet resolved.

getRegion(): string | null
const region = window.consentstack.getRegion()
if (region === "US-CA") {
  // Show the California-specific privacy choices link
}

Source: packages/sdk/src/types.ts:397. Wired through core.getRegion() at packages/sdk/src/index.ts:182.

getGeo()

Returns a GeoDetails object with city, postal code, latitude, longitude, region (e.g. US state code), timezone, and continent, or null if geo-detail is not enabled or available. Each field is string | null.

getGeo(): GeoDetails | null
const geo = window.consentstack.getGeo()
if (geo?.region === "CA") {
  // Likely California (when the resolved region is also US)
}

The method is declared at packages/sdk/src/types.ts:400; the GeoDetails shape is at packages/sdk/src/types.ts:184-192. Geo-detail availability is gated by plan. Wired at packages/sdk/src/index.ts:183.

clearCache()

Clears the local config cache and re-fetches the config from origin, bypassing the edge cache. Returns a Promise<void>.

clearCache(): Promise<void>
await window.consentstack.clearCache()

This is a developer tool. It is logged in ?cs-debug=true output. Implementation at packages/sdk/src/core.ts:1378-1411.

debug()

Prints SDK state to the browser console: site key, mode, current consent, and config version. Pass { verbose: true } to include the full config and state objects.

debug(options?: { verbose?: boolean }): void
// Quick overview
window.consentstack.debug()

// Full state dump
window.consentstack.debug({ verbose: true })

Debug query parameter

Add ?cs-debug=true (or ?cs-debug=1) to any page URL to enable the init waterfall, a console-printed timing breakdown of every SDK initialization phase:

Example output:

[ConsentStack] Init waterfall — ~XXms total
Config fetch              ~Xms  ██████████████████  (fresh)
Inject styles              ~Xms  █
Consent reconciliation     ~Xms  █  (none)
Platform adapters          ~Xms  █
Script blocker             ~Xms  █  (N rules)
Banner/UI                  ~Xms  █  (banner shown)

This is useful for diagnosing slow init times or verifying that config caching is working. No code changes required; just append the parameter to your URL.

Command queue

Before consent-core.js finishes loading, window.consentstack is an array. Push commands as tuples; the SDK replays them in order once initialized.

window.consentstack = window.consentstack || []
window.consentstack.push(["on", "ready", (data) => {
  console.log("Consent ready:", data.consent)
}])
window.consentstack.push(["setConsent", { analytics: true }])

Each pushed call is a tuple of [methodName, ...args]. The queue type is ConsentStackQueuedCall = [keyof ConsentStackAPI, ...unknown[]] (packages/sdk/src/types.ts:534). After the core loads, window.consentstack is replaced with the live API object (packages/sdk/src/index.ts:38-62).

Waiting for the SDK

The SDK initializes asynchronously. If your code runs before the script has loaded, window.consentstack will be undefined. Use the ready event or a guard check:

// Option 1: Guard check
if (window.consentstack) {
  const consent = window.consentstack.getConsent()
}

// Option 2: Wait for ready event
function onSdkReady() {
  window.consentstack.on("ready", ({ effectiveConsent }) => {
    console.log("Effective consent:", effectiveConsent)
  })
}

if (window.consentstack) {
  onSdkReady()
} else {
  // Poll until available
  const interval = setInterval(() => {
    if (window.consentstack) {
      clearInterval(interval)
      onSdkReady()
    }
  }, 50)
}

First-page analytics

To fire an analytics event on the landing page, gate it on hasConsent():

window.consentstack = window.consentstack || []
window.consentstack.push(["on", "ready", () => {
  if (window.consentstack.hasConsent("analytics")) {
    trackPageView()
  }
}])

With the standard two-tag install, consent state is seeded from the edge before your page scripts run, so hasConsent() reflects real effective consent on the very first page load. Landing page views from visitors whose consent model grants analytics (implicitly or from a stored decision) are no longer lost while the SDK initializes.

If effective consent changes later (the visitor accepts, rejects, or updates preferences), react with onConsentChange():

window.consentstack.push(["onConsentChange", (consent) => {
  if (consent.analytics) {
    trackPageView()
  }
}])

What's next

  • Automatic script blocking: how the SDK manages third-party scripts based on consent state
  • Consent models: understand opt-in, opt-out, notice, and notice-required behavior
  • Quickstart: add ConsentStack to your site in under 5 minutes
  • MCP Server: connect Claude, Cursor, or any MCP client and manage sites, banner config, and compliance scans by agent