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

# Automatic properties

> These tables list every property that the Grain Analytics SDK adds to an event.

This page lists the properties that the Grain Analytics SDK collects on its own, and the
properties that you collect. The tables come from the Analytics Web SDK reference. Every event
has a `Collected` column with one of three values.

| Value          | Meaning                                                           |
| -------------- | ----------------------------------------------------------------- |
| `always`       | The SDK adds the property to every event, with or without consent |
| `with consent` | The SDK adds the property only after the user grants consent      |
| `by you`       | You pass the property in the `track` call or the template method  |

The TypeScript types of the published package win over this page when they disagree.

## Standard events

### `page_view`

Sent on every page navigation.

| Property                        | Type   | Collected      | Description                                                                     |
| ------------------------------- | ------ | -------------- | ------------------------------------------------------------------------------- |
| `page`                          | string | `always`       | Current page path, with or without query parameters based on `stripQueryParams` |
| `title`                         | string | `with consent` | Document title                                                                  |
| `full_url`                      | string | `with consent` | Complete URL, including query parameters and hash                               |
| `referrer`                      | string | `with consent` | Previous page URL, from `document.referrer`                                     |
| `referrer_domain`               | string | `with consent` | Domain of the referrer                                                          |
| `referrer_category`             | string | `with consent` | One of `organic`, `paid`, `social`, `direct`, `email`, `referral`               |
| `previous_page`                 | string | `with consent` | Previous page in the same session                                               |
| `landing_page`                  | string | `with consent` | First page of the session, only on page views after the first                   |
| `session_id`                    | string | `with consent` | Current session identifier                                                      |
| `utm_source`                    | string | `with consent` | UTM source parameter, if present                                                |
| `utm_medium`                    | string | `with consent` | UTM medium parameter, if present                                                |
| `utm_campaign`                  | string | `with consent` | UTM campaign parameter, if present                                              |
| `utm_term`                      | string | `with consent` | UTM term parameter, if present                                                  |
| `utm_content`                   | string | `with consent` | UTM content parameter, if present                                               |
| `first_touch_source`            | string | `with consent` | First-touch attribution source                                                  |
| `first_touch_medium`            | string | `with consent` | First-touch attribution medium                                                  |
| `first_touch_campaign`          | string | `with consent` | First-touch attribution campaign                                                |
| `first_touch_referrer_category` | string | `with consent` | First-touch referrer category                                                   |
| `device`                        | string | `always`       | `Mobile`, `Tablet`, or `Desktop`                                                |
| `browser`                       | string | `always`       | `Chrome`, `Firefox`, `Safari`, `Edge`, `Opera`, or `Unknown`                    |
| `os`                            | string | `always`       | `Windows`, `macOS`, `Linux`, `Android`, `iOS`, or `Unknown`                     |
| `language`                      | string | `always`       | Browser language, from `navigator.language`                                     |
| `timezone`                      | string | `always`       | User time zone, for example `America/New_York`                                  |
| `screen_resolution`             | string | `always`       | Screen resolution, for example `1920x1080`                                      |
| `viewport`                      | string | `always`       | Viewport size, for example `1440x900`                                           |
| `timestamp`                     | number | `always`       | Milliseconds since the epoch                                                    |

### `_grain_session_start`

Sent when a session begins, on page load.

| Property                        | Type   | Collected      | Description                      |
| ------------------------------- | ------ | -------------- | -------------------------------- |
| `session_id`                    | string | `always`       | Unique session identifier        |
| `landing_page`                  | string | `with consent` | First page of the session        |
| `referrer`                      | string | `with consent` | Referrer URL, if present         |
| `referrer_domain`               | string | `with consent` | Referrer domain, if present      |
| `referrer_category`             | string | `with consent` | Referrer category                |
| `utm_source`                    | string | `with consent` | UTM source, if present           |
| `utm_medium`                    | string | `with consent` | UTM medium, if present           |
| `utm_campaign`                  | string | `with consent` | UTM campaign, if present         |
| `utm_term`                      | string | `with consent` | UTM term, if present             |
| `utm_content`                   | string | `with consent` | UTM content, if present          |
| `first_touch_source`            | string | `with consent` | First-touch attribution source   |
| `first_touch_medium`            | string | `with consent` | First-touch attribution medium   |
| `first_touch_campaign`          | string | `with consent` | First-touch attribution campaign |
| `first_touch_referrer_category` | string | `with consent` | First-touch referrer category    |
| `screen_resolution`             | string | `with consent` | Screen resolution                |
| `viewport`                      | string | `with consent` | Viewport size                    |
| `device`                        | string | `always`       | `Mobile`, `Tablet`, or `Desktop` |
| `browser`                       | string | `always`       | Browser name                     |
| `os`                            | string | `always`       | Operating system                 |
| `language`                      | string | `with consent` | Browser language                 |
| `timezone`                      | string | `with consent` | User time zone                   |
| `timestamp`                     | number | `always`       | Session start time               |

### `_grain_session_end`

Sent when a session ends, on page unload or close.

| Property      | Type   | Collected      | Description                       |
| ------------- | ------ | -------------- | --------------------------------- |
| `session_id`  | string | `always`       | Session identifier                |
| `duration`    | number | `always`       | Session duration in milliseconds  |
| `event_count` | number | `always`       | Events tracked during the session |
| `page_count`  | number | `with consent` | Pages viewed during the session   |
| `timestamp`   | number | `always`       | Session end time                  |

### `_grain_heartbeat`

Sent at a fixed interval to measure engagement. Read
[Automatic tracking](/core/automatic-tracking) for the intervals.

| Property         | Type   | Collected      | Description                                            |
| ---------------- | ------ | -------------- | ------------------------------------------------------ |
| `heartbeat_type` | string | `always`       | `periodic` or `page_load`                              |
| `status`         | string | `always`       | `active` or `inactive`                                 |
| `page`           | string | `with consent` | Current page path                                      |
| `session_id`     | string | `with consent` | Current session identifier                             |
| `duration`       | number | `with consent` | Milliseconds since the last heartbeat, `periodic` only |
| `event_count`    | number | `with consent` | Events since the last heartbeat, `periodic` only       |
| `timestamp`      | number | `always`       | Heartbeat time                                         |

### `_grain_consent_granted`

Sent when the user grants analytics consent.

| Property              | Type   | Collected | Description                      |
| --------------------- | ------ | --------- | -------------------------------- |
| `previous_session_id` | string | `always`  | Session ID from before consent   |
| `new_user_id`         | string | `always`  | Persistent user ID after consent |
| `timestamp`           | number | `always`  | Consent grant time               |

## Template events

The template methods send these events. You call the method and pass the properties. Read
[Template events](/core/template-events).

### `login`

```typescript Analytics Web theme={null}
await grain.trackLogin({
  method: 'email',
  success: true,
  errorMessage: 'Invalid credentials',
  loginAttempt: 1,
  rememberMe: true,
  twoFactorEnabled: false
});
```

| Property           | Type    | Collected | Description                                            |
| ------------------ | ------- | --------- | ------------------------------------------------------ |
| `method`           | string  | `by you`  | Authentication method, for example `email` or `google` |
| `success`          | boolean | `by you`  | Whether the login succeeded                            |
| `errorMessage`     | string  | `by you`  | Error message when `success` is `false`                |
| `loginAttempt`     | number  | `by you`  | Login attempt number                                   |
| `rememberMe`       | boolean | `by you`  | Whether "remember me" was checked                      |
| `twoFactorEnabled` | boolean | `by you`  | Whether two-factor authentication is on                |

### `signup`

```typescript Analytics Web theme={null}
await grain.trackSignup({
  method: 'email',
  source: 'landing_page',
  plan: 'free',
  success: true
});
```

| Property       | Type    | Collected | Description                             |
| -------------- | ------- | --------- | --------------------------------------- |
| `method`       | string  | `by you`  | Registration method                     |
| `source`       | string  | `by you`  | Where the user signed up from           |
| `plan`         | string  | `by you`  | Selected plan                           |
| `success`      | boolean | `by you`  | Whether the signup succeeded            |
| `errorMessage` | string  | `by you`  | Error message when `success` is `false` |

### `purchase`

```typescript Analytics Web theme={null}
await grain.trackPurchase({
  orderId: 'ORD-12345',
  total: 99.99,
  currency: 'USD',
  items: [
    { id: 'ITEM-1', name: 'Product A', price: 49.99, quantity: 2, category: 'electronics' }
  ],
  paymentMethod: 'credit_card',
  shippingMethod: 'express',
  tax: 8.50,
  shipping: 5.99,
  discount: 10.00,
  couponCode: 'SAVE10'
});
```

| Property         | Type   | Collected | Description                               |
| ---------------- | ------ | --------- | ----------------------------------------- |
| `orderId`        | string | `by you`  | Unique order identifier                   |
| `total`          | number | `by you`  | Total order amount                        |
| `currency`       | string | `by you`  | Currency code, for example `USD` or `EUR` |
| `items`          | array  | `by you`  | Purchased items                           |
| `paymentMethod`  | string | `by you`  | Payment method                            |
| `shippingMethod` | string | `by you`  | Shipping method                           |
| `tax`            | number | `by you`  | Tax amount                                |
| `shipping`       | number | `by you`  | Shipping cost                             |
| `discount`       | number | `by you`  | Discount amount                           |
| `couponCode`     | string | `by you`  | Applied coupon code                       |

### `checkout`

Sent by `trackCheckout` when the checkout starts. The properties are the same as `purchase`,
plus `success`.

```typescript Analytics Web theme={null}
await grain.trackCheckout({
  orderId: 'ORD-12345',
  total: 99.99,
  currency: 'USD',
  items: [],
  paymentMethod: 'credit_card',
  success: true
});
```

### `search`

```typescript Analytics Web theme={null}
await grain.trackSearch({
  query: 'wireless headphones',
  results: 42,
  filters: { category: 'electronics', price: 'under-50' },
  sortBy: 'popularity',
  category: 'products',
  success: true
});
```

| Property   | Type    | Collected | Description                  |
| ---------- | ------- | --------- | ---------------------------- |
| `query`    | string  | `by you`  | Search query text            |
| `results`  | number  | `by you`  | Number of results            |
| `filters`  | object  | `by you`  | Applied filters              |
| `sortBy`   | string  | `by you`  | Sort order                   |
| `category` | string  | `by you`  | Search category              |
| `success`  | boolean | `by you`  | Whether the search succeeded |

### `add_to_cart`

```typescript Analytics Web theme={null}
await grain.trackAddToCart({
  itemId: 'PROD-123',
  itemName: 'Wireless Headphones',
  price: 49.99,
  quantity: 1,
  currency: 'USD',
  category: 'electronics',
  variant: 'black'
});
```

| Property   | Type   | Collected | Description                                    |
| ---------- | ------ | --------- | ---------------------------------------------- |
| `itemId`   | string | `by you`  | Product ID                                     |
| `itemName` | string | `by you`  | Product name                                   |
| `price`    | number | `by you`  | Unit price                                     |
| `quantity` | number | `by you`  | Quantity added                                 |
| `currency` | string | `by you`  | Currency code                                  |
| `category` | string | `by you`  | Product category                               |
| `variant`  | string | `by you`  | Product variant, for example a color or a size |

### `remove_from_cart`

Sent by `trackRemoveFromCart`. The properties are the same as `add_to_cart`.

## Custom events

A custom event carries the properties that you pass. Read [Name events](/core/event-naming) for
the naming convention.

```typescript Analytics Web theme={null}
await grain.track('button_clicked', {
  button_id: 'signup-cta',
  button_text: 'Get Started',
  page: '/pricing',
  position: 'hero'
});
```

The SDK adds properties to every custom event. System events that start with `_grain_` are the
exception.

| Property                                                                                            | Collected      | Source                  |
| --------------------------------------------------------------------------------------------------- | -------------- | ----------------------- |
| `device`, `browser`, `os`                                                                           | `always`       | Device detection        |
| `utm_source`, `utm_medium`, `utm_campaign`, `utm_term`, `utm_content`                               | `with consent` | The current session     |
| `first_touch_source`, `first_touch_medium`, `first_touch_campaign`, `first_touch_referrer_category` | `with consent` | First-touch attribution |
| `session_id`                                                                                        | `with consent` | The current session     |

As a result, every event has device context and attribution data for matrices and conversion
tracking. A call of `grain.track('signup', { plan: 'pro' })` sends:

```json theme={null}
{
  "plan": "pro",
  "device": "Desktop",
  "browser": "Chrome",
  "os": "macOS",
  "utm_source": "google",
  "utm_medium": "cpc",
  "utm_campaign": "spring_sale",
  "first_touch_source": "google",
  "first_touch_medium": "cpc",
  "first_touch_campaign": "spring_sale",
  "first_touch_referrer_category": "paid",
  "session_id": "..."
}
```

Properties that a custom event often needs:

| Group          | Properties                                   |
| -------------- | -------------------------------------------- |
| Context        | `page`, `section`, `component`               |
| Action details | `action_type`, `target`, `value`             |
| User state     | `is_authenticated`, `user_plan`, `user_role` |
| Experiment     | `experiment_id`, `variant`                   |
| Performance    | `duration`, `load_time`                      |

## Property scope

An event-scoped property exists on the event that carries it. Most properties are event-scoped.

A user-scoped property exists on any event of the user. In a matrix, the filter "User Property
(Any Event)" finds users who logged the property at least one time, on any event. These
properties are user-scoped:

| Property                      | Set on                 | Queried on                             |
| ----------------------------- | ---------------------- | -------------------------------------- |
| `first_touch_*`               | `_grain_session_start` | Any event                              |
| `utm_*`                       | `page_view`            | Any event, including conversion events |
| Properties from `setProperty` | The `setProperty` call | Any event                              |

Read [User properties](/core/user-properties).

## Referrer categories

| Category   | Meaning                                                                   |
| ---------- | ------------------------------------------------------------------------- |
| `organic`  | A search engine: Google, Bing, Yahoo, and others                          |
| `paid`     | Paid advertising, detected from `gclid`, `fbclid`, and similar parameters |
| `social`   | A social network: Facebook, Twitter, LinkedIn, and others                 |
| `email`    | An email client: Gmail, Outlook, and others                               |
| `referral` | Any other website                                                         |
| `direct`   | No referrer: a typed URL or a bookmark                                    |

## Browser and OS values

| Property  | Values                                                    |
| --------- | --------------------------------------------------------- |
| `browser` | `Chrome`, `Firefox`, `Safari`, `Edge`, `Opera`, `Unknown` |
| `os`      | `Windows`, `macOS`, `Linux`, `Android`, `iOS`, `Unknown`  |

## Configuration

Three options of `GrainConfig` change what the SDK collects.

| Option                        | Effect                                                        |
| ----------------------------- | ------------------------------------------------------------- |
| `disableAutoProperties: true` | The SDK adds no automatic properties                          |
| `stripQueryParams: true`      | The `page` property has no query string. This is the default. |
| `allowedProperties: [...]`    | Only the listed property names are sent                       |

```typescript Analytics Web theme={null}
import { createGrainAnalytics } from '@grainql/analytics-web';

const grain = createGrainAnalytics({
  tenantId: 'your-tenant-id',
  disableAutoProperties: false,
  stripQueryParams: true,
  allowedProperties: ['page', 'button_id', 'action']
});
```

## Rules for properties

1. Do not send `null` or `undefined` values. Leave the property out.
2. Use `snake_case` for property names.
3. Add `page`, `section`, and `component` to UI events.
4. Use the same event names across a user journey, so that a track can join them.
5. Test attribution with a URL that holds UTM parameters and a referrer.
6. Collect only the data that a report needs, and obey consent.
