Back to the blog

Persisting UTM Parameters on Checkout Resume Records

Persisting captured UTM parameters on checkout resume records removes dependence on the original tab’s sessionStorage. A matching database write verifies persistence, while resume retrieval and end-to-end conversion delivery remain unverified.

Introduction

I built a path to persist captured UTM parameters from browser sessionStorage on a checkout resume record. Before this change, getUtmParams() captured the parameters on page entry, but there was no backend persistence tying them to that record. The change adds that connection through createCheckoutResume() and the checkout_resumes.utm_params database column.

The important boundary is access to the original page session, not how much time has passed. sessionStorage survives navigation and reloads within the same tab. An asynchronous checkout, bookmarked link, or later visit does not necessarily lose it. A separate tab or device, though, normally cannot access the original tab’s storage.

I verified that a test object sent through createCheckoutResume() was stored correctly in checkout_resumes.utm_params. That confirms database persistence: the saved values no longer depend on the original tab’s storage. I have not yet verified retrieval during resume or delivery into a downstream conversion event, so this result does not establish complete attribution continuity.

The Rule That Shipped

The shipped rule has three parts: capture UTM parameters into sessionStorage through getUtmParams() on first page load; send them through createCheckoutResume() to the API and persist them in checkout_resumes.utm_params; and map the five standard UTM parameters, source through term, into Everflow’s sub1 through sub5 tracking parameters.

Those five fields are utm_source, utm_medium, utm_campaign, utm_content, and utm_term. The downstream mapping follows LifeScale’s existing convention. The evidence confirms that these five fields map into five tracking slots, but it does not identify which UTM field goes into each subN slot. I cannot infer those assignments from the field names or their order in a list.

Capturing values on first page load does not establish a never-overwrite policy, either. The available record does not say whether a later tagged URL replaces existing values, whether partial sets are merged, or whether an existing capture takes precedence. That’s why I describe this as a capture-and-persist implementation, not a proven first-touch attribution policy.

UTM values can now be stored on the checkout resume record, and the downstream mapping is implemented. Neither fact, on its own, demonstrates that every resumed checkout retrieves and forwards the saved values.

Worked Example

The documented case uses a test object containing UTM parameter values. I ran it through createCheckoutResume() to test the persistence path, then checked checkout_resumes.utm_params. The stored parameters matched the test object as intended.

The input, operation, and result are concrete even though the literal values are not recorded: a UTM test object was submitted through createCheckoutResume(), and matching UTM data was stored in the database column associated with the checkout resume record.

Because the object’s contents are unspecified, I cannot show a campaign name, claim that all five fields were populated, or say the test covered missing fields or empty values. The test also does not establish that the object came from an actual tagged landing page. It validates persistence, not browser capture, retrieval during resume, or a recorded Everflow conversion carrying the expected sub1–sub5 values.

Challenges and Tradeoffs

This approach separates temporary browser capture from persistence on a record. sessionStorage holds the captured values before createCheckoutResume() sends them to the API. The database column then stores them without requiring continued access to the original page session. That addresses a storage boundary, not the full cross-device resume flow.

Keeping the values only in sessionStorage would leave later processing dependent on access to that session. Capturing directly into backend storage is another possible design, but its write frequency would depend on when records were created, how requests were deduplicated, and what triggered persistence. It would not necessarily require a write on every landing-page visit. In the implemented path, persistence happens with checkout-resume creation; I do not have measurements showing lower cost or better performance.

Reusing the existing sub1–sub5 convention gives the implementation a shared mapping pattern, but numbered slots are less self-describing than named UTM fields. Debugging still requires an explicit field-to-slot reference. The evidence supports alignment with the existing convention, not claims of reduced engineering effort or better reporting outcomes.

Failure handling is another open question. The record does not establish what happens when browser storage is unavailable or the persistence request fails. Those conditions need verification; I cannot assume a retry or fallback.

Verification Process

My confirmed check compared the submitted UTM test object with the value stored in checkout_resumes.utm_params after createCheckoutResume() ran. The values matched. That directly verifies the storage step, but it is not a complete acceptance test for the attribution path.

A fuller check would test the remaining boundaries separately. Browser checks should first establish what getUtmParams() captures on entry and what happens when values already exist. Repeat tagged visits, partial parameter sets, and cleared sessionStorage would help distinguish replacement, merge, and retention behavior. The current evidence does not establish any of those policies.

A resume check should then show that the saved values are read from the checkout resume record when the original page session is unavailable. Database persistence makes that possible, but successful retrieval has not been demonstrated. A failure check should also establish how an unsuccessful API request is surfaced and whether a later attempt preserves the intended values. Neither behavior is confirmed here.

Finally, downstream verification needs the actual field-to-slot assignments and a trace through to a recorded conversion. The evidence does not establish that a UTM-tagged checkout resume has reached a live Everflow conversion with the expected sub1–sub5 values. Until that trace exists, I keep the implemented mapping separate from proof that its values arrive end to end.

Conclusion

I added backend persistence between browser UTM capture and downstream tracking. The verified result is that createCheckoutResume() correctly stores submitted UTM data in checkout_resumes.utm_params. The implementation also maps the five standard UTM fields into Everflow’s sub1–sub5 slots using the existing convention.

The limits remain specific: the test object’s literal contents are unspecified, the exact field-to-slot assignments are not detailed, overwrite and merge behavior are unestablished, and neither retrieval during resume nor a live conversion trace is verified. The change provides storage tied to the checkout resume record. It does not yet prove uninterrupted attribution from landing through conversion.