---
title: Consent snapshot
description: Reference for every field of the c15t consent snapshot a JavaScript
  app reads from the kernel, @c15t/browser or createConsentRuntime, grouped by
  the question each field answers.
group: frameworks
lastModified: "2026-10-10T16:01:45+01:00"
---
## Read a snapshot

`kernel.getSnapshot()`, `consent.getSnapshot()` from `@c15t/browser`, and
every `subscribe` listener give you the same frozen object. It describes the
visitor's permissions, what they recorded, the policy that applies and the
surface to show.

A field keeps the same object when its value did not change, so compare with
`===` to skip work:

```ts
let previous = kernel.getSnapshot();
kernel.subscribe((next) => {
	if (next.effectivePermissions !== previous.effectivePermissions) {
		updateGates(next.effectivePermissions);
	}
	previous = next;
});
```

`updateGates` stands for your own code.

## May this run now?

|Field|Type|What it holds|
|--|--|--|
|`effectivePermissions`|`Record<category, boolean>`|Whether each category is allowed now. Scripts, iframes and network rules follow this. Under an opt-out policy an optional category can be `true` before any choice.|
|`restrictions`|per category, a list of `'explicit-denial'`, `'strict-scope'` or `'gpc'`|Why a category is denied.|
|`privacySignals.gpc`|`{ detected, override, active }`|The browser's Global Privacy Control signal, a test override, and the one c15t honors.|
|`nextDeadline`|`number \|null`|The next time, in epoch milliseconds, when a choice or notice expires and permissions or the prompt can change.|

## What did the visitor decide?

|Field|Type|What it holds|
|--|--|--|
|`explicitChoice`|`{ version, categories } \|null`|The visitor's recorded decision per category, each with `value` and `confirmedAt`. Only accept, reject and save write it. `null` when they never chose.|
|`noticeDismissal`|`{ dismissedAt, fingerprint } \|null`|When the visitor acknowledged the current notice. Not a grant.|
|`vendorChoice`|`{ denied, confirmedAt } \|null`|Vendors the visitor turned off, outside IAB.|
|`subject`|`{ subjectId?, externalId?, identityProvider? } \|null`|The consent subject the backend knows.|

Read permissions to gate code and recorded choices to report decisions.
[How consent works](/docs/concepts/how-consent-works#a-permission-is-not-a-recorded-choice)
explains why.

## What does the policy require?

|Field|Type|What it holds|
|--|--|--|
|`promptRequirement`|`{ kind: 'choice' \|'notice', reason } \|{ kind: 'none' }`|The interaction still owed. `reason` is `missing`, `expired` or `policy-changed`.|
|`resolution`|`{ status, policy }`|How the policy resolved: `matched`, `no-match`, `failed` or `unconfigured`. `policy` is set only when `matched`.|
|`policyPending`|`boolean`|`true` until the transport's first answer. The UI stays hidden while it is `true`.|
|`policyRule`|resolved rule|The rule c15t evaluates with: the matched rule, or a strict opt-in fallback. Has `id`, `model`, `prompt`, `scope`, `preselectedCategories`, `actions`, `rights` and `validity`.|
|`model`|`'opt-in' \|'opt-out' \|'iab' \|'none'`|The permission model in force.|
|`consentCategories`|category list or `null`|Categories configured or discovered on the page. `null` means the policy's full scope.|
|`evaluationPolicy`|object|The validated projection c15t evaluated. For DevTools and debugging.|

Check `resolution.status === 'matched'` before you trust `policyRule` as the
visitor's real policy. While the policy loads or after it fails, `policyRule`
is the strict fallback.

## What should the UI show?

|Field|Type|What it holds|
|--|--|--|
|`activeUI`|`'banner' \|'dialog' \|'none' \|null`|The surface to render. Derived from the prompt, and changed by `set.activeUI`.|
|`translations`|`{ language, translations } \|null`|The copy for the resolved language.|
|`branding`|`'c15t' \|'inth' \|'consent' \|null`|The brand for the "Secured by" tag.|
|`hosting`|`'inth' \|'self-hosted' \|null`|Who runs the backend, as `/init` reported it. Separate from `branding`. `null` before init, for `offline()` and `custom()` transports that do not send it, and for backends older than the field. Not verified.|

## Context and bookkeeping

|Field|What it holds|
|--|--|
|`overrides`|The country, region, language and GPC inputs you set.|
|`location`|The country and region the backend reported.|
|`user`|The identified user, if any.|
|`policySnapshotToken`|A signed token sent back with saves so the backend can check the policy.|
|`iab`|The IAB state under an IAB policy: vendor list, TC string and purpose and vendor choices. `null` otherwise.|
|`vendors`|Declared vendors, or `null`.|
|`revision`|A counter that increases on every change.|
|`evaluatedAt`|When the snapshot was last evaluated, in epoch milliseconds.|

## Check it works

1. Log `getSnapshot()` after `ready()` resolves. `resolution.status` is
   `matched`, `policyPending` is `false`, and `explicitChoice` is `null` for a
   new visitor.
2. Accept all and log it again. `explicitChoice.categories` has an entry per
   category and `promptRequirement.kind` is `none`.
