Skip to main content

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) — RejectAll also clears hasLegitimateInterest for every item. Aligned with TCF Policy 5.4 (reject ≡ objection to LI).
  • falseRejectAll clears hasConsent but preserves the existing hasLegitimateInterest per 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 } }

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.

PersonalizedAcceptAll / 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.consentType written to storage
  • consent.type published on the context
  • ChangeMeta.type passed 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.