# Events



OptinStack emits events through two channels simultaneously:

1. **SDK events** via `OptinStack.events.on()` / `OptinStack.events.off()`
2. **DOM CustomEvents** on `document.documentElement` with the `optinstack:` prefix

Both fire for every event. Use whichever fits your integration.

<Callout type="info">
  SDK initialization is not an event. Await `window.OptinStack.ready` instead of listening for a `ready` event.
</Callout>

## Subscribing [#subscribing]

### SDK event API [#sdk-event-api]

```js
await window.OptinStack.ready;

function handler(detail) {
  console.log(detail);
}

window.OptinStack.events.on('consent-updated', handler);
window.OptinStack.events.off('consent-updated', handler);
```

### Wildcard listener [#wildcard-listener]

```js
window.OptinStack.events.on('*', (eventName, detail) => {
  console.log('Event:', eventName, detail);
});
```

### DOM CustomEvents [#dom-customevents]

```js
document.documentElement.addEventListener('optinstack:consent-updated', (e) => {
  console.log('Consent changed:', e.detail.choices);
});
```

Events have `bubbles: true` so they propagate through the document tree.

## Consent events [#consent-events]

### `accept-all` [#accept-all]

Fires immediately before all categories are set to `true`.

```js
window.OptinStack.events.on('accept-all', () => {
  console.log('User accepted all categories');
});
```

### `reject-all` [#reject-all]

Fires immediately before optional categories are set to `false`.

```js
window.OptinStack.events.on('reject-all', () => {
  console.log('User rejected optional categories');
});
```

### `consent-updated` [#consent-updated]

Fires after consent choices change. This is the primary event for reacting to consent state.

```js
window.OptinStack.events.on('consent-updated', (detail) => {
  console.log(detail.id); // Consent record ID
  console.log(detail.choices); // { necessary, analytics, marketing, preferences }
  console.log(detail.previousChoices); // Previous state (if available)
  console.log(detail.action); // "accept_all" | "reject_all" | "submit" | "preferences"
  console.log(detail.source); // "banner" | "preferences" | "api"
  console.log(detail.timestamp); // Unix timestamp
});
```

<Callout type="info">
  This event only fires when choices actually change. Duplicate updates are deduplicated.
</Callout>

### `consent-cleared` [#consent-cleared]

Fires after `renew()` clears stored consent and resets choices to the active rule defaults. Use this when you need to hide consent-gated UI until the visitor makes a new choice.

```js
window.OptinStack.events.on('consent-cleared', (detail) => {
  console.log(detail.id);
  console.log(detail.choices);
  console.log(detail.timestamp);
});
```

### `open-preferences` [#open-preferences]

Fires when the preferences dialog should open. You can also dispatch `optinstack:open-preferences` on `window` from page code. See [Banner control](/banner-control).

## Blocking events [#blocking-events]

These events fire when the runtime blocks or unblocks third-party resources based on consent.

### `script-blocked` / `script-unblocked` [#script-blocked--script-unblocked]

```js
window.OptinStack.events.on('script-blocked', (detail) => {
  console.log(detail.src);
  console.log(detail.element); // HTMLScriptElement
  console.log(detail.categories); // ["analytics", "marketing"]
});
```

### `iframe-blocked` / `iframe-unblocked` [#iframe-blocked--iframe-unblocked]

```js
window.OptinStack.events.on('iframe-blocked', (detail) => {
  console.log(detail.src);
  console.log(detail.element); // HTMLIFrameElement
  console.log(detail.categories);
});
```

### `map-blocked` / `map-unblocked` [#map-blocked--map-unblocked]

Specific to Webflow map embeds:

```js
window.OptinStack.events.on('map-blocked', (detail) => {
  console.log(detail.element);
  console.log(detail.categories);
});
```

### `storage-blocked` [#storage-blocked]

Fires when access to `localStorage` or `sessionStorage` is blocked for a key that requires consent:

```js
window.OptinStack.events.on('storage-blocked', (detail) => {
  console.log(detail.key);
  console.log(detail.storageType); // "localStorage" | "sessionStorage"
  console.log(detail.categories);
});
```

### `cookie-blocked` [#cookie-blocked]

Fires when a tracker is blocked or purged:

```js
window.OptinStack.events.on('cookie-blocked', (detail) => {
  console.log(detail.name);
  console.log(detail.categories);
  console.log(detail.action); // "blocked" | "purged"
});
```

## Reporting events [#reporting-events]

### `consent-sync-failed` [#consent-sync-failed]

Fires when the runtime cannot record a consent update:

```js
window.OptinStack.events.on('consent-sync-failed', (detail) => {
  console.error(detail.error, detail.payload);
});
```

## Event reference [#event-reference]

| Event                 | Payload                                                          | Description                                  |
| --------------------- | ---------------------------------------------------------------- | -------------------------------------------- |
| `accept-all`          | :                                                                | All categories accepted                      |
| `reject-all`          | :                                                                | Optional categories rejected                 |
| `consent-updated`     | `{ id, choices, previousChoices?, action?, source?, timestamp }` | Consent state changed                        |
| `consent-cleared`     | `{ id, choices, timestamp }`                                     | Stored consent cleared and defaults restored |
| `open-preferences`    | :                                                                | Preferences dialog should open               |
| `script-blocked`      | `{ src, element, categories }`                                   | Script blocked                               |
| `script-unblocked`    | `{ src, element, categories }`                                   | Script unblocked                             |
| `iframe-blocked`      | `{ src, element, categories }`                                   | Iframe blocked                               |
| `iframe-unblocked`    | `{ src, element, categories }`                                   | Iframe unblocked                             |
| `map-blocked`         | `{ element, categories }`                                        | Map embed blocked                            |
| `map-unblocked`       | `{ element, categories }`                                        | Map embed unblocked                          |
| `storage-blocked`     | `{ key, storageType, categories }`                               | Storage access blocked                       |
| `cookie-blocked`      | `{ name, categories, action }`                                   | Tracker blocked or purged                    |
| `consent-sync-failed` | `{ error, payload }`                                             | Consent record could not be reported         |
