This page documents the public extension points GTM Kit exposes for developers: wp-config.php constants for environment-specific overrides, PHP filters and actions for customising the data layer and consent surface, and the one browser-side event you can listen for.
Override settings via wp-config.php constants
GTM Kit lets you override the settings stored in the database with constants defined in wp-config.php. This is handy when you copy a production database into a staging or local environment and want to point GTM somewhere else without editing the database row.
Constants support is opt-in. You must set GTMKIT_ON to true before any other constant is honoured. Keeping constants off on production avoids the extra config lookups; keeping the master switch in one place makes it easy to disable all overrides at once.
// Master switch. True activates constants support; false turns it off.
define( 'GTMKIT_ON', true );
// The GTM container ID.
define( 'GTMKIT_CONTAINER_ID', 'GTM-XXXXXXX' );
// Or true, in which case the saved option is ignored.
define( 'GTMKIT_CONTAINER_ACTIVE', false );
// Log debug messages to the browser console.
define( 'GTMKIT_CONSOLE_LOG', false );
// Do not limit purchase tracking to once per order. Handy during development.
define( 'GTMKIT_WC_DEBUG_TRACK_PURCHASE', true );
define( 'GTMKIT_EDD_DEBUG_TRACK_PURCHASE', true );
Filters
The hooks below are stable, public API. Premium add-ons and third-party integrations register against these instead of forking core.
gtmkit_datalayer_content
The array of fields pushed into the data layer immediately after the GTM container script. Use this to add page-level dimensions (logged-in user role, custom segmentation, A/B-test bucket) without editing templates.
$datalayer_data = apply_filters( 'gtmkit_datalayer_content', [] );
add_filter(
'gtmkit_datalayer_content',
static function ( array $data ): array {
$user = wp_get_current_user();
if ( $user->ID > 0 ) {
$data['user_role'] = $user->roles[0] ?? '';
}
return $data;
}
);
gtmkit_header_script_data
The data object exposed as window.gtmkit_data in <head>. Empty by default. Push values here so frontend code can read them without a separate HTTP request.
$data = apply_filters( 'gtmkit_header_script_data', [] );
add_filter(
'gtmkit_header_script_data',
static function ( array $data ): array {
$data['site_currency'] = get_option( 'woocommerce_currency', 'USD' );
return $data;
}
);
gtmkit_resolve_tax_mode
Since GTM Kit 2.11. Applied once per data layer payload to decide whether values are reported including or excluding tax. Receives the resolved boolean from the “Exclude tax” toggle (true for ex-tax, false for inc-tax) and returns the value GTM Kit will use for that payload.
add_filter( 'gtmkit_resolve_tax_mode', function ( $exclude_tax ) {
// Force ex-tax for a specific payment-gateway downstream pipeline,
// regardless of the global toggle setting.
return true;
} );
Use this when a downstream tool (a GA4 server container, a specific Ads account, a BigQuery view) needs a different tax convention than the rest of your stack. The filter applies uniformly to ecommerce.value, per-item price, and the per-item discount field within the payload it gates.
gtmkit_resolve_item_discount
Since GTM Kit 2.11. Applied once per cart item to the resolved per-item coupon discount. Lets you adjust the discount field on items in cart and order events. Receives the resolved discount value, the cart-item array, and the resolved $exclude_tax boolean.
add_filter( 'gtmkit_resolve_item_discount', function ( $discount, $item, $exclude_tax ) {
// Round the discount to the nearest whole currency unit.
return round( $discount );
}, 10, 3 );
gtmkit_consent_default_settings_enabled
Since GTM Kit 2.8.4. Master toggle for the entire consent block. When false, GTM Kit emits zero consent surface area: no defaults, no window.gtmkit.consent JS API, no event dispatch. Use this to let a CMP or a GTM-based consent solution own the flow without double-firing.
$consent_enabled = (bool) apply_filters(
'gtmkit_consent_default_settings_enabled',
(bool) $admin_toggle_value
);
// Force-enable the consent block even when the admin toggle is off.
add_filter( 'gtmkit_consent_default_settings_enabled', '__return_true' );
gtmkit_consent_default_state
Since GTM Kit 2.8.4. Per-category Consent Mode v2 default state. As of 2.9 the filter fires from inside the default signal source’s read callable, so it runs only when the default source is active (the master toggle is on and no higher-priority source has registered). To always provide state, register your own source via gtmkit_consent_signal_sources instead.
$consent_defaults = apply_filters( 'gtmkit_consent_default_state', $option_backed_state );
// Force analytics_storage to granted regardless of the admin setting.
add_filter(
'gtmkit_consent_default_state',
static function ( array $state ): array {
$state['analytics_storage'] = 'granted';
return $state;
}
);
gtmkit_consent_region
Since GTM Kit 2.8.4. Region codes the consent defaults apply to. Maps to Consent Mode v2’s region parameter. Region codes are emitted into the gtag('consent', 'default', ...) payload as a JSON array under the region key. Returning anything other than an array is coerced to an empty array (the region key is then omitted). Fires regardless of the master toggle.
$consent_region = apply_filters( 'gtmkit_consent_region', $option_value );
// Apply the consent defaults only in EEA + UK from PHP.
add_filter(
'gtmkit_consent_region',
static fn(): array => [ 'AT', 'BE', 'BG', 'DE', 'DK', 'FR', 'GB' /* ... */ ]
);
gtmkit_header_script_attributes
Since GTM Kit 2.8.4. Attributes set on GTM Kit’s inline <script> tags. From GTM Kit 2.10, the default attribute set is driven by the Settings → Consent → CMP script attributes UI; this filter remains the canonical extension point for code that needs to add or override attributes beyond what the admin UI exposes.
$script_attributes = apply_filters( 'gtmkit_header_script_attributes', $built );
// Add a custom CMP attribute via filter (equivalent to the admin UI's Custom field).
add_filter(
'gtmkit_header_script_attributes',
static function ( array $attrs ): array {
$attrs['data-acme-consent'] = 'block';
return $attrs;
}
);
- Applies to all inline scripts whose handle starts with
gtmkit-exceptgtmkit-delay. - Runs after the setting-driven build, so filter values override the admin UI and the Custom-attribute field.
- The always-on cache-compat attributes (
data-cfasync="false",data-nowprocket="") are emitted regardless of the CMP setting; remove them via this filter only if you have a deliberate reason (a Cloudflare Rocket Loader or WP Rocket integration that needs them off). - Custom attribute names are restricted to
[a-zA-Z0-9_-]at save and at render time. Names outside that set are stripped to the valid remainder; an empty remainder drops the attribute entirely.
gtmkit_introductions
Since: 2.12.0.
Register an announcement modal that shows once per user on GTM Kit admin pages. Add-on plugins use this to surface launch announcements, version-bump notes, or one-time onboarding content without building their own modal infrastructure. Entries that are not Introduction_Interface instances are silently dropped.
use TLA_Media\GTM_Kit\Admin\Introductions\Domain\Introduction_Interface;
add_filter(
'gtmkit_introductions',
function ( array $intros ): array {
$intros[] = new My_Plugin_Feature_Launch_Introduction();
return $intros;
}
);
Each registered class implements Introduction_Interface with five methods: get_id() (stable kebab-case id), get_priority() (lower wins; ~100 for welcomes, ~200 for version bumps, ~500 for marketing), should_show() (page, version, or custom predicates), get_render_mode() ('component' for a bundled React component matching the id, 'blocks' for the generic-blocks payload), and get_blocks() (the generic-blocks array; empty for 'component' render mode).
The framework also exposes a JS API and a REST dismissal route for add-ons that need them. window.gtmkit.introductions.registerComponent( id, Component ) registers a React component for a bundled-component intro; a race-safe pending queue handles registration before the framework boots. POST /wp-json/gtmkit/v1/introductions/{id}/seen records a per-user dismissal (capability check is edit_posts); the modal calls this automatically via the onDismiss prop passed to bundled components.
gtmkit_consent_signal_sources
Since GTM Kit 2.9. Registry of consent signal sources. Each source describes one way to read the current Consent Mode v2 state. The runtime resolves the highest-priority active source and reads through it. Sources register with a priority field; higher priority wins. To override the default fallback, register at priority above 10.
/**
* @param array<string, array{
* id: string,
* priority: int,
* is_active: callable(): bool,
* read: callable(): array<string, 'granted'|'denied'>,
* }> $sources Map keyed by source id.
*/
$sources = apply_filters( 'gtmkit_consent_signal_sources', [] );
add_filter(
'gtmkit_consent_signal_sources',
static function ( array $sources ): array {
$sources['my_cmp'] = [
'id' => 'my_cmp',
'priority' => 90,
'is_active' => static fn(): bool => function_exists( 'my_cmp_is_loaded' )
&& my_cmp_is_loaded(),
'read' => static function (): array {
$cmp = my_cmp_get_state();
return [
'analytics_storage' => $cmp['stats'] ? 'granted' : 'denied',
'ad_storage' => $cmp['marketing'] ? 'granted' : 'denied',
// ... fill the remaining five categories.
];
},
];
return $sources;
}
);
read callables must return granted or denied for each of the seven Consent Mode v2 categories: ad_personalization, ad_storage, ad_user_data, analytics_storage, personalization_storage, functionality_storage, security_storage. Missing keys default to denied. The registry is consulted only when the master toggle is on.
gtmkit_event_should_defer
Since GTM Kit 2.9. Per-event deferral decision. The dataLayer push helpers consult this filter before each push. Default false (events fire). Return true to skip a push when consent is missing for the categories an event requires.
$should_defer = (bool) apply_filters(
'gtmkit_event_should_defer',
false,
$event_name,
$event_payload,
$consent_state
);
// Defer ad-related events when ad_storage is denied.
add_filter(
'gtmkit_event_should_defer',
static function ( bool $should_defer, string $event_name, array $payload, array $consent ): bool {
$needs_ad_consent = in_array( $event_name, [ 'view_item', 'purchase' ], true );
if ( $needs_ad_consent && ( $consent['ad_storage'] ?? 'denied' ) === 'denied' ) {
return true;
}
return $should_defer;
},
10,
4
);
A return of true causes the dataLayer push helper to skip the push. The event is not queued anywhere by core.
gtmkit_strong_block_required_categories
Since GTM Kit 2.10. The list of Consent Mode v2 categories that must all be granted before the strong-block shim unmasks GTM. Default ['analytics_storage', 'ad_storage']. Only honoured when the script gating mode is Strong block; in Always load and Load under Consent Mode v2 control the filter has no effect.
$required_categories = apply_filters(
'gtmkit_strong_block_required_categories',
[ 'analytics_storage', 'ad_storage' ]
);
// Analytics-only setup: unblock GTM as soon as analytics_storage is granted.
add_filter(
'gtmkit_strong_block_required_categories',
static fn(): array => [ 'analytics_storage' ]
);
gtmkit_consent_admin_badges
Since GTM Kit 2.10.1. Status banners rendered above the sections of the Consent settings page. Add-ons that take over a piece of the consent surface push an entry so users see immediately that a higher-priority source is in play.
/**
* @param array<int, array{
* id: string,
* message: string,
* severity: 'info'|'warning'|'success'|'error',
* }> $badges Existing badge entries.
*/
$badges = apply_filters( 'gtmkit_consent_admin_badges', [] );
add_filter(
'gtmkit_consent_admin_badges',
static function ( array $badges ): array {
$badges[] = [
'id' => 'acme-consent-bridge',
'message' => 'Acme Consent Bridge is active. The defaults below are bypassed.',
'severity' => 'info',
];
return $badges;
}
);
Each entry needs a non-empty id and message. severity defaults to info when omitted or set outside the allowed set (info, warning, success, error).
Actions
gtmkit_consent_updated
Since GTM Kit 2.9. Server-side broadcast that consent state has transitioned. Sources fire this after their backing store changes, so other listeners (scripted shims, deferred-event queues) can react without polling. Sources fire this only on transition, not on every read; listeners do not need to dedupe.
/**
* @param array<string, 'granted'|'denied'> $new_state Full state, all 7 categories.
* @param string $source_id Which source dispatched.
*/
do_action( 'gtmkit_consent_updated', $new_state, $source_id );
// Log consent transitions server-side.
add_action(
'gtmkit_consent_updated',
static function ( array $new_state, string $source_id ): void {
error_log( sprintf(
'[gtmkit] consent updated by %s: %s',
$source_id,
wp_json_encode( $new_state )
) );
},
10,
2
);
This action is independent from the JS-side gtmkit:consent:updated event. A source that wants to broadcast on both sides is responsible for firing each.
JS event
gtmkit:consent:updated
Since GTM Kit 2.8.4. A CustomEvent dispatched on window whenever window.gtmkit.consent.update(state) runs. Mirrors the server-side gtmkit_consent_updated action on the client. See Push consent updates from custom code for the full JS surface including the update() signature, the event’s detail shape, and how to read the current state via window.gtmkit.consent.state.