UTM and Discount Configuration Guide

1. Purpose

When an event URL is shared — on a poster, by a vendor, in a Facebook post, in a newsletter, or in a club WhatsApp group — the entries it produces should be traceable back to that specific placement. This guide explains how to construct those URLs, what conventions to follow, and how to attach a discount code to a partner link.

The mechanism is provided by the event payment plugin (event-payment-plugin-woocommerce), which captures attribution parameters and a discount code when the visitor lands on the WordPress site, carries them across the registration-portal round trip, and writes them onto the resulting WooCommerce order.

Covered in this guide:

  • The parameters the plugin supports and what each one is for

  • The naming convention to apply across all channels

  • Per-channel recipes with worked examples

  • Configuring the WooCommerce coupon behind a discount link

  • The constraints that will silently cost you attribution if ignored

  • How to read the captured data back out

This guide is descriptive — it explains the mechanism and the conventions. For configuring the event and its products in the first place, see Configure an Event. For onboarding a new organisation onto the platform, see Provision a Registration Portal Tenant.

2. Audience & Prerequisites

This guide is for the person publishing event links — marketing, event organiser, or club administrator. You need:

  • The event’s public WordPress landing page URL

  • WooCommerce admin access on the tenant site, if you intend to create discount codes

  • Event payment plugin 2.1.0 or later installed on the tenant site — attribution capture does not exist in earlier versions

To confirm the plugin version is new enough, request any page on the site with a test parameter and check that a cookie comes back:

curl -sI "https://<tenant-site>/?utm_source=test&utm_campaign=test" | grep -i set-cookie

An epa_utm cookie in the response means capture is active. No cookie means the plugin is too old, or the request did not reach the WordPress front end.

3. How Attribution Flows

Understanding the path explains every constraint later in this guide.

  1. Capture. The visitor opens a tagged link to any front-end page on the tenant WordPress site. On WordPress init, the plugin reads the utm_* and discount parameters out of the query string and writes them into two cookies — epa_discount_code and epa_utm — scoped to that host, for 72 hours.

  2. Round trip. The visitor completes entry in the registration portal, which runs on a different host. The cookies are not visible there, and they do not need to be — they simply wait.

  3. Order creation. admin-service creates the WooCommerce order and returns the visitor to the pay-for-order page, which is back on the original WordPress host, so the cookies are in scope again.

  4. Application. On the pay page the plugin writes the UTM values onto the order as metadata, and applies the carried discount code to the order using WooCommerce’s own coupon engine. The discounted total renders before the visitor pays.

The consequence worth internalising: attribution is written at the pay page, from a cookie set on the landing page. Anything that breaks the link between those two points loses the attribution.

4. Supported Parameters

Parameter Purpose

utm_source

Who or where the visitor came from — the specific partner, placement, venue or platform. This is the field that varies most.

utm_medium

The channel type — how the link was delivered. Drawn from a small fixed vocabulary.

utm_campaign

The event and year. One value per event, never varies within an event.

utm_content

The specific creative or variant, when one source produces several links (poster A vs poster B, first send vs reminder).

utm_term

Historically a paid-search keyword. Captured and stored, but there is no established use for it in this system. Leave it off unless you have a specific need.

discount (or coupon)

A WooCommerce coupon code to apply automatically. Both spellings are accepted; if both appear, discount wins.

No other parameters are captured. Platform click identifiers such as gclid or fbclid pass through harmlessly but are not stored.

5. Naming Convention

Consistency matters more than cleverness. Fix utm_campaign and utm_medium to closed vocabularies, and let utm_source carry the variation.

Format rules, applied everywhere:

  • Lowercase only

  • Hyphens between words, never spaces or underscores

  • No personal names, e-mail addresses or any other personal information — this data lands on an order record

  • Never abbreviate inconsistently: pick wcsc or wc-schools, and use it forever

5.1. Campaign

One value per event, formed as the event slug plus the year:

utm_campaign=tdw2026          # Tour de Worcester 2026
utm_campaign=rooibos2026      # Rooibos MTB 2026

Decide this once when the event opens and put it in the event’s configuration notes. Every link for that event uses it unchanged, forever. This is what makes "how did the 2026 event perform overall" answerable.

5.2. Medium

A closed vocabulary. Adding new values erodes the ability to compare channels, so extend it deliberately, not casually.

Value Use for

qr

A QR code scanned off printed material

print

A typed or short URL printed on material, where the reader keys it in

social

Organic social media posts

email

Newsletters and mailing-list sends

whatsapp

Club and community WhatsApp groups

referral

A partner, vendor or club website linking to the event

Keep qr and print distinct. If both share one value you cannot tell whether people scan posters or type the URL, which is exactly the question that decides next season’s poster design.

5.3. Source

The specific origin. This is the only field with an open vocabulary, and it should be as specific as you can usefully make it — spar-worcester, not supermarket.

Maintain a list of the sources issued per event, so that the same partner is spelled the same way in every campaign and you can reconcile the totals afterwards.

5.4. Content

Optional. Use it when one source produces more than one link, to tell the variants apart:

utm_content=a3-poster         # versus a1-poster
utm_content=send-1            # versus send-2 (the reminder)
utm_content=entries-open-post # versus final-call-post

6. Channel Recipes

All examples land on the event’s public entry page. Substitute your own host, page and campaign.

6.1. Poster QR Codes

Issue a separate source per venue, so that you learn which locations work:

https://tourdeworcester.co.za/entries?utm_source=spar-worcester&utm_medium=qr&utm_campaign=tdw2026&utm_content=a3-poster

Do not encode that full string into the QR code. A long URL produces a dense QR grid that scans poorly on a poster — at distance, at an angle, in direct sun. Instead:

  1. Create a short redirect slug on the WordPress site, for example /go/spar-worcester.

  2. Point it at the fully tagged URL with a 302 redirect.

  3. Encode only the short slug in the QR code.

This also means a printed poster can be retargeted later — change the redirect, not the print run.

6.2. Vendors and Partners

Pair the attribution with a discount code, so the partner has something to offer:

https://tourdeworcester.co.za/entries?utm_source=cyclelab-worcester&utm_medium=referral&utm_campaign=tdw2026&discount=cyclelab10

Give each partner their own coupon code, even when the discount value is identical across partners. The coupon is recorded on the order itself, which makes it a far more durable attribution signal than the cookie — see Treat the coupon as the hard signal.

6.3. Social Media

utm_source is the platform; utm_content is the individual post:

https://tourdeworcester.co.za/entries?utm_source=facebook&utm_medium=social&utm_campaign=tdw2026&utm_content=entries-open-post

This lets you see which post worked without exploding the source list into dozens of one-off values.

6.4. Newsletters

https://tourdeworcester.co.za/entries?utm_source=waitlist&utm_medium=email&utm_campaign=tdw2026&utm_content=send-1

Here utm_source names the audience segmentwaitlist, past-entrants, wpca-members — because for a mailing list the segment is the meaningful distinction, not the platform. Use utm_content to separate the sends within one campaign.

Most mailing platforms can append UTM parameters automatically per send. Configure it there rather than hand-editing every link, so you never ship a half-tagged link — see Tagging is last-touch, and overwrites wholesale for why partial tagging is actively harmful.

6.5. Club WhatsApp Groups

https://tourdeworcester.co.za/entries?utm_source=wcsc&utm_medium=whatsapp&utm_campaign=tdw2026

WhatsApp links get forwarded well beyond the group they were posted in. Read a WhatsApp source as "reach seeded via this club", not "members of this club". If you need a clean count for a club — for a rebate or a challenge, say — issue that club a discount code and count redemptions instead.

7. Configuring the Discount Coupon

The discount parameter carries a code; the code must already exist as a WooCommerce coupon on the tenant site, or the plugin silently skips it and the visitor pays full price.

To create one: WooCommerce → Marketing → Coupons → Add coupon.

The settings that matter for event entries:

Setting Guidance

Coupon code

Lowercase. It appears in a URL that will be shared widely, so treat it as public. Do not use a code you would be unhappy to see posted in an open forum.

Discount type / amount

Percentage or fixed. Percentage travels better across events with different entry fees.

Expiry date

WooCommerce expiry is exclusive. A coupon with expiry 2026-08-31 stops working at 00:00 on 31 August, so the last usable day is 30 August. Set it one day later than the last day you want it usable.

Product categories

Restrict to the entries category. Without this the discount also applies to CSA day licences, which are a pass-through cost and should not be discounted.

Exclude products

Belt-and-braces alongside the category restriction — explicitly exclude the CSA day licence product.

Usage limit per coupon

Leave at 0 (unlimited) for a broad campaign; set a cap for a limited offer.

Usage limit per user

Normally 1. Enforced by billing e-mail for guest checkout.

Limit usage to X items

Read this one carefully. Setting it to 1 discounts a single entry line. On an order where one person enters three riders, only one entry is discounted. Leave it blank if the offer is meant to cover every rider on the order.

Individual use only

Tick it unless you intend codes to stack.

7.1. Worked Example

The Tour de Worcester waitlist campaign uses a coupon configured as:

Setting Value

Code

waitlist

Type

Percentage, 10%

Expires

2026-08-31 (last usable day 30 August)

Product categories

Entries

Excluded products

CSA Day License

Usage limit per coupon

Unlimited

Usage limit per user

1

Limit usage to X items

1

Individual use only

Yes

Paired with this link:

https://tourdeworcester.co.za/?utm_source=waitlist&utm_medium=email&utm_campaign=tdw2026&utm_content=send-1&discount=waitlist

Note the effect of Limit usage to X items = 1: a parent entering two children on one order receives 10% off one entry only. That may well be intended — it is called out here because it is the setting most often configured by accident.

8. Constraints

These are the ways attribution is lost. Each one is a property of the mechanism described in How Attribution Flows, not a bug.

Capture runs on WordPress. A tagged link pointing straight at the registration portal captures nothing at all — no cookie is ever set, and the entry arrives unattributed. Always link to a page on the tenant’s public WordPress site and let the normal entry flow carry the visitor onward.

Any front-end page works, including the home page, so a link to / is functional. Linking to the event’s entry page is still better, simply because it saves the visitor a click.

8.2. The host must not change

The cookies are host-only. If the poster reads www.example.co.za and the pay page runs on example.co.za, the cookie set on the first host is never sent to the second, and the attribution and the discount are both lost.

Pick one canonical host, and verify that the other redirects to it. If a redirect exists it must happen before WordPress processes the request — a WordPress-level canonical redirect fires after capture has already written the cookie to the wrong host.

8.3. Tagging is last-touch, and overwrites wholesale

Each visit carrying any utm_* parameter replaces the entire stored set. A link carrying only utm_source therefore wipes the campaign and medium recorded by an earlier click.

Always emit the same field set on every link for a campaign. This is the main argument for configuring UTM parameters centrally in the mailing platform rather than assembling them by hand.

A visit carrying no utm_* parameters leaves the existing values untouched, so ordinary browsing between the click and the entry is harmless.

8.4. The window is 72 hours

The cookies live for 72 hours from the click. A visitor who clicks the newsletter on Monday, thinks about it, and enters the following weekend arrives with neither attribution nor discount.

For a campaign where a longer consideration window is expected, the lifetime is adjustable through the epa_discount_cookie_ttl filter — a development change, not a setting in the WordPress admin. Raise it with the development team before the campaign, not after.

8.5. Attribution is written at the pay page

An entry that never reaches the WooCommerce pay-for-order page never gets its metadata written. Free entries and any flow that bypasses payment are not attributed.

8.6. In-app browsers break the chain

A QR code scanned from within Facebook, Instagram or WhatsApp opens in that app’s embedded browser. If the visitor later reopens the link in Safari or Chrome to finish entering, the cookies do not travel with them.

Nothing in the URL can prevent this. It is the strongest practical reason to attach a discount code to any link that genuinely matters.

8.7. Treat the coupon as the hard signal

UTM values are a soft signal — they depend on a cookie surviving a multi-host round trip, a 72-hour window, and the visitor staying in one browser.

The coupon code is a hard signal. It is applied to the order itself and survives cookie loss, device switches and in-app browsers. When a number has to be defensible — a partner rebate, a revenue share, a per-club count — count coupon redemptions and use the UTM data only as supporting colour.

9. Reading the Data Back

The captured values are written onto the order as metadata:

Meta key Contents

_utm_source, _utm_medium, _utm_campaign, _utm_term, _utm_content

The captured attribution values

_epa_utm_captured

Timestamp at which attribution was stored

_epa_carried_discount_processed

Timestamp of the single auto-apply attempt for this order

These keys are underscore-prefixed, which means WooCommerce hides them from the order screen’s Custom Fields panel. There is currently no admin UI and no export column for attribution data. Reporting requires a direct database query, as below. If regular campaign reporting is required, that gap needs a work item.

On a site with High-Performance Order Storage enabled, attribution lives in wp_wc_orders_meta:

SELECT
    MAX(CASE WHEN m.meta_key = '_utm_campaign' THEN m.meta_value END) AS campaign,
    MAX(CASE WHEN m.meta_key = '_utm_medium'   THEN m.meta_value END) AS medium,
    MAX(CASE WHEN m.meta_key = '_utm_source'   THEN m.meta_value END) AS source,
    COUNT(DISTINCT o.id)  AS orders,
    SUM(o.total_amount)   AS revenue
FROM wp_wc_orders o
JOIN wp_wc_orders_meta m ON m.order_id = o.id
WHERE o.status = 'wc-completed'
GROUP BY o.id
ORDER BY campaign, medium, source;

Coupon redemptions — the hard signal — are counted from the coupon’s own usage count under Marketing → Coupons, or per order from the order’s applied coupon lines.

10. Pre-Publication Checklist

Before a link goes onto a poster, into a send, or out to a partner:

  1. utm_campaign matches the value already in use for this event.

  2. utm_medium is drawn from the fixed vocabulary in Medium.

  3. utm_source is specific, and recorded on the per-event source list.

  4. All links in the campaign carry the same set of fields — see Tagging is last-touch, and overwrites wholesale.

  5. The link lands on a page of the tenant WordPress site, on the canonical host.

  6. If a discount code is present, the coupon exists, is published, has not expired, and its category restrictions and item limits are as intended.

  7. The link has been opened once in a private browser window and the discount confirmed to appear on the pay page.

That last step is the one that catches everything else. Do it once per campaign.