Tally is a free, fast form builder that many startups and agencies now use instead of Typeform. Like other hosted builders, it runs in an iframe, so your site's Google Tag Manager cannot see submissions directly. Tally solves this better than most: on submission it posts a structured message to the parent page that includes the form ID and the submitted fields.
This guide shows how to catch that message in GTM and send the conversion to GA4, Google Ads and Meta.
What Tally Sends
When a respondent submits an embedded Tally form, the iframe posts a message to the parent window. The message is a JSON string with an event of Tally.FormSubmitted and a payload containing formId, respondentId and a fields array with each question's label and value.
Step 1: The Listener
Create a Custom HTML tag named Listener - Tally Submit and fire it on All Pages or on the pages with the embed:
<script>
(function () {
window.addEventListener('message', function (e) {
if (!e.origin || e.origin.indexOf('tally.so') === -1) { return; }
var data = e.data;
if (typeof data === 'string') {
try { data = JSON.parse(data); } catch (err) { return; }
}
if (!data || data.event !== 'Tally.FormSubmitted') { return; }
var payload = data.payload || {};
var email = '';
var phone = '';
(payload.fields || []).forEach(function (field) {
var type = (field.type || '').toUpperCase();
if (type === 'INPUT_EMAIL' && field.value) { email = String(field.value).trim().toLowerCase(); }
if (type === 'INPUT_PHONE_NUMBER' && field.value) { phone = String(field.value).trim(); }
});
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'form_submit_success',
form_tool: 'tally',
form_id: payload.formId || '',
response_id: payload.respondentId || '',
user_email: email || undefined,
user_phone: phone || undefined
});
});
})();
</script>
Only the email and phone are read from the fields. Everything else in the payload stays out of the dataLayer.
Step 2: Variables and Trigger
Data Layer Variables: form_id, response_id, user_email, user_phone. Custom Event trigger: form_submit_success, with a form_id condition if you have several forms.
Step 3: Tags
- GA4 Event
generate_leadwithform_idandresponse_id. Mark as a key event. - Google Ads Conversion Tracking with your conversion ID and label, Conversion Linker on All Pages, and a User-Provided Data variable from
user_emailanduser_phonefor Enhanced Conversions. - Meta Lead with advanced matching, deduplicated against the Conversions API if you run server-side GTM.
Step 4: Store the Click ID in Tally
In the Tally form, add a hidden field named gclid. Tally fills hidden fields from URL parameters on the embed URL. Add a small script on your page that appends ?gclid= with the stored click ID to the Tally embed src before the iframe loads, or use Tally's option to pass through the parent page's URL parameters. Once each response carries the click ID, you can export qualified leads and upload them to Google Ads as offline conversions.
Step 5: Test
- GTM Preview on the page with the Tally embed. Submit a test response.
- Confirm
form_submit_successappears withform_id,response_id, and the email and phone if you included those fields. - Confirm one fire each of GA4, Google Ads and Meta.
- Check the Google Ads request in a tag debugger such as the Insighter Debugger extension for the conversion label and hashed user data.
- If you set up the hidden field, open the response in Tally and confirm the gclid value.
Common Problems
- No message. Confirm the embed is the Tally iframe. The listener ignores messages from other origins.
- Email not captured. The field type name differs in some payloads. Log the payload in the console once and match the type string Tally uses for your email question.
- Double firing. Tally's own GA integration plus your GTM tag. Turn one off.
Summary
Tally gives you the cleanest hosted-form tracking of the bunch: a single structured message with the form ID and fields. One listener in GTM turns it into a dataLayer event you can send to GA4, Google Ads and Meta with Enhanced Conversions attached.
