Overview

Many churches spend money on Facebook, Google Ads, email campaigns, and other marketing efforts but lose the original referral source after someone submits a form. This recipe demonstrates how to implement first-touch marketing attribution in Rock RMS.

The solution captures UTM parameters when a visitor first arrives at your website, preserves them in cookies, automatically populates hidden fields on Rock forms, and writes the original attribution to the person's record through a reusable workflow.

This implementation has been tested on Rock RMS 17.7 and has been used across multiple form types including Workflow Entry forms, Connection Requests, and Prayer Requests.

This was developed and tested at Prairie Lakes Church and is shared with the Rock Community in the hope that it serves as a reusable foundation for first-touch marketing attribution.

What You'll End Up With

After completing this recipe your Rock instance will:

  • Capture utm_source
  • Capture utm_medium
  • Capture utm_campaign
  • Record the first touch date (first time they clicked on a link)
  • Record the first form date (first time they filled out a form)
  • Preserve the visitor's original source
  • Automatically populate hidden workflow fields
  • Write attribution to the Person record
  • Prevent existing attribution from being overwritten
  • Reuse the same workflow for multiple forms

Architecture

Visitor clicks campaign link

        ↓

UTM parameters detected

        ↓

First-touch cookies created

        ↓

Connection Request or Rock Form

        ↓

UTM Attribution Workflow

        ↓

Person record updated

This recipe assumes you're comfortable with:

  • Rock workflows
  • Person Attributes
  • JavaScript
  • Lava
  • Editing website themes
  • Workflow Entry or Form Builder


Step 1 – Create Person Attributes

Create the following Person attributes, all in a new category called UTM for easy access/display:

(Name                  |       Key)

First Source         |      FirstSource

First Medium       |     FirstMedium

First Campaign   |     FirstCampaign

First Touch Date |     FirstTouchDate

First Form Date  |     FirstFormDate

*First Source, Medium, and Campaign are all of type text, while first touch and first form dates are of type date

Screenshot_2026-07-14_141222.png

Screenshot_2026-07-14_141324.png


Step 2 – Install the Global UTM Capture Script

Add the provided JavaScript functions to your website (websites --> *your rock site* --> edit --> advanced settings --> page header content (add inside the <script> tags)) Adding it here allows for cookies to be captured on every page of your website.

This step contains two separate functions:

  1. Capturing and preserving first-touch cookies globally.
  2. Populating Connection Request tracking fields when a compatible Tracking section is present.

The first function supports the whole attribution system. The second function only applies to forms rendered with the expected Connection Request Tracking fieldset.

Ensure you have links with utm parameters to test out. I used the Cardinal Path Google Analytics Campaign Tagging Tool - template Google Sheet to store my test links.

Step 2A – Capture First-Touch UTM Values

The first function:

  • Reads UTM parameters
  • Stores them in cookies
  • Creates a first-touch cookie
  • Does not overwrite an existing first-touch attribution cookie

See Appendix A for this script


Step 2B – Populate Connection Request Tracking Fields

This function is intended for Connection Opportunity Signup blocks that display a fieldset titled Tracking (See section 3). It locates tracking attributes by their displayed labels, fills them from the first-touch cookies, records the current date as the first form date, and hides the Tracking section from the visitor. Rock Forms do not need to use this section. Their workflow actions can read the cookies directly with Lava, as shown later in the recipe.


See Appendix B for this script


Cookie Notice

This implementation stores first-touch marketing attribution in first-party browser cookies for up to 365 days. Before deploying this solution, review your organization's privacy policy and any applicable consent requirements to ensure the use of marketing attribution cookies aligns with your legal and organizational policies.

The cookie lifetime can be adjusted by changing the expiration value in the JavaScript if your organization requires a shorter or longer retention period.


Step 3 – Create Connection Request Tracking Attributes

Name               |      Key                |     Type   |    Category    |

-----------------------------------------------------------------------------------------

UTM Source    |    UTMSource   |    Text     |    UTM Tracking     |

UTM Medium  |    UTMMedium |   Text      |   UTM Tracking     |

UTM Campaign | UTMCampaign | Text     |   UTM Tracking    |

First Touch Date | FirstTouchDate | Text   |   UTM Tracking    |

First Form Date  | FirstFormDate   |  Text  |   UTM Tracking    |

Go to settings --> attribute categories --> plus button (to add new attribute category) 

Name the category UTM Tracking (I set mine to just tracking but the more specific the better!) and set the entity type to connection request.

Screenshot_2026-07-14_142857.png

Next, go to Entity Attributes. Add a new entity attribute and set the entity type to Connection Request. Leave the qualifier field and qualifier value blank so the attributes can be used by every supported Connection Type. If the attributes are qualified to one Connection Type, they will not appear on other Connection Opportunity Signup blocks. Name it according to the attribute you are setting and repeat this for each attribute.

Screenshot_2026-07-14_143814.png

The workflow and entity attributes use field type text because they are populated directly from browser cookies and Lava. The final Person attributes may be either Text or Date depending on your reporting preferences.


Step 4 – Choose an Implementation Path

Option A – Connection Opportunity Signup

This path uses:

Connection Request attributes

        ↓

UTM Attribution Feeder

        ↓

Marketing Attribution Workflow


Make sure you:

  1. Include the Tracking category in the block configuration.
  2. Ensure the tracking attributes can be rendered by the block.
  3. Attach the feeder workflow to the applicable Connection Type (See step 6).
  4. Trigger it when the request starts.
  5. Pass the Connection Request values and person into the central workflow (See step 5).

Go to one of the pages on your site that use the connection opportunity signup block. Edit the block settings for the connection opportunity signup block and ensure that these settings match. The connection opportunity may change depending on which page you are on. 

Screenshot_2026-07-14_144411.png

See sections 5 and 6 for setting up the workflows necessary to take the attributes filled here and write them to a person record.


Option B – Rock Form That Creates or Identifies a Person

This path uses form attributes and actions:

Attributes:

  • Person
  • First Name
  • Last Name
  • Email
  • Phone (optional)
  • UTM Source
  • UTM Medium
  • UTM Campaign
  • First Touch Date
  • First Form Date

Actions

  • Set or create the Person. (Action Type: Person attribute from fields, set corresponding attribute values, person: person)
  • Fill UTM Source (Action Type: Attribute Set Value)
  • Fill UTM Medium (Action Type: Attribute Set Value)
  • Fill UTM Campaign (Action Type: Attribute Set Value)
  • Fill First Touch Date (Action Type: Attribute Set Value)
  • Fill First Form Date (Action Type: Attribute Set Value)
  • Activate the Marketing Attribution workflow. (Action Type: Activate Workflow, select UTM/Marketing Attribution (setup in step 5) as Workflow type, then map the keys of person, the UTM attributes (separately), and the two date attributes under the workflow attribute key section

For the Fill actions, use the corresponding lava in the text value section

{{ 'utm_source' | ReadCookie }}

{{ 'utm_medium' | ReadCookie }}

{{ 'utm_campaign' | ReadCookie }}

{{ 'first_touch_date' | ReadCookie }}

{{ 'Now' | Date:'yyyy-MM-dd' }}

Screenshot_2026-07-14_145323.png

The above image is a showcase of the actions needed, along with some other actions that are a part of the form's actual objective.

Screenshot_2026-07-14_145538.png

The above image shows one of the actions in detail.

Screenshot_2026-07-14_145755.png

The above image shows the activate person writing workflow action and the exact key mappings necessary.


Option C – Rock Form That Creates Another Entity

Useful for forms like prayer request

Add the same form attributes as in option B above

Add actions:

  • Fill First Name From Entity
  • Fill Last Name From Entity
  • Fill Email From Entity
  • Fill Phone Number From Entity

All actions are of type Attribute Set from Entity

ensure entity is required is set to yes, and use Id instead of Guid is set to no. Then, in the Lava Template section, fill in the corresponding lava

{{ Entity.FirstName }}

{{ Entity.LastName }}

{{ Entity.Email }}

{{ Entity.PhoneNumber }}

Then, add the rest of the actions from option B.


Step 5 - Set Up UTM/Marketing Attribution Workflow

This workflow has some of the same attributes as the forms from step 4; Person, UTM Source, Medium, Campaign, First Touch Date, and First Form Date. Unique to this workflow is the Can Update Attribution attribute (text). This attribute is used as a true/false flag to determine if a person record can be updated.

Actions for this form:

  • Determine if person record can be updated (Action Type: Attribute Set Value; Attribute: Can Update Attribution) Use the following lava in the text field below
{%- assign person = Workflow | Attribute:'Person','Object' -%}

{%- assign createdDate = person.CreatedDateTime | Date:'yyyyMMdd' -%}
{%- assign cutoffDate = 'Now' | DateAdd:'-60','d' | Date:'yyyyMMdd' -%}

{%- assign existingSource = person | Attribute:'FirstSource' -%}
{%- assign existingMedium = person | Attribute:'FirstMedium' -%}
{%- assign existingCampaign = person | Attribute:'FirstCampaign' -%}

{%- if person
and createdDate >= cutoffDate
and existingSource == empty
and existingMedium == empty
and existingCampaign == empty
-%}
True
{%- else -%}
False
{%- endif -%}

This Lava verifies that a valid Person object was provided, confirms the Person is within the attribution window, and checks that First Source, First Medium, and First Campaign are still empty. If all conditions are met, it returns True; otherwise, it returns False, preventing existing attribution from being overwritten.

*The cutoff date of 60 days was an organizational decision that can be easily changed depending on your goals.

The rest of the actions use the filter run if Can Update Attribution Equal To (text value) True

  • Add Source to Person Record (Type: Person attribute set, Person: Person, Person Attribute: First Source)
  • Add Medium to Person Record
  • Add Campaign to Person Record
  • Add First Touch Date to Person Record
  • Add First Form Date to Person Record

Ensure each action has the corresponding person attribute selected

Screenshot_2026-07-14_150032.pngScreenshot_2026-07-14_150131.png

Step 6 - Set Up UTM Attribution Feeder Workflow

Used to prepare the UTM data from connection requests into a readable form for the UTM/Marketing Attribution Workflow

The attributes needed are the same as the workflow in step 5, with the exception of Can Update Attribution.

The workflow has the following actions:

  • Set Person (Type: Attribute Set From Entity; Entity required: yes; use Id instead of Guid, no) Use this lava: {{ Entity.PersonAlias.Guid }}

Screenshot_2026-07-14_150222.png

  • Fill UTM Source, Medium, Campaign, First Touch Date, First Form Date (all separate)
  • Activate UTM/Marketing Attribution Workflow (same process as for forms)

Action type for fills: attribute set from entity; entity required: yes; use Id instead of Guid: no

use the following lava (corresponding to the current attribute being filled)

{{ Entity | Attribute:'UTMSource' }}

{{ Entity | Attribute:'UTMMedium' }}

 {{ Entity | Attribute:'UTMCampaign' }}

{{ Entity | Attribute:'FirstTouchDate' | Date:'yyyy-MM-dd' }}

{{ Entity | Attribute:'FirstFormDate' | Date:'yyyy-MM-dd' }}

Attaching This Workflow

Connections --> Connection Types (settings wheel icon) --> Select the connection type to attach the workflow to --> edit --> workflows --> plus button --> launch workflow when: request started Workflow type: UTM Attribution Feeder

Screenshot_2026-07-14_150406.png


Step 7 - Testing

After configuring the form and workflows, verify that each part of the attribution process is working correctly.


Browser Verification

Confirm that the following browser cookies have been created:

  • utm_source
  • utm_medium
  • utm_campaign
  • first_touch_date

These cookies should be present after visiting the site with UTM parameters in the URL.


Form Verification

Submit a test form and confirm that:

  • First Name populates correctly.
  • Last Name populates correctly.
  • Email populates correctly.
  • Phone populates correctly (if applicable).
  • The Person is identified or created correctly.
  • UTM Source populates.
  • UTM Medium populates.
  • UTM Campaign populates.
  • First Touch Date populates.
  • First Form Date populates.


Workflow Verification

Confirm that:

  • The UTM/Marketing Attribution workflow runs successfully.
  • The workflow receives the Person.
  • The workflow receives all UTM values.
  • The workflow receives both date values.


Person Record Verification

Verify that the Person record contains:

  • First Source
  • First Medium
  • First Campaign
  • First Touch Date
  • First Form Date


Regression Test

Submit another form for the same individual.

Verify that:

  • The original first-touch attribution is not overwritten.
  • Existing Person attribution values remain unchanged.


Quality Control Checklist

  • Before considering the implementation complete, verify the following:
  • Required form attributes exist.
  • Optional attributes have been added where needed.
  • Person attributes exist.
  • The global JavaScript is installed.
  • Browser cookies are created successfully.
  • Form actions are configured in the correct order.
  • The Set Person action identifies the correct individual.
  • Entity-based forms populate Person information before the Set Person action executes.
  • UTM fields populate from browser cookies.
  • First Form Date records the current date.
  • The UTM/Marketing Attribution workflow runs successfully.
  • The Person record is updated correctly.
  • Existing first-touch attribution is never overwritten.


Final Result

  • After completing this recipe:
  • A visitor arrives from a campaign.
  • Their original UTM values are preserved.
  • Future forms reuse the same attribution.
  • Existing attribution is never overwritten.
  • Marketing reports always reflect the original source that brought someone to your church.

Below is an excerpt from the extended attributes section of a test person's record in our database showing the UTM data we gathered.

Screenshot_2026-07-14_150521.png


Downloads


Appendix


A (cookie creation script)

(function () {
const params = new URLSearchParams(window.location.search);

const source = params.get('utm_source');
const medium = params.get('utm_medium');
const campaign = params.get('utm_campaign');

function setCookie(name, value, days) {
const expires = new Date(Date.now() + days * 86400000).toUTCString();

document.cookie =
name + '=' + encodeURIComponent(value) +
'; expires=' + expires +
'; path=/' +
'; SameSite=Lax';
}

function getCookie(name) {
return document.cookie
.split('; ')
.find(row => row.startsWith(name + '='));
}

// Only save first touch once
if ((source || medium || campaign) && !getCookie('first_touch')) {
const firstTouch = {
source: source || '',
medium: medium || '',
campaign: campaign || '',
capturedDate: new Date().toISOString()
};

const firstTouchJson = JSON.stringify(firstTouch);

// Add cookie so Rock/Lava can read it
setCookie('first_touch', firstTouchJson, 365);

// Optional individual cookies, easier for Rock workflows
setCookie('utm_source', firstTouch.source, 365);
setCookie('utm_medium', firstTouch.medium, 365);
setCookie('utm_campaign', firstTouch.campaign, 365);
setCookie('first_touch_date', new Date().toISOString().split('T')[0], 365);
}
})();


B (populate UTM tracking fields for connection requests script)

$(function () {

// Find the Tracking fieldset.
// If this page does not have a Tracking section, stop immediately.
const $trackingFieldset = $("fieldset").filter(function () {
return $(this).find("h4").first().text().trim() === "Tracking";
});

if (!$trackingFieldset.length) {
return;
}

function getCookie(name) {
const value = "; " + document.cookie;
const parts = value.split("; " + name + "=");

if (parts.length === 2) {
return decodeURIComponent(parts.pop().split(";").shift());
}

return "";
}

function setFieldByLabel(labelText, value) {
const $field = $trackingFieldset
.find("label")
.filter(function () {
return $(this).text().trim() === labelText;
})
.closest(".form-group")
.find("input, textarea, select");

if ($field.length && value) {
$field.val(value).trigger("change");
}
}

const firstTouchRaw = getCookie("first_touch");
let firstTouch = {};

try {
firstTouch = firstTouchRaw ? JSON.parse(firstTouchRaw) : {};
} catch (e) {
console.warn("Could not parse first_touch cookie", e);
}

const firstTouchDate = (
firstTouch.capturedDate ||
getCookie("first_touch_date") ||
""
).split("T")[0];

const today = new Date().toISOString().split("T")[0];

setFieldByLabel("UTM Source", firstTouch.source || getCookie("utm_source"));
setFieldByLabel("UTM Medium", firstTouch.medium || getCookie("utm_medium"));
setFieldByLabel("UTM Campaign", firstTouch.campaign || getCookie("utm_campaign"));
setFieldByLabel("First Touch Date", firstTouchDate);
setFieldByLabel("First Form Date", today);

// Hide the entire Tracking section after filling it.
$trackingFieldset.hide();

});