Skip to main content

HTML Consent API

window.c15t API

Call c15t before and after the tag loads

window.c15t is the only global the script tag uses. Before the tag runs, it is an array of calls. Push [method, ...args] entries onto it:

<script>
  window.c15t = window.c15t || [];
  c15t.push(['config', { legalLinks: { privacyPolicy: { href: '/privacy' } } }]);
  c15t.push(['on', 'ui', (surface) => console.log('surface:', surface)]);
</script>

When the tag runs, it replaces the array with the API object and replays the calls in order. Always push onto the existing array. window.c15t = [[...]] throws away calls that other scripts queued earlier, including the DevTools tag's.

c15t.push([...]) keeps working after the tag has loaded, and runs each call by the same rules as a queued one. So a snippet written as window.c15t = window.c15t || []; c15t.push([...]) works whether it runs before or after the tag. push returns the number of calls it received.

Which calls you can queue

Queued methodWhen it runs
config, init, on, onInitIn place, in queue order, as the bundle loads.
subscribeOnce the client exists.
acceptAll, rejectAll, save, dismissNotice, saveIAB, showBanner, openDialog, closeDialog, setLanguage, identify, mountUI, processIframesIn queue order, once the policy has resolved. A queued openDialog therefore stays open instead of being replaced by the banner.
Any other method, such as getSnapshot, has, hasConsented, isVendorAllowed, ready or dispose, and names c15t does not knowSkipped, with a console warning. Call them from onInit or after ready().

A queued call that throws is reported with console.error, and the calls after it still run. A queue cannot hand back a return value, which is why reads are skipped.

How options merge

Options merge in this order, with later ones winning:

  1. The tag's data-* attributes.
  2. Each queued config call, in order.
  3. Options passed to c15t.init(), when the tag has data-manual.

ui, ui.banner and ui.dialog merge key by key, so a config call that sets ui.theme keeps the legal links the tag's attributes set. Every other option replaces the earlier value.

config after the client has started logs a warning and does nothing.

Start c15t yourself

With data-manual on the tag, the bundle installs window.c15t and waits. c15t.js still requires a backend URL or hosted factory when init() runs. Use c15t.offline.js for browser-only policy resolution. Manifest and custom modes need the @c15t/browser ES module, a headless script with your own UI, or the IAB script for an IAB policy.

Call c15t.init() when your page is ready, for example once it knows the visitor's country:

<script>
  window.c15t = window.c15t || [];
  document.addEventListener('DOMContentLoaded', () => {
    c15t.push(['init', { overrides: { country: document.body.dataset.country } }]);
  });
</script>

init() reads the original tag's attributes even if the tag has since been removed, layers queued config calls and its own options on top, starts the client and returns it. A second call returns the same client. Until init(), queued actions and subscribe wait, and direct calls to methods that read or change consent throw call c15t.init() first.

After c15t.dispose(), a new c15t.init() starts a fresh client with the same attributes and queued config calls.

Read state

MethodReturnsUse it for
ready()a promise of the snapshotWait until the policy has resolved. Safe to call before init().
has(category)booleanWhether a category is allowed right now. Accepts a condition such as { and: ['measurement', 'marketing'] }.
hasConsented()booleanWhether the visitor has answered the choice prompt, by a recorded choice or an acknowledgement of a prompt with nothing to decide.
getSnapshot()the snapshotThe full consent state: permissions, recorded choice, policy, prompt and surface.
subscribe(listener)an unsubscribe functionCall listener with the snapshot after every change.
isVendorAllowed(id)booleanWhether a vendor may run now: it is declared, its category is allowed and the visitor has not switched it off. false for an id nothing declares, with a console warning in development.
getDeclaredVendors()an array of vendorsThe vendors from vendors, the backend and the slugs on scripts, gated tags and iframes. Empty under an IAB policy.
getVendorChoice()the vendor decision, or nullWhich vendors the visitor switched off, in denied.
on(event, listener)an unsubscribe functionListen for ready, consent, ui or error. Safe to call before init(). See events and callbacks.

A permission is not a recorded choice

has('measurement') answers "may measurement code run now?" Under an opt-out policy it is true before the visitor has done anything. Use it, or snapshot.effectivePermissions, to gate code.

hasConsented() and snapshot.explicitChoice answer "what did the visitor decide?" They change only when the visitor accepts, rejects or saves. Use them to report or display the visitor's decision, never to gate a vendor. How consent works lists which to read for each task.

Answers can change once the policy resolves, so wait for c15t.ready() or a ready listener before you read them at page load.

Record a choice

Call these only in response to a visitor's click or key press, never at load.

MethodWhat it records
acceptAll()Without IAB, allows every category the dialog offers. Under an IAB policy it confirms the vendors' declared processing through the CMP. Optional categories without declared consent purposes remain denied. See categories under an IAB policy.
rejectAll()Denies every optional category the dialog offers.
save(choices)The categories you name, such as { measurement: true, marketing: false }. Categories the dialog does not offer are ignored. A vendors map, such as { vendors: { 'x-pixel': false } }, records vendor switches too. Fails under an IAB policy; use saveIAB().
saveIAB()Confirms the IAB purpose and vendor choices set on the CMP. c15t.iab.js only. See IAB TCF.
dismissNotice()Acknowledges a notice. Grants nothing. Resolves with { ok: false, reason: 'not-required' } when no notice is showing.
identify(user)Links the consent record to a signed-in user, such as { externalId: 'user_123', identityProvider: 'auth0' }. An identityToken verifies the link.

acceptAll(), rejectAll(), save() and saveIAB() resolve to a result with an ok flag. The choice applies in the browser and the banner closes before the backend request finishes. When the request fails, ok is false, the choice stays in effect, and c15t retries the request later.

Show and hide the UI

MethodWhat it does
showBanner()Shows the banner. Records nothing.
openDialog()Opens the preference dialog. Records nothing.
closeDialog()Closes the dialog, or the banner when no dialog is open. Closing the dialog brings the banner back while the policy still owes a choice. Records nothing.
setLanguage(code)Switches language and re-renders with that language's copy, from the backend or, in offline mode, from the bundled copy and i18n.messages. See translations.
mountUI(options?)Removes the stock UI and mounts it again, with new UI options or the configured ones. Throws in c15t.headless.js.

In c15t.headless.js, showBanner, openDialog and closeDialog still change the surface c15t reports through the ui event, so your own banner can follow them.

Lifecycle

MethodWhat it does
config(options)Adds options before the client starts. See configuration.
init(options?)Starts the client. Only needed with data-manual.
onInit(listener)Calls listener with the client once it exists, at once if it already does. Returns a function that cancels the call.
push(...calls)Runs [method, ...args] calls by the queue rules above.
processIframes()Pauses gated iframes that consent does not allow and restores the ones it allows. Needed only with iframeBlocker: { disableAutomaticBlocking: true }. See embeds.
dispose()Removes the UI, the DevTools panel, page listeners, blockers and the scripts c15t added. Code that already ran keeps running.

Properties

PropertyWhat it holds
versionThe @c15t/browser version.
pkg@c15t/browser, @c15t/browser/offline, @c15t/browser/headless or @c15t/browser/iab, by bundle.
modehosted, offline, manifest or custom once started, else null. A transport factory passed as mode reports custom, except hosted() and offline().
hosting'inth' or 'self-hosted' once /init reports who runs the backend, else null. Stays null in offline mode and with backends older than the field.
clientThe started client, or null. It has the same methods plus kernel, runtime, options, consentCategories, ui and setOverrides().
devtoolsThe DevTools panel once c15t.devtools.js mounted it, else null.
hosted, offline, manifest, customTransport factories. c15t.js exports only hosted; c15t.offline.js exports only offline. Headless and IAB export all four, for calls such as c15t.init({ mode: c15t.manifest({ … }) }) with data-manual.

c15t.js reports pkg: '@c15t/browser' and mode: 'hosted' once started.

c15t.client.setOverrides({ country, region, language, gpc }) changes the location, language or GPC signal policy matching uses. The DevTools Location tab uses the same inputs.

Check it works

  1. Open the browser console on a page with the tag and run await c15t.ready(). It resolves with a snapshot whose resolution.status is matched.
  2. Run c15t.has('measurement') and c15t.hasConsented() before and after you click Reject All. Under an opt-in policy both start false; afterwards hasConsented() is true and has stays false.
  3. Run c15t.push(['openDialog']). The dialog opens.