# Consent Experiences API (/docs/consent-management/configuration/consent-experiences/consent-experiences-api)

This document describes the JavaScript API for **Consent Experience UIs**. You can use `window.transcend` to control UI visibility, change language, listen for visibility changes, and apply runtime customizations.

If you are on **Legacy Consent UIs**, most of your existing integrations will continue to work. Several methods are unchanged, but Consent Experience UIs also add new capabilities for runtime configuration and remounting. A number of Legacy Consent UIs methods have been removed — see the Removed in Consent Experience UIs section.

The API is available globally in the browser once the Consent Experience UIs script loads. In most cases you do **not** need to call anything to show the consent banner, see Auto-prompt vs. manual display below. Use `ready()` or `readyQueue` when you want to own when and how the UI appears or re-renders, gather UI data for e.g. metrics, or respond to banner events.

## Unchanged from Legacy Consent UIs [#unchanged-from-legacy-consent-uis]

These methods work the same way as in Legacy Consent UIs. Existing integrations that call them should not require changes:

- `ready()` / `readyQueue`
- `showConsentManager()`
- `hideConsentManager()`
- `autoShowConsentManager()`
- `setActiveLocale()` / `getActiveLocale()`
- `setUiVariables()`
- `addEventListener()` / `removeEventListener()` (though there are some changes to the `view-state-change` event)

## New in Consent Experience UIs [#new-in-consent-experience-uis]

- `mountConsentApp()` — mount or remount the UI (useful for debugging purposes when SPAs remove the UI DOM node)
- `getVariant()` — read the active Consent Experience slug
- `setConfig()` — update UI configuration at runtime

## Removed in Consent Experience UIs [#removed-in-consent-experience-uis]

The following methods were available on `window.transcend` in Legacy Consent UIs but are **not** part of the Consent Experience UIs API. If your site calls any of these, please update your integration before migrating.

| Method                                        | Description in Legacy Consent UIs                 | Migration in Consent Experience UIs                                                                                                                           |
| --------------------------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `viewStates`                                  | Set of possible UI view states                    | This is equivalent to the set of Consent Experiences configured within the Admin Dashboard                                                                    |
| `getViewState()`                              | Returns the current UI view state                 | Use `getVariant()` to track the current experience shown                                                                                                      |
| `getPolicies()`                               | Returns configured privacy/cookie policy metadata | Configure policies in the Transcend dashboard; use `setUiVariables()` for dynamic copy                                                                        |
| `getUiVariables()`                            | Returns the current UI variable map               | Track variable values in your own application state                                                                                                           |
| `setPrivacyPolicy()` / `setSecondaryPolicy()` | Sets the privacy policy URLs at runtime           | Configurable through setting the `data-links` attribute on airgap.js which accepts a comma delimited URL array                                                |
| `toggleConsentManager()`                      | Toggles the consent UI open or closed             | Call `showConsentManager()` or `hideConsentManager()` explicitly                                                                                              |
| `doNotSell()`                                 | Opens or triggers the "Do Not Sell" flow          | Configure the equivalent experience in the Transcend dashboard for your privacy regime and call `.showConsentManager({ viewState: '{do-not-sell-slug}' })`    |
| `optOutNotice()`                              | Opens or triggers an opt-out notice               | Configure the equivalent experience in the Transcend dashboard for your privacy regime and call `.showConsentManager({ viewState: '{opt-out-notice-slug}' })` |

## Quick start [#quick-start]

### Auto-prompt vs. manual display [#auto-prompt-vs-manual-display]

By default, Consent Experience UIs handle banner display for you. If **auto prompt** is enabled for the visitor's privacy regime in your [Transcend Admin Dashboard](https://app.transcend.io/consent-manager/regional-experiences) configuration, the consent banner is shown automatically on page load when appropriate, no JavaScript integration required.

Use display methods within `ready()` or `readyQueue` **only when you want to own display logic yourself**. In that case, disable auto prompt for the relevant regime(s) in the Admin Dashboard, then use the ready hooks to call e.g. `showConsentManager()` on your own schedule; for example, it can be called when the visitor clicks a "Privacy Settings" link.

You would still use `ready()` for non-display integrations (e.g. listening for `view-state-change` events) even when auto prompt is enabled.

### Manual display example [#manual-display-example]

In order to manually display the UI when auto prompt is disabled, wait for airgap.js (if you're loading airgap.js synchronously, you can simply put a script tag after the airgap.js script tag), then register your callback with `transcend.ready()`:

```html
<script src="https://transcend-cdn.com/airgap.js"></script>
<script>
  window.transcend.ready((transcend) => {
    // Open the consent manager when the user clicks "Privacy Settings"
    document
      .getElementById('privacy-settings')
      ?.addEventListener('click', () => transcend.showConsentManager());
  });
</script>
```

If the Consent Experience UIs script has already finished loading by the time your code runs, `transcend.ready()` invokes your callback immediately; there's no need to check initialization state yourself.

## API overview [#api-overview]

| Method                                         | Description                                                                                             |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `ready()`                                      | Queue a callback until the API is ready, or run immediately if already initialized                      |
| `readyQueue`                                   | Low-level queue for callbacks registered before airgap initializes                                      |
| `mountConsentApp()`                            | Mount or remount the consent UI (automatically performed on init, but may be useful for debugging SPAs) |
| `showConsentManager()`                         | Open the consent banner or preferences modal                                                            |
| `hideConsentManager()`                         | Close the consent UI                                                                                    |
| `autoShowConsentManager()`                     | Open the UI only when auto-prompt rules allow it                                                        |
| `setActiveLocale()`                            | Change the display locale                                                                               |
| `getActiveLocale()`                            | Get the current display locale                                                                          |
| `getVariant()`                                 | Get the active Consent Experience slug                                                                  |
| `setConfig()`                                  | Update UI configuration at runtime                                                                      |
| `setUiVariables()`                             | Inject dynamic values into UI copy                                                                      |
| `addEventListener()` / `removeEventListener()` | Listen for UI events (e.g. `view-state-change`)                                                         |

## Waiting for the API [#waiting-for-the-api]

`ready()` and `readyQueue` are unchanged from Legacy Consent UIs. The Consent Experience UIs API becomes available in stages. Which method you use depends on how early your code runs:

| When your code runs                          | What to use                                                    |
| -------------------------------------------- | -------------------------------------------------------------- |
| **Before airgap has initialized**            | Stub `window.transcend` and push to `readyQueue`               |
| **After airgap, before the UI API is ready** | `ready()`                                                      |
| **After the UI API is ready**                | Call API methods directly, or use `ready()` (runs immediately) |

```markdown
airgap.js loads → transcend.ready() available
     ↓
UI loads and mounts → full API available → queued callbacks run
     ↓
transcend.ready() calls your callback immediately
```

### `ready(callback)` [#readycallback]

Unchanged from Legacy Consent UIs. Queues a function to run once the full `window.transcend` API is initialized (`showConsentManager`, `setActiveLocale`, event listeners, and so on). Use this **after airgap.js has initialized** but whenever you need to wait for the UI API.

If initialization has already completed, your callback is invoked right away.

```typescript
ready(callback: (api: TranscendUiAPI) => void): void;
```

```typescript
window.airgap.ready(() => {
  window.transcend.ready((transcend) => {
    transcend.addEventListener('view-state-change', (event) => {
      console.log('Consent UI state:', event.detail.viewState);
    });
  });
});
```

### `readyQueue` [#readyqueue]

Unchanged from Legacy Consent UIs. A low-level array of callbacks that run when the API finishes initializing. In most cases you should prefer `ready()` instead.

Use `readyQueue` only when you need to queue UI logic **before airgap has initialized** — for example, when auto prompt is disabled and you have an inline script that runs before the airgap bundle loads. Stub out the `window.transcend` object and push your callback onto the queue:

```typescript
readyQueue: Array<(transcend: TranscendUiAPI) => void>;
```

```typescript
// Before airgap or the Consent UI script has loaded
window.transcend = window.transcend ?? {};
window.transcend.readyQueue = window.transcend.readyQueue ?? [];

window.transcend.readyQueue.push((transcend) => {
  transcend.addEventListener('view-state-change', (event) => {
    console.log('Consent UI state:', event.detail.viewState);
  });
});
```

When the Consent Experience UIs script loads, any callbacks in `readyQueue` are preserved and executed once the API is ready. After that, use `ready()` for any additional callbacks.

## Mounting the UI [#mounting-the-ui]

### `mountConsentApp()` [#mountconsentapp]

Creates and mounts the consent UI in the page. This runs automatically when the Consent UI script loads, you normally do not need to call it yourself.

Call it again to **remount the UI from scratch**. This is useful when a single-page app framework (React, Vue, etc.) removes the consent UI's container from the DOM - for example during client-side navigation, route changes, or hydration mismatches. Remounting restores the banner and modal without reloading the page.

```typescript
mountConsentApp(): void;
```

When called, it:

1. Removes any existing consent UI container (`#transcend-shadow-root`)
2. Creates a fresh container and renders the UI
3. Loads your theme stylesheet
4. Runs `autoShowConsentManager()` — which shows the banner automatically if auto prompt is enabled for the visitor's regime

```typescript
// Remount after the consent UI was removed from the DOM
window.transcend.mountConsentApp();
```

**Example — remount when a React root replaces page content**

```typescript
// After React re-renders and removes transcend's container from the DOM
function ensureConsentUiMounted() {
  if (!document.getElementById('transcend-shadow-root')) {
    window.transcend.mountConsentApp();
  }
}

// Call after navigation or when you detect the UI is missing
ensureConsentUiMounted();
```

<Callout type="info" title="Note">
  Remounting rebuilds the UI but does not reset consent state. The visitor's choices remain available via [`window.airgap.getConsent()`](../../index.mdx).
</Callout>

## Showing and hiding the consent UI [#showing-and-hiding-the-consent-ui]

These methods are unchanged from Legacy Consent UIs.

The Consent Manager has up to three layers, depending on your configuration:

- **Banner** — the first-layer notice shown at the bottom or top of the page
- **Modal** — the full preferences panel where users manage purposes
- **Trackers** — the cookie or vendor detail view opened from the modal

### `showConsentManager(options?)` [#showconsentmanageroptions]

Opens the consent UI for the visitor. Displays the banner or modal based on your active UI variant.

```typescript
showConsentManager(options?: { viewState?: string }): void;
```

| Option      | Type                | Description                                                                                                                                                                                                             |
| ----------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `viewState` | `string` (optional) | Consent Experience slug to use when opening (e.g. `"banner-into-modal"`). Must match an experience configured in your Transcend dashboard. If omitted, the default experience for the visitor's privacy regime is used. |

```typescript
// Open with the default variant for this visitor
window.transcend.showConsentManager();

// Open a specific UI variant
window.transcend.showConsentManager({ viewState: 'banner-into-modal' });
```

### `hideConsentManager()` [#hideconsentmanager]

Closes the consent UI, regardless of which layer is currently visible. Fires a `view-state-change` event with `viewState: 'Closed'`.

```typescript
hideConsentManager(): void;
```

```typescript
window.transcend.hideConsentManager();
```

### `autoShowConsentManager(options?)` [#autoshowconsentmanageroptions]

Runs the same display logic Transcend uses on page load when **auto prompt** is enabled in the Admin Dashboard for the visitor's privacy regime. You rarely need to call this yourself since it is invoked automatically during initialization unless you have taken over display logic.

The UI is shown if any of the following are true:

1. Auto prompt is enabled for the visitor's regime in the Admin Dashboard, and the user has not confirmed consent
2. Prompting is forced on via config (`prompt: "true"` or `"on"`)

If the visitor has already confirmed consent, this method does nothing.

```typescript
autoShowConsentManager(options?: { viewState?: string }): void;
```

Accepts the same `viewState` option as `showConsentManager()`.

```typescript
window.transcend.autoShowConsentManager();
```

## Language and locale [#language-and-locale]

These methods are unchanged from Legacy Consent UIs.

### `setActiveLocale(locale)` [#setactivelocalelocale]

Changes the language displayed in the consent UI and reloads the corresponding translations.

```typescript
setActiveLocale(locale: LocaleValue): void;
```

`LocaleValue` is a BCP 47 locale tag. Common values include `'en'`, `'fr-FR'`, `'de-DE'`, `'es-ES'`, `'pt-BR'`, and `'ja-JP'`. The locale must be enabled for your active experience within the Consent Experience Editor.

```typescript
window.transcend.setActiveLocale('fr-FR');
```

You can call this to keep the consent UI in sync with a language picker on your site:

```typescript
siteLanguagePicker.addEventListener('change', (event) => {
  window.transcend.setActiveLocale(event.target.value);
});
```

### `getActiveLocale()` [#getactivelocale]

Returns the locale currently displayed in the consent UI.

```typescript
getActiveLocale(): LocaleValue;
```

```typescript
const locale = window.transcend.getActiveLocale();
// e.g. "en", "fr-FR"
```

## Customization [#customization]

### `getVariant()` [#getvariant]

Returns the ID of the Experience currently in use (e.g. `"banner-into-modal"`, `"modal-only"`). Experiences are configured in the Transcend dashboard and mapped to privacy regimes.

```typescript
getVariant(): string | undefined;
```

```typescript
const variant = window.transcend.getVariant();
```

### `setConfig(config)` [#setconfigconfig]

Updates Consent UI settings at runtime without reloading the page. Provided values are merged into the active configuration.

```typescript
setConfig(config: Partial<LoadOptions>): void;
```

Some common uses include:

```typescript
// Change which Experience is shown for a privacy regime
window.transcend.setConfig({
  regimeVariantMap: {
    GDPR: 'modal-only',
    CPRA: 'banner-into-modal',
  },
});

// Opens the shadow root the UI is shown in
window.transcend.setConfig({
  shadowRoot: 'open',
});
```

### `setUiVariables(variables)` [#setuivariablesvariables]

Unchanged from Legacy Consent UIs.

Replaces placeholder variables in consent UI copy with dynamic values from your site — for example, your company name or a link to your privacy policy.

```typescript
setUiVariables(variables: Record<string, string>): Promise<void>;
```

```typescript
await window.transcend.setUiVariables({
  companyName: 'Acme Corp',
  privacyPolicyUrl: 'https://example.com/privacy',
});
```

Variable names must match the placeholders defined in your Transcend message templates.

### Script tag options [#script-tag-options]

You can pass configuration to the Consent UI via `data-*` attributes on the script tag that loads it. Attribute names use kebab-case and are converted automatically (e.g. `data-ui-z-index` sets the UI stacking order).

```html
<script
  src="https://transcend-cdn.com/consent-ui-v2.js"
  data-ui-z-index="9999"
  data-prompt="true"
></script>
```

## Events [#events]

`window.transcend` supports standard DOM event listener methods. These are unchanged from Legacy Consent UIs.

```typescript
addEventListener(type: 'view-state-change', listener: EventListener): void;
removeEventListener(type: 'view-state-change', listener: EventListener): void;
```

Register listeners with `ready()` to ensure the API is available:

```typescript
window.transcend.ready((transcend) => {
  transcend.addEventListener('view-state-change', handleViewStateChange);
});
```

### `view-state-change` [#view-state-change]

Fired whenever the consent UI opens, closes, or navigates between layers. The event itself existed in Legacy Consent UIs; Consent Experience UIs changes the set of view states that are returned.

**Event detail:**

```typescript
{
  viewState: 'Banner' | 'Modal' | 'Trackers' | 'Closed';
  previousViewState: 'Banner' | 'Modal' | 'Trackers' | 'Closed' | null;
}
```

| `viewState`  | Description                                                          |
| ------------ | -------------------------------------------------------------------- |
| `'Banner'`   | The first-layer banner is visible                                    |
| `'Modal'`    | The preferences modal is visible                                     |
| `'Trackers'` | The cookie or vendor detail view is visible (Consent Experience UIs) |
| `'Closed'`   | All consent UI layers are hidden                                     |

**Listen for open and close:**

```typescript
window.transcend.addEventListener('view-state-change', (event) => {
  const { viewState, previousViewState } = event.detail;

  if (viewState === 'Closed') {
    console.log(`Consent UI closed (was: ${previousViewState})`);
  } else {
    console.log(`Consent UI opened: ${viewState}`);
  }
});
```

**Run logic when the visitor closes the preferences modal:**

```typescript
window.transcend.addEventListener('view-state-change', (event) => {
  const { viewState, previousViewState } = event.detail;

  if (viewState === 'Closed' && previousViewState === 'Modal') {
    // Visitor dismissed the preferences modal
  }
});
```
