# MoreminiMore tracking

Tracking is enabled in production builds and omitted from the Astro development server so local previews do not pollute production data. Production starts with every non-essential Google Consent Mode signal denied. No analytics, advertising, or recorder script is requested until ConsentOS emits the verified `consentos:consent-change` event with the corresponding accepted category.

## Installed tools

- ConsentOS — the loader follows a non-networking denied-default bootstrap and runs before any tracking tag. Its `analytics` category gates GA4, Umami, and the recorder; `marketing` gates Meta Pixel.
- Google Analytics 4 — measurement ID `G-74BHREDLC3`.
- Meta Pixel — pixel ID `418349260078648`, including PageView event IDs.
- Umami Analytics — website ID `b2e87a6c-0b64-43c8-bb09-e406ffca0af1`.
- Umami Recorder — 15% sample rate, strict masking, five-minute maximum sessions. `.contact-form` is excluded through both the recorder block selector and rrweb's `rr-block` class.

If a visitor withdraws analytics or marketing consent after a tag was loaded, the page reloads after ConsentOS stores the new choice. The reloaded page stays tag-free for the withdrawn category. The Meta noscript fallback is intentionally omitted because it cannot be safely consent-gated without JavaScript.

## Events

| Event | Trigger | Properties |
| --- | --- | --- |
| `page_view` / `PageView` | Each production page load | Automatic page data |
| `contact_clicked` / `Contact` | Phone, email, or LINE link | `method`, `location` |
| `service_selected` / `ViewContent` | Service landing-page link | `service_path`, `location` |
| `form_started` | First focus inside a contact form | `form_location` |
| `form_submitted` / `Lead` | Contact API confirms a successful submission | `form_location`, shared random event ID for Meta deduplication |

No names, phone numbers, email addresses, form values, or other personally identifiable information are included in these client-side events.

## Contact-form tracking

Each page uses the same short contact form. `form_started` is emitted once on the first focus. The client creates a UUID event ID for each validated submission and sends a boolean snapshot of the already-applied ConsentOS state. The same ID is retained across network or ambiguous-response retries; it rotates only after confirmed success or an actual edit to the form contents. `form_submitted` and the Meta standard `Lead` browser event are emitted only after `/api/contact` returns success. The browser event contains only the page or service location and reuses the same event ID as Meta CAPI for deduplication.

The contact endpoint sends email through Amazon SES first. Only after SES succeeds:

- With marketing consent and `META_ACCESS_TOKEN`, Meta CAPI receives `Lead`, the shared event ID, server IP/user agent, validated `_fbp`/`_fbc`, and normalized name/contact values hashed with SHA-256. Thai phone numbers are normalized to country code `66` before hashing. Raw form values are not sent to Meta.
- With analytics consent, `GA4_API_SECRET`, and a safely retrieved GA client ID, Google Measurement Protocol receives `form_submitted`, session/context identifiers, explicit advertising consent state, and no PII, hashes, or user properties. The browser helper skips its GA copy to avoid a duplicate event; Umami still receives the PII-free event.

Provider requests have short timeouts and are handled with `Promise.allSettled`, so missing tokens, upstream errors, or timeouts never change a successful form response. There is no public conversion relay route. Event IDs are UUID-v4 validated and retained in a bounded 15-minute pending/completed registry. Concurrent pending requests receive a processing response; a retry of a completed delivery receives success without sending a second email, while failed SES attempts release the ID for retry. Delivery and provider logs contain status only and never include form values, credentials, tokens, or upstream response bodies.
