Conversion Tracking Spec
Do not launch a single Google Ad until this is done and verified.
This is what makes ads work. Google Ads optimizes toward whatever you define as success. Define nothing and it optimizes toward cheap clicks, which are worth nothing. Define "someone filled out the form" and it will hunt for people who fill out forms.
Total build time: 2–3 hours for whoever knows the codebase.
Accounts to create first
| Account | Why | Cost |
|---|---|---|
| Google Analytics 4 | Traffic, sources, behavior, conversion destination | Free |
| Google Tag Manager | Deploy and change tags without a code deploy every time | Free |
| Google Search Console | The only source of truth for organic keyword rankings | Free |
| Google Ads | Ads and conversion import | Free to open |
Use one Google account that both you and your GM can access. Do not use a personal Gmail that only one person controls. Set up a Google Workspace account on your domain ([email protected]) if you haven't — it also makes your outbound email far less likely to land in spam.
Link the accounts: GA4 ↔ Google Ads, GA4 ↔ Search Console, GTM → GA4.
The four conversion events
1. generate_lead — form submitted (PRIMARY)
The one that matters. Fires on successful submission of the contact form.
// Fire on successful API response, not on button click.
// Button click fires even when validation fails and will inflate your numbers.
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'generate_lead',
form_name: 'tour_request',
tour_interest: formData.tour, // from the ?tour= query param or the select
referral_source: formData.hearAboutUs, // your "How did you hear about us?" field
value: 250, // see note below
currency: 'USD',
});
On value: give every lead an estimated dollar value so Google can optimize for revenue rather than lead count. If a booking averages $1,000 and one lead in four books, a lead is worth about $250. Adjust once you have real numbers. Without a value, Google treats a tire-kicker and a 12-person family group identically.
Redirect to a thank-you page (/thank-you) after submission. It gives you a clean conversion trigger, a place to put the "what happens next" message, and a remarketing audience. Right now the form appears to submit in place.
2. phone_call_click — tap-to-call
// On every tel: link
window.dataLayer.push({
event: 'phone_call_click',
phone_number: '307-690-5501',
page_location: window.location.pathname,
value: 200,
currency: 'USD',
});
Do this properly with Google's call tracking. In Google Ads, enable a Google forwarding number for website calls. It swaps the number on the page for a tracking number when the visitor arrives from an ad, and reports call duration. You then count only calls over 60 seconds as conversions, which filters out wrong numbers and hang-ups. Requires one snippet on the site.
This matters a lot for you specifically — your entire pitch is "call and you'll get the owner." Phone is likely to be your highest-quality channel and if you don't track it you'll wrongly conclude your ads aren't working.
3. email_click — mailto tapped
window.dataLayer.push({
event: 'email_click',
page_location: window.location.pathname,
value: 150,
currency: 'USD',
});
4. view_tour_details — engaged tour page view (SECONDARY ONLY)
Fires when someone is on a tour detail page for 45+ seconds or scrolls past 75%.
window.dataLayer.push({
event: 'view_tour_details',
tour_name: 'Old Faithful',
engagement_type: 'time_45s',
});
Mark this as secondary in Google Ads. Never optimize toward it. It's a useful diagnostic — it tells you which tours generate interest even when they don't generate a form fill. If you make it a primary conversion, Google will happily buy you thousands of people who read your page and leave.
Google Tag Manager setup
- Container on every page (Next.js:
next/scriptwithstrategy="afterInteractive"inapp/layout.tsx) - GA4 configuration tag, fires on all pages
- Custom Event triggers on each of the four
dataLayerevents - GA4 event tags for each, passing the parameters through
- In GA4: Admin → Events → mark
generate_lead,phone_call_clickandemail_clickas conversions - In Google Ads: Tools → Conversions → Import from GA4
- Set primary/secondary:
generate_leadprimary,phone_call_clickprimary,email_clicksecondary,view_tour_detailssecondary
Also enable
Enhanced Conversions. Sends a hashed version of the email address the user typed into the form back to Google, so it can match conversions to ad clicks even when cookies fail. Materially improves data quality, especially on iOS. Requires a checkbox in Google Ads plus passing the hashed email — do it as part of this build, not later.
Consent Mode v2. Required if you get any EU traffic (you will — international visitors are a real segment for Yellowstone). Without it, Google Ads may stop recording conversions from those users entirely.
Server-side tagging. Not now. Revisit in year two if ad spend justifies it.
Verification checklist
Before a single dollar of ad spend:
- Submit the form yourself →
generate_leadappears in GA4 DebugView -
tour_interestcorrectly captures the?tour=query param - Tap the phone link on mobile →
phone_call_clickfires - Tap the email link →
email_clickfires - Conversions appear in Google Ads within 24 hours (there's a lag)
- Conversion values are populating, not showing as $0
- Test on both mobile and desktop — most tour traffic is mobile
- Test with an ad blocker enabled to see what you lose
- Confirm the thank-you page redirect works and isn't indexable (
noindex)
Use GA4's DebugView plus GTM Preview mode. Do not assume it works because the code is deployed.
What you'll be able to answer once this is running
- Which keywords produce leads, not just clicks
- What a lead costs from each channel
- Which tour pages convert best, so you know where to send ad traffic
- Whether Instagram traffic converts or just browses
- Whether to shift budget from one campaign to another
Without it, you're spending money blind and Google's automation has nothing to learn from.