Attribution
Campaign attribution for placements such as the VRCMods tool links. The browser stores the first landing per visitor, and key events are recorded against it for 30 days.
First-landing capture (browser)
wwwroot/js/conversion-events.js runs on every page that loads head.ejs. On the first landing that has utm_source, utm_medium, utm_campaign, utm_content, or an external document.referrer, it writes the crateyard_attr cookie:
- Value: URL-encoded JSON with
utm_source,utm_medium,utm_campaign,utm_content(each up to 100 characters),referrer(origin and path only, query string dropped, up to 200 characters), andlanded_at(ISO time). Path=/; SameSite=Lax; Max-Age=2592000(30 days), plusSecureon HTTPS. The cookie is not HttpOnly so the script can read it.- First touch wins: an existing unexpired cookie is never overwritten. Direct visits with no attribution write nothing.
The same script attaches the cookie fields to every window.crateyardTrack GA4 event as utm_source, utm_medium, utm_campaign, utm_content, and source_site=vrcmods when the source or referrer host is vrcmods.com.
Export beacon
POST /api/attribution/export
Records one export for /stats. The mesh editor calls this after a successful model download or VRChat export, because those exports run in the browser. Pixel-art sprite and animation exports are recorded by their own server endpoints.
Authentication is optional. The endpoint consumes no credits or tokens.
Example request (use a valid CSRF cookie and matching header):
curl -X POST https://example.com/api/attribution/export \
-H 'Cookie: crateyard_csrf=example-token' \
-H 'X-CSRF-Token: example-token'Requires the CSRF token (X-CSRF-Token header matching the crateyard_csrf cookie). The request has no body.
Response: 200 {"ok": true}. Returns 403 for an invalid CSRF token and 429 with Retry-After when rate limited (60 requests per minute per user and IP).
Visitors without a valid crateyard_attr cookie are not recorded. Nothing is stored for them.
Recorded events
Server-side events are stored in attribution_events only when the request carries a valid first-landing cookie:
signup: password, email-code, and Google signup.tool_run: asset creator text-to-image, image-to-3D (from upload or existing file), and image-to-3D preview tasks.export: pixel-art sprite and animation exports, and the mesh editor beacon above.checkout: a manual top-up was initiated, retained to attribute the eventual grant.purchase: successful pay-as-you-go token grants. Stripe webhooks carry no cookie, so a purchase inherits the most recent signup, tool, export or checkout attribution whose landing is still within 30 days.
/stats (admin) shows the last 30 days grouped by utm_content.
GA4 setup
Register these as event-scoped custom dimensions in GA Admin so they appear in reports: source_site, utm_source, utm_medium, utm_campaign, utm_content. Mark token_purchase as a key event alongside sign_up and begin_checkout.