> ## Documentation Index
> Fetch the complete documentation index at: https://docs.scanova.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Event Model Reference

> Complete field reference for browser and server conversion events — what to send, what each field does, and how events are stored and enriched.

Every event you send to Scanova Conversion Tracking — whether from the browser or your server — follows a common structure. This page documents every field you can include and explains what the tracking pipeline does with it.

## Browser events (`POST /ct`)

Browser events are sent automatically by the SDK or manually via `scanova('track', ...)`. They represent actions that happen in the user's browser.

### Fields you can send

| Field             | Type            | Required | Description                                                                                                                                                                                         |
| ----------------- | --------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `event_id`        | UUID string     | No       | Unique identifier for this event. Auto-generated by the SDK if omitted. Send a stable ID from your code if you want safe retries.                                                                   |
| `site_id`         | string          | Yes      | Your tracking site ID from the dashboard.                                                                                                                                                           |
| `event_type`      | string          | Yes      | The type of event. Use snake\_case (e.g. `page_view`, `cta_click`, `signup_completed`).                                                                                                             |
| `scan_session_id` | UUID string     | No       | Set automatically by the SDK from the `scnv` URL parameter. Required for QR attribution.                                                                                                            |
| `web_session_id`  | UUID string     | No       | Set automatically by the SDK per browser session (30-minute timeout).                                                                                                                               |
| `visitor_id`      | UUID string     | No       | Set automatically by the SDK. Persists for 1 year via cookie.                                                                                                                                       |
| `page_url`        | string          | No       | Full URL of the current page including query string.                                                                                                                                                |
| `page_title`      | string          | No       | Page title. Not sent by the Browser SDK; only useful for custom direct API integrations.                                                                                                            |
| `referrer`        | string          | No       | The referring URL.                                                                                                                                                                                  |
| `event_time`      | ISO 8601 string | No       | Event time in UTC. Defaults to server receive time if omitted. Note: the Browser SDK sends a `timestamp` key which is a separate, internal field — when calling the API directly, use `event_time`. |
| `device`          | object          | No       | Browser/device context — see below.                                                                                                                                                                 |
| `metadata`        | object          | No       | Custom key-value data. Max 10 KB, max depth 5. No raw email addresses.                                                                                                                              |

**`device` object fields:**

| Field           | Description                     |
| --------------- | ------------------------------- |
| `user_agent`    | Browser user-agent string       |
| `screen_width`  | Screen width in pixels          |
| `screen_height` | Screen height in pixels         |
| `language`      | Browser language (e.g. `en-US`) |

### Example browser event payload

```json theme={null}
{
  "event_id": "f9ac7db6-f900-4d8e-8918-c846834195a8",
  "site_id": "N74rwgykxCwUe7SDFb2BMVN8Kc0aCQvKajiUKz9MSk3Bldn70jw8uLE3dUTgeS6r",
  "event_type": "cta_click",
  "scan_session_id": "7ad26d4f-3181-4ef8-b6ca-b8f59499dd43",
  "page_url": "https://yoursite.com/?scnv=7ad26d4f-3181-4ef8-b6ca-b8f59499dd43",
  "referrer": "",
  "timestamp": "2026-05-13T10:00:00.000Z",
  "device": {
    "user_agent": "Mozilla/5.0 ...",
    "screen_width": 1680,
    "screen_height": 1050,
    "language": "en-US"
  },
  "metadata": {
    "button_text": "Start Free Trial",
    "section": "pricing"
  }
}
```

***

## Server events (`POST /server-events`)

Server events are sent from your backend using an API key. They represent actions that happen on your server, such as completed purchases, confirmed sign-ups, or leads created in your CRM.

### Fields you can send

| Field              | Type            | Required    | Description                                                                                    |
| ------------------ | --------------- | ----------- | ---------------------------------------------------------------------------------------------- |
| `event_id`         | UUID string     | Recommended | Unique event ID for safe retries. Generate once, persist, reuse on retry.                      |
| `site_id`          | string          | Yes         | Your tracking site ID. Must match the API key's authorized site.                               |
| `event_name`       | string          | Yes         | Semantic event name (e.g. `purchase`, `signup`, `lead`).                                       |
| `scan_session_id`  | UUID string     | Yes         | Links this server event to the originating QR scan. Pass from browser to your server.          |
| `web_session_id`   | UUID string     | No          | The browser's web session ID — pass from the browser if available, for richer session linking. |
| `visitor_id`       | UUID string     | No          | The browser's persistent visitor ID — pass from the browser if available.                      |
| `event_time`       | ISO 8601 string | No          | When the event occurred. Defaults to time of receipt.                                          |
| `conversion_value` | object          | No          | `{ "amount": 99.99, "currency": "USD" }`. Currency must be a 3-letter ISO 4217 code.           |
| `user_identifiers` | object          | No          | Hashed user identifiers — see below. Never send raw email or phone.                            |
| `properties`       | object          | No          | Custom key-value metadata. Max 10 KB. No raw emails.                                           |
| `consent`          | string          | No          | `granted`, `denied`, or `pending`.                                                             |

**`user_identifiers` object fields (all hashed):**

| Field         | Description                                          |
| ------------- | ---------------------------------------------------- |
| `email_hash`  | SHA-256 hash of the user's lowercase email address   |
| `phone_hash`  | SHA-256 hash of the user's E.164-format phone number |
| `external_id` | Your internal user/customer ID                       |

### Example server event payload

```json theme={null}
{
  "event_id": "550e8400-e29b-41d4-a716-446655440000",
  "site_id": "N74rwgykxCwUe7SDFb2BMVN8Kc0aCQvKajiUKz9MSk3Bldn70jw8uLE3dUTgeS6r",
  "event_name": "purchase",
  "scan_session_id": "7ad26d4f-3181-4ef8-b6ca-b8f59499dd43",
  "conversion_value": { "amount": 49.99, "currency": "USD" },
  "user_identifiers": {
    "email_hash": "b94d27b9934d3e08a52e52d7da7dabfac484efe04294e576fc583c381f2d9c5d",
    "external_id": "usr_1234"
  },
  "properties": {
    "order_id": "ord_9876",
    "plan": "pro"
  }
}
```

***

## Event naming conventions

Use consistent, descriptive names in `snake_case`. A clear naming scheme makes reports easier to read.

| Good               | Avoid                        |
| ------------------ | ---------------------------- |
| `page_view`        | `pageView`, `PageView`, `pv` |
| `cta_click`        | `click1`, `btnClick`         |
| `signup_completed` | `signup`, `reg`              |
| `purchase`         | `buy`, `order_placed_final`  |

## Idempotency

Both browser and server events support an `event_id` field for deduplication:

* If the same `event_id` arrives more than once, the duplicate is stored but marked `is_duplicate = 1`
* Duplicates are excluded from analytics views automatically
* **Always reuse the same `event_id` when retrying a failed server event**

## Privacy and GDPR

* **Never send raw email addresses** in `metadata` or `properties`. Use `user_identifiers` with hashed values.
* The `consent` field controls PII handling in the pipeline:
  * `granted` — all fields are stored normally
  * `denied` or `pending` — `page_url`, `referrer`, `city`, and `metadata` are stripped before storage
  * The `consent` value itself is always stored as an audit trail

### Sending consent from the browser

The Browser SDK does not have a built-in consent API. The recommended approach is to **conditionally load the SDK** based on your consent management platform's decision:

```html theme={null}
<script>
  // Only load the SDK after the user grants consent
  if (userHasGrantedConsent()) {
    (function(w,d,s,o,f,js,fjs){
      w['ScanovaTrackingObject']=o;w[o]=w[o]||function(){(w[o].q=w[o].q||[]).push(arguments)};
      js=d.createElement(s),fjs=d.getElementsByTagName(s)[0];
      js.id=o;js.src=f;js.async=1;fjs.parentNode.insertBefore(js,fjs);
    })(window,document,'script','scanova','https://cdn.scanova.io/ct/js/qcg.min.js');
    scanova('init', 'YOUR_SITE_ID', { autoPageview: true });
  }
</script>
```

### Sending consent from the server

For server events, pass the `consent` value directly in every request payload alongside your other fields.
