Policy Packs

Different countries need different consent experiences. Policy packs let you define those rules once — c15t picks the right one automatically based on where the visitor is.

A policy pack is an ordered array of policies. Each policy targets a region or country and controls the consent model, which categories are in scope, what UI is shown, and how consent is recorded.

There are three ways to configure policy packs:

  1. inth.com (recommended) — use inth.com as your hosted backend. Configure packs visually in the dashboard or via API — no code changes required. Works with any frontend, including static sites.
  2. Self-hosted backend — define packs in code via policyPacks and resolve them from real request geo data. Full control over policy logic and storage.
  3. Offline fallback — pass the same policy shapes to the frontend via offlinePolicy.policyPacks. Use this mainly for local development, demos, deterministic testing, or resilience when the backend is temporarily unreachable. If you omit offlinePolicy.policyPacks, c15t falls back to a synthetic worldwide opt-in banner instead of no-banner mode.

In both hosted and self-hosted modes, the backend is always the source of truth. Offline packs are a preview or fallback layer and never override a live backend decision.

Quickstart

The fastest way to get started is with the built-in presets:

PresetModelUIMatches
europeOptIn()opt-inbannerEEA + UK countries + geo fallback
europeIab()iabbannerEEA + UK countries + geo fallback (TCF 2.3)
californiaOptOut()opt-outnoneUS-CA region
quebecOptIn()opt-inbannerCA-QC region
worldNoBanner()nonenonedefault fallback

Most apps only need these presets — pick the ones that match your regions, pass them to your backend config or provider, and you're done. Customize individual fields or write fully custom policies when you need more control.

For banner/dialog actions, policy packs can also control grouped button arrangement:

That expresses arrangement only. Button appearance like stroke, filled, or ghost lives in the UI theme.

What Users See

Each policy combination produces a different consent experience:

Policy ConfigUser Experience
model: 'opt-in', ui.mode: 'banner'Banner appears, nothing loads until the user consents
model: 'opt-out', ui.mode: 'none'No banner, everything loads immediately — user opts out via a "Do Not Sell" link
model: 'none', ui.mode: 'none'No banner, all categories auto-granted silently
model: 'opt-in', ui.mode: 'dialog'Full-screen dialog, nothing loads until the user consents
model: 'iab', ui.mode: 'banner'IAB TCF 2.3 banner with vendor-level controls

How Policy Resolution Works

When a visitor arrives, c15t walks the policy pack in priority order:

  1. Match by region — checks for a policy targeting the specific region (e.g., US-CA, CA-QC)
  2. Match by country — if no region match, checks for a country-level policy (e.g., US, DE)
  3. Fallback (geo failure) — if geo-location failed (no country detected), uses the policy marked with match.fallback
  4. Fall back to default — if nothing matches, uses the policy marked as the default
  5. No match, no default — resolves to no-banner mode (silent, no consent UI)

Within the same matcher type, the first policy in the array wins. Pack order matters when two policies target the same country or region.

The fallback step is distinct from default: isDefault is a catch-all for known locations that don't match any specific policy ("rest of world"), while fallback is a safety net for unknown locations when geo-headers are missing ("assume strictest"). The europeOptIn() and europeIab() presets include fallback: true by default so EU-level consent applies when geo fails.

Info

Only one default and one fallback policy are allowed. Use inspectPolicies() to surface overlapping matchers and other warnings before deployment.

Inspect the resolved policy from any client component:

Common Patterns

The 80% case — strict in Europe, light in California, silent everywhere else:

Region overrides country — stricter rules for California than the rest of the US:

Different wording per region — use i18n.messageProfile to vary copy without changing the consent model:

In that setup, the eu policy uses only the eu language set. So Europe can resolve to en, fr, or de, but not to es or any other locale defined only in default. If the browser asks for an unsupported locale, c15t falls back to the eu profile's fallbackLanguage.

Re-Prompting on Policy Change

When you change a policy in a way that affects consent semantics — like adding a category, changing the consent model, or modifying allowed actions — c15t automatically re-prompts returning users.

This works through the material policy fingerprint: a hash of only the consent-affecting fields (model, categories, scope, allowed actions, grouped action layout, direction, proof settings). Presentation-only changes like copy, button styling, or scroll lock do not trigger re-prompts.

ChangeRe-prompts?
Add a consent categoryYes
Change model from opt-out to opt-inYes
Remove an allowedActionYes
Change uiProfile or button stylingNo
Update translation copyNo
Change scrollLockNo

Design Guidelines

  • Start from presets. Use policyPackPresets to get running, then customize for your needs.
  • Keep packs small. A handful of regional policies is better than dozens of tiny fragments.
  • Think risk, not geography. Geography is just a matcher — the real question is what consent behavior each region needs.
  • Always include a default. Unless "no banner for unmatched traffic" is intentional.
  • Set a fallback for geo failures. Mark your strictest policy with match.fallback=true so users in unknown locations still see a consent banner. The europeOptIn() and europeIab() presets do this automatically.
  • Keep policy IDs stable. They appear in debugging output, snapshots, and audit records.
  • Use inspectPolicies() before deploying. It catches overlapping matchers, missing defaults, and IAB misconfigurations.

Info

For provider setup, see the Next.js policy pack guide. For backend configuration, see the self-host guide.