EMQ jest per-event: dlaczego ViewContent ma 6, a Purchase 9 - i jak podnieść funnel
Niskie EMQ na ViewContent czy AddToCart to nie błąd - te zdarzenia odpalają się przed podaniem danych. Czemu funnel ma 5-6, Purchase 8-9 i jak podnieść funnel external_id z hasha maila.
Wchodzisz w Events Manager, otwierasz “Jakość dopasowania zdarzeń”, patrzysz na liczby i robi ci się gorąco. Purchase ma 8,6. ViewContent 6,2. AddToCart 5,7. InitiateCheckout 5,8. Pierwsza myśl: coś się zepsuło, wdrożenie funnela jest dziurawe, połowa danych nie dochodzi.
Nie jest. To uniwersalny kształt i prawie każdy poprawnie skonfigurowany funnel server-side tak właśnie wygląda. EMQ funnela jest niskie z natury, a Purchase wysokie z natury, i wynika to nie z błędu, tylko z tego, kiedy w ścieżce zakupowej odpalają się poszczególne zdarzenia. Dane, które masz w momencie ViewContent, są po prostu uboższe niż dane, które masz w momencie zakupu.
W tym artykule wyjaśniam, czemu EMQ liczy się osobno dla każdego zdarzenia, dlaczego funnel siedzi na 5-6, a Purchase na 8-9, co i tak da się funnelowi dać po stronie serwera, oraz jaka jedna technika realnie podnosi EMQ funnela. Na koniec - czego z tym wszystkim nie warto oczekiwać, żeby nie gonić wskaźnika, którego z definicji nie da się dobić.
Czym jest EMQ i czemu liczy się osobno dla każdego zdarzenia
EMQ, czyli Event Match Quality (Jakość dopasowania zdarzeń), to ocena Meta w skali od 0 do 10. Mówi, jak dobrze zdarzenie, które wysyłasz, da się dopasować do konkretnej osoby w bazie Meta. Im więcej trafnych sygnałów dopasowania w user_data (mail, telefon, fbp, external_id, IP, user agent, imię, miasto), tym wyżej Meta ocenia szansę, że trafiło w realnego człowieka, który widział twoją reklamę.
Kluczowe jest słowo “zdarzenie”. EMQ nie jest jedną liczbą dla całego pixela - jest liczone osobno dla każdego typu zdarzenia. Purchase ma swoje EMQ, ViewContent swoje, AddToCart swoje. I to ma głęboki sens, bo każde z tych zdarzeń odpala się w innym momencie wizyty, a w różnych momentach wizyty masz o kliencie różną ilość danych.
To pierwsza rzecz, którą trzeba sobie poukładać w głowie: jeśli porównujesz EMQ ViewContent do EMQ Purchase i dziwisz się, że różnią się o trzy punkty, porównujesz dwa zdarzenia, które dzieli przepaść w dostępnych danych. To nie ten sam wskaźnik dla tego samego stanu wiedzy o użytkowniku.
Dlaczego funnel jest z natury niski: zdarzenia przed-PII
ViewContent odpala się, gdy ktoś otwiera kartę produktu. AddToCart - gdy wrzuca do koszyka. InitiateCheckout - gdy wchodzi do koszyka albo zaczyna składać zamówienie. We wszystkich tych momentach jest jedno wspólne: klient nie podał jeszcze żadnych danych osobowych. Nie zostawił maila, nie wpisał telefonu, nie podał imienia ani adresu.
To są zdarzenia “przed-PII” (przed podaniem danych identyfikujących). W chwili, gdy się odpalają, jedyne, czym dysponujesz, to identyfikatory techniczne przeglądarki: cookie pixela fbp, adres IP i user agent. To realne sygnały i Meta z nich korzysta, ale są słabsze niż mail czy telefon. fbp to anonimowy identyfikator pierwszej strony, IP bywa współdzielone (jedna sieć firmowa, jeden router domowy, CGNAT u operatora), user agent jest mało selektywny.
Stąd sufit. Mając do dyspozycji tylko fbp + IP + user agent, EMQ funnela fizycznie nie wejdzie na poziom Purchase. Wynik 5-6 na ViewContent czy AddToCart to nie objaw zepsutego wdrożenia, tylko górna granica tego, co da się osiągnąć na danych dostępnych przed checkoutem. Jeśli ktoś obiecuje 9 na ViewContent bez dodatkowych identyfikatorów, albo nie rozumie mechanizmu, albo pokaże sztuczkę, którą za chwilę tu opiszę.
Dlaczego Purchase jest wysoki: checkout to pełne dane
Purchase odpala się na stronie podziękowania, czyli już po tym, jak klient przeszedł cały checkout. A żeby przejść checkout, musiał podać dane: mail (zawsze - bez niego nie ma potwierdzenia zamówienia), zwykle telefon (do kuriera), imię i nazwisko, adres dostawy, czasem miasto i kod pocztowy.
To jest właśnie ten zestaw sygnałów, który Meta uwielbia. Mail i telefon to mocne, selektywne identyfikatory - jeśli Meta zna ten mail (a zna miliony), dopasowanie jest niemal pewne. Dorzuć do tego imię, miasto, fbp z tej samej sesji i external_id, a EMQ Purchase ląduje w okolicach 8-9.
W jednym z wdrożeń, na którym pracowałem - realny sklep modowy na Shoperze - rozkład wyglądał dokładnie tak: ViewContent około 6,2, AddToCart około 5,7, InitiateCheckout około 5,8, Purchase około 8,6. Wszystkie zdarzenia z oznaczeniem połączenia “Wiele” w Events Managerze, czyli odbierane jednocześnie z przeglądarki i z serwera. To jest zdrowy, dobrze skonfigurowany funnel. Nie ma w nim nic do “naprawienia” w sensie podniesienia funnela do poziomu Purchase, bo to niemożliwe. Jest natomiast sporo do zrobienia, żeby funnel wycisnąć do jego realnego sufitu i ten sufit trochę podnieść.
Co i tak możesz dać funnelowi po stronie serwera
Zanim przejdę do najmocniejszej dźwigni, warto upewnić się, że funnel w ogóle dostaje wszystko, co mu się należy z poziomu serwera. Bo nawet bez maila da się dla zdarzeń przed-PII zrobić więcej, niż robi domyślne wdrożenie samego pixela w przeglądarce.
Po pierwsze, wysyłaj funnel server-side przez Conversions API, nie tylko przez pixel. Pixel w przeglądarce gubi część zdarzeń przez adblocki, ITP Safari i blokady iOS - skalę tej utraty rozkładam na czynniki w Adblock i iOS ITP: ile konwersji traci e-commerce w 2026?. Server-side CAPI dokłada drugi, niezależny kanał odbioru. Jak postawić CAPI na Shoperze bez wtyczek pośredniczących, opisuję w Meta CAPI na Shoper bez wtyczek.
Po drugie, dopilnuj, żeby do każdego zdarzenia funnela dochodził fbp, IP klienta i user agent. Brzmi oczywiste, ale w praktyce server-side często gubi te pola: serwer tagowania widzi własne IP zamiast IP użytkownika, albo fbp nie jest przepisywane z przeglądarki na serwer. Bez tego EMQ funnela spada poniżej tego, co osiągalne - zamiast 6 masz 3-4. To są te punkty, które dostajesz “za darmo”, samym poprawnym przepisaniem identyfikatorów technicznych.
Po trzecie, wyślij external_id. I tu zaczyna się najciekawsze, bo większość wdrożeń wysyła external_id, który prawie nikomu nie pomaga.
Dźwignia, której większość nie używa: external_id z hasha maila
external_id to pole user_data, w które możesz wpisać dowolny stały identyfikator klienta - taki sam w przeglądarce i na serwerze, taki sam między zdarzeniami. Meta używa go jako dodatkowego klucza dopasowania i, co ważne, do sklejania zdarzeń tej samej osoby w czasie. Dobrze dobrany external_id potrafi podnieść EMQ funnela, bo daje stały punkt zaczepienia tam, gdzie nie ma maila.
Problem w tym, że domyślnie do external_id wpada zwykle user_id sklepu, czyli identyfikator zalogowanego klienta. I tu jest pułapka pokrycia.
Pokrycie: user_id mają tylko zalogowani
W typowym sklepie zalogowani to mniejszość. Gros zamówień idzie jako zakup bez rejestracji albo gość, który zakłada konto dopiero przy pierwszym zamówieniu. W praktyce user_id jest obecne w kilku procentach ruchu. W tym wdrożeniu, o którym pisałem, external_id oparty na user_id pokrywał około 5% zdarzeń. To znaczy, że dla 95% zdarzeń funnela pole external_id było po prostu puste - nie dawało nic.
Mail to inna historia. Mail klient podaje prawie zawsze, choćby raz - przy zakupie. A skoro mail jest, to można z niego zrobić external_id. Po przeniesieniu external_id z user_id na hash maila pokrycie skoczyło z około 5% do około 92%. To nie jest kosmetyka - to różnica między polem, które prawie zawsze jest puste, a polem, które prawie zawsze jest wypełnione.
Spójność i RODO: pre-hash 64-hex po obu stronach
Tu pojawiają się dwa wymagania, które trzeba spełnić jednocześnie: dopasowanie musi działać, a surowy mail nie może wyciekać.
Rozwiązanie: maila hashujesz raz, na wejściu, do postaci SHA-256, czyli 64-znakowego ciągu szesnastkowego, i ten gotowy hash wkładasz do external_id zarówno w pixelu, jak i w serwerowym CAPI. Przeglądarka i serwer muszą wstawić w to pole identyczny ciąg, inaczej Meta potraktuje je jak dwa różne identyfikatory i dedup oraz sklejanie się nie powiedzie. Stąd dyscyplina normalizacji przed hashowaniem: mail najpierw trim, potem lowercase, dopiero potem SHA-256. Każda spacja czy wielka litera po jednej stronie i hashe się rozjadą.
Drugi warunek to zgodność z RODO. Z przeglądarki nie powinien wychodzić surowy mail - i nie wychodzi, bo wychodzi już gotowy hash. Zarówno pixel, jak i serwerowy CAPI wykrywają, że dostały wartość, która jest poprawnym 64-znakowym hashem heksadecymalnym, i nie hashują jej ponownie (podwójne hashowanie zabiłoby dopasowanie). Efekt: zero surowego maila opuszczającego przeglądarkę, a mimo to spójny, dopasowalny identyfikator po obu stronach. To ta sama filozofia “dane wychodzą zhashowane u źródła”, co przy danych transakcyjnych dla Google - różnice i analogie między tymi dwoma światami opisuję w Trwałość identyfikatorów a match rate Enhanced Conversions. To dwie strony tej samej monety: Google nazywa to match rate, Meta nazywa to EMQ, mechanika dopasowania po hashowanych danych jest pokrewna.
Krok dalej: trwały external_id na funnelu
Hash maila w external_id świetnie ratuje Purchase i InitiateCheckout, ale jest jeden haczyk. ViewContent i AddToCart odpalają się zanim klient poda mail w tej wizycie. Skąd wziąć hash maila na karcie produktu, skoro klient dopiero ją otwiera?
Z poprzedniej sesji. To jest ten krok, który większość wdrożeń pomija, a który realnie podnosi EMQ funnela, a nie tylko zakupu.
Mechanizm jest prosty: w momencie, gdy hash maila staje się dostępny - przy zakupie albo przy zalogowaniu - zapisujesz go w cookie pierwszej strony lub w localStorage. Następnie przy każdym ViewContent i AddToCart odczytujesz ten zapisany hash i, jeśli istnieje, wkładasz go w external_id. Dzięki temu nawet ViewContent na karcie produktu, otwarty na początku nowej wizyty, dostaje stały identyfikator użytkownika - bo ten użytkownik kiedyś już zostawił mail.
W praktyce wygląda to tak: nowy odwiedzający, który nigdy nic nie kupił i nie ma konta, dalej będzie miał na ViewContent tylko fbp + IP + user agent, bo nie ma skąd wziąć jego maila. Ale każdy powracający, który wcześniej choć raz przeszedł checkout albo się zalogował, wnosi na ViewContent swój trwały external_id. A powracający to często znaczący kawałek ruchu sklepu z aktywną reklamą. Stąd realny wzrost EMQ funnela - nie do poziomu Purchase, ale wyraźny.
Ważny detal spójności: hash zapisany w cookie musi być w dokładnie tym samym formacie, co hash wysyłany na Purchase, czyli to samo SHA-256 z tej samej znormalizowanej postaci maila. Inaczej external_id na ViewContent i na Purchase tej samej osoby będą się różnić i Meta nie sklei ich w jedną ścieżkę.
Czego nie oczekiwać: funnel nigdy nie dobije Purchase
Teraz najważniejsza rzecz dla spokoju ducha. EMQ funnela nigdy nie zrówna się z EMQ Purchase i to jest absolutnie w porządku. Nawet z trwałym external_id z hasha maila funnel nadal nie ma maila ani telefonu podanego w bieżącej wizycie dla nowych użytkowników, a oni zawsze stanowią część ruchu. Purchase ma pełen zestaw danych dla niemal każdego zdarzenia, funnel ma go tylko dla części. Ta różnica jest wpisana w strukturę ścieżki zakupowej, nie da się jej usunąć i nie ma sensu jej gonić.
Dlatego cel przy optymalizacji EMQ funnela to nie “dobić do 9”, tylko “wycisnąć realny sufit dla danych dostępnych przed-PII”. W praktyce: upewnij się, że fbp, IP i user agent dochodzą bez strat, dodaj external_id z hasha maila zamiast z user_id, dorzuć trwały odczyt tego hasha z poprzednich sesji. Po tym funnel siedzący wcześniej na 4-5 wchodzi w okolice 6, a powracający użytkownicy ciągną średnią wyżej. To jest sukces. Wskaźnik, który próbowałbyś podnieść ponad ten poziom, po prostu nie istnieje na danych, które masz.
Drugi wniosek praktyczny: nie panikuj i nie przebudowuj działającego wdrożenia tylko dlatego, że ViewContent pokazuje 6. Najpierw sprawdź, czy 6 to twój sufit, czy może jednak gubisz fbp albo IP i siedzisz na 3-4 zamiast na zasłużonych 6. Dopiero potem sięgaj po external_id. Mechanizm dopasowania po hashowanych danych jest zresztą wspólny dla Google i Meta - po stronie Google ten sam temat, czyli różnica między pomiarem w przeglądarce a na serwerze, rozkładam w Enhanced Conversions: browser-side vs server-side.
Podsumowanie
Niskie EMQ na ViewContent czy AddToCart to nie błąd wdrożenia, tylko naturalny kształt funnela. Te zdarzenia odpalają się przed-PII, więc dysponują tylko fbp + IP + user agent i ich sufit to okolice 5-6. Purchase odpala się po checkoucie, ma mail, telefon, imię i adres, więc siedzi na 8-9. EMQ liczy się osobno dla każdego zdarzenia, bo każde ma inny stan wiedzy o użytkowniku - porównywanie funnela do Purchase to porównywanie dwóch różnych zestawów danych.
Co da się zrobić: wysyłać funnel server-side przez CAPI obok pixela, dopilnować fbp/IP/user agent bez strat, i przede wszystkim oprzeć external_id na hashu maila (pokrycie skacze z kilku procent do ponad dziewięćdziesięciu) zamiast na user_id zalogowanych. Krok dalej to trwały external_id: zapis hasha maila w cookie albo localStorage przy zakupie czy logowaniu i odczyt go na ViewContent oraz AddToCart, dzięki czemu powracający wnoszą stały identyfikator już na górze lejka. Wszystko to przy zachowaniu RODO - z przeglądarki wychodzi gotowy 64-znakowy hash, nigdy surowy mail, a obie strony wykrywają gotowy hash i nie hashują go ponownie.
Czego nie oczekiwać: funnel nigdy nie dobije Purchase i nie powinien. Cel to wycisnąć realny sufit danych przed-PII, nie gonić wskaźnik, którego z definicji nie da się osiągnąć. Jak ten cały zestaw - server-side CAPI, dedup, trwałe identyfikatory i RODO-safe hashowanie - składa się w jeden spójny pomiar na Shoperze, pokazuję na stronie tracking dla Shopera.