PHP Filters
While our goal is to make the plugin very simple to use through the user interface, this can be limiting for users and developers who need much more granular control over the plugin's output. For those users we provide filters that give them the option to adjust the plugin's behavior programmatically, making the options almost limitless.
Filters can be added to the
functions.phpfile in your child-theme or by using them in a custom plugin. The easiest and safest way is using them in the functions.php file: functions.php
If you think there is a good use case for a new filter, let us know by sending a feature request here.
Marketing Conversion Value Filter
Use the marketing conversion value filter in order to recalculate the conversion value. The output will only affect the conversion value of the paid ads pixels (marketing pixels). The Google Analytics conversion value output will not be touched.
add_filter( 'pmw_marketing_conversion_value_filter', 'filter_conversion_value', 10, 2 );
Example:
add_filter('pmw_marketing_conversion_value_filter', function ($order_total, $order) {
/**
* The order total is the value that is being output as configured
* within the plugin. If you wish to override this and calculate
* the value from scratch, the filter also provides the order object
* with the raw order values.
*
* Example: The average cost to prepare an order for shipping is
* 10% of the order value. Therefore we remove 10% of the order value on
* each order.
**/
return $order_total * 0.9;
}, 10, 2);
Additional Google Ads Conversion Pixels
This filter will only work if the main Conversion ID and Conversion Label are set in the Pixel Manager under Tracking Pixels → Google (Ads & GA4) (which activates the Google Ads conversion tracking in the first place).
With the following filter the Pixel Manager provides a way to add more than one Google Ads conversion pixel programmatically.
This filter will add additional conversion ID and label pairs to the output of the Google Ads pixel.
It will add the output to every page with the Google Ads remarketing pixel, including the purchase confirmation page.
Place the following code into your functions.php and replace the placeholders.
There are two examples below. The first and more common one shows how to add single conversion ID and label pairs. This is when you use different Google Ads accounts to run campaigns for the same website. The second example shows how to add multiple conversion labels for the same conversion ID. This is useful if you want to track different purchase conversion actions in the same Google Ads account. That is if you use one Google Ads account and run campaigns for different websites and want a separate conversion action for each website.
Here's an example that adds single conversion ID and label pairs:
add_filter('pmw_google_ads_conversion_identifiers', function ($conversion_identifiers) {
$conversion_identifiers['CONVERSION_ID_2'] = 'CONVERSION_LABEL_2';
$conversion_identifiers['CONVERSION_ID_3'] = 'CONVERSION_LABEL_3';
return $conversion_identifiers;
});
Here's an example that adds multiple conversion labels for the same conversion ID:
add_filter('pmw_google_ads_conversion_identifiers', function ( $conversion_identifiers ) {
// Add multiple conversion labels for the same conversion ID
$conversion_identifiers['CONVERSION_ID_1'] = [
$conversion_identifiers['CONVERSION_ID_1'], // add the existing label, otherwise it will be removed
'CONVERSION_LABEL_2',
'CONVERSION_LABEL_3',
];
return $conversion_identifiers;
});
Additional Facebook Pixels
This filter requires Pixel Manager 1.64.0 or higher, and it will only work if the main Pixel ID is set in the Pixel Manager under Tracking Pixels → Facebook (Meta) (which activates the Facebook pixel in the first place).
With the following filter the Pixel Manager provides a way to add more than one Facebook (Meta) pixel programmatically.
Every event, on every page, is sent to all configured pixels. This is useful if you want to track the same shop in more than one Facebook ad account or dataset.
Before you add a second pixel, check whether sharing your existing pixel with the other ad account solves your case. A single data source keeps all conversions, audiences and learnings together and remains Meta's own best practice. More on that trade-off in the FAQ.
The filter receives an array that is already seeded with the pixel from the settings, including its Conversion API token and test event code. Append your additional pixels to it and return the array.
Each pixel is an array with the following keys:
pixel_id(required): The Facebook pixel ID. It has to be numeric. Entries without a valid pixel ID are ignored, and a pixel ID that appears more than once is only used once.capi_token(optional, Pro): A Conversion API access token for this pixel. If set, all server-side events, including purchases, the generic server-side events and the subscription lifecycle events, are also sent to this pixel. If omitted, the pixel only receives browser events.test_event_code(optional, Pro): A Conversion API test event code for this pixel. Each pixel is tested with its own code.
Place the following code into your functions.php and replace the placeholders.
Here's an example that adds a second pixel with browser events only:
add_filter('pmw_facebook_pixel_identifiers', function ( $pixel_identifiers ) {
$pixel_identifiers[] = [
'pixel_id' => 'PIXEL_ID_2',
];
return $pixel_identifiers;
});
Here's an example that adds a second pixel including the Conversion API (Pro):
add_filter('pmw_facebook_pixel_identifiers', function ( $pixel_identifiers ) {
$pixel_identifiers[] = [
'pixel_id' => 'PIXEL_ID_2',
'capi_token' => 'CAPI_TOKEN_2',
'test_event_code' => 'TEST_EVENT_CODE_2', // optional
];
return $pixel_identifiers;
});
Browser and Conversion API events share the same event ID per event, and Facebook deduplicates them per pixel, so each pixel receives every event exactly once.
If you are using the server-side proxy, the Pixel Manager automatically syncs the additional pixels to the proxy. After adding or changing pixels in the filter, the new configuration is picked up within a few minutes. To push it right away, open the Pixel Manager settings under Server-Side → SweetCode Server-Side Proxy and click Sync Now.
Verify that the additional pixels are tracking
- Open any page of your shop and check the data layer in the browser console.
pmwDataLayer.pixels.facebook.pixel_idslists every pixel the browser initializes. Access tokens are never part of the data layer, they stay on the server. - Run
fbq.getState().pixelsin the browser console. It shows one entry per initialized pixel. - Watch the network tab for requests to
facebook.com/tr. Each event produces one request per pixel, all with the sameeid(event ID) so Meta can deduplicate against the server-side events. - For the Conversion API, use Meta's Test Events tool in each pixel's own Events Manager view, with that pixel's
test_event_code. - The debug info in the Pixel Manager lists all configured pixels in the Meta sections, including the Meta Event Setup Tool and Meta Business Category Event Restrictions checks.
Do not make the filter output depend on the current page, the current product or the logged-in user. Server-side events run in contexts that have nothing to do with the page the visitor was on, for example a purchase triggered by a WooCommerce order hook or a subscription renewal, so a conditional filter can result in a pixel that receives the browser event but not the matching server-side event. Return the same set of pixels on every request.
Every additional pixel multiplies the number of requests to Meta, in the browser and, if it carries a Conversion API token, also on your server. Only add the pixels you really need.
More about how the multi-pixel output behaves, including advanced matching, consent and the mobile bridge: Multiple Meta (Facebook) pixels.
Protect the Google Ads Conversion Adjustments Feed
This filter only applies when the Google Ads Conversion Adjustments feature is active.
The Pixel Manager exposes a public CSV feed at
/wp-json/pmw/v1/google-ads/conversion-adjustments.csvthat Google Ads' scheduled bulk uploader fetches on its own schedule. The feed contains recent cancelled and refunded order data (order ID, adjustment time, value, currency).By default the URL is reachable without authentication, because Google's scheduler does not carry session credentials. If you want to lock the feed down so a competitor or scraper cannot harvest your refund data, register the
pmw_google_ads_conversion_adjustments_credentialsfilter to require HTTP Basic Auth.
The filter must return an array with user and pass keys. When credentials are returned, the feed responds with 401 Unauthorized for any request that does not present matching Authorization: Basic credentials. The feed is also rate-limited to 30 requests per minute per IP regardless of whether the filter is active.
Add the snippet to your functions.php:
add_filter('pmw_google_ads_conversion_adjustments_credentials', function () {
return [
'user' => 'gads-feed',
'pass' => 'paste-a-long-random-string-here',
];
});
Then, in Google Ads:
- Go to Goals → Conversions → Uploads → Schedules.
- Edit the existing conversion-adjustments.csv schedule (or create a new one).
- Paste the same
uservalue into the Username (optional) field and the samepassvalue into the Password (optional) field. - Save and run a manual test upload to confirm Google Ads can reach the feed.
A small number of hosts running PHP under FastCGI strip the Authorization header before PHP sees it. If Google Ads reports authentication failures after enabling the filter, ask your host to forward the Authorization header to PHP, or add this rule to your site's .htaccess:
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
Use a password manager or run openssl rand -base64 32 to generate a long random string. The longer the password, the safer the feed.
Adjust Google Analytics Config Parameters
To keep the user interface lightweight we only included basic parameters like Enhanced Link Attribution. For those who need much more granular control over the Google Analytics config parameters, we provide a filter. The filter can also be used to override parameters from the user interface.
GA4 processes IPs from pageviews anonymized by design. This can't be changed. IP Anonymization in Google Analytics For Universal Analytics we've set the default parameters to also anonymize IPs. That setting can be overwritten with the filter below.
The following code will remove the anonymize_ip parameter on all Google Universal Analytics configs:
add_filter('pmw_ga_ua_parameters', function ($analytics_parameters, $analytics_id) {
unset($analytics_parameters['anonymize_ip']);
return $analytics_parameters;
}, 10,2);
The following code will adjust the parameters only for the given Google Universal Analytics property:
add_filter('pmw_ga_ua_parameters', function ($analytics_parameters, $analytics_id) {
if('UA-12345678-3' == $analytics_id){
unset($analytics_parameters['anonymize_ip']);
/**
* The following parameter setting will override the one set
* in the user interface.
**/
$analytics_parameters['link_attribution'] = true;
}
return $analytics_parameters;
}, 10,2);
Or maybe, you want to set much more specific settings for link_attribution in your Google Universal Analytics property, like specified here:
add_filter('pmw_ga_ua_parameters', function ($analytics_parameters, $analytics_id) {
if('UA-12345678-3' == $analytics_id){
$analytics_parameters['link_attribution'] = [
'cookie_name' => '_gaela',
'cookie_expires' => 60,
'levels' => 2
];
}
return $analytics_parameters;
}, 10,2);
And with the following filter you can enable the debug mode in GA4.
add_filter('pmw_ga_4_parameters', function ($analytics_parameters, $analytics_id) {
$analytics_parameters['debug_mode'] = true;
return $analytics_parameters;
}, 10,2);
Product ID Output Filter for Paid Ads Pixels
To keep the UX simple and the setup consistent over several advertising channels, there is only one setting in the UX to adjust the product ID output. The same ID then will be used for all paid ads pixels. The standard setting is either the post ID (e.g.
14), the ID for the WooCommerce Google Feed plugin (e.g.woocommerce_gpf_14) or the SKU (e.g.Hoodie). In some cases you might need to use a different ID type on different channels. Maybe you're using the post ID for Google Ads, and the SKU for Meta (Facebook). In this case the following filter will adjust the output for a specific pixel. It is even possible to completely customize the ID, if necessary.
We strongly recommend using the post ID for all product catalogs and for the product ID output. It is the most compatible way and causes the least trouble.
Set specific product ID types per channel
By adding the pixel name to the filter name, you can choose which pixel you want to adjust. For Meta (Facebook) use
pmw_product_id_type_for_facebook, for Microsoft Ads (Bing) usepmw_product_id_type_for_bing, etc. You can use the following pixel filters:
adrollbing(for the Microsoft Ads pixel)facebook(for the Meta pixel)google_adslinkedinoutbrainpinterestredditsnapchattaboolatiktoktwitter
The default values are
post_idfor the post ID,gpffor the WooCommerce Google Feed ID output (e.g.woocommerce_gpf_14), andskufor the SKU.
Here's an example on how to switch the product ID output for Meta (Facebook) to the SKU:
add_filter('pmw_product_id_type_for_facebook', function () {
return 'sku';
});
Here's an example to switch the Meta (Facebook) output to post ID:
add_filter('pmw_product_id_type_for_facebook', function () {
return 'post_id';
});
Custom product IDs
You have even the option to completely customize the product ID output and assign it to a specific channel with the
pmw_product_idsfilter.
In the following example use the pmw_product_ids filter to add one or more custom IDs for each product.
In a second step, use the pmw_product_id_type_for_ filter to assign the new custom ID to one or more specific pixels.
- The following filter creates the custom product IDs.
add_filter('pmw_product_ids', function ($product_ids, $product) {
$product_ids['custom1'] = 'custom_type_' . $product->get_id();
$product_ids['custom2'] = 'custom_pinterest_catalog_' . $product->get_sku();
return $product_ids;
}, 10, 2);
- Then assign the new custom product IDs to the channels of your choice.
add_filter('pmw_product_id_type_for_google_ads', function () {
return 'custom1';
});
add_filter('pmw_product_id_type_for_pinterest', function () {
return 'custom2';
});
Google Analytics Product ID Output Filter
By default, the plugin uses the
post IDas the identifier for Google Analytics. This filter allows you to change this to theSKU.
The main reasons why the plugin uses the post ID by default are:
- The
post IDis more reliable. A shop owner might not add SKUs to all products, leaving the field sometimes empty. But we need to send an identifier to Google Analytics. (In the case the shop owner doesn't add a SKU to a product, we will fall back to thepost ID.) - The products can be identified by the product name in Google Analytics anyway.
- It is easier to search for a product ID in WooCommerce or in Google Analytics, so it's also more practical to use the
post ID. - Some Google tools (e.g. Google Ads scripts) return product IDs in lowercase only, even if they were uploaded with mixed or uppercase characters. Since the
post IDis purely numerical, it is unaffected by case-sensitivity issues.
add_filter('pmw_product_id_type_for_google_analytics', function () {
return 'sku';
});
View Item List Trigger Filter
The plugin uses a smart trigger for the
view_item_listevent. It only triggers if a product is actually visible in the viewport for more than 1 second. If a visitor scrolls up and down and sees a product several times,view_item_listwill be triggered each time (again, only if visible for more than one second). The following filter allows tweaking that behavior.
Lazy loading products is supported too 😀
This will only work on a website where caching is off, or after flushing the cache each time you change the settings.
The following settings are available:
testMode: It activates the test mode, which shows a transparent overlay on each product for whichview_item_listhas been triggered.backgroundColor: Change the background color of the test overlay in case product images are in use where the overlay would not be visible. This is only relevant for the test mode.opacity: By default, the overlay is half transparent. Adjust the opacity to a level that suits better. This is only relevant for the test mode.repeat: By default, the plugin resendsview_item_listevents when a visitor scrolls up and down and sees a product multiple times. Turn this off by setting the value tofalse. Then the plugin will send only oneview_item_listevent when a product becomes visible on a page.threshold: This sets how much of a product card must be visible before the event is triggered. With a setting of1the event triggers only when 100% of the product is visible. The default is0.8.timeout: This value instructs the plugin how long a product must be visible before theview_item_listevent is triggered. The timer resets each time the product leaves the viewport. The time must be set in milliseconds, and the default value is1000milliseconds (1 second).
add_filter('pmw_view_item_list_trigger_settings', function ($settings) {
$settings['testMode'] = true;
$settings['backgroundColor'] = 'green';
// $settings['backgroundColor'] = 'rgba(60,179,113)';
$settings['opacity'] = 0.5;
$settings['repeat'] = true;
$settings['threshold'] = 0.8;
$settings['timeout'] = 1000;
return $settings;
});
Another simple way to enable the view_item_list demo mode is by appending the parameter
vildemomodeto the URL you want to test. Don't forget the?. Example:https://example.com/shop/?vildemomode. This method even works on websites with caching turned on. It will use the default settings.


Cross Domain Linker Settings for Google
Googles domain linker functionality enables two or more related sites on separate domains to be measured as one. You'll find more information about this functionality here and here.
The domain linker values need to be passed as an array to the filter. The plugin will then output all values as a JavaScript formatted domain linker script.
You'll find a list of all possible parameters over here.
Basic example with multiple domains: You can list multiple string values in the domain's property. When the domain's property has at least one value, gtag.js will accept incoming domain links by default. This allows you to use the same code snippet on every domain.
add_filter('pmw_google_cross_domain_linker_settings', function (){
return [
"domains" => [
'example.com',
'example-b.com',
]
];
});
Example output:
gtag('set', 'linker', {
'domains': ['example.com', 'example-b.com']
});
decorate_forms: If you have forms on your site that point to the destination domain, set the decorate_forms property to true.
add_filter('pmw_google_cross_domain_linker_settings', function (){
return [
"domains" => [
'example.com',
'example-b.com',
],
"decorate_forms" => true,
];
});
url_position: To configure the linker parameter to appear in the URL after a fragment (#) instead of as a query parameter (?) (e.g. https://example.com#_gl=1~abcde5~), set the url_position parameter to fragment.
add_filter('pmw_google_cross_domain_linker_settings', function (){
return [
"domains" => [
'example.com',
'example-b.com',
],
"decorate_forms" => true,
"url_position" => 'fragment',
];
});
accept_incoming: Once a user arrives at a page on the destination domain with a linker parameter in the URL, gtag.js needs to be configured to parse that parameter.
If the destination domain has been configured to automatically link domains, it will accept linker parameters by default. No additional code is required on the destination domain.
If the destination domain is not configured to automatically link domains, you can instruct the destination page to look for linker parameters. Set the accept_incoming property to true.
add_filter('pmw_google_cross_domain_linker_settings', function (){
return [
"accept_incoming" => true
];
});
Custom Brand Taxonomy
If you are using your own product attribute to store brand names for products, you can use this filter to output the brand names for the pixels. The filter must return the taxonomy name for the brand attribute. Usually, it is the attribute slug, prefixed with
pa_. In this example, it would bepa_custom-brand. Depending on how the taxonomy has been created, it also can only be the slugcustom-brand. If one doesn't work make sure to try the other.
add_filter('pmw_custom_brand_taxonomy', function (){
return 'pa_custom-brand';
});
Disable adding the tax to product prices
By default, the Pixel Manager outputs the prices on product pages depending on the tax settings in WooCommerce. Some themes don't use the same logic and display product prices without tax, even though the setting in WooCommerce is set to display them including the taxes. In those cases, the Pixel Manager will still output the prices with tax. If you want to instruct the Pixel Manager to also output the products without taxes, please use the following filter.
Don't mix this up with the setting for the purchase confirmation page. The plugin offers a setting to include or exclude tax and shipping on the purchase confirmation page. This is a different setting and only affects the output for the conversion pixels on the purchase confirmation page.
add_filter('pmw_output_product_prices_with_tax', '__return_false');
Add Facebook tracking exclusion patterns
Facebook doesn't allow the tracking of URLs that contain potentially violating personal data (PII). This is why Facebook has implemented a check that under certain conditions detects such URLs and throws a warning in the event manager.
Such a URL might look like this: https://example.com/shop-feature/?firstname=John&lastname=Doe
By default, the Pixel Manager tracks all URLs, because generally WordPress and WooCommerce don't add PII data to URLs. But, WordPress and WooCommerce customizations may add such PII to URLs. In such a case you might run into warning messages in the Facebook event manager, requesting you to fix those.
In order to address this, we implemented a filter that gives you the option to add URL exclusion patterns. Once activated the Pixel Manager will exclude all URLs from tracking in Facebook that match one or more of the exclusion patterns that have been added to the configuration.
The patterns that you can use are regular expression string patterns. In the background, the Pixel Manager uses a RegExp constructor which you can feed with those string patterns. Take a look at this article to get a better idea of how such a pattern can be constructed. Or, take a look at regex101.com with the following example, which shows a way to construct a matching pattern (take note of the backslashes which are necessary to escape forward slashes in the URL).
add_filter('pmw_facebook_tracking_exclusion_patterns', function($patterns) {
$patterns[] = 'parcel-panel';
return array_unique($patterns);
});
Block IPs from all tracking (browser and server-side)
From version 1.57.1 the Pixel Manager supports a unified IP exclusion filter pmw_ip_exclusion_list that blocks all tracking for specified IPs:
- Browser pixel firing (e.g.
fbq(),gtag(),ttq(),pintrk()) — suppressed on the frontend - Browser-initiated server-to-server events (add_to_cart, page_view, etc.) — blocked before sending
- Server-side purchase events (CAPI / S2S purchase hits) — blocked on the WooCommerce server
- SSP purchase proxy events — blocked before sending to SweetCode Cloud
The IPs can be written either as normal IPs or as CIDR ranges. IPv4 and IPv6 are supported.
Learn more about how to specify a CIDR range over here: https://www.ipaddressguide.com/cidr
When the exclusion list is populated, the Pixel Manager automatically activates client-side IP detection (via external services like Cloudflare, ipify, etc.) to determine the visitor's IP, even if no server-to-server integration is active. This adds a small initial delay (~100ms) on the first page load while the IP is fetched and cached for the session.
add_filter('pmw_ip_exclusion_list', function($ip_exclusions) {
// Exact IPv4 address
$ip_exclusions[] = '203.0.113.50';
// IPv4 CIDR range (entire /24 subnet)
$ip_exclusions[] = '198.51.100.0/24';
// Exact IPv6 address
$ip_exclusions[] = '2001:db8::1';
// IPv6 CIDR range
$ip_exclusions[] = '2001:db8::/32';
return array_unique($ip_exclusions);
});
The older pmw_exclude_ips_from_server_2_server_events filter is deprecated since version 1.57.1 but still works for backward compatibility. It only blocked browser-initiated S2S events. Migrate to pmw_ip_exclusion_list to also block browser pixels and server-side purchase events.
Block IPs from server-to-server events (deprecated)
This filter is deprecated. Use pmw_ip_exclusion_list instead, which provides broader coverage including browser pixels and server-side purchase events.
From version 1.27.8 the Pixel Manager automatically prevents server-to-server events that are triggered by known bots. This reduces the load on the server.
The following filter allows users of the Pixel Manager to add more IPs and IP ranges to the exclusion list.
The IPs can be written either as normal IPs or as CIDR ranges.
Learn more about how to specify a CIDR range over here: https://www.ipaddressguide.com/cidr
add_filter('pmw_exclude_ips_from_server_2_server_events', function($ip_exclusions) {
// normal IP
$ip_exclusions[] = '123.123.123.123';
// CIDR range
$ip_exclusions[] = '123.123.123.123/32';
return array_unique($ip_exclusions);
});
Disable subscription renewal tracking for all tracking pixels
Use the following filter to disable subscription renewal tracking for all pixels.
add_filter('pmw_subscription_renewal_tracking', '__return_false');
Disable Google Analytics subscription renewal tracking
Use the following filter to disable Google Analytics subscription renewal tracking.
add_filter('pmw_google_analytics_subscription_renewal_tracking', '__return_false');
Disable Facebook CAPI subscription renewal tracking
Use the following filter to disable Facebook CAPI subscription renewal tracking.
add_filter('pmw_facebook_subscription_renewal_tracking', '__return_false');
Suppress the browser purchase conversion for specific orders
Available in Pixel Manager 1.31.2 and later, in both the free and the pro version.
Use the pmw_conversion_prevention filter to stop the Pixel Manager from firing the browser purchase conversion for an individual order. Returning true withholds the order data from the data layer on the order received page, which means no browser pixel fires a purchase event for that order: not Google Ads, not Meta, not Google Analytics, none of them.
The filter receives two arguments:
| Argument | Type | Description |
|---|---|---|
$prevent | bool | Whether to suppress the conversion. Default false. |
$order | WC_Order | The order the confirmation page was reached for. |
What it does and does not cover
- It affects the browser purchase conversion only. Server-side purchase events run on order status transitions and are gated separately. To suppress those, use
pmw_skip_s2s_purchase_event. - It is an addition to the checks the Pixel Manager already performs. Orders in
failed,cancelledorrefundedstatus, and visits by user roles excluded from tracking, never fire a purchase conversion in the first place. - Because no pixel fires, the Pixel Manager does not write its duplication prevention marker on the order. The order therefore stays eligible for Automatic Conversion Recovery (pro), which is what makes the deferral pattern below work.
Deferring a conversion until the order is paid
The most common use for this filter is a payment gateway that creates orders in pending payment status and confirms the payment later: bank transfer, invoice, manual review, or a custom gateway. The customer still lands on the order received page, so the browser purchase conversion fires for an order that may never be paid.
Write the filter so it evaluates the current order status rather than suppressing the gateway outright. ACR re-evaluates this same filter on the customer's next visit to the shop, so as soon as the order reaches a paid status the filter returns false and the full purchase conversion is recovered, including the Google Ads conversion.
add_filter('pmw_conversion_prevention', function ($prevent, $order) {
if (!$order instanceof WC_Order) {
return $prevent;
}
// Only this gateway is affected. Every other gateway keeps its current behavior.
if ($order->get_payment_method() !== 'my_custom_gateway') {
return $prevent;
}
// Hold the conversion back until the order reaches a paid status.
// wc_get_is_paid_statuses() returns `processing` and `completed` by default.
if (!in_array($order->get_status(), wc_get_is_paid_statuses(), true)) {
return true;
}
return $prevent;
}, 10, 2);
Make the suppression conditional on the order status, as in the example above. A filter that returns true for a gateway unconditionally discards the conversion permanently, because the order never becomes eligible for recovery either.
Orders recovered this way are reported in the ACR column of the Payment Gateway Tracking Accuracy report, so you can verify that the deferral works instead of silently losing conversions.
Requirements and limits of the deferred conversion
The recovery half of this pattern depends on ACR, so its requirements apply: the customer has to return to the shop with the same browser after the order reaches a paid status. If that never happens, the conversion is not recorded at all.
For platforms that have a server-side counterpart in the Pixel Manager, prefer the Status-Driven Purchase Conversions recipe instead. It routes the purchase through the platform's Conversion API, which fires on the paid-status transition itself and needs no return visit. Google Ads has no server-side purchase API in the Pixel Manager, which is why the filter above combined with ACR is the available route for Google Ads conversions.
Skip server-side purchase events for specific orders
Available in Pixel Manager Pro 1.58.10 and later.
Use the pmw_skip_s2s_purchase_event filter to prevent the Pixel Manager from firing server-side purchase events for specific orders. Returning true skips the purchase event for every server-side platform (Facebook CAPI, TikTok Events API, Pinterest Conversions API, Snapchat CAPI, Reddit CAPI, and GA4 Measurement Protocol), including the SweetCode Cloud (SSP) proxy path. The order is also excluded from the Google Ads Conversion Adjustments CSV feed so it cannot later be sent as a RETRACT or RESTATE adjustment.
The filter receives three arguments:
| Argument | Type | Description |
|---|---|---|
$skip | bool | Whether to skip the purchase event. Default false. |
$order | WC_Order | The WooCommerce order. |
$context | string | The pixel class name (e.g. SweetCode\Pixel_Manager\Pixels\Facebook\Facebook_CAPI) or conversion_adjustments_feed. |
Common use case: exclude marketplace orders that were imported programmatically (Amazon, eBay, etc.) and therefore should not be counted as conversions.
The following example skips every server-side purchase event for orders imported by the magnalister plugin. Magnalister prefixes every imported order's customer note with magnalister-Verarbeitung, which makes the match simple regardless of the source marketplace.
add_filter('pmw_skip_s2s_purchase_event', function ($skip, $order, $context) {
// Skip every PMW server-side purchase event and Google Ads conversion
// adjustment for magnalister-imported marketplace orders.
if (strpos((string) $order->get_customer_note(), 'magnalister-Verarbeitung') === 0) {
return true;
}
return $skip;
}, 10, 3);
You can also target a specific platform by checking the $context argument:
use SweetCode\Pixel_Manager\Pixels\Facebook\Facebook_CAPI;
add_filter('pmw_skip_s2s_purchase_event', function ($skip, $order, $context) {
// Only skip the Facebook CAPI purchase event, leave all other platforms alone.
if ($context === Facebook_CAPI::class && $order->get_payment_method() === 'some_gateway') {
return true;
}
return $skip;
}, 10, 3);
Filter the Google Ads Conversion Adjustments feed
Available in Pixel Manager Pro 1.60.1 and later.
Use the pmw_conversion_adjustments_feed_row filter to exclude or modify individual rows of the Google Ads Conversion Adjustments CSV feed (the RETRACT / RESTATE feed built from cancelled orders and refunds).
Return an empty value (null, false, or []) to drop a row from the feed. You can also modify the row's fields; the Pixel Manager re-applies its own validation afterwards (clamping negative values to 0, enforcing Google's column order), so the feed always stays within Google's spec.
This is the recommended way to customize the feed. Unlike pmw_skip_s2s_purchase_event, it only affects the feed, so there is no risk of accidentally suppressing your live server-side purchase events.
The filter receives three arguments:
| Argument | Type | Description |
|---|---|---|
$row | array | The associative row data (see keys below). |
$order | WC_Order | The order being adjusted. |
$type | string | The adjustment source: cancelled or refund. |
The $row array has the following keys:
| Key | Description |
|---|---|
order_id | The order number Google uses to match the original conversion. |
conversion_name | The configured conversion name. |
adjustment_time | ISO 8601 timestamp, e.g. 2026-06-25T13:00:00+00:00. |
adjustment_type | RETRACT or RESTATE. |
adjusted_value | The new order value (RESTATE only; empty for RETRACT). |
currency | The currency code (RESTATE only). |
Example: only send adjustments for orders that came from a Google Ads click
The following example drops every order that has no Google Ads click ID. The Pixel Manager stores the click ID on the order as _wpm_gclid when the customer reaches the order received page. Orders imported from marketplaces (Amazon, eBay, etc. via Channable, M2E Cloud, magnalister, and similar integrators) never reach that page, so they never carry a click ID and are dropped.
add_filter('pmw_conversion_adjustments_feed_row', function ($row, $order) {
// Drop orders that have no Google Ads click ID recorded.
if (empty($order->get_meta('_wpm_gclid', true))) {
return null;
}
return $row;
}, 10, 2);
Filtering the feed reduces noise in your Google Ads logs, but it does not improve the accuracy of your data, and it can reduce it.
Google also matches conversions through Enhanced Conversions (hashed email and phone number) even when no readable click ID is present. Those orders have no local _wpm_gclid, so a click-ID filter will drop their legitimate RETRACT / RESTATE adjustments, and Google will keep counting the original (now cancelled or refunded) conversion.
Google's own documentation states that the "this conversion does not exist" responses can be safely ignored. We recommend uploading all adjustments and ignoring those warnings, and we treat the contradiction between that documentation and the warnings shown in the Google Ads UI as a low-priority inconsistency on Google's side. Use this filter only if you want to quiet the logs, not as a fix for conversion accuracy.
Mark custom order flows as backend-manual
Available in Pixel Manager Pro 1.58.10 and later.
Use the pmw_is_backend_manual_order filter to teach the Pixel Manager about custom order-creation flows (B2B quote-to-order, push-cart, pay-for-order, etc.) that neither set _created_via = 'admin' nor trigger WooCommerce Order Attribution. When an order is recognized as backend-manual, the Pixel Manager re-captures the customer's browser identifiers on the purchase confirmation page so server-side purchase events carry the real customer context instead of being empty or attributed to the staff member who created the order.
The filter receives two arguments:
| Argument | Type | Description |
|---|---|---|
$is_backend_manual | bool | Whether PMW already considers this a backend-manual order. |
$order | WC_Order | The WooCommerce order being evaluated. |
Example: flag every order created by a custom quote plugin that stores its origin in order meta.
add_filter('pmw_is_backend_manual_order', function ($is_backend_manual, $order) {
if ($order->get_meta('_my_quote_plugin_origin') === 'staff_quote') {
return true;
}
return $is_backend_manual;
}, 10, 2);
Enable Facebook Hybrid Mobile App Events
If you're using a wrapper to make your website available as a hybrid mobile app on iOS or Android, you can use the following filter to enable the Facebook hybrid mobile app events bridge.
add_filter('pmw_facebook_mobile_bridge_app_id', function () {
return 'YOUR_APP_ID';
});
Find more information about the Facebook hybrid mobile app events bridge over here
Add more selectors for specific events
If you are using a custom theme that doesn't implement the standard WooCommerce classes on buttons like add-to-cart or begin-checkout, the events won't be triggered. In this case, you can use the following filters to add more selectors for specific events.
First try to use the body selector. In most cases, this alone will work and fix the trigger.
Don't use document as selector. It won't work.
add-to-cart event
First, try the body selector. In most themes, this will work.
add_filter('pmw_add_selectors_add_to_cart', function () {
return [
'body',
];
});
If that doesn't work, you'll have to add a selector that is specific to the button that triggers the add-to-cart event.
add_filter('pmw_add_selectors_add_to_cart', function () {
return [
'.custom-add-to-cart-selector',
];
});
begin-checkout event
First, try the body selector. In most themes, this will work.
add_filter('pmw_add_selectors_begin_checkout', function () {
return [
'body',
];
});
If that doesn't work, you'll have to add a selector that is specific to the button that triggers the begin-checkout event.
add_filter('pmw_add_selectors_begin_checkout', function () {
return [
'.custom-begin-checkout-selector',
];
});
Order Fees Filter
This filter controls the payment processor fees that the Pixel Manager deducts from the marketing conversion value. These are the fees your payment provider keeps out of your payout: the customer never pays them, and they are not part of any WooCommerce order figure. They are deducted under both the Order Subtotal and the Profit Margin option in General → Order configuration → Marketing value logic.
Unlike taxes and shipping costs, which are standardized in WooCommerce, payment gateway fees have no standard storage location. Some gateways save them in a dedicated order meta field, and many do not save them at all. The Pixel Manager reads them for the popular gateways listed under Shop Settings. For every other gateway, use this filter to calculate them yourself.
Since version 1.64.1, $order_fees no longer includes amounts that were added to the order as a WooCommerce order fee (a gift wrap fee, a cash on delivery surcharge, or a deposit instalment).
Such a surcharge is money the customer pays, not a cost, and the WooCommerce order subtotal never contained it, so deducting it removed it a second time and reported less than the products were worth. If you relied on the previous behaviour, add $order->get_total_fees() back inside this filter.
add_filter('pmw_order_fees', function($order_fees, $order){
// The $order_fees variable contains the payment processor fees
// that the Pixel Manager has been able to extract from the
// order meta fields of popular payment gateways.
// You can use the value of $order_fees as a starting point
// and add your own calculated fees to it. Or you can
// completely override the value of $order_fees
// and return your own value.
// If the payment method is braintree_cc
// then the order fee is 0.29 + 2.09% of the order total.
// Add it to the order_fees.
if ($order->get_payment_method() == 'braintree_cc') {
$order_fees += 0.29 + ($order->get_total() * 0.0209);
}
return $order_fees;
}, 10, 2);
Split Payment Order Role
Deposit and partial-payment plugins split one sale across several WooCommerce orders. The Pixel Manager reports each sale once, in full, at the moment the deposit is paid, on the order that represents the sale. Since version 1.64.1 this works out of the box for Deposits & Partial Payments for WooCommerce (Acowebs, free and Pro) and WooCommerce Deposits (Webtomizer, woocommerce.com).
If you use a different deposits plugin, the pmw_split_payment_order_role filter tells the Pixel Manager what role an order plays:
standard— a regular order, tracked normally.payment_leg— the order only collects an instalment for a sale that lives on its parent order. The purchase is reported with the parent order's products and value, and the duplication prevention marker is written on the parent, so the other instalments of the same sale stay silent. Server-side purchase events for the order itself are skipped.follow_up_invoice— the order re-invoices a part of a sale that was already reported when its parent order was placed. It never reports a purchase, neither in the browser nor server-side.
add_filter('pmw_split_payment_order_role', function ($role, $order) {
// Example: a deposits plugin that stores its instalment orders
// with a custom created_via and the sale on the parent order.
if (
$order instanceof WC_Order
&& 'my_deposits_plugin' === $order->get_created_via()
&& $order->get_parent_id() > 0
) {
return 'payment_leg';
}
return $role;
}, 10, 2);
For payment_leg orders the parent must be a regular shop order that carries the product line items, otherwise the Pixel Manager falls back to standard handling.
Set the maximum of orders to analyze for the tracking accuracy analysis
The tracking accuracy analysis may take too much time to complete. This is especially the case if you have a slow server, a short PHP timeout, or a clogged Action Scheduler queue.
The following filter allows you to set the maximum number of orders that should be analyzed.
Try 100 orders first. If that works, you can increase the number.
The analysis runs overnight, so you'll have to wait until the next day to see the results.
If that doesn't help, you'll probably need to fix the Action Scheduler queue.
add_filter('pmw_tracking_accuracy_analysis_max_order_amount', function () {
return 100;
});
Adjust Outbrain event name mapping
If you've been working with Outbrain before and have been using different event names than the default ones in the Pixel Manager, you can use the following filter to adjust the event name mapping.
add_filter('pmw_outbrain_event_name_mapping', function ( $mapping ) {
$mapping['purchase'] = 'purchase';
return $mapping;
});
Adjust Taboola event name mapping
If you've been working with Taboola before and have been using different event names than the default ones in the Pixel Manager, you can use the following filter to adjust the event name mapping.
add_filter('pmw_taboola_event_name_mapping', function ( $mapping ) {
$mapping['purchase'] = 'purchase';
return $mapping;
});
Suppress the version info output in the developer console
The Pixel Manager prints a single line to the browser console on every page load, even when the Console Logger is switched off:
Pixel Manager for WooCommerce: pro | distro: fms | active license: yes | version: 1.63.0
If you want to suppress it, you can use the following filter.
add_filter('pmw_show_version_info', '__return_false');
This filter is available in version 1.58.5 and later.
The line isn't removed, it's demoted into the Console Logger. With the logger off nothing is printed, and it reappears when you enable the logger with ?pmwloggeron for debugging.
Add custom parameters to the Google Analytics purchase event
These filters are only available in version 1.44.0 and later.
The Pixel Manager automatically sends various standard parameters to Google Analytics. With the custom order parameters filters, you can add additional parameters to the Google Analytics purchase event on order and order item level.
Order level custom parameters
Use cases for order level custom parameters are:
- Customer segmentation
- Based on total order value
- Based on the number of orders
- Weather conditions at the location of the customer at the time of the purchase
- Expected delivery time
Order level custom parameters example
add_filter('pmw_custom_order_parameters', function ( $custom_parameters, $order ) {
$custom_parameters['custom_parameter_a'] = 'custom_value_a';
$custom_parameters['custom_parameter_b'] = 'custom_value_b';
return $custom_parameters;
}, 10, 2);
Order item level custom parameters
Use cases for order item level custom parameters are:
- Supplier for this particular product on this particular order
- Customization of the product
- Gift wrapping option
Order item level custom parameters example
add_filter('pmw_custom_order_item_parameters', function ( $custom_parameters, $order_item, $order ) {
$custom_parameters['custom_parameter_1'] = 'custom_value_1';
$custom_parameters['custom_parameter_2'] = 'custom_value_2';
return $custom_parameters;
}, 10, 3);
How to use the Google Analytics custom parameters filters
-
Add the custom parameters to the Google Analytics purchase event by using one or both of the filters above.
-
Configure the custom parameters in Google Analytics as custom dimensions or metrics:
-
Wait one day for the data to be processed by Google Analytics.
-
Now you can use the custom parameters in Google Analytics reports.
Filter script opening attributes
These filters are only available in version 1.45.1 and later.
Example: Add the Cloudflare data-cfasync attribute to the script tag.
add_filter('pmw_opening_script_string_attributes', function ( $attributes ) {
// The value is an array of text strings that will be concatenated.
$attributes['data-cfasync'] = ['false'];
return $attributes;
});
Filter the Pixel Manager options
This filter is only available in version 1.46.2 and later.
Every now and then you might have a special setup or a special requirement that can't be fulfilled by using the standard settings. In such cases, you can use the following filter to adjust the Pixel Manager's options before it processes them.
Be aware that we might change the structure of the settings array in future versions. So be careful when using this filter. Changes to structure happen very rarely, but they can happen.
/**
* Purpose: Filter the Pixel Manager's options before it processes them.
*
* Place this code into functions.php of your child theme.
*
* Example:
* Some shops use the same install to serve on different domains.
* The following example shows how to adjust the Google Analytics measurement ID based on the host.
* You can use the same logic to adjust any other pixel ID or setting.
**/
add_filter('pmw_options', function ($options) {
// Use the error_log to get a better understanding of the options array.
// error_log('options: ' . print_r($options, true));
$host = $_SERVER['HTTP_HOST'];
if (preg_match("/.*example.nl/", $host)) {
$options['google']['analytics']['ga4']['measurement_id'] = 'abc';
} elseif (preg_match("/.*example.us/", $host)) {
$options['google']['analytics']['ga4']['measurement_id'] = 'def';
}
return $options;
});
Google tag ID
With the following filter you can override the default Google tag ID. The override is applied on every request, so it takes effect even when the tag ID is cached.
The pmw_google_tag_id filter is available as of version 1.60.1. In earlier versions (1.58.5 to 1.60.0) use google_tag_id instead; it still works as a deprecated alias.
<?php
/**
* Override the default Google tag ID
*/
add_filter('pmw_google_tag_id', function ($tag_id) {
return "AW-1234567890";
});
Suppress Cart Item Inline Script Output
This filter is only available in version 1.54.2 and later.
Some themes use JavaScript-based renderers that don't properly hide <script> tags, causing them to be visible on the page. This is a theme issue, but if you can't easily change the theme, you can use this filter to suppress the inline script output for cart item data.
The Pixel Manager outputs small inline <script> tags after each cart item name to track cart interactions (like remove_from_cart events). When these scripts are suppressed, the plugin falls back to loading the cart item data via AJAX, so tracking continues to work.
Filter Signature
apply_filters('pmw_output_cart_item_data', $output, $cart_item, $cart_item_key, $action)
| Parameter | Type | Description |
|---|---|---|
$output | bool | Whether to output the script. Default: true |
$cart_item | array | Cart item data containing product_id and variation_id |
$cart_item_key | string | Unique cart item key |
$action | string | The current action hook name (see below) |
Action Hook Values
The $action parameter tells you which hook triggered the output:
woocommerce_after_cart_item_name- Cart pagewoocommerce_after_mini_cart_item_name- Mini cart widgetwoocommerce_mini_cart_contents- Mini cart fallback
Examples
Suppress all cart item inline scripts:
add_filter('pmw_output_cart_item_data', '__return_false');
Suppress only on the cart page:
add_filter('pmw_output_cart_item_data', function($output, $cart_item, $cart_item_key, $action) {
if ($action === 'woocommerce_after_cart_item_name' && is_cart()) {
return false;
}
return $output;
}, 10, 4);
Suppress on cart and checkout pages:
add_filter('pmw_output_cart_item_data', function($output, $cart_item, $cart_item_key, $action) {
if ($action === 'woocommerce_after_cart_item_name' && (is_cart() || is_checkout())) {
return false;
}
return $output;
}, 10, 4);
Suppress only in mini cart:
add_filter('pmw_output_cart_item_data', function($output, $cart_item, $cart_item_key, $action) {
if (in_array($action, ['woocommerce_after_mini_cart_item_name', 'woocommerce_mini_cart_contents'])) {
return false;
}
return $output;
}, 10, 4);
Product Data Layer Output in Product Loops
These filters are only available in version 1.62.1 and later.
The Pixel Manager outputs a hidden marker element and a small inline <script> tag after each product in WooCommerce product loops (through the woocommerce_after_shop_loop_item hook). This output powers the view_item_list and select_item events for product lists.
Some page builders and themes sanitize the output of that hook. The tags get stripped, and the content of the script becomes visible as raw text inside each product card. Known examples are the Elementor Products widget and theme builders that render their own product cards. This is a page builder issue, but if you can't wait for a fix, the Pixel Manager provides two filters to work around it.
Defer the output to the footer (recommended)
The pmw_defer_product_data_layer_to_footer filter moves the product data layer output into wp_footer, out of reach of sanitizing page builders, and keeps tracking fully working. The Pixel Manager prints the data scripts in the footer and re-inserts the marker elements into the product cards with a small script (matched through the standard WooCommerce post-{id} loop item class).
Filter Signature
apply_filters('pmw_defer_product_data_layer_to_footer', $defer, $product)
| Parameter | Type | Description |
|---|---|---|
$defer | bool | Whether to defer the output to the footer. Default: false |
$product | WC_Product | The product being output |
Defer everywhere (start here):
add_filter('pmw_defer_product_data_layer_to_footer', '__return_true');
This works no matter which page builder, theme or widget renders your product grids. Use it first to confirm that the filter solves your problem. If it does, you can narrow it down to the specific widget afterwards.
Defer only inside the Elementor Products widget:
The woocommerce-products widget is part of Elementor Pro. If you only run the free Elementor plugin, or your product grid comes from your theme (for example a theme builder module), this snippet never runs and nothing changes. Use the global filter above instead.
add_action('elementor/frontend/widget/before_render', function ($widget) {
if ('woocommerce-products' === $widget->get_name()) {
add_filter('pmw_defer_product_data_layer_to_footer', '__return_true');
}
});
add_action('elementor/frontend/widget/after_render', function ($widget) {
if ('woocommerce-products' === $widget->get_name()) {
remove_filter('pmw_defer_product_data_layer_to_footer', '__return_true');
}
});
The same pattern works for other page builders. Toggle the filter on right before the problematic widget or module renders, and off right after, using the hooks that the page builder provides. Make sure the widget name you check for is the one your grid actually uses, otherwise the filter is never applied.
Products that a widget loads after the initial page render, for example through AJAX pagination or a load-more button, are not covered by the footer pass. Only defer where necessary.
Suppress the output entirely
If you don't need view_item_list and select_item tracking for the affected products, you can suppress the output entirely with the pmw_output_product_data_layer_script filter. All other tracking, such as product pages, cart and purchase events, is unaffected.
Filter Signature
apply_filters('pmw_output_product_data_layer_script', $output, $product, $meta_tag)
| Parameter | Type | Description |
|---|---|---|
$output | bool | Whether to output the product data layer. Default: true |
$product | WC_Product | The product being output |
$meta_tag | bool | true for the meta tag output in the head of product pages, false in loop context |
Suppress everywhere:
add_filter('pmw_output_product_data_layer_script', '__return_false');
Suppress only inside the Elementor Products widget:
The woocommerce-products widget is part of Elementor Pro. If you only run the free Elementor plugin, or your product grid comes from your theme, this snippet never runs and nothing changes.
add_action('elementor/frontend/widget/before_render', function ($widget) {
if ('woocommerce-products' === $widget->get_name()) {
add_filter('pmw_output_product_data_layer_script', '__return_false');
}
});
add_action('elementor/frontend/widget/after_render', function ($widget) {
if ('woocommerce-products' === $widget->get_name()) {
remove_filter('pmw_output_product_data_layer_script', '__return_false');
}
});
SSP Additional Domains (Multi-Domain Support)
This filter requires the Server-Side Proxy to be active on your primary domain. Available since Pixel Manager 1.57.1.
If your single WordPress installation serves multiple domains (without WordPress Multisite), you can connect each domain to its own SSP proxy endpoint using this filter. This is useful when a single WooCommerce store is accessible through more than one domain name.
How it works:
- Set up the Server-Side Proxy for your primary domain as usual through the Pixel Manager settings.
- In the SweetCode Cloud portal, create a new domain entry for each additional domain and copy the sync token.
- Add the filter below to your child theme's
functions.php.
The Pixel Manager will automatically:
- Output the correct SSP proxy URL and verification token for visitors on each domain
- Push your CAPI destination configs to each additional domain's SSP endpoint
- Keep all domains in sync whenever you update your pixel settings or on the daily sync schedule
add_filter('pmw_ssp_additional_domains', function ($domains) {
// Add one entry per additional domain
$domains[] = [
'sync_token' => 'ssp_tok_xxxxxxxxxxxx', // Sync token from SweetCode Cloud portal
'proxy_hostname' => 'ssp.otherdomain.com', // The SSP proxy hostname for this domain
'shop_origin' => 'https://otherdomain.com', // The WordPress site URL on this domain
];
// Add more domains as needed
// $domains[] = [
// 'sync_token' => 'ssp_tok_yyyyyyyyyyyy',
// 'proxy_hostname' => 'ssp.thirddomain.com',
// 'shop_origin' => 'https://thirddomain.com',
// ];
return $domains;
});
Parameters for each domain entry:
| Parameter | Description |
|---|---|
sync_token | The domain sync token from the SweetCode Cloud portal. Each domain has its own unique token. |
proxy_hostname | The full SSP proxy hostname (e.g. ssp.otherdomain.com). Must match the domain you created in SweetCode Cloud. |
shop_origin | The full origin URL of the WordPress site on this domain, including the protocol (e.g. https://otherdomain.com). Must match exactly what appears in the browser's address bar. |
The initial config push for additional domains happens on the next daily sync, or you can trigger it from the Pixel Manager under Server-Side → SweetCode Server-Side Proxy. After adding the filter, trigger a sync to activate the additional domains immediately.
The Pixel Manager settings UI only shows the status of your primary SSP domain. To verify that additional domains are synced correctly, check the SweetCode Cloud portal — each domain's sync status, routing status, and event activity are visible there.