Instellen van AdPage Webhook Matching
Webhook Matching zorgt dat je purchase-events server-side gevalideerd en ontdubbeld worden voordat ze naar GA4, Meta, Google Ads, TikTok of Pinterest gaan. Hieronder de zes stappen om dit live te zetten.
Wat doet Webhook Matching?
Browser-side purchase events kunnen worden geblokkeerd door ad-blockers, cookies, of ITP. Backend-webhooks vanuit je commerce-platform (Shopify, WooCommerce, PrestaShop, etc.) zijn server-side maar missen browser-context (gclid, user-agent, items array). De Webhook Matching van AdPage koppelt beide via een match-identifier, merged de payloads, en stuurt één gevalideerd event door. Resultaat: geen dubbele conversies, geen gemiste conversies.
Stap 1 — Activeer Webhook Matching in AdPage
Open de AdPage container van je klant
Ga naar Webhook request logs
Zorg dat de callback url juist staat ingesteld. De endpoint staat standaard op data
Stap 2 — Upload de Tag Template in sGTM
Open de server container in Google Tag Manager
Ga naar Templates → Tag Templates → New
Klik op de drie puntjes rechtsboven → Import → kies
adpage-event-notifier.tpl. Je kunt deze hier downloaden: https://adpage.b-cdn.net/GTM-Templates/adpage-event-notifier.tplKlik Save, de template "AdPage Event Notifier" verschijnt in de templates-lijst
Maak twee tags aan met deze template:
Tag 1 — Prepare (browser-side):
Mode:
PrepareContainer ID: (uit AdPage, stap 1)
Platform preset: kies wat past (zie stap 3)
Affiliation: webshop-naam
Trigger: een browser-event vlak voor checkout (bv.
view_cart,add_to_cart, ofbegin_checkout)
Tag 2 — Trigger (server-side webhook):
Mode:
TriggerContainer ID: idem
Platform preset: idem
Trigger: de webhook-event van je commerce-platform (bv.
trytagging_purchasevoor WC/PrestaShop,orders/createvoor Shopify)
Stap 3 — Kies de juiste BasketKey (Matcher)
De BasketKey is wat Prepare en Trigger aan elkaar koppelt. Beide kanten moeten dezelfde waarde meesturen, anders matcht EN ze niet.
Kies in het veld Platform preset de juiste optie:
Setup | Preset | BasketKey |
|---|---|---|
Klant heeft AdPage Tagging plugin/script geïnstalleerd | AdPage Tagging (universal) ⭐ |
|
Native Shopify (zonder Tagging plugin) | Shopify |
|
Native Magento | Magento |
|
Native Lightspeed C-Series | Lightspeed |
|
Native Shopware 6 | Shopware |
|
Iets anders / custom integratie | Custom | zelf invullen |
Aanrader: voor 95% van onze klanten is AdPage Tagging (universal) de juiste keuze. Dit werkt platform-onafhankelijk omdat onze tagging-stack overal hetzelfde user_id UUID gebruikt — in de browser (cookie trytagging_user_id) én in de webhook (marketing.user_id).
Het veld Identifier-waarde override kun je leeg laten — de template haalt de waarde automatisch op uit event data of cookies. Vul alleen iets in als de auto-pull faalt; je ziet dat in de sGTM console-log als een error met de melding "Match identifier value could not be resolved".
Advies: bouw dit in twee fasen
Zet Webhook Matching niet in één keer volledig live. Werk in twee stappen:
Fase 1 — Matching valideren zonder door te sturen Richt Stap 1 t/m 3 in (AdPage-activatie, Prepare- en Trigger-tags, BasketKey), maar sla Stap 4 (Measurement Protocol client) nog over. Prepare en Trigger draaien dan al tegen elkaar, maar er wordt nog niets naar GA4 of de ad-platforms doorgestuurd. Laat dit een paar dagen tot een week draaien en volg de Match-gezondheid in het dashboard. Gebruik "Analyseer missed" en de Event details (Payload check, Stale cookies-uitsplitsing) om issues op te lossen.
Fase 2 — Pas omzetten als de match-rate goed is Is de match-rate stabiel en acceptabel (richtlijn: ruim boven 90%, en "Niet bereikt" grotendeels verklaarbaar/incidenteel)? Voer dan pas Stap 4 en verder uit om de Measurement Protocol client aan te maken en de purchase-events daadwerkelijk door te sturen naar GA4 en de gekoppelde platforms.
Waarom: als je de client meteen aanzet terwijl de matching nog niet goed staat, stuur je vanaf dag één onvolledige of dubbele conversiedata door naar GA4/Ads/Meta — en dat is achteraf lastiger te corrigeren dan vooraf te voorkomen.
Stap 4 - Measurement Protocol client aanmaken
De Webhook Matching callback arriveert op je sGTM /data endpoint. Daar moet een Client klaar staan om de binnenkomende Measurement Protocol-request te claimen en om te zetten naar een event dat downstream tags (GA4, Google Ads, FB CAPI, etc.) kunnen oppakken.
Open de server container in Tag Manager
Ga naar Clients → New
Klik Choose client type → onder More kies Measurement Protocol (GA4) ✓
Geef de client een herkenbare naam, bv.
Webhook Matching - GA4Vul de client-instellingen in:
Path / Activation: vul hier de standaard callback url in "/data"
Stap 5 — Purchase event doorsturen naar GA4
Om te voorkomen dat je 2 of 3 keer een purchase event gaat versturen (één keer vanuit de client-side GA4 request, één keer vanuit de Webhook Client, en één keer vanuit de Webhook Matching), zal je in de triggers op je GTM server container in moeten stellen dat er maar één purchase event doorgestuurd wordt.
1) Schakel de browser-purchase uit
In je GTM server container:
Open de bestaande Google Analytics tag en open de trigger
Voeg een voorwaarde toe die de Event Name 'purchase' tegenhoudt. Dus de exception: trigger wanneer event =
purchaseén event komt vanuit de GA4 Client.
2) Voeg een nieuwe trigger toe
In dezelfde Google Analytics tag voeg je nu een trigger toe:
Kies de grijze optie aangepast als trigger
Voeg de voorwaarde 'Client Name komt overeen met [client-naam die je zelf gekozen hebt]' toe
Als je die twee stappen uitvoert zal je dus een GA4 tag hebben met 2 triggers. Eén trigger die alle GA4 client events doorstuurt, behalve 'purchase'. En één trigger die de 'purchase' event doorstuurt vanaf je webhook matching client.
Stap 5 - Testen en debuggen
Voordat je live gaat, controleer je of Prepare en Trigger daadwerkelijk matchen.
Testen in preview modus
Start Preview modus op zowel de web- als de server-container in GTM.
Doorloop een testbestelling tot en met de bevestigingspagina.
Controleer in de preview van de Prepare-tag (browser-side):
Basket ID (de BasketKey uit Stap 3) staat gevuld en is exact gelijk aan de waarde die je straks bij de Trigger-tag terugziet — dit is de gedeelde identifier die beide zijden aan elkaar koppelt.
Controleer in de preview van de Trigger-tag (server-side, na binnenkomst webhook) dat dezelfde Basket ID aanwezig is.
Zie je bij de Trigger geen match op basis van de Basket ID, ga terug naar Stap 3 (BasketKey/Matcher) en de Troubleshooting-melding "Match identifier value could not be resolved".
Webhook Logs gebruiken om de trigger te testen
Wil je de Trigger-kant testen zonder een nieuwe bestelling te plaatsen? Gebruik de Webhook Logs:
Ga in AdPage naar de container → Webhook Logs (tabblad naast Webhook Matching).
Zoek de gewenste webhook op transaction_id of reference_id.
Klik op het replay-icoon (oranje) achter de rij.
Start Preview modus op de server-container in GTM en kopieer de Debug Signal-header.
Plak deze in het veld Preview Header in de "Webhook Replay"-popup en klik Versturen.
De webhook wordt opnieuw verstuurd in debug-modus, zodat je in de GTM-preview precies ziet hoe de Trigger-tag deze specifieke payload verwerkt — zonder dat er een dubbele conversie naar GA4/Meta/etc. gaat.
Het Webhook Matching dashboard
Onder Webhook Matching → Match-gezondheid zie je in één oogopslag hoe goed Prepare en Trigger de afgelopen periode (standaard 7 dagen) matchen, en waar het misgaat.
Match-gezondheid lezen
Het percentage bovenaan is het aandeel orders dat succesvol is verwerkt (matched, in-app browser-afhandeling of cookieless), tegenover het totaal aantal binnengekomen orders.
Gematcht: Prepare en Trigger zijn gekoppeld op de Basket ID — dit is de "gezonde" flow.
In-app browsers: orders vanuit in-app browsers (Instagram/Facebook-app e.d.) waar cookies beperkt werken.
Cookieless: orders zonder trackable cookie, zie ook "Unconsented conversies doorsturen" in de Geavanceerd-sectie.
Niet bereikt: orders die niet zijn gematcht. Hover over dit segment voor de uitsplitsing naar oorzaak:
Stale cookies — de Prepare-cookie was al verlopen toen de Trigger binnenkwam (te lange tijd tussen checkout-start en orderbevestiging).
Overig/onbepaald — restcategorie, meestal incidenteel.
Onder de balk staat het aantal orders zonder consent — dit zijn orders die (deels) niet getrackt worden in GA4 omdat de bezoeker tracking heeft geweigerd.
Analyseer missed: draait een analyse op de "niet bereikt"-orders om te zien of Waarschijnlijkheids-matching of Trigger-only doorsturen (zie Geavanceerd) die alsnog had kunnen redden.
Event details bekijken
Klik op Details bij een rij in Recente events om een individueel event te inspecteren:
Status, Bron (Transaction ID en Callback-tijd (hoe snel de trigger de callback verwerkte) staan bovenaan.
Payload check laat zien of er een data-afwijking is gevonden, bv. "value ≠ som van items" wanneer het ordertotaal niet overeenkomt met de som van de items (binnen tax/shipping-tolerantie). Dit is een waarschuwing, geen blokkade — het event gaat gewoon door, maar het is de moeite waard om te checken of de klant-integratie de juiste velden meestuurt.
Tracking-consent toont of consent is toegestaan of geweigerd, met de onderliggende Consent Mode-vlaggen (analytics_storage, ad_storage, gcs).
Browser toont de user-agent, handig om patronen te herkennen (bv. veel "Niet bereikt" vanuit één browser/OS-combinatie).
Prepare buffered en Trigger verwerkt tonen de exacte timestamps van beide events — het verschil hiertussen is relevant bij "Stale cookies"-issues.
Onderaan vind je vier tabbladen: Outgoing payload (wat naar GA4/Meta/etc. is gestuurd), Prepare data, Order data en Trigger body (raw) — handig om te zien wat er binnenkwam vóór verwerking.
Kopieer payload en Replay… (zelfde replay-functie als in Webhook Logs) staan onderaan.
Stap 8 — Wat zit er in de outgoing payload
Event Notifier's callback naar sGTM bevat een complete payload die meteen bruikbaar is voor alle grote ad-platforms. Onze template forwarded automatisch:
GA4 (Measurement Protocol)
event_name: "purchase"
client_id: (van browser of webhook marketing.ga4_client_id)
transaction_id: (van order)
value, tax, shipping: (van order)
currency: EUR / USD / etc.
items[]: (volledige array met item_id, item_name, price, quantity, item_brand, item_category, etc.)
engagement_time_msec: 100 (default)
page_location: echte thank-you URL (Shopify Web Pixel sandbox-URLs worden automatisch opgeschoond)
gcs, gcd: Consent state
ip_override: server-side geocoding werkt
Meta CAPI (Facebook + Instagram)
fbp: _fbp cookie (Facebook browser ID)
fbc: _fbc cookie (Facebook click ID)
em: email (SHA256 wordt door FB CAPI tag gedaan)
fn, ln, ph: first_name, last_name, phone (SHA256 idem)
zp, ct, st, country: zip, city, state, country
external_id: customer.id (cross-order stable)
client_ip_address: IP voor IP-matching
client_user_agent: UA voor browser-fingerprinting
event_id: voor browser-pixel deduplicatie
Google Ads (Conversion + Remarketing)
_gcl_aw, _gcl_dc, _gcl_gb: gclid cookies (set door Conversion Linker)
FPGCLAW, FPGCLDC: first-party gclid variants
gclid, gbraid, wbraid: URL-parameters (iOS app campaigns)
client_id, ga_session_id: voor stiching met page_view events
value, currency: conversion value
TikTok Events API
_ttp, ttp: TikTok pixel cookie
ttclid: TikTok click ID (uit URL ?ttclid=)
email, phone_number: (TikTok gebruikt volledige veldnamen ipv FB's afkortingen)
first_name, last_name, city, state, zip_code, country_code: idem
external_id: customer.id
ip, user_agent: voor matching
Pinterest Conversions API
_epik, epik: Pinterest click ID cookie + URL param
em, fn, ln, ph, zp: (zelfde conventie als FB CAPI)
ct, st, country: adres-velden
external_id: customer.id
client_ip_address: IP
client_user_agent: UA
Consent forwarding (alle platforms)
EN stuurt de Consent Mode state in twee vormen mee zodat álle CAPI tags het kunnen respecteren:
ad_storage, ad_user_data, ad_personalization, analytics_storage: "granted"/"denied"
consent.ad_storage, consent.ad_user_data, …: idem (genest object)
gcs, gcd: Google Consent strings (voor GA4 / Google Ads)
Troubleshooting
"Match identifier value could not be resolved" — De BasketKey wordt niet gevonden in event data. Open in sGTM console de error-log → kijk in EventDataSnapshot waar de UUID staat → vul die pad in via "Identifier-waarde override" in de tag-config.
Trigger geeft 308 redirect — URL eindigt met /trigger/ zonder waarde. Betekent dezelfde root-cause als hierboven. Re-check de BasketKey resolutie.
Trigger geeft 400 — Body validatie faalt. Meestal omdat transaction_id of value leeg is. Bij AdPage Tagging plugin (WC/PS) zit dit in ecommerce.transaction_id — controleer dat de webhook die meestuurt.
missed status in EN logs — Trigger arriveerde zonder bijhorende Prepare. Browser-side Prepare faalde waarschijnlijk (ad-blocker, ITP, of geen browser-event vlak voor checkout). Check sGTM preview op de storefront.
callback_failed — sGTM ontvangt EN's callback niet. Check de callback URL in AdPage's tenant-config; meestal mist er een /data suffix of staat er een verkeerde subdomain.
Geavanceerd
Multi-market (Shopify Markets / multi-locale shops): vul in het veld
GA4 Measurement IDsalle GA4 properties komma-gescheiden (bv.G-NL123, G-BE456, G-DE789). EN forwarded dan per-property de_ga_<MID>cookie zodat sGTM downstream kan routeren.WooCommerce zonder Tagging plugin: vul de exacte
wp_woocommerce_session_<hash>cookie-naam in het veld "WooCommerce session cookie naam".Custom event identifier: kies preset = Custom en vul zelf identifier-naam + variabele-referentie in.
Duplicaat-preventie bij dubbele identifiers: komt een order zowel met een canonieke identifier (bv. vtnl-...) als met een kale variant (bv. alleen het ordernummer) binnen, dan wordt de kale variant genegeerd en gaat alleen de canonieke door.
Waarschijnlijkheids-matching: probeert een order die anders missed zou zijn alsnog te koppelen aan een recente Prepare op basis van items, bedrag, klant-identiteit en timing. Bij voldoende zekerheid wordt het event met status Waarschijnlijk (i.p.v. matched) naar GA4 gestuurd. Standaard uit — zet dit alleen aan als je dit per klant vertrouwt.
Trigger-only doorsturen: stuurt een order die anders missed zou zijn (geen Prepare, ook niet via waarschijnlijkheids-matching) tóch naar GA4, op basis van de GA4 client_id uit de ga-cookie van de Trigger zelf. Alleen mogelijk als die clientid aanwezig is — anders blijft de order missed. Status wordt Trigger-only. Standaard uit.
Unconsented conversies doorsturen: stuurt een order waarbij tracking-consent is geweigerd (dus geen GA4 client_id) tóch naar GA4, als privacy-veilige cookieless ping: een wegwerp-client_id met "denied"-consentvlaggen en zonder PII, zodat GA4 de conversie kan modelleren. Status wordt Unconsented. Standaard uit — zet dit alleen aan na overleg met de klant over privacy/consent, want dit stuurt data door ook al is er geen toestemming voor tracking gegeven.
Hulp nodig?
Heb je je klant in een platform-setup waar dit artikel niet op antwoord geeft — vraag het Tracking & Tools-team via Slack #adpage-tracking of mail support@adpage.io met de container-ID en een screenshot van de sGTM console-log.









