Eloqua Technical · Salesforce · QR Attendance

How to Track Hundreds of Small Training Sessions in Eloqua with One Form, QR Codes and a Salesforce Campaign Hierarchy

A practical architecture for capturing event attendance at scale using a single Eloqua form, a Training Attendance CDO, smart QR codes and a salesperson-first Salesforce Campaign structure — with a downloadable certificate of attendance as the conversion hook.

📅 First published: 9 February 2026

⏱ Complexity: Advanced • 🎯 Focus: QR Capture, Eloqua CDO Modelling, Salesforce Campaigns, Certificates

Flow diagram showing QR code, Eloqua landing page, Training Attendance CDO and Salesforce Campaign mapping.
I got asked a question that sounds like “we need a form”, but it is not a form problem at all. Sales teams are running hundreds of tiny training sessions, sometimes two or three attendees, and nobody can reliably answer one basic thing: who attended what. The solution is not 300 forms. The solution is event identity plus attendee association, built once, then scaled forever.

Greg Staunton

The end solution — try it first

Below is the final attendee experience: scan a QR code, complete a short form, and have attendance automatically recorded against the correct Salesforce Campaign.

Treat this like you are a real attendee. Update the Campaign fields, generate the QR code, scan it with your phone, and submit the form. You will then see all the values that Salesforce passed into Eloqua and would be written to your Attendance CDO.

If this feels right, the rest of this section shows you exactly how to build it.

INTERACTIVE

Do this now:

  • Edit the Campaign fields
  • Click Generate QR Code
  • Scan with your phone
  • Submit the form
  • Review the captured values
https://acme.lightning.force.com/lightning/r/Campaign/701xx00000ABC123/view
A
Acme Inc
C
Campaign • Child Campaign (Event Session)

Paris – 10 February 2026

Details Registration enabled
Click here to test
Parent Campaign
Acme Training Events 2026
Start Date / Time
10 Feb 2026 • 14:00
City
Paris
Owner Email
Details Related Activity

Campaign Information

Edit
Event Session Fields (Editable demo)

Change any values below, then click Generate QR Code. Scan the QR with your phone to validate the link.

From ‘Hundreds of Events’ to One Smart System: How to Model Event Attendance in Eloqua and Salesforce

Introduction: When forms are not the problem.

Most marketing automation teams think that event attendance problems are a “form problem.”

They are not.

If you have hundreds of small HCP-led training sessions, scattered across regions, led by different salespeople, happening in overlapping cities and dates, your issue is not “we need more forms.”

Your issue is data modelling.

Specifically:

  • How do you uniquely identify each event?
  • How do you reliably associate the right people to the right event?
  • How do you do this at scale without building and maintaining hundreds of nearly identical forms?

This article walks through a real-world request that landed on my desk, my structured thinking process, three technical design options, and why I believe a CDO-driven, single-form, QR-based model is the cleanest way forward.

Along the way, I will also cover:

  • How this can fit into a Salesforce Campaign hierarchy, parent campaign to child event campaigns.
  • How QR codes can be generated in a way that integrates cleanly with Salesforce.
  • How hidden URL parameters can dynamically populate a single Eloqua landing page.
  • And why “ten-mile-long forms” are a terrible idea.

The problem to solve

Manager's request

  • Sales teams run hundreds of small HCP training sessions every year.
  • Many sessions have only two or three attendees.
  • There is no consistent way to know who attended what.
  • Salespeople need to capture attendance easily on phones or tablets.
  • Attendees must exist in Salesforce, linked to the correct event.
  • Marketing ops cannot build or manage hundreds of individual forms.
  • The solution must work for both in-person and online sessions.
  • It must not create massive operational overhead.

On the surface, this sounds like:

“We need a better form.”

In reality, the real problem was:

“We have no clear, scalable way to identify events and tie attendees to them.”

That is a data architecture problem, not a form design problem.

My thinking: reframing the problem

Before proposing solutions, I always try to restate the problem more precisely.

Here is how I framed it internally:

The core problems

  • Hundreds of HCP-led training sessions.
  • No consistent visibility of who attended what.
  • No capacity to build or manage hundreds of forms.
  • Events that may overlap by date, city, or leader.

Therefore, the real challenge is:

Event identity + attendee association.

Not:

  • “We need more forms.”
  • “We need a better landing page.”
  • “We need a different CRM field.”

We need a single, reusable form, backed by a strong data model that can represent any event.

That brings us to the core design principle of this solution.

Solution

QR-based attendance capture using a single Eloqua form, a Training Attendance CDO, and a Salesforce Campaign hierarchy

We are going to solve this with a simple rule: one reusable form for the attendee, backed by a strong data model that represents any event. This avoids hundreds of forms, messy naming conventions, and broken reporting.

The attendee experience stays frictionless: scan a QR code, enter your email and a couple of fields, submit, and optionally download a certificate of attendance as the incentive to complete it.

  • Salesforce holds the event record (child campaign) and can generate the QR code from the campaign fields
  • The QR code points to one Eloqua landing page and carries event context in the URL
  • The Eloqua landing page reads URL parameters and sets hidden fields on the form on page load
  • The Eloqua form never changes, and creates one CDO record per attendee per event
  • Optional: On submit, the attendee downloads a certificate, and the business gets clean attendance data

Let us break this down in plain English. The problem is not “how do we build a form”. The problem is: how do we uniquely identify each event and then associate attendees to that event without building new assets every time.

In this article I am going to prove three things:

  1. You can track attendance for hundreds of events using one Eloqua form and one Eloqua landing page.
  2. You can model attendance properly using an Eloqua Training Attendance CDO where each record represents one person attending one event.
  3. You can use a Salesforce Campaign hierarchy (parent = salesperson, child = event) to unlock reporting, and generate QR codes directly from the Campaign page.

The goal is a solution that a salesperson will actually use, and that marketing ops will not regret six months later. That means no ten mile long forms, no form sprawl, and no mystery data that cannot be reported on.

How it works in the real world

1) Salesforce defines the event

event identity

A parent campaign represents the salesperson or territory. A child campaign represents the specific session. Salesforce generates a QR code using campaign fields as URL parameters and stores the QR image URL on the campaign.

2) The QR bridges the room to Eloqua

capture

The salesperson prints the QR or shows it on a slide. Attendees scan it on their phones and land on one Eloqua landing page used for every event.

3) One form creates clean attendance data

reporting

JavaScript reads the URL parameters and sets hidden form fields. When the attendee submits, Eloqua creates a Training Attendance CDO record, meaning one person attended one event. Reporting becomes reliable, and Salesforce writeback (campaign member) is optional.

Salesforce owns event identity. The QR carries that identity to Eloqua. A single reusable form captures attendance, and the CDO makes reporting clean.

Three ways to solve this (and why we chose ours)

Before landing on the final design, we considered three realistic patterns. All three work technically, but they have very different operational and reporting consequences. Below, I outline each option in plain terms before comparing them side by side.

Option 1 — Eloqua-native, CDO-driven attendance (our recommended approach)

This is the model we have been describing throughout the article.

  • Salesforce holds event identity as child Campaigns under a salesperson parent Campaign.
  • A button on the child Campaign generates a QR code that always points to one Eloqua landing page.
  • The QR URL carries event context (Campaign ID, name, date, city, leader, brand, salesperson email).
  • The Eloqua landing page reads those values and populates hidden fields on a single reusable form.
  • On submit, Eloqua creates one Training Attendance CDO record per person per event.
  • Attendees can optionally download a certificate of attendance as a completion incentive.

This keeps the architecture simple, keeps the form count at exactly one, and gives you a clean, queryable attendance history in Eloqua that is not polluted by contact updates or Salesforce sync timing.

Side-by-side comparison

Dimension Option 1 — Eloqua CDO (Recommended) Option 2 — SFDC Campaign-first Option 3 — Webhook + Service
Number of forms 1 1 1
Where event identity lives SFDC child Campaign + QR URL SFDC child Campaign Service-generated Event_ID
Where attendance is recorded Eloqua Training Attendance CDO Primarily SFDC Campaign Members Service + Eloqua CDO + SFDC
Speed to implement Fast Medium Slowest
Scalability High High Very high
Deduplication Basic (Eloqua logic) Medium (SFDC rules) Best (true idempotency)
Sales friendliness High (simple QR button) High High (if tool is slick)
Dependency risk Lowest Medium (SFDC governance) Medium (needs service reliability)
Certificates Easy (LP confirmation) Easy Easiest and most controllable

Our recommendation: start with Option 1. It solves the core problem cleanly, delivers immediate value, and can be extended later to Option 2 or Option 3 without rewriting the front end.

Step 1: Start with the data model: the Training Attendance CDO

Before forms, landing pages, or QR codes, you must design the data model that will hold attendance. This is what keeps your solution scalable, reportable, and clean.

Build the Training Attendance CDO in Eloqua

data model first

The core idea is simple: each record in this CDO represents one person attending one specific event. Everything else in the architecture flows from this decision.

How you must configure this CDO in Eloqua:

  • Connect the CDO to Contacts using Email Address (this is how attendance links back to the person).
  • Do NOT set a unique identifier on the CDO.
  • Allow multiple records per contact, because the same person can attend many events.

This setup ensures that every form submission creates a new attendance record, rather than overwriting previous data. That is what enables clean reporting over time.

Step 2: Create one Eloqua form for one landing page

Your entire attendance system should hinge on a single, reusable Eloqua form that lives on one landing page. This is what prevents form sprawl and keeps your data model clean.

Build a single “Training Attendance” form in Eloqua

one form only

This form should be designed for the attendee, not the event. Keep it simple: email, country, zip code, and any other minimal fields you genuinely need from the person.

Form processing steps (critical setup):

  1. Update Contacts only on these three fields:
    • Email address
    • Country
    • Zip code
  2. Write all other fields to the Training Attendance CDO only.
    This includes:
    • Salesforce Campaign ID (child campaign)
    • Campaign Name
    • Event date / time
    • City
    • HCP leader
    • Brand / business unit
    • Salesperson email
    • Source = QR

⚠️ Important data architecture rule

Your Training Attendance CDO must NOT have a unique identifier. This is intentional. Every form submit should create a new record, because one person can attend many different events over time.

This separation of responsibilities is what keeps your system clean:

  • Contacts = who the person is.
  • Training Attendance CDO = what events they attended.

Step 3: Build one reusable Eloqua landing page

Demo UI: clicking Submit hides the form and shows the thank you message on the same page. No redirect.

eloqua-blind-submit.js
secure session

Replace ELOQUA_FORM_POST_URL with your real Eloqua form post URL. This swaps the form for the thank you state on any 2xx response.

<!-- Minimal HTML (form left) expects these IDs: gs-attendance-form, gs-form-wrap, gs-thanks -->

<script>
(function () {
  // TODO: Replace with your Eloqua blind form post endpoint (exact URL from Eloqua form HTML)
  // Example shape:
  // https://s1234.t.eloqua.com/e/f2?elqFormName=Training_Attendance&elqSiteId=1234
  var ELOQUA_FORM_POST_URL = "https://YOUR_ELOQUA_DOMAIN/e/f2";

  var form = document.getElementById("gs-attendance-form");
  var wrap = document.getElementById("gs-form-wrap");
  var thanks = document.getElementById("gs-thanks");
  var error = document.getElementById("gs-form-error");

  function showError(msg) {
    if (!error) return;
    error.textContent = msg;
    error.classList.remove("hidden");
  }

  function clearError() {
    if (!error) return;
    error.textContent = "";
    error.classList.add("hidden");
  }

  function serialize(obj) {
    var parts = [];
    for (var key in obj) {
      if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
      parts.push(encodeURIComponent(key) + "=" + encodeURIComponent(obj[key]));
    }
    return parts.join("&");
  }

  if (!form) return;

  form.addEventListener("submit", function (e) {
    e.preventDefault();
    clearError();

    var email = (document.getElementById("gs-email") || {}).value || "";
    var country = (document.getElementById("gs-country") || {}).value || "";
    var zipCode = (document.getElementById("gs-zip") || {}).value || "";

    email = email.trim();
    country = country.trim();
    zipCode = zipCode.trim();

    if (!email || !country || !zipCode) {
      showError("Please complete email, country and zip code.");
      return;
    }

    // IMPORTANT ARCHITECTURE RULE:
    // - Contacts: only update Email Address, Country, Zip Code via form processing steps
    // - Attendance details: write to Training Attendance CDO via processing steps
    // - Do NOT add hidden fields here (avoid future complications)
    // - The CDO has NO unique identifier so each submit creates a new record
    var payload = {
      emailAddress: email,
      country: country,
      zipCode: zipCode
    };

    var xhr = new XMLHttpRequest();
    xhr.open("POST", ELOQUA_FORM_POST_URL, true);
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

    xhr.onreadystatechange = function () {
      if (xhr.readyState !== 4) return;

      if (xhr.status >= 200 && xhr.status < 300) {
        if (wrap) wrap.classList.add("hidden");
        if (thanks) thanks.classList.remove("hidden");
        form.reset();
      } else {
        showError("Something went wrong. Please try again.");
      }
    };

    xhr.send(serialize(payload));
  });
})();
</script>
Success criteria: attendee stays on this page, form hides, thank-you appears, and Eloqua processing steps handle Contact + CDO writes.

Step 4: Create event identity in Salesforce using a salesperson-first Campaign hierarchy

This step defines what an “event” actually is in Salesforce. Get this right and everything downstream becomes simple.

Start by defining how an event exists in Salesforce. The cleanest approach for this use case is a Campaign hierarchy where:

  • Parent campaign represents the salesperson, for example: Greg’s Training Events 2026
  • Child campaign represents a real event session, for example: Paris – 10 February 2026 or Berlin – 17 March 2026

Why this structure works

It makes reporting natural. Leadership can roll up results by salesperson, city, brand, or time period, while still being able to drill down into each individual event.

Recommended fields on the child campaign (the actual event)

  • Campaign Name (make it readable for printouts and certificates)
  • Start Date / Time
  • City
  • HCP Leader
  • Business Unit / Brand
  • Owner Email (salesperson email)
  • Attendance QR Code URL (a custom field we will populate)

Once every event session is a child campaign, we have solved the hardest part: event identity. Now we just need a low-friction way to associate attendees to that child campaign.

https://acme.lightning.force.com/lightning/r/Campaign/701xx00000ABC123/view
A
Acme Inc
C
Campaign Child Campaign (Event Session)

Paris – 10 February 2026

Details Registration enabled
Parent Campaign
Acme Training Events 2026
Start Date / Time
10 Feb 2026 • 14:00
City
Paris
Owner Email
events@acme.inc
Details Related Activity

Campaign Information

Event Session Fields
Campaign Name
Paris – 10 February 2026
Start Date / Time
10 Feb 2026 • 14:00
City
Paris
Event Lead
Maria Santos
Business Unit
Acme Digital Services
Owner Email (Sales)
events@acme.inc

Step 5: Build a QR Code Generator wired to Salesforce (one-time dev step)

This is a one-time engineering task. The goal is to create a small internal tool that Salesforce can call to generate a QR image from a Campaign URL, then write that image URL back to the child Campaign.

What the devs need to build

Create a lightweight QR generator page that Salesforce can call with a pre-built event URL, for example:

https://your-qr-tool.example/generate?
  url=https://greg-staunton.com/event-registration?cid=701xx00000ABC123...
        

At a high level, the tool should:

  • Accept a URL parameter from Salesforce
  • Generate a QR code from that URL
  • Return a downloadable PNG
  • (Later) allow Salesforce to store that PNG URL on the Child Campaign

This is the “basic version”

For now, just make this work in a browser. In a later step, you can automate it with a Salesforce Flow that passes the Campaign URL into this tool.

Live QR Generator

Paste any URL and generate a QR code in real time.

qr-generator.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>QR Generator</title>
  <link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
</head>
<body style="background:#0a0a0a;color:#f5f5f5;font-family:'DM Sans',sans-serif">

<!-- UI omitted here for brevity in the panel: see working version on the right -->

<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>
<script>
const urlInput = document.getElementById('url-input');
const generateBtn = document.getElementById('generate-btn');
const qrResult = document.getElementById('qr-result');
const qrCanvas = document.getElementById('qr-canvas');
const downloadBtn = document.getElementById('download-btn');
let currentQR = null;

generateBtn.addEventListener('click', () => {
  const url = urlInput.value.trim();
  if (!url) { alert('Please enter a URL'); return; }

  if (currentQR) qrCanvas.innerHTML = '';

  currentQR = new QRCode(qrCanvas, {
    text: url,
    width: 256,
    height: 256,
    colorDark: '#000000',
    colorLight: '#ffffff',
    correctLevel: QRCode.CorrectLevel.H
  });

  qrResult.classList.remove('hidden');
});

downloadBtn.addEventListener('click', () => {
  const canvas = qrCanvas.querySelector('canvas');
  if (canvas) {
    const link = document.createElement('a');
    link.download = 'qr-code.png';
    link.href = canvas.toDataURL();
    link.click();
  }
});
</script>
</body>
</html>

Step 6: Pass event context to the registration page when the user clicks “Generate QR Code”

What this step does (and what it does not do)

  • This step builds a single URL to https://greg-staunton.com/event-registration with event details appended as querystring parameters.
  • Salesforce does not submit a form. It simply reads field values and concatenates them into a URL.
  • In this demo, clicking Generate QR Code opens a lightbox so you can see the final URL and verify the parameters.
READ ME

Before you click:

  • You will see the exact URL that will be used to create the QR code.
  • Check the parameters in that URL.
https://acme.lightning.force.com/lightning/r/Campaign/701xx00000ABC123/view
A
Acme Inc
C
Campaign Child Campaign (Event Session)

Paris – 10 February 2026

Details Registration enabled
Parent Campaign
Acme Training Events 2026
Start Date / Time
10 Feb 2026 • 14:00
City
Paris
Owner Email
events@acme.inc
Details Related Activity

Campaign Information

Event Session Fields (Editable demo)

Update any fields below, then click Generate QR Code to see the URL that would be sent to the registration page.

Give this to your Salesforce developer. The code below shows the exact logic your “Generate QR Code” button should run in a Quick Action, LWC, or Screen Flow. It simply reads Campaign field values, concatenates them into a properly encoded URL for https://greg-staunton.com/event-registration, and then opens or passes that URL to your QR generator. No form submission is required.

Step 6 — Button wiring (SFDC developer example)
// Demo-only: "Generate QR Code" button builds a URL from field values
// Base landing page:
const BASE_URL = "https://greg-staunton.com/event-registration";

function buildRegistrationUrl(fields) {
  const params = new URLSearchParams({
    cid: fields.campaignId,
    campaignName: fields.campaignName,
    eventDate: fields.eventDate,
    city: fields.city,
    eventLead: fields.eventLead,
    businessUnit: fields.businessUnit,
    salespersonEmail: fields.salespersonEmail,
    source: fields.source
  });

  return `${BASE_URL}?${params.toString()}`;
}

// In a real Salesforce implementation you would:
// - Read Campaign fields (record data)
// - Run this in an LWC / Quick Action / Screen Flow component
// - Either open the URL or hand it to the QR generator service

// This page demo reads values from inputs:
const fields = {
  campaignId: document.getElementById("sfdc-field-cid").value.trim(),
  campaignName: document.getElementById("sfdc-field-name").value.trim(),
  eventDate: document.getElementById("sfdc-field-date").value.trim(),
  city: document.getElementById("sfdc-field-city").value.trim(),
  eventLead: document.getElementById("sfdc-field-lead").value.trim(),
  businessUnit: document.getElementById("sfdc-field-bu").value.trim(),
  salespersonEmail: document.getElementById("sfdc-field-email").value.trim(),
  source: document.getElementById("sfdc-field-source").value.trim()
};

const url = buildRegistrationUrl(fields);

// Then either:
// window.open(url, "_blank");  // open registration page
// OR show in a modal/lightbox (as this demo does)

Step 7: Generate the QR code on the fly from the Salesforce Campaign

Here is where the magic happens in Salesforce

Clicking Generate QR Code reads the Campaign fields, concatenates them into one registration URL for https://greg-staunton.com/event-registration, and renders a scannable QR code in a lightbox. Use your phone to scan it and verify the event context is being passed correctly.

INTERACTIVE

Do this now:

  • Edit the fields
  • Click Generate QR Code
  • Scan with your phone
https://acme.lightning.force.com/lightning/r/Campaign/701xx00000ABC123/view
A
Acme Inc
C
Campaign • Child Campaign (Event Session)

Paris – 10 February 2026

Details Registration enabled
Parent Campaign
Acme Training Events 2026
Start Date / Time
10 Feb 2026 • 14:00
City
Paris
Owner Email
events@acme.inc
Details Related Activity

Campaign Information

Edit
Event Session Fields (Editable demo)

Change any values below, then click Generate QR Code. Scan the QR with your phone to validate the link.

Give this to your Salesforce developer. The logic is simple: read Campaign field values, build one URL using querystring parameters, then render a QR code for that URL in a modal. No form submission is required. In Salesforce, this is typically implemented as a Quick Action (LWC) or a Screen Flow that hosts an LWC.

Step 7 — QR generation wiring (SFDC developer example)
// Base landing page:
const BASE_URL = "https://greg-staunton.com/event-registration";

// Build URL from Campaign fields:
const params = new URLSearchParams({
  cid: Campaign.Id,
  campaignName: Campaign.Name,
  eventDate: Campaign.EventDateTime__c,
  city: Campaign.City__c,
  eventLead: Campaign.EventLead__c,
  businessUnit: Campaign.BusinessUnit__c,
  salespersonEmail: Campaign.Owner.Email,
  source: "QR"
});

const url = `${BASE_URL}?${params.toString()}`;

// Render a QR code in a modal (LWC or Flow screen component)
new QRCode(qrTargetEl, {
  text: url,
  width: 256,
  height: 256,
  correctLevel: QRCode.CorrectLevel.H
});

Step 8: Hook the Salesforce Campaign parameters into an Eloqua landing page

What happens when the attendee arrives on the Eloqua page

The QR code opens your registration page with the event context in the URL querystring. This step reads those parameters (for example cid, campaignName, eventDate, etc) and writes them into hidden inputs on the Eloqua form so the submit creates a clean, event-specific attendance record.

This is client-agnostic. The only requirement is that the landing page has hidden fields that match the parameter names.

Example parameters coming from Salesforce

cid=701xx00000ABC123&campaignName=Paris+-+10+February+2026&eventDate=2026-02-10T14%3A00%3A00Z&city=Paris&eventLead=Maria+Santos&businessUnit=Acme+Digital+Services&salespersonEmail=events%40acme.inc&source=QR
Eloqua LP — parameter → hidden field mapper
<!--
Step 8: Add this script to your Eloqua Landing Page.
It reads URL parameters and sets matching hidden inputs on the form.

How it maps:
  ?cid=...                 -> input[name="cid"] (or #cid)
  &campaignName=...        -> input[name="campaignName"]
  &eventDate=...           -> input[name="eventDate"]
  &city=...                -> input[name="city"]
  &eventLead=...           -> input[name="eventLead"]
  &businessUnit=...        -> input[name="businessUnit"]
  &salespersonEmail=...    -> input[name="salespersonEmail"]
  &source=...              -> input[name="source"]

Requirements:
- Your Eloqua form must include hidden fields for each value you want to store.
- Give each hidden field either:
    a) name="cid" style attributes, OR
    b) id="cid" style attributes
-->

<script>
(function () {
  // List the parameters we expect (and want to map into hidden fields)
  var KEYS = [
    "cid",
    "campaignName",
    "eventDate",
    "city",
    "eventLead",
    "businessUnit",
    "salespersonEmail",
    "source"
  ];

  function getParams() {
    // URLSearchParams automatically decodes + and %XX sequences
    var params = {};
    var sp = new URLSearchParams(window.location.search || "");
    KEYS.forEach(function (k) {
      var val = sp.get(k);
      if (val !== null && val !== "") params[k] = val;
    });
    return params;
  }

  function setField(key, value) {
    // Try to set by name first (common in Eloqua forms), then by id
    var byName = document.querySelector('input[name="' + key + '"]');
    if (byName) {
      byName.value = value;
      return true;
    }

    var byId = document.getElementById(key);
    if (byId && byId.tagName && byId.tagName.toLowerCase() === "input") {
      byId.value = value;
      return true;
    }

    return false;
  }

  function ensureHiddenFieldExists(key) {
    // Optional: if you want the script to create missing hidden inputs automatically
    // This can be helpful for demos, but for production you should add fields in Eloqua form editor.
    var existing = document.querySelector('input[name="' + key + '"]') || document.getElementById(key);
    if (existing) return;

    var form = document.querySelector("form");
    if (!form) return;

    var input = document.createElement("input");
    input.type = "hidden";
    input.name = key;
    input.id = key;
    form.appendChild(input);
  }

  // Run after DOM is ready (Eloqua forms sometimes load quickly, but this is safest)
  function init() {
    var params = getParams();

    // If you'd rather NOT create fields automatically, comment out this block.
    KEYS.forEach(function (k) { ensureHiddenFieldExists(k); });

    // Set values
    Object.keys(params).forEach(function (k) {
      setField(k, params[k]);
    });

    // Optional: helpful for troubleshooting during build
    // console.log("Event params applied to hidden fields:", params);
  }

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", init);
  } else {
    init();
  }
})();
</script>

Implementation tip: keep the parameter names and the hidden field names identical. That keeps debugging painless and avoids mapping tables later. If a parameter is missing, the script simply leaves that hidden field blank.

Event registration

What the attendee sees

Enter your email, country, and zip code. When you submit, the page shows a thank you message. In production this would blind-submit to Eloqua so the attendee stays on the same page.

What the page does behind the scenes

  • Reads SFDC querystring parameters (cid, campaignName, eventDate, city, etc).
  • Writes them into hidden fields on the form (so every submission is tied to the correct event).
  • Blind-submits the form so the attendee stays on the same page, then shows a thank you message.
event-registration — URL params + blind submit
// 1) Read params from URL and apply to hidden fields on the form
// 2) On submit, do a "blind submit" (send data without leaving the page)
// 3) Swap the form to a thank-you state

Part 8: Wire the Salesforce QR link into an Eloqua landing page

Give this to your Eloqua developer

This landing page is the destination behind the QR code. It does three things:

  1. Reads the Salesforce querystring parameters (cid, campaignName, eventDate, city, etc) from the URL that was encoded into the QR code.
  2. Sets hidden fields on the Eloqua form with those values so the submission is tied to the correct event. Keep the parameter names identical to the hidden field names.
  3. Blind-submits the form (no redirect) and swaps to an on-page thank you message. This keeps the attendee experience clean and mobile friendly.

Parameters expected from Salesforce

cid, campaignName, eventDate, city, eventLead, businessUnit, salespersonEmail, source

What the Eloqua developer must configure

  • Replace ELOQUA_FORM_POST_URL with the real Eloqua form POST endpoint.
  • Ensure the Eloqua form has the matching hidden fields (same names as the URL parameters).
  • Confirm only the attendee fields are visible (email, country, zip), everything else is hidden.
Part 8 — Eloqua Landing Page HTML (drop-in)
<!--
PART 8: Eloqua Landing Page (event-registration)

HOW TO USE:
1) Create a new Eloqua Landing Page.
2) Paste this full block into the HTML.
3) Replace ELOQUA_FORM_POST_URL below with your real Eloqua form POST endpoint.
4) Map the visible fields to Contact:
   - emailAddress
   - country
   - zipCode
5) Map the hidden fields to your Attendance CDO fields (or form processing steps):
   - cid, campaignName, eventDate, city, eventLead, businessUnit, salespersonEmail, source
6) The form blind-submits into a hidden iframe so the attendee stays on-page.
-->

<section class="bg-white py-12 md:py-16">
  <div class="max-w-6xl mx-auto px-6">

    <div class="max-w-md">
      <article class="bg-white rounded-2xl border border-gray-200 p-6 shadow-sm">
        <div class="space-y-4">

          <!-- Logo area -->
          <div class="inline-flex items-center justify-center h-10 px-4 rounded-xl border border-gray-200 bg-white text-gray-900 font-semibold">
            Logo
          </div>

          <!-- Form state -->
          <div id="eloqua-form-wrap" class="space-y-4">

            <form id="eloqua-form"
                  method="post"
                  action="ELOQUA_FORM_POST_URL"
                  target="eloqua_blind_iframe"
                  novalidate
                  class="space-y-4">

              <!-- Visible fields (Contact updates) -->
              <div>
                <label for="emailAddress" class="block text-sm font-semibold text-gray-900">Email address</label>
                <input id="emailAddress" name="emailAddress" type="email" required
                       class="mt-2 w-full rounded-xl border border-gray-300 bg-white px-4 py-3 text-gray-900 outline-none
                              focus:border-[#49e278] focus:ring-4 focus:ring-[#49e278]/20"
                       placeholder="name@company.com" />
              </div>

              <div>
                <label for="country" class="block text-sm font-semibold text-gray-900">Country</label>
                <input id="country" name="country" type="text"
                       class="mt-2 w-full rounded-xl border border-gray-300 bg-white px-4 py-3 text-gray-900 outline-none
                              focus:border-[#49e278] focus:ring-4 focus:ring-[#49e278]/20"
                       placeholder="Portugal" />
              </div>

              <div>
                <label for="zipCode" class="block text-sm font-semibold text-gray-900">Zip code</label>
                <input id="zipCode" name="zipCode" type="text"
                       class="mt-2 w-full rounded-xl border border-gray-300 bg-white px-4 py-3 text-gray-900 outline-none
                              focus:border-[#49e278] focus:ring-4 focus:ring-[#49e278]/20"
                       placeholder="1000-000" />
              </div>

              <!-- Hidden event context fields (populated from URL parameters) -->
              <input type="hidden" id="cid" name="cid" />
              <input type="hidden" id="campaignName" name="campaignName" />
              <input type="hidden" id="eventDate" name="eventDate" />
              <input type="hidden" id="city" name="city" />
              <input type="hidden" id="eventLead" name="eventLead" />
              <input type="hidden" id="businessUnit" name="businessUnit" />
              <input type="hidden" id="salespersonEmail" name="salespersonEmail" />
              <input type="hidden" id="source" name="source" />

              <button type="submit"
                      class="w-full inline-flex items-center justify-center rounded-xl bg-[#49e278] px-5 py-3 font-semibold text-[#242226]
                             hover:brightness-95 focus:outline-none focus:ring-4 focus:ring-[#49e278]/30">
                Submit
              </button>

              <p class="text-xs text-gray-500">
                You will stay on this page. A thank you message will display after submit.
              </p>
            </form>

            <iframe name="eloqua_blind_iframe" id="eloqua_blind_iframe" title="Eloqua blind submit" class="hidden"></iframe>

          </div>

          <!-- Thank you state -->
          <div id="eloqua-thanks" class="hidden rounded-2xl border border-emerald-100 bg-emerald-50 p-5">
            <p class="font-semibold text-gray-900">Thank you, you’re checked in.</p>
            <p class="text-gray-700 mt-2">Your attendance has been recorded.</p>
          </div>

        </div>
      </article>
    </div>

  </div>
</section>

<script>
(function () {
  // ---------------------------
  // 1) URL param -> hidden fields
  // ---------------------------
  var KEYS = [
    "cid",
    "campaignName",
    "eventDate",
    "city",
    "eventLead",
    "businessUnit",
    "salespersonEmail",
    "source"
  ];

  function applyParams() {
    var sp = new URLSearchParams(window.location.search || "");
    KEYS.forEach(function (k) {
      var val = sp.get(k);
      if (val === null) return;

      var el = document.getElementById(k);
      if (el) el.value = val;
    });
  }

  // ---------------------------
  // 2) Blind submit UX (stay on-page)
  // ---------------------------
  function initForm() {
    var form = document.getElementById("eloqua-form");
    var wrap = document.getElementById("eloqua-form-wrap");
    var thanks = document.getElementById("eloqua-thanks");

    if (!form || !wrap || !thanks) return;

    // Apply params before any submit
    applyParams();

    form.addEventListener("submit", function (e) {
      // Keep user on the page regardless of Eloqua redirect settings
      e.preventDefault();

      // Minimal validation: require email
      var email = document.getElementById("emailAddress");
      if (email && !email.value.trim()) {
        email.focus();
        return;
      }

      // Submit into hidden iframe (no navigation)
      try { form.submit(); } catch (err) {}

      // Show thank you state
      wrap.classList.add("hidden");
      thanks.classList.remove("hidden");
    });
  }

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", initForm);
  } else {
    initForm();
  }
})();
</script>

Important: your Salesforce QR code must include the parameters as a properly formatted querystring (with & separators), for example: ?cid=701xx00000ABC123&campaignName=Paris+-+10+February+2026&eventDate=2026-02-10T14%3A00%3A00Z... If a parameter is missing, the script simply leaves that hidden field blank.

Conclusion: one scalable system, not hundreds of forms

At this point, you have replaced “form sprawl” with a clean, repeatable model: a single attendee experience, a single Eloqua landing page, and a single Attendance CDO that can represent any session. Salesforce becomes the source of truth for event identity, Eloqua becomes the source of truth for attendance history, and neither team is stuck maintaining hundreds of nearly identical assets.

The workflow is intentionally simple: a salesperson opens the Campaign, clicks Generate QR Code, and the QR code carries the session context to your registration page. The attendee submits the short form, and the system records the event details into the Attendance CDO.

Try the interactive demo again right at the top.

  1. Update the Campaign fields (event name, date/time, city, leader, business unit, salesperson email).
  2. Click Generate QR Code.
  3. Use your phone to scan the QR code and open the registration page.
  4. Fill out email, country, and zip code.
  5. On submit, view the “CDO record values recorded” to see exactly what would be stored in Eloqua.

Want clean, trustworthy UTM attribution flowing into Eloqua?

I help teams implement bulletproof first-touch and last-touch attribution using GTM, GA4 and Eloqua. Whether your data is unreliable, incomplete or your agency over-engineered it, I can rebuild your tracking so every lead is correctly sourced and every report finally makes sense.