This is an info Alert.
x402 Logo
  • Product
    • Become Agent-Ready
      • Merchants
        Agentic Commerce — list your store across ChatGPT, Gemini, Claude & Perplexity
      • Publishers
        Monetize your content when AI agents read, cite, or train on it
      • SaaS Companies
        Treat AI agents as first-class customers with agent-priced checkout
    • Monetize
      • Monetize MCP Server
        Charge per call on any MCP server in 2 minutes
      • Monetize AI Agents
        Turn n8n, Zapier, Activepieces workflows into revenue
      • Agent Feed
        Pay-per-query access to licensed publisher content for your agents
  • Resources
    • xpay Ecosystem
      • xpay✦ Tools
        1,000+ pay-per-use tools for your AI agents
      • Agent-Ready SaaS Index
        25,481 SaaS scored on agent-buyability
      • SaaS Pricing Database
        Pricing pages indexed across 1,000+ categories
      • Shopify Apps Directory
        Every Shopify app, with its full review history
      • WooCommerce Plugins Directory
        Every WooCommerce plugin, scored on how well it is maintained
      • GitHub
        Open source repositories
    • Agent Building
      • Agent Frameworks
        AI frameworks for building multi-agent systems
      • x402 Integration
        AI frameworks with x402 payment integration
      • Networks
        Blockchain networks supporting x402
    • Company
      • About xpay✦
        Our mission, products, and protocols
      • Blog
        Latest insights and updates
      • Docs
        Complete xpay documentation
  • Pricing
  • Blog
  • Docs
Get Started
  1. xpay✦ Commerce

  2. Directory

  3. WooCommerce plugins

  4. CMLS – Customer Match List for Google Ads & Meta Ads

CMLS – Customer Match List for Google Ads & Meta Ads

Export WooCommerce customers to Google Ads Customer Match lists. One auto-updated CSV per country, served on a secure URL. Works with Meta Ads too.

20+ active installsFree on WordPress.org
View on WordPress.orgSupport forum
Will this break my store?

What the WordPress.org registry says about keeping CMLS – Customer Match List for Google Ads & Meta Ads running.

WordPress compatibility
Tested to 7.0.2
Tested against the WordPress branch in use today.
Last updated
17 days ago
Requires PHP
7.4
Your host must be running at least this version.
Requires WordPress
6.0
Requires other plugins
woocommerce
These must be installed and active first.
Contributors
1
A single maintainer. Worth knowing if the plugin is load-bearing for your store.

20+ active installsWordPress.org reports installs in bands, not exact counts.
Maintenance & trust

Scored on how CMLS – Customer Match List for Google Ads & Meta Ads is looked after — not on how many stores run it.

Well maintained
Not enough public feedback to put a confident number on this one. Little public feedback — score rests mostly on release activity. What we can see is below.

Maintenance
35 / 35
Updated 15 days ago.
WordPress compatibility
20 / 20
Tested to WP 7.0.2 (current).
Support responsiveness
Not enough data
Only 0 support thread(s) — not enough to judge.
Merchant satisfaction
Not enough data
No ratings yet.
Listing transparency
3 / 10
Provides: description
2 of 5 measures had too little evidence to score. They are left out of the total rather than counted as zero — otherwise a plugin would be marked down for being small rather than for being poorly kept.Measured 2026-08-01 from the WordPress.org plugin registry.
Ratings

No one has rated this plugin on WordPress.org yet. That is a statement about the ratings page, not about the plugin — plenty of well-kept plugins never collect them.

Build Google Ads Customer Match lists from your WooCommerce orders: one CSV file per billing country, updated automatically on a schedule. Each country file is downloadable via a private and secure (tokenized) URL — ideal for marketing teams that run per-country campaigns and want clean, single-market audience lists.

The values are hashed with the same SHA-256 normalization used by both Google Ads Customer Match and Meta Ads Custom Audiences, so the same file can also be uploaded manually to Meta Ads Manager (see FAQ).

Why this export?
Not all purchases are captured by Google Ads tracking; periodic uploads of Customer Match Lists help targeting and campaign performance. Even Google’s automatic conversion-based customer lists (auto-enabled for eligible accounts from August 2026) only contain tag-tracked conversions — this plugin exports from the order database itself, so ad-blocked, phone, offline and historical orders are included too (see FAQ). The export reflects your order database as-is — applying your store’s consent choices and legal requirements to it is your responsibility as the merchant (see the GDPR / privacy FAQ).

Why per-country files?
Marketing teams optimize Google Ads campaigns per country. Mixing a German audience into a Romanian campaign’s match list pollutes the targeting. This plugin keeps each market in its own file.

How it works

  1. A one-time backfill walks every completed WooCommerce order, splits them by billing country, hashes the customer fields and writes one CSV per country.
  2. A scheduled job (1, 6, 12, or 24 hours) picks up new completed orders since the last run and appends them to the right country file. Orders that turn completed late (delayed payment confirmation, manual completion) are appended the moment they complete, so they are never missed.
  3. Each country file is downloadable from a secret URL that you hand to your marketing team. They can paste it into Google Ads Data Manager → HTTPS connector and Google fetches the latest list on its schedule OR do one-time manual upload.

Features

  • Per-country splitting — one CSV per ISO billing country, never mixed.
  • Background backfill from the admin (no CLI needed) — runs via Action Scheduler.
  • One-time backfill via WP-CLI with status filter, date filter (--days=N, --since, --until), dry-run, and batch-size control.
  • Scheduled incremental appends every 1, 6, 12, or 24 hours.
  • Per-(email, country) deduplication via a dedicated indexed database table — no full-file scans, single indexed insert per row.
  • CSV exported columns headers: Email, Phone, First Name, Last Name, Country, Zip.
  • Hashed per Google spec: SHA-256 of trimmed/lowercased values, phone normalized to E.164 with the leading + — the same hashing Meta Ads Custom Audiences accept.
  • Secret tokenized URLs — one per country, all resettable from the admin or via CLI.
  • Storage hardening — files live under wp-content/uploads/customer-match-list-google-ads/ with filenames containing a 64-bit hash derived from the token.
  • REST endpoint stream — the CSV is sent chunked with nocache_headers().
  • HPOS-first SQL — direct JOIN on wc_orders + wc_order_addresses (or posts+postmeta on classic). Designed for stores with tens of millions of orders.

Admin Dashboard Settings

WooCommerce → Marketing → Customer Match List.

From there you can:

  • Pick the cron interval (1/6/12/24 h).
  • See the access token; reset it (regenerates every URL, renames every file).
  • See per-country status: row count, file size, and the tokenized URL ready to copy.
  • Start a background backfill with a date-range preset.
  • Watch backfill progress live (orders processed, rows written, rows skipped, watermark).
  • Delete every country file and reset the dedup table.
  • Do an automated check that confirms the webserver does not expose the CSV directory via a public file listing.

WP-CLI

  • wp cmls backfill — one-time backfill of historical orders, split by country. Flags: --statuses=completed (default), --all-statuses, --days=N, --since=YYYY-MM-DD, --until=YYYY-MM-DD, --dry-run, --batch-size=5000, --force.
  • wp cmls run-now — runs the incremental writer immediately (same code path as the scheduled cron).
  • wp cmls status — prints a table with one row per country: row count, file size, URL.
  • wp cmls reset-token — regenerates the token and renames every country file to match.
  • wp cmls delete-file — deletes every country CSV and truncates the dedup table.

Why one file per country?

Marketing teams optimize Google Ads campaigns per country. Mixing audiences degrades match-rate signals and wastes budget on irrelevant impressions. Each country file is independent and can be wired into its own Customer Match list. Google’s Data Manager pulls each URL on its own schedule, so you can refresh some markets without affecting others.

How do I plug a URL into Google Ads?

In Google Ads → Tools and Settings → Audience Manager → Your data sources, set up a Customer Match audience and choose the HTTPS data source. Paste the country-specific URL from the plugin’s admin page. Google Ads fetches the file on its own cadence; our plugin keeps the file fresh per the schedule you picked.

Google says it’s turning on “conversion-based customer lists” — do I still need this plugin?

Yes — they complement each other. In 2026 Google began automatically enrolling accounts that use both Enhanced Conversions and Customer Match into conversion-based customer lists (enrollment emails went out from June 2026; data processing starts August 18, 2026). Those lists are built solely from what the Google Ads tag captures on your site from that point on: customers using ad blockers or declining your consent banner never enter them, and neither do phone, imported, or historical orders.

This plugin builds Customer Match lists from the WooCommerce order database instead — every completed order, including full order history via backfill, split into one file per country, with a mailing-address fallback when an order has no email or phone. Keep both: attach the automatic conversion-based lists and the per-country lists this plugin feeds, and Google deduplicates matched customers on its side.

Does the file work with Meta (Facebook) Ads?

Yes. Meta Ads Custom Audiences use the same SHA-256 hashing of normalized (trimmed, lowercased) values, so a country CSV can be uploaded to Meta Ads Manager as a customer list — map the columns in Meta’s upload screen. Note that Meta does not pull files from URLs; the pull-from-URL workflow is Google-specific, so on Meta use manual upload.

How long does the backfill take?

Roughly 10 seconds per 150,000–200,000 orders on typical hardware. The query path is direct SQL against wc_orders + wc_order_addresses (HPOS).
For 1M+ orders the CLI is still the most reliable option because no PHP/web timeout applies; the admin Action-Scheduler-backed runner also handles 1M+ but each batch must complete within the host’s PHP limit and if it does not then it won’t finish.

What happens to orders with no billing country?

In theory it’s impossible as billing and shipping is country based; but in case this ever happens they are skipped. There is no correct file to route them to, and Google’s per-country Customer Match campaigns can’t use them anyway. The same applies to orders with an invalid country code.

What about orders with no email?

They get written. Google Customer Match accepts a row with any one of: email, phone, or full mailing address (first name + last name + zip). The plugin includes a row as long as one of those identifiers is present alongside a valid country. Deduplication is still single-column: it uses email when available, otherwise phone, otherwise the mailing address.

Google handles cross-identifier deduplication on its side after matching to a Google account — so a customer who orders once with email + phone and again with only their phone will appear as two rows in the CSV but as one user in your Customer Match audience.

Will the same customer appear in multiple country files?

Yes — but only if they actually ordered from multiple countries!

I ran the backfill twice — why didn’t it write more rows the second time?

This is by design, as every (email, country) pair is recorded the first time it’s seen and never re-written.

To regenerate from scratch (for example after toggling debug mode or changing normalization), use WooCommerce → Marketing → Customer Match List → Danger zone → Delete all files & reset dedup or wp cmls delete-file, then run the backfill again.

I get a 401 “REST API has been restricted to authenticated users” when I open the URL

Some security plugins (“Admin and Site Enhancements” / ASE has this option, Disable REST API, etc.) block all unauthenticated REST traffic. This plugin does not attempt to override those policies — that would be hostile to the security setting you deliberately chose.

If you hit this error, whitelist the plugin’s feed prefix in your security plugin’s exclusion list: /wp-json/cmls/v1/feed/. Google Ads (and any other configured consumer) needs unauthenticated GET access to that prefix — the tokenized URL is the sole authentication mechanism.

Phone numbers — how reliable is the normalization without libphonenumber?

For most countries (RO, DE, UK, FR, ES, US, NL, BE, PT, IT mobile, …) it’s correct. The routine accepts +, 00, the country’s national trunk 0, and bare national numbers (using the order’s billing country to fill in the dialing code).

There is a known limitation for Italian landlines: in Italy the leading 0 is part of the national number, not a trunk prefix, so the routine may strip a digit it shouldn’t. Italian mobile numbers (3xx) are fine.

If you need strict accuracy across all locales, swap CMLS_Writer::normalize_phone() for a libphonenumber-based implementation.

Is WP-Cron reliable enough for the incremental updates?

WordPress’s built-in cron is request-triggered: it only fires if someone hits the site. On low-traffic stores it may drift. For predictable runs, configure a real system cron that calls wp-cron.php every minute — standard WordPress practice and recommendation.

Can I disable hashing for debugging?

Yes. use the integrated define CMLS_DISABLE_HASHING in wp-config.php:

define( 'CMLS_DISABLE_HASHING', true );

Does this support High-Performance Order Storage (HPOS) / Legacy Postmeta?

Yes. The plugin declares custom_order_tables compatibility and queries wc_orders + wc_order_addresses directly when HPOS is enabled. On classic (postmeta) storage it falls back to wp_posts + wp_postmeta with a single conditional-aggregation query per batch.

Does the plugin send any data outside my server?

The plugin makes no requests to third-party services — no analytics, no tracking pixels, no telemetry. All processing happens on your WordPress install. (The directory-listing safety check is a one-second request from your server to its own uploads URL — see the directory-listing FAQ.)

However, the whole point of the workflow is that Google Ads (or any other consumer you configure) will pull the CSV from your site at the tokenized URL. Anyone in possession of that URL can retrieve a file containing:

  • SHA-256 hashes of the customer’s email, phone, first name, and last name;
  • the customer’s billing country and postal code in plaintext.

Treat the tokenized URL as a secret. If it leaks, use Reset Token in the admin (or wp cmls reset-token) to invalidate all existing URLs and rename the files on disk. See the site’s Privacy Policy page — this plugin registers suggested policy text via WordPress’s Privacy tool.

Where are the files stored?

wp-content/uploads/customer-match-list-google-ads/customer-match-{hash}-{country}.csv. The {hash} is a 16-character hex derived from the secret token, so filenames are not guessable. An index.php placeholder in the directory prevents listing if your host happens to have it enabled and then it notifies the shop owner (when visiting the dashboard).

What about directory listing?

The settings page does a one-second loopback HTTP request to the uploads directory URL (/wp-content/uploads/customer-match-list-google-ads/) when you open it, caches the result for an hour, and shows one of three banners:

  • Green — safe. The server returned 200/401/403/404 with no listing markers (typically because the bundled index.php silence file is served, or because the webserver explicitly blocks directory listings).
  • Red — exposed. The server returned 200 with Index of … or with our CSV filename prefix in the body. Anyone who guesses the directory URL can enumerate your country files. Fix it: Apache → Options -Indexes (or rely on the WordPress-shipped .htaccess); nginx → autoindex off; in the relevant location block.
  • Yellow — could not verify. The loopback request failed within the one-second budget (NAT / split-DNS / firewall blocking PHP self-calls). Open the URL shown in the notice in a private/incognito browser window — if you see a listing, restrict it; otherwise you’re fine.

The check is cached for an hour and re-runs immediately if you click Re-check now.

What about Google’s 540-day lookback window?

Google Ads Customer Match uses data uploaded within the last 540 days. The plugin doesn’t enforce this so you can decide how much history to include. The “Last 540 days” preset in the backfill range dropdown matches that window. Older data is still appended if you ask for it, but Google won’t actively use it.

Can I include orders that aren’t `completed`?

Yes. wp cmls backfill --statuses=processing,completed,on-hold for an explicit list, or --all-statuses for everything. The scheduled (incremental) writer is wc-completed only by design — Customer Match doesn’t benefit from including pending or cancelled orders.

How is the deduplication table sized?

One row per unique (email_hash, country). At ~110 bytes per row, 1M unique customers across all countries is roughly 100 MB.

Will deactivating WooCommerce break it?

The plugin’s admin page shows a notice and stops scheduling cron, but data isn’t lost. Re-activate WC and the plugin resumes from the noted order.

If you uninstall the plugin then the uninstall.php routine cleans up the option keys, database tables, and any generated files.

GDPR / privacy

The files contain hashed personal information (email, phone, name) and plaintext country + ZIP. Treat the directory and its access URLs as sensitive data!

The plugin only creates files on your own server and does not read consent-management records: every order in the selected range is exported as-is. Whether a given customer may be included in an advertising audience — and whether Google or Meta may receive the file — depends on your store’s consent setup and local legal requirements; applying them is your responsibility as the data controller.

If a customer requests deletion under GDPR / similar:

  1. Delete or anonymize the WooCommerce order(s).
  2. Run Danger zone → Delete all files & reset dedup (or wp cmls delete-file).
  3. Re-run the backfill — the deleted customer is gone from the files.

Targeted per-row deletion isn’t supported on purpose: the SHA-256 hashing is one-way and rebuilding from source is fast.

Categories
Ads & product feeds
Plugin details
Version1.1.2
Last updated2026-07-17 10:21pm GMT
Added2026-07-14
Requires WordPress6.0
Tested up to7.0.2
Requires PHP7.4

Tags on WordPress.org
cmls
customer match
google ads
meta ads
woocommerce
Alternatives
Other plugins in the same categories.
Product Feed PRO for WooCommerce by AdTribes – Product Feeds for WooCommerce
80K+ installs
4.7(1,061)
Product Feed Manager for WooCommerce – CTX Feed – Support 220+ Shopping & Social Channels
80K+ installs
4.6(825)
Pixel Manager for WooCommerce – Conversion Tracking, Google Ads, GA4, TikTok, Dynamic Remarketing
40K+ installs
4.9(398)
TrueAna – True Analytics Dashboard
2K+ installs
5.0(30)
Product Feed Manager For WooCommerce – Sell on 200+ Online Marketplaces
10K+ installs
4.8(253)
Product Feed for Google Shopping, Microsoft Advertising and 40+ Channels for WooCommerce Merchant
2K+ installs
4.8(86)
x402 Logo

The agent-readiness stack for the AI shopping era — helping merchants, publishers and SaaS companies get discovered, cited and transacted with by ChatGPT, Perplexity, Claude, Gemini and the custom shopping agents underneath them.

CompanyAgentically Inc. (d/b/a xpay✦)1875 Mission St, Ste 103San Francisco, CA 94103, United Stateslegal@xpay.sh · privacy@xpay.sh
or ask your AI app
Company
About xpayAgency PartnersGitHubDiscordllms.txt
DevelopersDocumentationAPI ReferenceSDKs & LibrariesQuickstart GuideOpenAPI Spec
Stay Updated
Occasional product updates and agent-readiness playbooks from xpay (Agentically Inc.) — typically a couple of emails a month. Double opt-in: we email you a link to confirm before sending anything, and every email has one-click unsubscribe.
Social
  • For Publishers
    • News
    • Finance
    • Dev / Tech
    • Travel
    • View all verticals
  • Agent Feed
    • AI Search Engines
    • RAG Builders
    • Browser Agents
    • Vertical Research
    • Browse full catalog
  • Agent-Ready Index
    • SaaS Pricing Database
    • Agent-Ready SaaS Index
    • Verified band
    • AI & ML
    • Sales & CRM
  • Products
    • Pricing Widget
    • Monetize MCP Server
    • Paywall
    • Smart Proxy
    • Monetize AI Agents
    • xpay x402 Facilitator
  • Agentic Economy
    • Timeline
    • Resources
    • Manifesto
    • Stack
  • Agentic Commerce
    • Get listed
    • ChatGPT Ads
    • How ChatGPT Ads work
    • ChatGPT Ads · Apparel
    • ChatGPT Ads · Health & Beauty
    • xpay Listings · Amazon + Google
    • Pricing
    • Free audit
    • Shopify
    • WooCommerce
    • Apparel & Accessories
    • Health & Beauty
    • Overview
  • Commerce Index
    • Shopify apps directory
    • Agentic Commerce Ready Index
    • Methodology
    • Pet brands · WooCommerce
    • Pet brands · Shopify
  • Marketplace
    • 🛍️ xpay.deals — agentic storefront for deals
  • Protocols
    • Overview
    • x402
    • MPP
    • UCP
    • ACP
    • AP2
    • TAP
    • A2A
  • Agent Frameworks
    • Overview
    • LangChain
    • CrewAI
    • Claude MCP
    • AutoGPT
    • LangChain vs Mastra
    • LangGraph vs Pydantic AI
  • Company
    • About xpay
    • Blog
    • Docs
    • GitHub
  • Free prompts
    • Ecommerce prompts
    • Email marketing prompts
    • Product description prompts
    • Facebook ad prompts
    • Skincare prompts
    • Supplement prompts
    • Wine prompts
    • Electronics prompts

© 2025 Agentically Inc. All rights reserved.
Privacy PolicyTerms of UseAcceptable Use Policy