API Docs

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), and landed_at (ISO time).
  • Path=/; SameSite=Lax; Max-Age=2592000 (30 days), plus Secure on 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.