Managed & regulations

The source-available core is self-sufficient and offline by default — zero network calls. When you need per-country behavior, audit-grade consent receipts or a certified CMP, plug in a backend through the adapter interface: your own, or the upcoming ConsentLoop Cloud.

Regulation presets (local, no backend)

PresetModelBehavior
gdpr (default)Opt-inNothing optional runs before consent. Equal Accept/Reject. Not dismissable. New categories re-prompt.
us-optoutOpt-outCategory default: true applies immediately (method: "implied"); banner offers opting out; Esc/dismiss persists the implied choice. CCPA/CPRA-style.
noneOpt-in UILike gdpr but dismissable without a choice.
ConsentLoop.run({
  regulation: "us-optout",
  categories: {
    analytics: { default: true },
    marketing: { default: true },   // visitors opt *out* of these
  },
  content: { translations: { en: { banner: {
    title: "Your privacy choices",
    description: 'We share some data with advertising partners. Opt out anytime — see "Do Not Sell or Share".',
    links: [{ label: "Do Not Sell or Share My Personal Information", href: "/do-not-sell" }],
  } } } },
});

The adapter contract

Two optional functions. Everything is fail-open: a slow or broken backend never blocks the banner or the page.

ConsentLoop.run({
  adapter: {
    // Called once before the banner would show (skipped for returning visitors).
    // Decide per-jurisdiction behavior server-side.
    async init({ config, stored }) {
      const res = await fetch("https://api.your.app/consent/decision");
      const { show, regulation, lang } = await res.json();
      return { show, regulation, lang };
      // show: false  -> no banner needed here; defaults applied as implied consent
      // regulation   -> override the preset for this visitor (e.g. "us-optout" for CA)
      // lang         -> force a language
    },
    // Fire-and-forget consent receipt after every choice (uses sendBeacon when available).
    persist(record) {
      navigator.sendBeacon("https://api.your.app/consent/receipts", JSON.stringify(record));
    },
  },
});

Timeout: the banner waits at most manage.timeout (default 750 ms) for init(), then proceeds with local config. The consent record you receive is the full audit object — id, timestamps, revision, per-service choices, regulation, method.

ConsentLoop Cloud coming soon

Our managed plan is the same adapter, hosted: one line instead of a custom backend.

ConsentLoop.run({
  manage: {
    endpoint: "https://api.consentloop.com/v1",
    siteId: "site_123",
  },
});
🔒Fair-source promise: the npm package stays free to run on any site (FSL-1.1-MIT — each release becomes MIT after two years) and fully functional offline. Cloud is additive — the wire protocol above is public, so you can always self-host an adapter instead.

Consent-aware rendering on your server

Consent is a first-party cookie (cl_consent), so your server can read it too:

// e.g. in middleware — short keys: a = accepted categories
const raw = cookies.get("cl_consent");
const consent = raw ? JSON.parse(raw) : null;
const analyticsAllowed = consent?.a?.includes("analytics") ?? false;