Legitimate interest and filter pipeline
This page documents the configuration surface introduced by the
enable-legint-and-filters change: per-element legitimate-interest (LI)
activation, the objectLegIntOnReject flag, the CPC
closeCpcOnBulkSelect flag, the declarative filters pipeline, and the
ACM policyUrls array.
LI activation is per-element
Each purpose and each vendor is LI-interactable independently. There is no cross-check between vendors and purposes: a vendor can be LI-active even if none of its purposes are, and a purpose can be LI-active even if no vendor claims it. The only ways to disable LI are:
disableLegInt.purposes— list of purpose ids (or'all').disableLegInt.vendors— list of vendor ids (or'all').
For TCF the standard rules still apply on top: purposes 1, 3, 4, 5 and 6
are never LI-interactable (TCF v2.2 policy), and a TCF vendor is
LI-interactable only if the GVL declares legIntPurposes or
flexiblePurposes for it.
{
frameworks: [
{ tcf: { cmpId: 297, disableLegInt: { purposes: 'all', vendors: 'all' } } },
{ custom: {
disableLegInt: { purposes: [3] }, // purpose 3 is consent-only
customVendorsConfig: { /* ... */ },
} },
],
}
objectLegIntOnReject
Per-framework boolean, default true. Controls what a RejectAll event
does to legitimate interest:
true(default) —RejectAllalso clearshasLegitimateInterestfor every item. Aligned with TCF Policy 5.4 (reject ≡ objection to LI).false—RejectAllclearshasConsentbut preserves the existinghasLegitimateInterestper item. Useful in jurisdictions where a global reject is not treated as LI objection (e.g. CNIL France).
{ tcf: { objectLegIntOnReject: false } }
ui.closeCpcOnBulkSelect
Boolean under ui, default true. Controls whether the CPC
(preference center) closes after an Activate all / Deactivate all click:
true(default) — mirrors the first-layer banner behaviour: click → save → dismiss.false— CPC stays open and re-renders from the post-filter state, so the user sees the effect of bulk actions without losing context.
{ ui: { closeCpcOnBulkSelect: false } }
Related UI flag: ui.activateVendorsOnLegalScopeSelections
When true, toggling a purpose or special feature on in the CPC also
activates every vendor that declares it in its scope. Default false.
{ ui: { activateVendorsOnLegalScopeSelections: true } }
Filter pipeline
Declarative transformation applied to every CoreContext.setData(key)
before the value is stored and published. Filters are registered
per-key and run synchronously.
interface UserConfigType {
filters?: {
[K in CoreContextKey]?: (
next: CoreContextData[K],
prev: CoreContextData[K] | undefined,
meta: ChangeMeta,
) => CoreContextData[K];
};
}
type ChangeMeta = {
type?: 'AcceptAll' | 'RejectAll' | 'CloseBanner' | 'Personalized' | string;
screen?: 'first' | 'second';
source?: string;
};
Typical use-cases: RAI-style custom purposes where one toggle must never change value, publisher-specific pinning (e.g. keep LI always off on a regulated vendor regardless of the banner action), or per-geography overrides.
Example — freeze one purpose
Avacy.init({
frameworks: [/* ... */],
filters: {
consent(next, prev) {
if (!next?.choices) return next;
const prevP3 = prev?.choices?.find(
(c) => c.framework === 'custom' && c.type === 'customPurpose' && c.id === 3,
);
const prevConsent = prevP3 ? !!prevP3.hasConsent : false;
return {
...next,
choices: next.choices.map((c) =>
c.framework === 'custom' && c.type === 'customPurpose' && c.id === 3
? { ...c, hasConsent: prevConsent }
: c,
),
};
},
},
});
Error handling
If a filter throws, the core logs a warning and keeps the original
value (graceful fallback). The filter surface is sync only — do not
return a Promise.
Personalized → AcceptAll / RejectAll coalescing
After a Personalized save, if the merged final choices exactly match
what AcceptAll (or RejectAll) would have produced for every
framework, the event is tagged with the canonical type. The promotion
propagates to:
RawConsentMetadata.consentTypewritten to storageconsent.typepublished on the contextChangeMeta.typepassed to registered filters
This lets filters react to user intent rather than the payload shape:
meta.type === 'AcceptAll' fires whether the user clicked the
first-layer button or hand-toggled every item in the CPC to on.
ACM policyUrls array
AcmProviderConfig.policyUrls: string[] replaces the legacy
policyUrl: string. Legacy JSON providers lists (Google AC list) are
normalized at fetch time:
// legacy fetch: { policyUrl: 'https://x/privacy' }
// internal: { policyUrls: ['https://x/privacy'] }
Inline configurations must use the array form directly:
{
acm: {
acmProvidersConfig: {
providers: [
{ id: 1, name: 'X', policyUrls: ['https://x.com/privacy'] },
],
},
},
}
TCF vendor URLs include legIntClaim when applicable
TCFVendorSetting.privacyPolicyUrls is a single array containing both
the privacy policy URL and the legIntClaim URL (when distinct and the
vendor is LI-interactable). There is no separate legIntClaimUrls
field. Banner UIs render the array as a flat list of links.