ActiveCampaign UTM Tracking: How to Pass UTM Parameters to Every Contact
If your ActiveCampaign contact record has an email address but no campaign source, the UTM parameters never reached the contact field.
That is usually a handoff problem, not an ActiveCampaign reporting problem.
I first wrote about passing UTMs to ActiveCampaign in 2018. The basic idea still works: create fields, add them to the form, and populate them before submission. But the old three-step version leaves out the parts that now cause most of the trouble: field mapping, persistence across pages, consent, repeat visits, and verification.
This is the version I would use today.
The short version
| Job | Where it happens | What can go wrong |
|---|---|---|
| Capture the UTM values | The landing page or a site-side tracking script | The visitor arrives without UTMs, or consent prevents storage |
| Keep the values until conversion | First-party storage, the current URL, or your application | A refresh, another page, a redirect, or a multi-step form drops them |
| Send the values | Hidden form fields or a server-side API handoff | The fields exist in ActiveCampaign but are not mapped in the form or integration |
| Store the values | ActiveCampaign custom contact fields | A later blank submission or repeat visit overwrites useful data |
| Verify the result | The submitted contact record | The thank-you page works, but the fields are still empty |
A hidden field is only the delivery slot. It does not capture or remember anything by itself.
Where UTM fields live in ActiveCampaign now
As of August 2026, I separate three things that are easy to confuse:
- The custom contact field is the storage location on the ActiveCampaign contact record.
- The hidden form field is the input that accepts a value during an inline-form submission.
- The contact record is where I verify that the saved value actually arrived.
The current path for contact fields is Contacts → Fields → Contacts. ActiveCampaign calls this the Manage Fields page. Create fields such as utm_source, utm_medium, utm_campaign, utm_term, and utm_content there.
For an ActiveCampaign inline form, go to Website → Forms, edit the form, then open Fields → My Fields. Add or locate the custom fields and drag them into the form. ActiveCampaign supports a Hidden Field type for inline forms. Its documentation also says hidden fields are text-only and are not available for floating box, floating bar, or modal forms.
ActiveCampaign documents both paths in its current guides for custom contact fields and hidden form fields.
One important detail: creating a custom field does not automatically map it into every form or integration. A field can exist perfectly in ActiveCampaign and still stay blank forever because nothing sends a value to it. If that is your symptom, use my CRM field mapping checklist before changing the tracking code.
Choose the handoff method before touching the form
There are two practical methods, plus one stronger server-side variation.
| Method | Page refresh | Multi-step flow | Redirect | Iframe embed |
|---|---|---|---|---|
| Current URL → ActiveCampaign hidden field | Works while the tagged URL remains | Usually no | Only if the UTMs are forwarded | Only if the iframe URL receives them and the form accepts them |
| Site tracking script → stored values → hidden field | Yes, if storage is allowed | Yes, if every step can read the stored values | Yes, on the same site or with an explicit handoff | Only with a supported parameter, SDK, or messaging handoff |
| Site tracking script → server → ActiveCampaign API | Yes, after the server receives the values | Yes, if the application carries them through | Yes, if the application carries them through | Yes, if the embedded tool sends the values to your server |
The first method is fine for a short, single-page path.
The second method is what I use when the visitor may browse before converting. A site-side script captures the UTMs, stores the approved values, and fills the hidden inputs when the form loads or submits. HandL UTM Grabber is one way to handle that capture and persistence on WordPress, but the underlying requirement is the same with or without a plugin.
The API variation is useful when your WordPress form already submits to your own server or integration. The server can sync the contact and update its custom fields through the ActiveCampaign API. ActiveCampaign describes its API as the route for integrations and functionality outside its main interface.
An API does not recover attribution that was never captured. It only gives you a more controlled way to deliver the data you already have.
Method 1: pass UTM parameters into an ActiveCampaign hidden field
ActiveCampaign supports adding a query string to the hosted form URL. Its own example uses a hidden field whose personalization tag matches the query parameter after the percent signs are removed.
https://example.activehosted.com/f/15?utm_source=google&utm_medium=cpc&utm_campaign=summer_demo
This is useful when you link directly to an ActiveCampaign-hosted form.
An embedded form on a WordPress page is different. The UTMs in the browser address bar do not magically become values inside the generated ActiveCampaign fields. You need to pass them into the form.
For the most control, use the full embed code. ActiveCampaign says its simple embed uses JavaScript and updates automatically when the form changes, while the full embed uses HTML and CSS and must be pasted again after form changes. The full embed gives you actual hidden inputs that your site code can populate.
In the generated code, an input may look like this:
<input type="hidden" name="field[12]" value="" />
The number is the ActiveCampaign field ID in that form. Do not copy field[12] from this article. Use the exact name generated for your own field.
A minimal current-URL handoff looks like this:
<script>
const params = new URLSearchParams(window.location.search);
const sourceField = document.querySelector('input[name="field[12]"]');
if (sourceField && params.has('utm_source')) {
sourceField.value = params.get('utm_source');
}
</script>
Repeat the mapping for the fields you actually use. Place the script after the full embed markup, or run it after the form exists in the page.
This example deliberately does not pretend to solve persistence. If the visitor moves to an untagged page, the current URL no longer contains the values. That is when you need the second method.
Method 2: capture first, then send by form or API
A tracking script changes the order of operations:
- Read the approved attribution values on the landing page.
- Store them according to your consent setup.
- Retrieve them when the form is ready.
- Fill the ActiveCampaign hidden inputs, or send the same values through your server-side integration.
- Confirm the values on the contact record.
This handles a normal WordPress journey better because the landing page and conversion page do not have to be the same page.
It also makes the failure boundary clearer. If the stored value is correct but the ActiveCampaign field is blank, the problem is in the form mapping or API handoff. If the stored value is already blank, the problem happened earlier. My longer diagnosis order is in Why UTM Fields Are Blank Even When the Link Looks Right.
For Contact Form 7, the final handoff uses its own hidden-field and mail-tag mechanics before a CRM integration sees the value. I cover that separately in Contact Form 7 UTM Tracking.
First touch and last touch need different fields
If you send every visit into one field called utm_source, the newest value can replace the old one. That may be exactly what you want. It is not first-touch tracking.
I prefer explicit field names:
| Field set | Write rule | Question it answers |
|---|---|---|
first_utm_source, first_utm_medium, first_utm_campaign | Write once when empty | How did this contact first arrive? |
last_utm_source, last_utm_medium, last_utm_campaign | Update on the latest attributed visit or conversion | What brought this contact back before the latest conversion? |
Add term and content fields only if your campaigns use them and someone will inspect them. Ten empty CRM columns are not better attribution.
The first-touch rule must be enforced somewhere. Your site code, integration, automation, or server needs to check whether the first-touch field already has a value before replacing it. Update the last-touch fields deliberately.
ActiveCampaign also has a form option named Allow blank fields to overwrite existing field data. Its current form guide says this option is selected by default. For attribution fields, I review that setting under Options → Form Settings. Otherwise, a later submission with empty hidden fields can erase a useful value.
If you are still deciding which model belongs in the CRM, read First-Touch vs Last-Touch Attribution in WordPress.
Why the fields are still blank
The fields exist but the form does not send them
Creating utm_source under Contacts → Fields is not enough. Add the field to the inline form, map it in your WordPress integration, or include it in the API handoff.
The script reads only the current URL
The visitor landed with UTMs, browsed to another page, and submitted there. If nothing stored the original values, the form has nothing to send.
Consent changes when storage begins
Your consent tool may block the tracking script, delay it, or clear storage before the visitor submits. That can be correct behavior for your policy. It also means the attribution field may remain empty. I explain the implementation tradeoff in Cookie Consent Without Losing UTMs on WordPress.
A blank repeat submission overwrites a good value
This is easy to miss because the first test works. Review the blank-field overwrite setting and test the same email address twice.
The form is inside an iframe
The parent WordPress page cannot simply reach into a cross-origin iframe and fill its fields. Pass the values through a supported iframe URL parameter, embed SDK, or controlled message. If the embedded provider does not accept and store those values, WordPress cannot force it.
How I verify UTM parameters in ActiveCampaign
I do not stop at the thank-you message. I check the contact record.
- Open a tagged test URL with unmistakable values, such as
?utm_source=qa&utm_medium=manual&utm_campaign=activecampaign_test_202608. - If you expect persistence, navigate to an untagged page before opening the form.
- Submit the form with a unique email address.
- In ActiveCampaign, open Contacts, find that contact, and inspect the custom contact fields.
- Confirm each field separately. Do not accept a populated source field as proof that campaign and medium also mapped correctly.
- Repeat the test with the same email address and no UTMs. Confirm that your first-touch values remain and your blank-field behavior matches the rule you chose.
- Repeat once more with a second tagged visit. Confirm that last-touch changes only if that is the intended behavior.
If the first submission fails, inspect the form input before submit. If the browser input is blank, fix capture or persistence. If the browser input contains the value but the contact record is blank, fix the mapping or API request.
What I would not over-claim
- Saving UTMs in ActiveCampaign does not turn ActiveCampaign into a complete attribution model.
- A UTM value tells you what was attached to the visit. It does not prove that the campaign caused the conversion.
- An API handoff is not automatically more accurate. It is only more controlled after your server has the data.
- A tracking script does not override consent choices or browser restrictions.
- A hidden field does not survive refreshes, redirects, multi-step forms, or iframes unless the rest of the implementation carries the value through.
My final checklist
- Create named custom contact fields in ActiveCampaign.
- Add or map those fields in the exact form or integration that submits the contact.
- Choose current-URL capture or persistent site-side capture deliberately.
- Use separate first-touch and last-touch fields if you need both.
- Review whether blank form values can overwrite existing data.
- Test consented and non-consented paths according to your setup.
- Verify the actual contact record with a unique tagged submission.
- Repeat the test with the same email address.
The implementation is working only when the right value reaches the right contact field and stays there under the rules you chose.




Hey,
Thanks for this great article.
However, I’ll be careful, never use utm parameters inside the website that is already tracked by the GA.
It will rewrite the real source in Google Analytics.
It will trigger a new fake session in Google Analytics as well.
Your method should be used only for the form which are outside of the website.
Kind regards,
Yoan.
Hi Yoan,
I do not understand why it will rewrite the source? or it will create fake sessions in GA. HandL UTM Grabber does not interfere with GA at all. I appreciate if you can explain more.
Thanks,
Hey, Haktan,
I tried to make the plugin works with Active Campaign, but it’s passing the same parameters (the first that i tried). All the contacts are with the same UTM’s.
Can you help witi this problem?
Hi Joao,
Sure, can you give me more information? Which website are you testing this on?
Hi @Haktan
What if you’re using gravity forms in WordPress to ActiveCampaign? How do you change the code in WP (Step 3)? I have Handl plugin installed. Made the changes in Gravity form AC setting using hidden fields then changes.
(check box)Allow field to be populated dynamically
Parameter Name:
utm_source
I got it pass through GF to AC with a simple test adding /?utm_source=test&utm_medium=test to the end of my url
BUT This morning I got two real submissions but no utm data info passed in gravity form entry or AC entry. I can verify in GA that utm info was passed there. Leads or traffic are coming from Bing As an FB ads if that is helpful. Verified i have the UTMs setup correctly on those platforms.
Any help or directions would be helpful. Thanks!
Hi Josh,
I think you should use 3rd party integration tool for Gravity Form that supports ActiveCampaign, so you can map the form fields to AC fields.
HI Haktan,
I followed the process above and for some reason it’s still only passing %s in Active Campaign. Here’s a look at our code. We installed and activated the plugin, created hidden fields, created the form, and then modified the form with your wrapper code. Any thoughts?
[utm_medium] [/utm_medium]
Hi Zach,
For some reason i do not see your code… Can you join our Slack channel and copy the form code there? https://www.haktansuren.com/slack-handlwp/
Thanks
<code class="{html}"
[utm_medium] [/utm_medium]
Hello,
Thanks for the tutorial.
I´ve done and it´s working almost 100% but “gclicd” and handl_original_ref still just showing %s
Ps: I´ve created a ebook campaign at facebook just for instagram stories.
Can you help me?
Cheers.
please join our slack page https://www.haktansuren.com/slack-handlwp/ so i can help better.
I am having trouble getting this to work. Do I need to do step 3 if I have the WP Plug in Installed and Activated? If so, do I need to have the WP plug in at all?
yes definitely need. Because AC does not indicate which fields which UTMs out of the box.
I’m using SmartTheme (came with OptimizePress plugin), and it is integrated with ActiveCampaign.
I followed the process above.
But the custom fields justs are filled with %s.
it sounds like they do not render shortcodes. Can you send me some screenshot of the forms or maybe give me access so i can check it out. Feel free to DM me here (Official Slack channel for the plugin) https://www.haktansuren.com/slack-handlwp/
Hello, I’ve tried this — and it seems to be working. However, the when HANDL populates the UTM fields, it shows on the form. How does it stay hidden?
Hi Haktan, does it work with Clickfunnels if i paste the Html code directly i. their editor into a page to attach the form. I tried and it doesn’t work.
Thanks!
Can you share couple screenshot where you are adding this? I can test and tell you whether it will or i can find you another solution.
This post saved my day! Thanks a lot.
This method is work in 2023?
why not?
Can I also extract only the URL Path/Slug
Not in this version, you need to make a custom mod.