Kjeks
← All docs

Kjeks — core plugin

The consent engine for WordPress: categories, prior blocking, the banner, and the tracker inventory. Single-site or multisite.

Requires WordPress 6.8+PHP 8.3+Single-site & multisiteSource on GitHub →

101 Getting started

Install & activate

Download the latestkjeks.zip, then in WordPress go to Plugins → Add New → Upload Plugin and upload it.

  • Single site: activate it from Plugins. Admin lives under Settings → Cookie Consent.
  • Multisite: the plugin declares Network: trueNetwork Activate it. Admin lives underNetwork Admin → Settings → Cookie Consent.
On multisite, activation seeds one network-wide settings record and new subsites inherit tracker definitions lazily — nothing non-essential is copied per site.

Kjeks then updates itself from GitHub releases viaplugin-update-checker — new versions appear on your Plugins screen like any other update.

Show the banner & preferences

The consent banner renders automatically in the site footer. To let visitors reopen their choices or publish a cookie table, drop in a block or shortcode:

PurposeBlockShortcode
Reopen the consent bannerkjeks/preferences[kjeks_preferences]
Cookie declaration tablekjeks/cookie-declaration[kjeks_cookie_declaration]

Set your content

On the Cookie Consent admin page you set the banner heading and body, your privacy-policy URL, and an accent colour. These are stored once and resolved per site.

The consent categories

SlugDefaultMeaning
necessaryAlways onEssential; cannot be switched off.
preferencesDeniedRemembers choices like language or layout.
analyticsDeniedMeasurement and statistics.
marketingDeniedAdvertising and cross-site tracking.

Everything except necessary is denied until the visitor opts in. You can add categories with the kjeks_categories filter (see 301), but every non-necessary category is always optional.

Banner not showing? Usually by design — Kjeks skips it when a choice is already stored (~6 months; the visitor sees the Cookie settingstrigger instead), or when the browser sends Global Privacy Control(Brave sends GPC by default) and Kjeks honours it — auto-rejecting non-essential technologies (disable with the kjeks_honor_gpc filter). Browser features that hide cookie notices (Brave Shields, uBlock cookie-notice lists) can also remove it.

201 Consent & integrations

Register an integration

Kjeks fires the kjeks_register_integrations action oninit (priority 20). Register your trackers inside it withkjeks_register_integration( $id, $args ) — Kjeks then holds those scripts back until the matching category is granted.

add_action( 'kjeks_register_integrations', function () {
    kjeks_register_integration( 'matomo', [
        'category'    => 'analytics',
        'label'       => 'Matomo',
        'handles'     => [ 'matomo-tracker' ],
        'src_scripts' => [
            'https://cdn.example.com/matomo.js',
            [
                'src'   => 'https://cdn.example.com/extra.js',
                'attrs' => [ 'data-site' => '7' ],
            ],
        ],
        'inline'      => [ 'console.log( "loaded after consent" );' ],
    ] );
} );

Integration arguments

KeyTypeDescription
categorystringOne of preferences, analytics, marketing. Unknown or necessary falls back to marketing.
labelstringHuman-readable name shown in the cookie declaration.
handlesstring[]Registered script handles to gate (blocks an already-enqueued script until consent).
src_scriptsarrayExternal scripts to inject after consent. A plain URL string, or { src, attrs } to add attributes.
inlinestring[]Inline JS snippets to run after consent.

Helper functions

Prefer a one-liner? These wrap the same machinery:

FunctionUse
kjeks_enqueue_script( $handle, $src, $category, $deps = [] )Enqueue a script that loads only after the category is granted.
kjeks_add_inline_script( $category, $code, $id = '' )Run inline JS gated by a category.
kjeks_embed( $src, $category, $args = [] )Return consent-gated embed markup (iframes, pixels).
kjeks_is_granted( $category )Boolean check for server-side branching.
kjeks_preferences_link( $label = '', $display = true )Output/return a link that reopens the banner.
// Enqueue a script that only loads after a category is granted.
kjeks_enqueue_script( 'my-analytics', 'https://cdn.example.com/a.js', 'analytics' );

// Add inline JS gated by a category.
kjeks_add_inline_script( 'marketing', 'window.startPixel();' );

// Render a consent-gated embed (e.g. a YouTube iframe).
echo kjeks_embed( 'https://www.youtube.com/embed/VIDEO_ID', 'marketing' );

// Branch your own PHP on a granted category.
if ( kjeks_is_granted( 'analytics' ) ) {
    // …
}

// Output a "Cookie preferences" link that reopens the banner.
kjeks_preferences_link( 'Cookie preferences' );

Consent on the front end

The visitor's choice is stored in a cookie and localStorage under the key kjeks_consent, with a six-month lifetime. When consent changes, Kjeks dispatches events you (and add-ons) can listen for:

window.addEventListener( 'kjeks:granted', () => {
    // Fired when the visitor grants (or updates) consent.
} );

window.addEventListener( 'kjeks:withdrawn', () => {
    // Fired when the visitor withdraws consent.
} );
Multisite: trackers are defined network-wide and resolved per blog. A tracker with an empty sites list applies to the whole network; otherwise it applies only to the listed blog IDs.

301 Hooks, REST & CLI

Filters

FilterDefaultPurpose
kjeks_categoriesAdd or modify category definitions.
kjeks_privacy_urlPolicy URLOverride the resolved privacy-policy URL (receives blog ID).
kjeks_banner_contentStored contentOverride the resolved banner content (receives blog ID).
kjeks_honor_gpctrueWhether to honour the browser's Global Privacy Control signal.
kjeks_is_grantedOverride the server-side category-grant check.
kjeks_server_side_gatingfalseEnable conditional server-side gating of output.
kjeks_embed_htmlModify consent-gated embed markup.

WP-CLI

wp kjeks scan-config

Generate a scanner config from the sites and paths WordPress knows about.

wp kjeks scan-config [--paths=<paths>] [--output=<file>] [--include=<ids>]

Outputs { sites: [ { url, blog_id, policy_version, paths } ] } — feed it straight to the scanner.

wp kjeks import

Import reviewed scanner observations back into the tracker inventory.

wp kjeks import <file> [--blog_id=<id>]

The file accepts either shape:

# A single site
{
  "blog_id": 1,
  "observations": [ /* … tracker observations … */ ]
}

# Or several sites at once
{
  "sites": [
    { "blog_id": 1, "observations": [ /* … */ ] },
    { "blog_id": 2, "observations": [ /* … */ ] }
  ]
}

REST API

All routes live under the kjeks/v1 namespace and require manage_network on multisite, or manage_options on a single site.

Method & routeBody / query
GET /network-configReturns the current configuration.
POST /network-configreviews, remove, add, content, deleteOnUninstall, bannerDefaultVisible.
GET /scan-configOptional include and paths (comma-separated).
POST /importRequired blog_id and observations.

Options & tracker schema

OptionScopeShape
kjeks_network_trackersNetworkTracker records keyed by tracker ID.
kjeks_network_contentNetwork{ heading, body, privacy_url, accent }
kjeks_network_settingsNetwork{ delete_on_uninstall, banner_default_visible }
kjeks_policy_versionPer siteInteger; bumping it re-prompts visitors.

Each tracker record carries:

id, name, category, reviewed,provider, purpose, party,storage_type, domain, path,retention, source, documentation_url,first_observed, last_observed, and sites.

Next: automate discovery with the scanner, speed up review with the AI Reviewer, or wire Google tags via Google Consent Mode.