GoHighLevel · one-time implementation · proof of build

A seasonal tax landing page and callback workflow, built and tested end to end

Your brief describes a small, well-defined job: a new landing page, a four-field callback form, the contact created or updated with a campaign tag and pipeline entry, source information preserved, you notified, the prospect acknowledged, a thank-you shown, the pixel confirmed, and nothing in the existing funnel touched. Rather than describe that, I built it against my own GoHighLevel sub-account so you can submit the form yourself and see every step land.

Prepared by David Willis · Best Choice Automation September 2026 Fictional practice “Meridian Tax Advisors” · my sandbox, not yours
On screen

One submission, from the ad click to the pipeline board

The landing page opened from a UTM-tagged link, the four fields filled, the receipt in place of the form, the Worker's response behind it, then the contact in GoHighLevel with its tag, and the card on the new pipeline. This one was a live write, so the contact and card you see are real.

Recorded against the deployed endpoints on 7 September 2026. Public submissions from the page are dry-run; this recording used the operator token so the write is real.

“The new campaign must remain completely separate from an existing lead-magnet page and its automations. Nothing in the existing funnel or workflow should be changed.”

That sentence decides the method. Every object the campaign needs gets its own prefixed name, its own tag, its own pipeline and its own fields; nothing existing is edited, reused or re-pointed. And instead of promising that, the sub-account is snapshotted before the build and again after, and the diff is the deliverable: the only changes are additions.

Your scope, item by item

What you asked for, and what it does in the demo

Scope itemBuilt asVerified
New landing page, desktop and mobileA separate page with placeholder copy (yours replaces it), responsive two-column to single-columnlive
Short callback form: name, phone, email, optional summaryFour fields plus a consent line and a honeypot for bots. Phone and email validated before anything is sentlive
Create or update the contact correctlyMatch by email first, then phone; create only when neither matches. A returning prospect is updated, never duplicatedproven twice
Campaign-specific tagtax-2026-callback, applied on create and on match, used nowhere elseread back
Pipeline and stageNew pipeline Tax Season 2026 — Callbacks, entry stage Callback requested. A second request from the same person updates the open card and adds a note rather than opening a second cardread back
Preserve Facebook / UTM sourceutm_source, medium, campaign, content, term, fbclid, landing page and referrer captured on first landing, kept for the session, stored in ten campaign-prefixed fieldsread back
Immediate internal notification with the submitted informationEmail to you within a second, every field plus a direct link to the contactin inbox
Brief acknowledgment to the prospectPlain email: we got it, we'll call this number, reply if another is better. Sent through an authenticated domain, and noted on the contactin inbox
Clear thank-you after submissionThe form is replaced in place by a receipt: who we'll call, at what number, logged when, with a referencelive
Meta Pixel and conversion tracking workingPixel fires PageView and Lead in the browser; the server sends the same Lead to the Conversions API with a shared event id so Meta counts one lead, not twopixel live · CAPI needs your token
Test the complete processLive submissions from the page through to the inbox, plus 14 automated tests for the failure casesdone
Verify existing page, form and automations unchangedBefore and after snapshot of every pipeline, field, workflow, form, funnel, calendar and tag, diffed0 changed
The one honest partial. Server-side conversion tracking is wired and was tested, but the Meta access token in my sandbox expired in July and Meta will not issue a new one without a person in Business Manager. In your build it is your pixel and your token anyway; that is a five-minute step on your side, and I would confirm it in Events Manager with a test event code before sign-off.
Evidence

One submission, and what the CRM says a moment later

The callback request

POST /callback · 2026-09-07 21:40 UTC
{
  "name": "Jordan Alvarez", "phone": "(517) 555-0142",
  "email": "jordan@…", "summary": "First year self-employed and I got a CP2000 letter for 2024.",
  "utm_source": "facebook", "utm_medium": "paid_social",
  "utm_campaign": "tax-2026-video", "utm_content": "cp2000-hook", "fbclid": "IwAR0…"
}

HTTP 201  matched false · contact 0D055NUA00Ben5WieLJN · opportunity LFz4IdZuadd18XkoOtxf
          tag tax-2026-callback · pipeline "Tax Season 2026 — Callbacks" · stage "Callback requested"
          notified true · acknowledged true · 1.4 s

Read back from GoHighLevel: name split correctly, phone stored as +15175550142, source “Tax Season 2026 landing page”, the one tag, and all ten attribution fields populated, including the click id and the exact landing URL. The notification was in the inbox at 21:40:22, one second after the request.

The same person, forty seconds later, from a different phone

contact matching

This is where lead forms quietly go wrong: a second submission becomes a second contact, the history splits, and you ring the same person twice. Here the email is matched case-insensitively, the existing card is kept, and the new request lands as a note.

{ "email": "HelpPeople411+Jordan@…", "phone": "517-555-0199", "summary": "Also need last year amended." }

HTTP 200  matched true · contact 0D055NUA00Ben5WieLJN   // the same contact
          opportunity LFz4IdZuadd18XkoOtxf "existing"      // the same card, plus a note
          notified true · acknowledged true · 1.6 s
0
existing objects changed in the sub-account, by diff
11
objects added, every one prefixed “Tax”
14
automated tests, including the CRM-down case
2
demo contacts to delete when you have seen them

“Nothing else changed”, as a diff rather than a promise

snapshot before → after
pipelines      15 ->  16  +1 -0 ~0     + Tax Season 2026 — Callbacks [Callback requested, Reached, No answer — retry, Closed]
customFields   55 ->  65  +10 -0 ~0    + Tax — UTM Source … Tax — Callback requested at
workflows      75 ->  75  +0 -0 ~0
forms           8 ->   8  +0 -0 ~0
funnels        16 ->  16  +0 -0 ~0
calendars       8 ->   8  +0 -0 ~0
tags          443 -> 443  +0 -0 ~0

Existing objects: UNCHANGED (only additions).

The sub-account already holds 75 workflows, 16 funnels and 443 tags from other work, which is a fair stand-in for a live account with an existing lead magnet. The snapshot script runs again at handoff, so you get the same table for your account.

The failure cases, tested on purpose

node --test
✔ every field spelling the form could send produces one shape
✔ (517) 555-0142, 517-555-0142 and 15175550142 converge to +15175550142
✔ a bad email with a good phone is flagged, and still recorded
✔ no email and no phone is the only rejection
✔ the same request twice is held as a duplicate, not a second contact
✔ the CRM being unreachable returns 202 with the lead intact, and still notifies you
✔ a malformed body is a 400, not a stack trace
✔ a browser on the landing page is allowed; any other origin is not

The one that matters most is the sixth. If the CRM is down for ten minutes on the morning your ads go live, the form still says thank you, you still get the email with the details, and nothing is lost.

Your build versus this demo

What I would actually deliver in your sub-account

In your account the work is native GoHighLevel: a funnel page built from your copy and page order, a GHL form with hidden UTM fields, and one workflow, triggered by that form only, that tags, moves the contact into the new pipeline, emails you, emails the prospect, and redirects to the thank-you step. No custom code is required for what you described.

What you are looking at here runs the same steps through a small service on Cloudflare instead, for one reason: a demo cannot show your account, and a GHL form in my sandbox would prove nothing to you. The service exists so you can press the button and watch the contact, tag, card, attribution and both emails land. It also happens to be the fallback I would recommend if your existing lead magnet uses a broad “any form submitted” trigger that cannot safely be narrowed.

How I test before handoff

Your fifth question, answered concretely

1before touching anything

Snapshot the sub-account, read-only

  • Every pipeline, custom field, workflow, form, funnel, calendar and tag, saved to a file.
  • Read the existing lead-magnet workflow's trigger. If it fires on any form or any tag, that is the first thing we talk about, before I build.
gate: snapshot saved · existing trigger scope understood
2build

New objects only, all prefixed

  • Page, form, tag, pipeline, fields and workflow named for the campaign so they are obvious in every list.
  • The workflow trigger is filtered to the new form, never “any form”.
  • Sender address on an authenticated domain, so the acknowledgment is delivered, not just accepted.
gate: second snapshot shows additions only
3test

Real submissions, real inbox, real Events Manager

  • Submit from a phone and a desktop with a UTM-tagged URL; open the contact and check every field.
  • Submit the same person twice; confirm one contact and one card.
  • Confirm the notification and the acknowledgment arrived, with timestamps.
  • Watch PageView and Lead arrive in Events Manager, with the test event code, and confirm the browser and server events deduplicate.
  • Submit the existing lead-magnet form once and confirm its automation still behaves exactly as before.
gate: your completion checklist, every line ticked with evidence
4handoff

One page of notes, and the diff

  • Where each object lives, what to change if the copy changes, how to pause the campaign in one click.
  • Test contacts deleted, or left tagged test if you prefer to keep them.
gate: you have run one submission yourself and seen it land
What I expect to find in your account

Hypotheses, not findings

I have not seen your sub-account. These are the things that most often break the “keep it separate” requirement, listed so you can tell me which apply.

RiskWhy it mattersWhat I do about it
Existing workflow triggers on any form or a shared tagYour new form would enrol prospects in the lead-magnet sequenceRead the trigger first; filter to the new form; test the old form after
Hidden UTM fields not mapped on the formFacebook source arrives blank and you cannot tell which ad workedCampaign-prefixed fields, populated from the URL, checked on a real contact
Sender domain not authenticatedThe acknowledgment is “sent” but lands in spam or fails silently, which I saw in this buildVerify the domain in GHL email settings; send to a real inbox and look
Pixel already on the site header plus a pixel in the funnel pageDouble PageViews and inflated Lead countsOne pixel, one Lead event, event id shared with the server
Double-clicked submit buttonTwo contacts, two cards, two notificationsDisable on submit; dedupe the same request within ten minutes
Thank-you page shared with the lead magnetWrong message, and the wrong pixel event, after your formSeparate thank-you step in the new funnel
Where I would want your help

Named plainly

Nothing of yours was touched. Everything on this page ran in my own sub-account, and the two demo contacts it created will be deleted once you have seen them. Price and timeline are in my proposal, not on this page.