<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kamran Arshad</title>
    <description>The latest articles on DEV Community by Kamran Arshad (@marketingkamran).</description>
    <link>https://dev.to/marketingkamran</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4175763%2F94f534d1-ffa0-4df9-9c87-f74c6a4bb5f7.png</url>
      <title>DEV Community: Kamran Arshad</title>
      <link>https://dev.to/marketingkamran</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/marketingkamran"/>
    <language>en</language>
    <item>
      <title>Moving Shopify purchase tags out of Additional Scripts into a Custom Pixel</title>
      <dc:creator>Kamran Arshad</dc:creator>
      <pubDate>Wed, 21 Oct 2026 09:38:54 +0000</pubDate>
      <link>https://dev.to/marketingkamran/moving-shopify-purchase-tags-out-of-additional-scripts-into-a-custom-pixel-561j</link>
      <guid>https://dev.to/marketingkamran/moving-shopify-purchase-tags-out-of-additional-scripts-into-a-custom-pixel-561j</guid>
      <description>&lt;p&gt;On 26 August 2026 Shopify auto-upgraded the Thank you and Order status pages on every non-Plus store that hadn't done it manually. Per Shopify's docs, code pasted into &lt;strong&gt;Additional Scripts&lt;/strong&gt; on those pages is removed by the upgrade, not migrated. If a store's Google Ads, Meta or Snapchat purchase tag lived there, it stopped firing while the ad accounts kept spending.&lt;/p&gt;

&lt;p&gt;If you inherit a Shopify store, here's the migration I'd do, in order.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Read what was actually in Additional Scripts
&lt;/h2&gt;

&lt;p&gt;Shopify admin → Settings → Checkout → Additional scripts. It's been read-only since August 2025, but you can still read it. Copy the contents out and search for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gtag(   fbq(   googletagmanager   snaptr(   ttq.   &amp;lt;script src=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every hit is a conversion tag that needs a new home. That list matters more than any generic migration table, because it's what &lt;em&gt;this&lt;/em&gt; store was really running.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Prefer the official channel apps where they exist
&lt;/h2&gt;

&lt;p&gt;The Google &amp;amp; YouTube and Facebook &amp;amp; Instagram sales channels send purchase events without depending on the old page, so they survived the upgrade. If a hand-pasted tag duplicated what a channel app already sends, don't rebuild it. Delete it. Two sources for one purchase is how double counting starts.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Rebuild the rest as a Custom Pixel
&lt;/h2&gt;

&lt;p&gt;Settings → Customer events → Add custom pixel. Custom Pixels run in a sandbox and subscribe to standard events. The purchase one is &lt;code&gt;checkout_completed&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;analytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;checkout_completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checkout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orderId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          &lt;span class="c1"&gt;// use as transaction / event id&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;totalPrice&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currencyCode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Load the vendor script inside the sandbox, then send the event with&lt;/span&gt;
  &lt;span class="c1"&gt;// the same order ID your server-side integration uses for deduplication.&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;purchase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things to get right here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pass the order ID through.&lt;/strong&gt; If you also send purchases server side (Conversions API, Enhanced Conversions, Snap CAPI), the platforms deduplicate on a shared ID. Generate it nowhere: reuse the order's.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't trust the preview.&lt;/strong&gt; The pixel sandbox is not the live checkout.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Test the path that breaks
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;checkout_completed&lt;/code&gt; isn't guaranteed to fire on the thank-you page. With post-purchase upsells enabled it can fire on the first upsell page instead. Practitioners also report events firing before customer data is populated, which shows up as poor match quality. I haven't reproduced either on a store I control, so treat them as things to test, not facts. If the store runs upsells, test that path first.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Verify with a real order and a before/after
&lt;/h2&gt;

&lt;p&gt;Place a real order on a phone. Confirm the event arrived in each platform, not just that it fired in DevTools. Then compare daily purchase counts in Google Ads and Meta for the weeks before and after 26 August against Shopify orders. That comparison is your proof that something broke and that you fixed it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real lesson
&lt;/h2&gt;

&lt;p&gt;The only tracking that was never at risk was server-side, because it doesn't depend on a page the platform controls. Browser tags will keep getting broken by changes underneath you. Pair them with a server-side feed and a shared ID.&lt;/p&gt;

&lt;p&gt;I wrote a longer version for store owners, with a table of what survived the upgrade and what didn't: &lt;a href="https://www.marketingkamran.com/blog/shopify-additional-scripts-deadline-tracking.html" rel="noopener noreferrer"&gt;Shopify's Additional Scripts deadline and your purchase tracking&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Kamran Arshad. I run Google Ads, Meta Ads and SEO with conversion tracking behind every campaign at &lt;a href="https://www.marketingkamran.com/" rel="noopener noreferrer"&gt;Marketing Kamran&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>shopify</category>
      <category>javascript</category>
      <category>analytics</category>
    </item>
    <item>
      <title>Meta Conversions API deduplication: generate the event_id once, then pass it</title>
      <dc:creator>Kamran Arshad</dc:creator>
      <pubDate>Sat, 10 Oct 2026 18:05:09 +0000</pubDate>
      <link>https://dev.to/marketingkamran/meta-conversions-api-deduplication-generate-the-eventid-once-then-pass-it-2io2</link>
      <guid>https://dev.to/marketingkamran/meta-conversions-api-deduplication-generate-the-eventid-once-then-pass-it-2io2</guid>
      <description>&lt;p&gt;If you send purchases to Meta from both the browser (Pixel) and your server (Conversions API), Meta receives every order twice. The only thing stopping it from counting both is a shared identifier.&lt;/p&gt;

&lt;p&gt;Meta deduplicates when &lt;strong&gt;&lt;code&gt;event_name&lt;/code&gt; and &lt;code&gt;event_id&lt;/code&gt; match&lt;/strong&gt; across the two events. That's the whole rule. Most broken setups I audit break it in one of two ways.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure 1: each side invents its own ID
&lt;/h2&gt;

&lt;p&gt;This is the classic. The theme or tag manager generates something for the browser event, and the server integration generates something else:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Browser (Pixel)&lt;/span&gt;
&lt;span class="nf"&gt;fbq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;track&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Purchase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;49.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GBP&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;eventID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(Conversions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;API)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"event_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Purchase"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"event_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1047"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"action_source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"website"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two different IDs, so Meta has no way to know they're the same order. It counts both, and your reported revenue doubles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure 2: the ID isn't unique
&lt;/h2&gt;

&lt;p&gt;The quieter bug. If the "ID" is a checkout ID that gets reused, a session ID, or a timestamp, two genuinely different orders can share it. Meta then correctly merges them into one, and real sales disappear from reporting with no error anywhere.&lt;/p&gt;

&lt;p&gt;Double-counting looks like good news, so someone investigates. Silent loss looks like a slow decline and gets blamed on the ads.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: derive it from the order, once
&lt;/h2&gt;

&lt;p&gt;Use something that is unique per order and that both sides can read: the order ID.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Browser: on the order confirmation page&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eventId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`order_&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;fbq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;track&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Purchase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;eventID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;eventId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Server: when the order is created&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="na"&gt;event_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Purchase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;event_time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;event_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`order_&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// same string as the browser&lt;/span&gt;
    &lt;span class="na"&gt;action_source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;website&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;user_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;em&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;())]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;custom_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you must use a random UUID, generate it &lt;strong&gt;once&lt;/strong&gt; and pass it to the other side (for example, store it on the order). Two independently generated UUIDs are a duplication machine.&lt;/p&gt;

&lt;p&gt;Also watch for prefixes: &lt;code&gt;order_1047&lt;/code&gt; on one side and &lt;code&gt;1047&lt;/code&gt; on the other is a mismatch, not a detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hash customer data before it leaves your server
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;em&lt;/code&gt;, &lt;code&gt;ph&lt;/code&gt; and the other user fields must be SHA-256 hashed (after normalising, e.g. trimming and lowercasing email). Check a real payload before going live. A bug here isn't a tracking bug, it's sending raw personal data to a third party.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prove it works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Place one real order. Note the order number.&lt;/li&gt;
&lt;li&gt;In DevTools, filter the Network tab for &lt;code&gt;facebook&lt;/code&gt; and read the &lt;code&gt;eventID&lt;/code&gt; on the Purchase request.&lt;/li&gt;
&lt;li&gt;Find the server event for the same order in your logs or Events Manager and read its &lt;code&gt;event_id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Compare them character for character.&lt;/li&gt;
&lt;li&gt;Reconcile a settled week of orders against Events Manager purchases. Well above your order count means duplication; well below means loss.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I wrote a longer version for store owners, including a table of which identifiers are safe to use, here: &lt;a href="https://www.marketingkamran.com/blog/capi-duplicate-purchase-events-deduplication.html" rel="noopener noreferrer"&gt;Meta CAPI duplicate purchase events: how deduplication actually works&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Kamran Arshad. I run Google Ads, Meta Ads and SEO with conversion tracking behind every campaign at &lt;a href="https://www.marketingkamran.com/" rel="noopener noreferrer"&gt;Marketing Kamran&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>analytics</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
