HTML Consent API
Callbacks
Pick the hook for the job
| You want to | Use |
|---|---|
| Start or stop your own code as permissions change | The consent event, or callbacks.onPermissionsChanged |
| Know that a visitor clicked accept, reject or save | callbacks.onChoiceRecorded |
| Wait until c15t knows the visitor's permissions | The ready event, or c15t.ready() |
| Show, hide or switch your own banner | The ui event |
| Report failed backend calls | The error event, or callbacks.onError |
| Flush data before the reload after a withdrawal | callbacks.onBeforeConsentRevocationReload |
Listen from a script that does not use window.c15t | The c15t:* events on document |
Most pages need only the consent event. Prefer gating a vendor with a
gated script where you can,
because that works before any script of yours runs.
Listen for events
Queue on calls before the tag. c15t attaches them when it starts, so they
hear the first ready and consent events:
startAnalytics stands for your own code. After the tag has loaded,
c15t.on(event, listener) works directly and returns a function that removes
the listener.
| Event | Payload | When it fires |
|---|---|---|
ready | the snapshot | The policy has resolved and the UI can render. For a returning visitor, hasConsented() is reliable from here. |
consent | the snapshot | Permissions, the recorded choice or the vendor switches changed. This includes a stored choice restored at start, a save, an expiry and a GPC change. |
ui | 'banner', 'dialog' or 'none' | The surface c15t wants shown changed. |
error | the error | A backend call failed, or an IAB step failed. |
A ready listener added after the policy resolved runs at once with the
snapshot from that moment. A ui listener added after that runs at once
with the current surface. consent and error listeners only hear later
changes.
Listen on document
Each event is also dispatched on document as a CustomEvent named
c15t:ready, c15t:consent, c15t:ui or c15t:error, with the payload in
event.detail. A script that loads in any order, such as a tag manager
snippet, can listen without touching window.c15t:
Unlike c15t.on, a document listener added after an event fired does not
receive it. Add it before the c15t tag runs, or read the current state with
c15t.getSnapshot() once window.c15t is the API.
Config callbacks
Callbacks go under callbacks in a queued config call:
| Callback | Payload | When it runs |
|---|---|---|
onChoiceRecorded | { snapshot, confirmed, actionAt } | A visitor accepted, rejected or saved. confirmed lists the categories this action recorded. |
onPermissionsChanged | { snapshot, previous } | Effective permissions changed value, for any reason. |
onError | { error }, a message string | A backend call or another command failed. |
onBeforeConsentRevocationReload | { preferences } | Right before the reload that follows a withdrawal. Runs synchronously; keep it short. |
A recorded choice is not a permission change
onChoiceRecorded and onPermissionsChanged answer different questions:
onChoiceRecordedruns only for a visitor's own accept, reject or save. It does not run when a stored choice is restored at load. Use it to report consent rates or to thank the visitor.onPermissionsChangedruns whenever what may run changes. That includes a save that changed something, a stored choice restored at load, a policy resolving, a choice expiring and a GPC signal. Use it to start and stop your code.
A save that leaves every permission as it was can run onChoiceRecorded
without onPermissionsChanged. Dismissing a notice runs neither, because it
records no choice and changes no permission.
How consent works
explains the difference.
Follow every change with subscribe
c15t.subscribe(listener) calls your listener with the full snapshot after
every change, including UI and policy changes that the events above skip. It
suits code that renders from the whole state, such as a custom banner. Queue
it like on; it attaches as soon as the client exists.
Errors thrown by your listeners
A listener that throws does not fail the action that caused the event. c15t
logs the error with console.error, then runs the later on listeners for
the same event and dispatches the matching c15t:* event on document. The
same holds for a ready or ui listener that c15t.on() runs at once after
the policy resolved, so c15t.on() still returns the function that removes
the listener. Keep listeners quick, because they run synchronously while the
change is applied.
Check it works
- Add a
consentlistener that logseffectivePermissionsand anonChoiceRecordedcallback that logsconfirmed. - Open the page in a private window under an opt-in policy. Neither logs, because nothing is allowed yet and nobody has chosen.
- Click Accept All. Both log.
- Reload. The
consentlistener logs the restored permissions andonChoiceRecordedstays silent.