conversiontracking.io

Paperform Conversion Tracking: Google Ads, GA4 & Meta Pixel

Do you embed a Paperform on your website and need to track form submissions as conversions in Google Ads, Google Analytics 4, or Meta Pixel?

You’re in the right place.

Paperform loads as an iframe — a mini-website from another domain embedded inside yours. That causes a classic tracking problem that trips up most marketers and developers. This guide covers the correct solution: a postMessage listener in Google Tag Manager.


The Iframe Problem: Why Standard Tracking Doesn’t Work

When someone fills out your Paperform and submits it, that “thank you” confirmation happens inside the Paperform iframe at paperform.co — not on your website.

Your Google Analytics tag, your Meta Pixel, your Google Ads conversion tag — they all live on your domain. They don’t know anything happened inside the iframe.

What breaks if you ignore this

  • Google Analytics: zero form submission data, or wildly wrong attribution
  • Google Ads: conversions not recorded → your campaign looks like it’s not working → you pause it → you lose leads
  • Meta Pixel: your audiences don’t grow, lookalike campaigns underperform

The root issue: cross-domain iframe isolation. The iframe and your page can’t directly share data. They can only communicate via the browser’s postMessage API.


The Correct Solution: postMessage Listener in GTM

Paperform sends postMessage events to the parent window during the form lifecycle. Here’s what actually arrives in the browser (captured from a real submission):

// Form submitted — this is the conversion event
paperform:{"details":{"form_id":"69fce4c2b20e93f380049d04","eventName":"SubmittedForm","payload":{"submission_id":"69fce5e29dc9f15f54026abd","submitted_at":"2026-05-07 19:20:02","total":0,...}},"action":"analytics","id":"xstrr2sh"}

// Submission data — fires immediately after, contains field values
paperform:{"details":{"submission_id":"69fce5e29dc9f15f54026abd","data":[{"label":"What's your email?","value":"user@example.com","type":"email",...}]},"action":"submission","id":"xstrr2sh"}

Two things to note:

  • Messages are strings, not objects. They start with paperform: followed by JSON.
  • The conversion trigger is action: "analytics" + eventName: "SubmittedForm" — not a type field.

Your job: parse these strings and push the conversion to your dataLayer.

Google Tag Manager is the right place to do this. Here’s exactly how.


Step-by-Step: Paperform Conversion Tracking with GTM

Step 1: Add the postMessage Listener Tag

In GTM, create a Custom HTML tag:

Tag name: Listener — Paperform Submission

HTML:

<script>
  var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
  var eventer = window[eventMethod];
  var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";

  eventer(messageEvent, function(e) {
    if (typeof e.data !== "string") return;
    if (e.data.indexOf("paperform:") !== 0) return;
    if (e.origin.indexOf("paperform.co") === -1) return;

    var parsed;
    try { parsed = JSON.parse(e.data.slice(10)); } catch(err) { return; }

    if (parsed.action === "analytics" &&
        parsed.details &&
        parsed.details.eventName === "SubmittedForm") {
      window.dataLayer = window.dataLayer || [];
      window.dataLayer.push({
        event: "paperform_submitted",
        paperformId: parsed.id,
        paperformFormId: parsed.details.payload.form_id,
        paperformSubmissionId: parsed.details.payload.submission_id,
        paperformTotal: parsed.details.payload.total
      });
    }
  }, false);
</script>

Trigger: All Pages (DOM Ready)

Why DOM Ready? The listener needs to be attached before any iframe loads. DOM Ready fires early enough.


Step 2: Create a Custom Event Trigger

In GTM, go to Triggers → New:

  • Trigger type: Custom Event
  • Event name: paperform_submitted
  • Name: Custom Event — paperform_submitted

This trigger will fire whenever the dataLayer push from the listener lands.


Step 3: Add Your Conversion Tags

Now add tags that fire on the paperform_submitted trigger. One tag per destination.

Google Analytics 4 — Event Tag

  • Tag type: Google Analytics: GA4 Event
  • Event name: generate_lead (or form_submit, or whatever fits your setup)
  • Trigger: Custom Event — paperform_submitted
  • Tag type: Google Ads Conversion Tracking
  • Conversion ID + Label: from your Google Ads account (Tools → Conversions)
  • Trigger: Custom Event — paperform_submitted

Meta Pixel — Event Tag

If you use Meta Pixel via GTM:

  • Tag type: Custom HTML
  • HTML:
    <script>
      fbq('track', 'Lead');
    </script>
  • Trigger: Custom Event — paperform_submitted

Step 4: Test Before Publishing

Use GTM Preview mode (the “Preview” button in the top right):

  1. Open your page with the Paperform embed
  2. Fill out and submit the form
  3. In the GTM debug panel, look for the paperform_submitted event firing
  4. Verify your conversion tags appear in the “Tags Fired” section

Also check the browser console. After submission you should see the dataLayer push if you run:

window.dataLayer

Step 5: Publish

When Preview confirms everything fires correctly:

  • Click Submit in GTM
  • Add a version name: "Add Paperform submission tracking"
  • Publish

From this point, every Paperform submission on your site sends a conversion to your chosen platforms.


Common Issues and Fixes

The event isn’t firing

Check origin filtering. If you have a custom domain connected to Paperform (e.g. forms.yoursite.com), the origin in the postMessage will be your custom domain — not paperform.co. Update the origin check:

// Instead of:
if (event.origin.indexOf('paperform.co') === -1) return;

// Use your custom domain:
if (event.origin.indexOf('forms.yoursite.com') === -1) return;

// Or remove origin filtering entirely (less secure, but fine for debugging):
// (remove the origin check line)

Duplicate conversions

Each Paperform submission fires SubmittedForm once, so duplicates are unlikely with this method. If you do see doubles (e.g. two forms on one page both firing), add a submission ID check:

if (parsed.action === "analytics" &&
    parsed.details &&
    parsed.details.eventName === "SubmittedForm") {
  var submissionId = parsed.details.payload.submission_id;
  var dedupeKey = "pf_tracked_" + submissionId;
  if (sessionStorage.getItem(dedupeKey)) return;
  sessionStorage.setItem(dedupeKey, "1");

  window.dataLayer = window.dataLayer || [];
  window.dataLayer.push({
    event: "paperform_submitted",
    paperformId: parsed.id,
    paperformSubmissionId: submissionId
  });
}

Multiple Paperforms on one site

The paperformId dataLayer variable carries the form slug (e.g. xstrr2sh). Create a Data Layer Variable in GTM:

  • Variable type: Data Layer Variable
  • Variable name: paperformId

Then use it as a condition in your triggers if you want different conversions per form.


Beyond Basic Tracking: Enhanced Conversions

Basic tracking tells Google Ads that a conversion happened. Enhanced Conversions tell it who converted — hashed email, name, phone. This improves attribution significantly, especially on iOS where cookie-based tracking is unreliable.

Paperform fires a second postMessage immediately after SubmittedForm with action: "submission". It contains the actual field values:

// The submission data message — fires right after SubmittedForm
paperform:{"details":{"submission_id":"...","data":[
  {"label":"What's your email?","value":"user@example.com","type":"email"},
  ...
]},"action":"submission","id":"xstrr2sh"}

You can catch this in the same listener and push the email to the dataLayer for use in an Enhanced Conversions tag. The submission_id matches across both messages so you can correlate them.

That’s a deeper setup — but the listener you’ve already built is the foundation for it.


What About Paperform’s Built-In Integrations?

Paperform has native Google Analytics and Facebook Pixel integrations. Do not use them for conversion tracking on embedded forms.

Here’s why: those integrations fire from Paperform’s servers or Paperform’s domain — not yours. The conversion has no connection to your website’s cookies, sessions, traffic sources, or click IDs.

Result: an “orphan” conversion with no attribution. You see a number go up, but you can’t tell if it came from your Google Ads campaign, your Instagram post, or a direct visit. Useless for optimizing ad spend.

The GTM postMessage method fires from your website, so it inherits your full tracking context. That’s the only way to get correct attribution.


Need This Done Properly?

If Google Tag Manager isn’t your thing, or you want a bulletproof setup with enhanced conversions, proper deduplication, and GA4 funnel tracking — get in touch.

Or check the shop for prebuilt GTM setups for other form platforms.