You run Meta ads for a home appliance store in Almaty. The owner sends a screenshot: "Yesterday one order was paid by card, T-312 for 62,000 ₸, but Events Manager shows two purchases. Are the ads counting twice?" "That's just how Meta counts" won't satisfy him, and calling in the developer for every question like this is expensive. The store and the numbers in this lesson are illustrative.
You don't need to code to answer. You need to understand where the two records come from, where to look to see whether Meta combined them, and what to ask the developer if it didn't. That is the technical minimum: following an event from a person's action to the report, finding the step where something stops matching, and describing it so that someone else can reproduce it.
One order, two sending paths
T-312, 62,000 ₸
Browser
Thank-you page opened
Purchase
Server
Payment confirmed
Purchase
Two received records, one sale.
The minimum in one table
| What to know | What to do yourself | Where to learn and practise |
|---|---|---|
| Tagged links | Build a link to the scheme, read someone else's and spot a mistake in utm_source or utm_medium | UTM tags, the UTM builder |
| Google Tag Manager | Create a tag, a trigger and a variable, catch an event from the dataLayer, go through Preview | GTM, exercises 1 and 2 in the training lab |
| GA4 | Create a property and a stream, mark a key event, find generate_lead in DebugView | GA4 |
| Meta pixel | Send Lead and see it in Test Events | Pixels and conversions |
| Briefing a developer | Describe an event: when it fires, with which parameters, where to check it | The dataLayer specification |
| Reading code | Read a dataLayer.push, see which fields go out and in what form, and fix existing code | Exercise 1 in the training lab; 3-6 are for those who want to write code themselves, which is the next level |
| Spreadsheets and metrics | Work out CPL, cost per payment and ROAS from an export, reconcile the ad account with the lead list | Metrics and formulas, the glossary |
| The path to payment | Link a lead from the ads to a payment in the CRM | End-to-end analytics |
| Browser and server | Understand what the browser sends, what the server sends, and how Meta and GA4 remove duplicates | Server-side tracking, Conversions API |
The first five rows are week 1 of the Start track: a UTM scheme, a form event in the training lab, your own GA4, the Meta pixel and a spec for three events. Metrics and their formulas are written down in week 0, leads go into a CRM in week 3, and payback is worked out in the calculator in week 4. The training lab runs right in your browser and needs no GTM account. The UTM builder lowercases tags, turns spaces into underscores, transliterates Cyrillic and leaves Meta macros such as {{site_source_name}} alone. Its Meta, Google and Telegram presets fill in source and medium according to the scheme from the UTM lesson, but enter your own campaign: spring_2026, brand_search and channel_post are only there as examples.
What the minimum leaves out
Your first projects can do without server code, a server-side GTM container, CRM integrations over an API and SQL queries against the GA4 export in BigQuery. They belong to the developer or to the next level. But you need to recognise such setups and ask the right questions about them. At the appliance store the server-side purchase is sent by code the developer wrote, and the marketer has to know that path exists and not mistake it for a bug.
The line is drawn by responsibility. The marketer describes what should happen and checks what did happen. The developer changes the code of the site and the server. A ticket with no description, like "the analytics is lying", comes back to you full of questions, and a check without a marketer ends at "the tag fired".
Worked example: two purchases for order T-312
Let's go step by step through what to tell the owner.
The business event. The store's database holds one order, T-312, paid by card for 62,000 ₸. Everything else is checked against it: however many records the ad account shows, there is one sale.
Where the records come from. The Meta pixel sits on the thank-you page and sends Purchase from the browser. The store's server sends the same purchase through the Conversions API once the payment service confirms the payment. The second path is why the store set up S2S tracking: the buyer of order T-310 closed the tab before the thank-you page opened, and without the server Meta would never have learned about that purchase. The buyer of T-312 waited for the page, so there are two records. They are two paths for one purchase.
What the counter shows. In the total of events received in Events Manager, browser and server events are simply added together, without deduplication. If both paths work, two purchases for one order is exactly what you should expect to see there.
Did Meta combine them? Meta treats a browser event and a server event as one if their name and event_id match (deduplicating the pixel and the Conversions API) and the second arrives within 48 hours of the first. The Event Deduplication tab shows how deduplication works overall: what share of browser and server events arrived with a deduplication key, and how much they overlap. You can't find one live purchase in Events Manager, so the check is done on a test order in Test Events, which shows which event was processed and which was dropped as a duplicate. The server event appears there only if the developer sends it with the test_event_code from that tab, and the code is removed from production payloads afterwards (testing server events). Meta doesn't discard events sent with that code: the test purchase will count in the statistics and in ad measurement, so one order is enough.
The answer to the owner. "Two records for one order are fine if Meta kept only one of them. Today we'll check that on a test order together with the developer. If the browser and the server turn out to send different event_id values, that's a setup error, and then Meta may count the purchase twice."
GA4 has its own rule for the same problem: GA4 counts purchases with the same transaction_id once, but only in a web stream. There a purchase and its parameters are checked in DebugView, as Google's guide to validating ecommerce recommends.
A ticket the developer can reproduce
Say both Purchase events on the test order came out as processed. "The analytics doubles purchases" is something a developer can't check. This, they can:
Where: the store site, the thank-you page and the server-side payment send.
What we did: at 15:20 we placed a test order with Test Events open, and the developer sent its server event with
test_event_code.What we expected: one
Purchaseprocessed, the other dropped as a duplicate.What we saw: both
Purchaseevents processed. The browserevent_idis a random string, the server one is the order number.What's needed: send the pixel the same
event_idthat goes out from the server.Attached: a Test Events screenshot with the buyer's phone and name blacked out.
Each line answers a question you would otherwise be asked: where to look, how to reproduce it, what counts as the right result. Agree the test order with the owner in advance so the warehouse doesn't start packing it.
Exercise: your own minimum on a test site
Take the test site from week 1 of the Start track, or any site where you have GTM access. For each row of the table, save one piece of proof you can show: a link, a screenshot or a file.
Look at the second screenshot again first. If the tag fired on a button click rather than on a dataLayer event, it counts clicks, including clicks on a form that returned an error. Submit the form with an empty email: generate_lead should not arrive. The last item checks whether you can explain what you know. The paragraph for the owner needs three points: the records came by two paths, the overall Events Manager counter doesn't remove duplicates, and whether only one Purchase remains will show on a test order: if the event_id matches, one event gets dropped. For any item that didn't work out, your next lesson sits in the same row of the table.